mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2026-09-03 07:24:52 +08:00
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>
108 lines
3.8 KiB
Bash
Executable File
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"
|