From 5c02cd6afb864f009b335eb1f580fad66c388a5b Mon Sep 17 00:00:00 2001 From: Josh Sokol Date: Thu, 9 Jul 2026 22:03:52 -0500 Subject: [PATCH 1/2] ci(container-validation): fall back to testing bundle/SQL when prod artifacts absent (pre-GA) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The downloader stage pulled the bundle from public/bundles (prod) and, for the full-stack image, the SQL from database/master (prod). bump_downstream opens the docker update- PR at the TESTING cut, but that version's prod bundle/SQL don't exist until GA (testing -> master) — so container-validation on the PR failed (404) until GA. Try prod first (curl -f), fall back to bundles-test / database/testing so the update- PR validates green pre-GA; at GA the prod artifacts exist and are used. Contract-neutral: same bytes for a given version, only the source URL changes. Edited the generators (source of truth) and regenerated both Dockerfiles. Co-Authored-By: Claude Opus 4.8 (1M context) --- simplerisk-minimal/Dockerfile | 3 ++- simplerisk-minimal/generate_dockerfile.sh | 3 ++- simplerisk/Dockerfile | 6 ++++-- simplerisk/generate_dockerfile.sh | 6 ++++-- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/simplerisk-minimal/Dockerfile b/simplerisk-minimal/Dockerfile index b62dd3c..179d2da 100644 --- a/simplerisk-minimal/Dockerfile +++ b/simplerisk-minimal/Dockerfile @@ -6,7 +6,8 @@ FROM alpine/curl:8.12.1 AS downloader SHELL [ "/bin/ash", "-eo", "pipefail", "-c" ] RUN mkdir -p /var/www && \ - curl -sL https://simplerisk-downloads.s3.amazonaws.com/public/bundles/simplerisk-20260519-001.tgz | tar xz -C /var/www + { curl -fsSL https://simplerisk-downloads.s3.amazonaws.com/public/bundles/simplerisk-20260519-001.tgz \ + || curl -fsSL https://bundles-test.simplerisk.com/simplerisk-20260519-001.tgz; } | tar xz -C /var/www FROM php:${php_version}-apache diff --git a/simplerisk-minimal/generate_dockerfile.sh b/simplerisk-minimal/generate_dockerfile.sh index 433fd1a..2687e81 100755 --- a/simplerisk-minimal/generate_dockerfile.sh +++ b/simplerisk-minimal/generate_dockerfile.sh @@ -26,7 +26,8 @@ FROM alpine/curl:8.12.1 AS downloader SHELL [ "/bin/ash", "-eo", "pipefail", "-c" ] RUN mkdir -p /var/www && \\ - curl -sL https://simplerisk-downloads.s3.amazonaws.com/public/bundles/simplerisk-$release.tgz | tar xz -C /var/www + { curl -fsSL https://simplerisk-downloads.s3.amazonaws.com/public/bundles/simplerisk-$release.tgz \\ + || curl -fsSL https://bundles-test.simplerisk.com/simplerisk-$release.tgz; } | tar xz -C /var/www EOF fi diff --git a/simplerisk/Dockerfile b/simplerisk/Dockerfile index 300327a..f35157d 100644 --- a/simplerisk/Dockerfile +++ b/simplerisk/Dockerfile @@ -8,8 +8,10 @@ ARG DB_LANG=en SHELL [ "/bin/ash", "-eo", "pipefail", "-c" ] RUN mkdir -p /var/www && \ - curl -sL https://simplerisk-downloads.s3.amazonaws.com/public/bundles/simplerisk-20260519-001.tgz | tar xz -C /var/www && \ - curl -sL "https://github.com/simplerisk/database/raw/master/simplerisk-$DB_LANG-20260519-001.sql" > /simplerisk.sql + { curl -fsSL https://simplerisk-downloads.s3.amazonaws.com/public/bundles/simplerisk-20260519-001.tgz \ + || curl -fsSL https://bundles-test.simplerisk.com/simplerisk-20260519-001.tgz; } | tar xz -C /var/www && \ + { curl -fsSL "https://github.com/simplerisk/database/raw/master/simplerisk-$DB_LANG-20260519-001.sql" \ + || curl -fsSL "https://github.com/simplerisk/database/raw/testing/simplerisk-$DB_LANG-20260519-001.sql"; } > /simplerisk.sql # Using Ubuntu image FROM ubuntu:${ubuntu_version_code} diff --git a/simplerisk/generate_dockerfile.sh b/simplerisk/generate_dockerfile.sh index 7716e87..d55a36a 100755 --- a/simplerisk/generate_dockerfile.sh +++ b/simplerisk/generate_dockerfile.sh @@ -26,8 +26,10 @@ ARG DB_LANG=en SHELL [ "/bin/ash", "-eo", "pipefail", "-c" ] RUN mkdir -p /var/www && \\ - curl -sL https://simplerisk-downloads.s3.amazonaws.com/public/bundles/simplerisk-$release.tgz | tar xz -C /var/www && \\ - curl -sL "https://github.com/simplerisk/database/raw/master/simplerisk-\$DB_LANG-$release.sql" > /simplerisk.sql + { curl -fsSL https://simplerisk-downloads.s3.amazonaws.com/public/bundles/simplerisk-$release.tgz \\ + || curl -fsSL https://bundles-test.simplerisk.com/simplerisk-$release.tgz; } | tar xz -C /var/www && \\ + { curl -fsSL "https://github.com/simplerisk/database/raw/master/simplerisk-\$DB_LANG-$release.sql" \\ + || curl -fsSL "https://github.com/simplerisk/database/raw/testing/simplerisk-\$DB_LANG-$release.sql"; } > /simplerisk.sql EOF fi From 4acd8cf7f32dfb4d6a9820748b6283f954e59459 Mon Sep 17 00:00:00 2001 From: Josh Sokol Date: Thu, 9 Jul 2026 22:13:09 -0500 Subject: [PATCH 2/2] harden: gate the pre-GA bundle/SQL fallback behind a CI-only build-arg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Security-review follow-up: the first version baked the bundles-test/database-testing fallback into the Dockerfile unconditionally, so it applied to the GA publish too — a transient prod fetch failure at GA could silently ship testing bytes in a released image (and testing vs prod tarballs for a version aren't guaranteed byte-identical). Gate it behind ARG PREGA_BUNDLE_FALLBACK (default false): try prod, and only fall back to the testing source when the arg is true. container-validation sets it true (so pre-GA update- PRs validate); the GA publish (push-to-dockerhub / push-to-gh-pkgs) leaves it false → prod ONLY, hard-fail if the prod artifact is absent. Released images can never fall back to testing bytes. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/container-validation.yml | 8 ++++---- simplerisk-minimal/Dockerfile | 16 ++++++++++++++-- simplerisk-minimal/generate_dockerfile.sh | 16 ++++++++++++++-- simplerisk/Dockerfile | 21 +++++++++++++++++---- simplerisk/generate_dockerfile.sh | 21 +++++++++++++++++---- 5 files changed, 66 insertions(+), 16 deletions(-) diff --git a/.github/workflows/container-validation.yml b/.github/workflows/container-validation.yml index 6a015ac..730dba5 100644 --- a/.github/workflows/container-validation.yml +++ b/.github/workflows/container-validation.yml @@ -13,7 +13,7 @@ jobs: context_path: "simplerisk/" dockerfile_path: "simplerisk/Dockerfile" image_tag: "simplerisk/simplerisk:testing" - build_args: "ubuntu_version_code=jammy" + build_args: "ubuntu_version_code=jammy\nPREGA_BUNDLE_FALLBACK=true" simplerisk-noble: name: 'Verify simplerisk/simplerisk image based on Ubuntu 24.04 (Noble)' @@ -22,7 +22,7 @@ jobs: context_path: "simplerisk/" dockerfile_path: "simplerisk/Dockerfile" image_tag: "simplerisk/simplerisk:testing" - build_args: "ubuntu_version_code=noble" + build_args: "ubuntu_version_code=noble\nPREGA_BUNDLE_FALLBACK=true" simplerisk-minimal-php84: name: 'Verify simplerisk/simplerisk-minimal image based on PHP 8.3 with Apache' @@ -31,7 +31,7 @@ jobs: context_path: "simplerisk-minimal/" dockerfile_path: "simplerisk-minimal/Dockerfile" image_tag: "simplerisk/simplerisk-minimal:testing" - build_args: "php_version=8.3" + build_args: "php_version=8.3\nPREGA_BUNDLE_FALLBACK=true" simplerisk-minimal-php85: name: 'Verify simplerisk/simplerisk-minimal image based on PHP 8.4 with Apache' @@ -40,4 +40,4 @@ jobs: context_path: "simplerisk-minimal/" dockerfile_path: "simplerisk-minimal/Dockerfile" image_tag: "simplerisk/simplerisk-minimal:testing" - build_args: "php_version=8.4" + build_args: "php_version=8.4\nPREGA_BUNDLE_FALLBACK=true" diff --git a/simplerisk-minimal/Dockerfile b/simplerisk-minimal/Dockerfile index 179d2da..1e0304e 100644 --- a/simplerisk-minimal/Dockerfile +++ b/simplerisk-minimal/Dockerfile @@ -3,11 +3,23 @@ ARG php_version=8.4 FROM alpine/curl:8.12.1 AS downloader +# PREGA_BUNDLE_FALLBACK is a CI-ONLY switch, default false. bump_downstream opens +# the docker update- PR at the testing cut, before that version's PROD +# bundle exists (it lands in public/bundles/ only at GA). container-validation +# sets this true so the PR can validate against the testing bundle. The GA publish +# leaves it FALSE, so a released image is ALWAYS built from the prod bundle and +# NEVER silently falls back to testing bytes (even on a transient prod failure). +ARG PREGA_BUNDLE_FALLBACK=false + SHELL [ "/bin/ash", "-eo", "pipefail", "-c" ] RUN mkdir -p /var/www && \ - { curl -fsSL https://simplerisk-downloads.s3.amazonaws.com/public/bundles/simplerisk-20260519-001.tgz \ - || curl -fsSL https://bundles-test.simplerisk.com/simplerisk-20260519-001.tgz; } | tar xz -C /var/www + if curl -fsSL https://simplerisk-downloads.s3.amazonaws.com/public/bundles/simplerisk-20260519-001.tgz -o /tmp/bundle.tgz; then true; \ + elif [ "$PREGA_BUNDLE_FALLBACK" = "true" ]; then \ + echo "prod bundle absent — pre-GA CI fallback to bundles-test"; \ + curl -fsSL https://bundles-test.simplerisk.com/simplerisk-20260519-001.tgz -o /tmp/bundle.tgz; \ + else echo "prod bundle simplerisk-20260519-001.tgz not found and PREGA_BUNDLE_FALLBACK=false" && exit 1; fi && \ + tar xzf /tmp/bundle.tgz -C /var/www && rm -f /tmp/bundle.tgz FROM php:${php_version}-apache diff --git a/simplerisk-minimal/generate_dockerfile.sh b/simplerisk-minimal/generate_dockerfile.sh index 2687e81..e3f0f7f 100755 --- a/simplerisk-minimal/generate_dockerfile.sh +++ b/simplerisk-minimal/generate_dockerfile.sh @@ -23,11 +23,23 @@ if [ "$release" != "testing" ]; then cat << EOF >> "${SCRIPT_LOCATION}/Dockerfile" FROM alpine/curl:8.12.1 AS downloader +# PREGA_BUNDLE_FALLBACK is a CI-ONLY switch, default false. bump_downstream opens +# the docker update- PR at the testing cut, before that version's PROD +# bundle exists (it lands in public/bundles/ only at GA). container-validation +# sets this true so the PR can validate against the testing bundle. The GA publish +# leaves it FALSE, so a released image is ALWAYS built from the prod bundle and +# NEVER silently falls back to testing bytes (even on a transient prod failure). +ARG PREGA_BUNDLE_FALLBACK=false + SHELL [ "/bin/ash", "-eo", "pipefail", "-c" ] RUN mkdir -p /var/www && \\ - { curl -fsSL https://simplerisk-downloads.s3.amazonaws.com/public/bundles/simplerisk-$release.tgz \\ - || curl -fsSL https://bundles-test.simplerisk.com/simplerisk-$release.tgz; } | tar xz -C /var/www + if curl -fsSL https://simplerisk-downloads.s3.amazonaws.com/public/bundles/simplerisk-$release.tgz -o /tmp/bundle.tgz; then true; \\ + elif [ "\$PREGA_BUNDLE_FALLBACK" = "true" ]; then \\ + echo "prod bundle absent — pre-GA CI fallback to bundles-test"; \\ + curl -fsSL https://bundles-test.simplerisk.com/simplerisk-$release.tgz -o /tmp/bundle.tgz; \\ + else echo "prod bundle simplerisk-$release.tgz not found and PREGA_BUNDLE_FALLBACK=false" && exit 1; fi && \\ + tar xzf /tmp/bundle.tgz -C /var/www && rm -f /tmp/bundle.tgz EOF fi diff --git a/simplerisk/Dockerfile b/simplerisk/Dockerfile index f35157d..6a962f2 100644 --- a/simplerisk/Dockerfile +++ b/simplerisk/Dockerfile @@ -4,14 +4,27 @@ ARG ubuntu_version_code=noble FROM alpine/curl:8.12.1 AS downloader ARG DB_LANG=en +# PREGA_BUNDLE_FALLBACK is a CI-ONLY switch, default false. bump_downstream opens +# the docker update- PR at the testing cut, before that version's PROD +# bundle + SQL exist (they land in public/bundles/ and database/master only at GA). +# container-validation sets this true so the PR can validate against the testing +# sources. The GA publish leaves it FALSE, so a released image is ALWAYS built from +# the prod bundle/SQL and NEVER silently falls back to testing bytes. +ARG PREGA_BUNDLE_FALLBACK=false SHELL [ "/bin/ash", "-eo", "pipefail", "-c" ] RUN mkdir -p /var/www && \ - { curl -fsSL https://simplerisk-downloads.s3.amazonaws.com/public/bundles/simplerisk-20260519-001.tgz \ - || curl -fsSL https://bundles-test.simplerisk.com/simplerisk-20260519-001.tgz; } | tar xz -C /var/www && \ - { curl -fsSL "https://github.com/simplerisk/database/raw/master/simplerisk-$DB_LANG-20260519-001.sql" \ - || curl -fsSL "https://github.com/simplerisk/database/raw/testing/simplerisk-$DB_LANG-20260519-001.sql"; } > /simplerisk.sql + if curl -fsSL https://simplerisk-downloads.s3.amazonaws.com/public/bundles/simplerisk-20260519-001.tgz -o /tmp/bundle.tgz; then true; \ + elif [ "$PREGA_BUNDLE_FALLBACK" = "true" ]; then \ + echo "prod bundle absent — pre-GA CI fallback to bundles-test"; \ + curl -fsSL https://bundles-test.simplerisk.com/simplerisk-20260519-001.tgz -o /tmp/bundle.tgz; \ + else echo "prod bundle simplerisk-20260519-001.tgz not found and PREGA_BUNDLE_FALLBACK=false" && exit 1; fi && \ + tar xzf /tmp/bundle.tgz -C /var/www && rm -f /tmp/bundle.tgz && \ + if curl -fsSL "https://github.com/simplerisk/database/raw/master/simplerisk-$DB_LANG-20260519-001.sql" -o /simplerisk.sql; then true; \ + elif [ "$PREGA_BUNDLE_FALLBACK" = "true" ]; then \ + curl -fsSL "https://github.com/simplerisk/database/raw/testing/simplerisk-$DB_LANG-20260519-001.sql" -o /simplerisk.sql; \ + else echo "prod SQL simplerisk-$DB_LANG-20260519-001.sql not found and PREGA_BUNDLE_FALLBACK=false" && exit 1; fi # Using Ubuntu image FROM ubuntu:${ubuntu_version_code} diff --git a/simplerisk/generate_dockerfile.sh b/simplerisk/generate_dockerfile.sh index d55a36a..45db821 100755 --- a/simplerisk/generate_dockerfile.sh +++ b/simplerisk/generate_dockerfile.sh @@ -22,14 +22,27 @@ if [ "$release" != "testing" ]; then FROM alpine/curl:8.12.1 AS downloader ARG DB_LANG=en +# PREGA_BUNDLE_FALLBACK is a CI-ONLY switch, default false. bump_downstream opens +# the docker update- PR at the testing cut, before that version's PROD +# bundle + SQL exist (they land in public/bundles/ and database/master only at GA). +# container-validation sets this true so the PR can validate against the testing +# sources. The GA publish leaves it FALSE, so a released image is ALWAYS built from +# the prod bundle/SQL and NEVER silently falls back to testing bytes. +ARG PREGA_BUNDLE_FALLBACK=false SHELL [ "/bin/ash", "-eo", "pipefail", "-c" ] RUN mkdir -p /var/www && \\ - { curl -fsSL https://simplerisk-downloads.s3.amazonaws.com/public/bundles/simplerisk-$release.tgz \\ - || curl -fsSL https://bundles-test.simplerisk.com/simplerisk-$release.tgz; } | tar xz -C /var/www && \\ - { curl -fsSL "https://github.com/simplerisk/database/raw/master/simplerisk-\$DB_LANG-$release.sql" \\ - || curl -fsSL "https://github.com/simplerisk/database/raw/testing/simplerisk-\$DB_LANG-$release.sql"; } > /simplerisk.sql + if curl -fsSL https://simplerisk-downloads.s3.amazonaws.com/public/bundles/simplerisk-$release.tgz -o /tmp/bundle.tgz; then true; \\ + elif [ "\$PREGA_BUNDLE_FALLBACK" = "true" ]; then \\ + echo "prod bundle absent — pre-GA CI fallback to bundles-test"; \\ + curl -fsSL https://bundles-test.simplerisk.com/simplerisk-$release.tgz -o /tmp/bundle.tgz; \\ + else echo "prod bundle simplerisk-$release.tgz not found and PREGA_BUNDLE_FALLBACK=false" && exit 1; fi && \\ + tar xzf /tmp/bundle.tgz -C /var/www && rm -f /tmp/bundle.tgz && \\ + if curl -fsSL "https://github.com/simplerisk/database/raw/master/simplerisk-\$DB_LANG-$release.sql" -o /simplerisk.sql; then true; \\ + elif [ "\$PREGA_BUNDLE_FALLBACK" = "true" ]; then \\ + curl -fsSL "https://github.com/simplerisk/database/raw/testing/simplerisk-\$DB_LANG-$release.sql" -o /simplerisk.sql; \\ + else echo "prod SQL simplerisk-\$DB_LANG-$release.sql not found and PREGA_BUNDLE_FALLBACK=false" && exit 1; fi EOF fi