From 2a487a416a6fff7e6f873ea985a33a396819ece8 Mon Sep 17 00:00:00 2001 From: Dima Molodenskiy Date: Mon, 10 Aug 2026 17:10:37 +0200 Subject: [PATCH] Release 1.4.2: reproducible image builds Rebuilding a released tag previously resolved whatever dependency stack was current: the base image was an unpinned python:3.11-slim and pyproject declared biopython, numpy, scipy and matplotlib without bounds. Backfilling the 0.3.0-1.4.1 images made that concrete - each one pairs its tagged source with an August 2026 stack rather than the original. The image build now pins the base by digest and installs under docker/constraints.txt, which records the exact resolved versions. Both the test and runtime stages use it, so what the tests validate is what ships. pyproject gains upper bounds only (biopython<2, numpy<3, scipy<2, matplotlib<4). Exact pins would be wrong here: alphajudge is installed alongside other scientific packages, and == constraints in a library's metadata cause resolver conflicts for consumers. Lower bounds are omitted because older versions are untested; the caps guard against the next breaking major, and reproducibility is handled by the constraints file. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 9 +++++++++ docker/Dockerfile | 10 ++++++---- docker/constraints.txt | 17 +++++++++++++++++ pyproject.toml | 10 +++++----- 4 files changed, 37 insertions(+), 9 deletions(-) create mode 100644 docker/constraints.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dcb5fe6..a9e5859e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ ## Unreleased +## 1.4.2 - 2026-08-10 + +### Fixed +- Publishing a release now pushes a versioned Docker image. The CI workflow's `on:` block listed only `push` and `pull_request`, so the release-tagged build step could never run and Docker Hub carried only `:latest`. Images for the earlier 0.3.0 through 1.4.1 releases have been backfilled; those were built in August 2026, so they pair the tagged source with a contemporary dependency stack rather than the original one. + +### Changed +- The Docker image builds against a pinned base image digest and a `docker/constraints.txt` lock file, so rebuilding a released tag resolves the same dependency stack instead of whatever is current at build time. +- Runtime dependencies gain upper bounds (`biopython<2`, `numpy<3`, `scipy<2`, `matplotlib<4`) to guard against future breaking majors. No lower bounds were added, as older versions are untested. + ## 1.4.1 - 2026-08-07 ### Fixed diff --git a/docker/Dockerfile b/docker/Dockerfile index 0a6d222d..c584bf17 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.11-slim AS test +FROM python:3.11-slim@sha256:90744cff8f32887f075c47d747a173ff333e9e98801667af93c357fa9f5e28ff AS test ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 @@ -13,9 +13,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ # Install package and test deps COPY pyproject.toml README.md /app/ +COPY docker/constraints.txt /app/docker/constraints.txt COPY src /app/src RUN pip install --upgrade pip \ - && pip install . \ + && pip install -c docker/constraints.txt . \ && pip install pytest \ && pip cache purge @@ -27,7 +28,7 @@ COPY test_data /app/test_data RUN pytest -q -FROM python:3.11-slim AS runtime +FROM python:3.11-slim@sha256:90744cff8f32887f075c47d747a173ff333e9e98801667af93c357fa9f5e28ff AS runtime ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 @@ -36,9 +37,10 @@ WORKDIR /app # Install only runtime deps and package COPY pyproject.toml README.md /app/ +COPY docker/constraints.txt /app/docker/constraints.txt COPY src /app/src RUN pip install --upgrade pip \ - && pip install . \ + && pip install -c docker/constraints.txt . \ && pip cache purge # Sensible default; override in deployments diff --git a/docker/constraints.txt b/docker/constraints.txt new file mode 100644 index 00000000..93c9af6d --- /dev/null +++ b/docker/constraints.txt @@ -0,0 +1,17 @@ +# Exact dependency versions for the Docker image, so a rebuild of a given tag +# resolves the same stack. Library consumers are unaffected: pyproject.toml +# keeps loose bounds and this file is applied only by the image build. +# Regenerate with: pip list --format=freeze inside the runtime image. +biopython==1.88 +contourpy==1.3.3 +cycler==0.12.1 +fonttools==4.63.0 +kiwisolver==1.5.0 +matplotlib==3.11.1 +numpy==2.4.6 +packaging==26.3 +pillow==12.3.0 +pyparsing==3.3.2 +python-dateutil==2.9.0.post0 +scipy==1.17.1 +six==1.17.0 diff --git a/pyproject.toml b/pyproject.toml index 6d17912c..93e02ce0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,15 +4,15 @@ build-backend = "setuptools.build_meta" [project] name = "alphajudge" -version = "1.4.1" +version = "1.4.2" description = "Evaluate AlphaFold-predicted protein complexes using confidence metrics and interface biophysics." readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.10" dependencies = [ - "biopython", - "numpy", - "scipy", - "matplotlib", + "biopython<2", + "numpy<3", + "scipy<2", + "matplotlib<4", ] license = { text = "MIT" }