From a45f250169d30d73cc9ee606c04825fb1d409a01 Mon Sep 17 00:00:00 2001 From: 0xJacky Date: Sun, 2 Aug 2026 14:30:15 +0800 Subject: [PATCH] fix(ci): stop stale images being redeployed over newer ones MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The demo was serving an image five commits old even though CI had deployed the current one. Two causes, and the first was mine. Every manual deploy pasted a literal image SHA, and I kept reusing the same one while iterating on Worker code — quietly rolling the container back over what CI had already deployed. deploy-published.sh now defaults to the image for the current commit, so the common case needs no SHA at all, and it verifies the image exists before deploying. That guard earned its keep immediately: the very next invocation caught a hand-typed SHA that did not correspond to any commit. Only an explicit "manifest unknown" is fatal. Reaching Docker Hub fails intermittently with a transport EOF, and treating that as a missing image would block deploys on a network blip, so other errors warn and continue — Cloudflare rejects a genuinely missing image clearly enough on its own. Second cause: build.yml is path-filtered and cloudflare/** was not listed, so a Worker-only change never reached deploy-demo. The Worker would sit undeployed until some unrelated commit happened to trigger a build. Co-Authored-By: Claude Opus 5 --- .github/workflows/build.yml | 4 +++ cloudflare/deploy-published.sh | 51 ++++++++++++++++++++++++++++++---- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 16d74926..a1cf1177 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,6 +21,10 @@ on: - "resources/demo/*" - "Dockerfile" - "demo.Dockerfile" + # Without this a Worker-only change never reaches the deploy-demo job, so + # the demo keeps serving the previous Worker until some unrelated commit + # happens to trigger a build. + - "cloudflare/**" pull_request: types: [ opened, synchronize, reopened ] paths: diff --git a/cloudflare/deploy-published.sh b/cloudflare/deploy-published.sh index 8f25b46a..4b5f056d 100755 --- a/cloudflare/deploy-published.sh +++ b/cloudflare/deploy-published.sh @@ -14,14 +14,24 @@ # the running instance onto new bytes. set -eu -if [ $# -ne 1 ]; then - echo "usage: $0 " >&2 +here="$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)" +cd "$here" + +if [ $# -gt 1 ]; then + echo "usage: $0 [image-reference]" >&2 + echo " with no argument, resolves the image built for the current commit" >&2 exit 2 fi -image="$1" -here="$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)" -cd "$here" +if [ $# -eq 1 ]; then + image="$1" +else + # Default to this commit's image rather than making the caller paste a SHA. + # Pasting one is how a stale image once got redeployed over a newer one. + commit="$(git rev-parse HEAD)" + image="docker.io/uozi/nginx-ui-demo:sha-${commit}" + echo "resolved image for HEAD (${commit%"${commit#?????????}"}): $image" +fi case "$image" in *:latest) @@ -30,6 +40,37 @@ case "$image" in ;; esac +# Check the image exists before deploying. Not every commit produces one: the +# build workflow is path-filtered, so a Worker-only change has no image of its +# own and must reuse the previous commit's. +# +# Only an explicit "not found" is fatal. Reaching Docker Hub fails +# intermittently (transport EOF), and treating that as a missing image would +# block deploys for a network blip — so on any other error say so and let +# Cloudflare be the authority, since it reports a genuinely missing image +# clearly enough. +probe="" +for attempt in 1 2 3; do + if probe="$(docker manifest inspect "$image" 2>&1)"; then + probe="" + break + fi + case "$probe" in + *"manifest unknown"*|*"not found"*|*"no such manifest"*) + echo "no published image at $image" >&2 + echo "the build workflow is path-filtered, so this commit may not have produced" >&2 + echo "an image. Pass the tag of the last built commit instead." >&2 + exit 1 + ;; + esac + [ "$attempt" -lt 3 ] && sleep 3 +done + +if [ -n "$probe" ]; then + echo "warning: could not verify the image exists ($probe)" >&2 + echo "continuing; Cloudflare will reject a genuinely missing image" >&2 +fi + # Must live beside wrangler.jsonc, not in a temp directory: wrangler resolves # `main` and the container build context relative to the config file, so a # config in /tmp sends it looking for /tmp/src/index.ts.