From aa992062681938fee1f50e29f58a06a0ac311fe1 Mon Sep 17 00:00:00 2001 From: Rockdu Date: Thu, 30 Jul 2026 12:48:54 -0700 Subject: [PATCH] docker: fail the image build when requirements.txt is not satisfied Co-Authored-By: Claude Fable 5 --- .github/workflows/docker-build.yml | 1 + .github/workflows/pr-test.yml | 2 +- docker/Dockerfile | 9 ++++++++- docker/check_requirements.py | 27 +++++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 docker/check_requirements.py diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 8d051ca0..b863d0e8 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -25,6 +25,7 @@ on: - main paths: - 'docker/Dockerfile' + - 'docker/check_requirements.py' - 'requirements.txt' concurrency: diff --git a/.github/workflows/pr-test.yml b/.github/workflows/pr-test.yml index 6578a042..0e9fccf5 100644 --- a/.github/workflows/pr-test.yml +++ b/.github/workflows/pr-test.yml @@ -68,7 +68,7 @@ jobs: # Compare the PR merge commit with its current base. CHANGED_FILES=$(git diff --name-only HEAD^1 HEAD) # Same paths as docker-build.yml's post-merge filter. - if printf '%s\n' "$CHANGED_FILES" | grep -qE '^(docker/Dockerfile|requirements\.txt)$'; then + if printf '%s\n' "$CHANGED_FILES" | grep -qE '^(docker/Dockerfile|docker/check_requirements\.py|requirements\.txt)$'; then echo "changed=true" >> "$GITHUB_OUTPUT" else echo "changed=false" >> "$GITHUB_OUTPUT" diff --git a/docker/Dockerfile b/docker/Dockerfile index 6cb7b1fe..56f9b8bc 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -79,5 +79,12 @@ RUN git clone https://github.com/radixark/miles_diffusion.git /root/miles_diffus # =========================================== Pre-download =============================================== # Pre-download PaddleOCR (en) det+rec+cls models so the OCR-reward actors don't -# race-download them from bcebos at runtime. +# race-download them from bcebos at runtime. RUN python -c "from paddleocr import PaddleOCR; PaddleOCR(use_angle_cls=False, lang='en', use_gpu=False, show_log=False)" + +# ========================================= Sanity checks ================================================ + +# Fail the build if any requirements.txt entry ended up missing or stomped by a +# later --no-deps install, or if the top-level package no longer imports. +COPY docker/check_requirements.py /tmp/check_requirements.py +RUN python /tmp/check_requirements.py /tmp/requirements.txt && python -c "import miles" diff --git a/docker/check_requirements.py b/docker/check_requirements.py new file mode 100644 index 00000000..4ca7f693 --- /dev/null +++ b/docker/check_requirements.py @@ -0,0 +1,27 @@ +"""Fail the image build if any requirements.txt entry is missing or +version-mismatched (later --no-deps installs can silently stomp them).""" + +import sys +from importlib.metadata import PackageNotFoundError, version + +from packaging.requirements import Requirement + +failures = [] +for raw in open(sys.argv[1]): + line = raw.split("#", 1)[0].strip() + if not line or line.startswith("-"): + continue + req = Requirement(line) + if req.marker is not None and not req.marker.evaluate(): + continue + try: + installed = version(req.name) + except PackageNotFoundError: + failures.append(f"{req.name}: not installed") + continue + if req.specifier and not req.specifier.contains(installed, prereleases=True): + failures.append(f"{req.name}: installed {installed}, required {req.specifier}") + +if failures: + sys.exit("requirements not satisfied in image:\n " + "\n ".join(failures)) +print(f"requirements OK ({sys.argv[1]})")