Files
nginx-ui/cloudflare/deploy-published.sh
0xJacky a45f250169 fix(ci): stop stale images being redeployed over newer ones
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 <noreply@anthropic.com>
2026-08-02 14:30:15 +08:00

108 lines
3.8 KiB
Bash
Executable File

#!/bin/sh
# Deploy the demo Worker against an already-published container image.
#
# The default wrangler.jsonc builds the image from ../demo.Dockerfile, which is
# what you want locally. In CI the image has already been built and pushed by
# the docker-build job, so rebuilding it would burn another six-platform QEMU
# run and push the same bytes twice. This swaps the `image` field for the
# published reference and deploys, leaving wrangler.jsonc untouched.
#
# Usage: sh deploy-published.sh docker.io/uozi/nginx-ui-demo:sha-abc1234
#
# Pass an immutable tag, not :latest. Cloudflare pins the container application
# to a specific image, so redeploying with an unchanged reference does not roll
# the running instance onto new bytes.
set -eu
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
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)
echo "refusing to deploy a mutable :latest tag — pass an immutable one" >&2
exit 2
;;
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.
generated="wrangler.published.jsonc"
trap 'rm -f "$generated"' EXIT INT TERM
# Replace the local-build block with the published reference. Done with a
# targeted substitution rather than a JSON rewrite because wrangler.jsonc
# carries comments that a JSON parser would discard.
python3 - "$image" "$generated" <<'PY'
import re
import sys
image, out = sys.argv[1], sys.argv[2]
source = open('wrangler.jsonc', encoding='utf-8').read()
pattern = re.compile(
r'"image":\s*"\.\./demo\.Dockerfile",\s*'
r'"image_build_context":\s*"[^"]*",\s*'
r'"image_vars":\s*\{[^}]*\},',
re.S,
)
patched, count = pattern.subn(f'"image": "{image}",', source)
if count != 1:
raise SystemExit(
f'expected exactly one local-build image block in wrangler.jsonc, found {count}'
)
open(out, 'w', encoding='utf-8').write(patched)
PY
echo "deploying with image $image"
bunx wrangler deploy -c "$generated"