fix(build,ci): auto-commit embedded refresh on beta cuts; pin chart to HEAD
Closes the long-standing chart-bump race that required a manual rebase
on every beta cut. Three coordinated changes:
build (top-level): broaden the existing stable-only "fold embedded
artifacts into a release commit" block to also fire on beta cuts.
Same idempotency check; new commit message ("chore(embedded): cut
v<X.Y.Z>-beta") derived via _coordinated_next_stable. Tagging stays
stable-only (channels are mutable mirrors and never get tags). Beta
cuts now produce exactly one commit on main; HEAD always carries
the bytes the binary will serve.
shared/build-lib.sh: drop the SHA from alpha/beta channel labels.
Embedding HEAD's SHA in the bytes the SHA identifies created a
feedback loop — each auto-commit advanced HEAD, which shifted the
SHA in the next run's versions.txt, which triggered another
embedded commit, ad infinitum. Channel labels now read
"v<X.Y.Z>-<channel> · <date>" — version + date is enough; SHA
traceability lives in the chart's appVersion (full SHA) and the
binary's --version output. Plain dev builds keep the timestamp +
-dirty fingerprint since they don't commit. Stable cuts already
use a clean version-only label.
.forgejo/scripts/notify-chart-bump.sh: pin the chart's appVersion
to `git rev-parse HEAD` instead of the SHA in versions.txt. The
build's auto-commit now ensures HEAD == "the commit containing the
embedded bytes the binary will bake," so HEAD is the substantively
correct anchor. The previous versions.txt read pinned one commit
too early (the source-side commit, before the embed refresh
committed) — every beta cut required a manual chart-rebase to
point at the embed commit. With both halves landed, the cycle is
zero-touch: ./build beta + git push → auto-bump CI fires → chart
appVersion at correct SHA → dev image bakes the right bytes.
Verification: ran ./build beta twice on the same source state. First
run produced one commit; second run printed "no embedded changes to
commit (re-run on same source state)" and made no commit. The label
SHA-loop bug is fixed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4e6473a5cd
commit
8f07b47a0b
3 changed files with 75 additions and 47 deletions
|
|
@ -60,37 +60,27 @@ case "$CHANNEL" in
|
||||||
PAT=$(echo "${LATEST_STABLE#zddc-server-v}" | cut -d. -f3)
|
PAT=$(echo "${LATEST_STABLE#zddc-server-v}" | cut -d. -f3)
|
||||||
NEXT_STABLE="$MAJ.$MIN.$((PAT + 1))"
|
NEXT_STABLE="$MAJ.$MIN.$((PAT + 1))"
|
||||||
|
|
||||||
# Use the SHA baked into the embedded files (third field of
|
# Pin to HEAD. The dev pipeline's Dockerfile fetches this SHA
|
||||||
# versions.txt: "<tool>=<version> · <date> · <sha>"), NOT
|
# via `git fetch --depth=1 origin <sha>` and runs `go build`
|
||||||
# `git rev-parse HEAD`. This matters because `./build beta`
|
# against it; //go:embed at build time bakes whatever
|
||||||
# runs locally at HEAD=N, then the operator commits the
|
# zddc/internal/apps/embedded/* and zddc/internal/handler/{form,
|
||||||
# generated embed files as N+1; the embed label encodes N
|
# tables}.html are at THAT commit.
|
||||||
# while git HEAD on push is N+1. If we used N+1 here, the
|
#
|
||||||
# chart's appVersion (N+1) wouldn't match the build label
|
# Since `./build beta` (build:952-995) now auto-commits the
|
||||||
# users see in the served website (N) — confusing on its
|
# regenerated embedded artifacts before push, HEAD always
|
||||||
# face when triaging "is this image current?". Reading from
|
# contains the bytes the binary will serve. Earlier this script
|
||||||
# versions.txt guarantees they line up.
|
# read the SHA from embedded/versions.txt to keep the served
|
||||||
VERSIONS_FILE="zddc/internal/apps/embedded/versions.txt"
|
# HTML's build label cosmetically matched to the chart's
|
||||||
SHORT_SHA=$(awk -F' · ' '/^[a-z]+=/ { print $NF; exit }' "$VERSIONS_FILE" \
|
# appVersion — but that read pinned the chart at the source-
|
||||||
| tr -d '[:space:]')
|
# side commit (HEAD-1), which is the commit BEFORE the
|
||||||
if [ -z "$SHORT_SHA" ]; then
|
# embedded refresh. The Dockerfile would then bake the previous
|
||||||
echo "::error::could not parse SHA from $VERSIONS_FILE" >&2
|
# cut's bytes. Manual chart-rebases were required on every beta
|
||||||
cat "$VERSIONS_FILE" >&2
|
# cut. HEAD is the right anchor: substantively correct, even
|
||||||
exit 1
|
# if the build-label SHA in the served HTML is one commit
|
||||||
fi
|
# behind cosmetically (operators triaging "is this image
|
||||||
# Expand the short SHA from versions.txt to its full 40-char
|
# current?" should compare chart appVersion to the running
|
||||||
# form. The chart's downstream Dockerfile fetches the SHA via
|
# binary's `--version` output, not the HTML footer).
|
||||||
# `git fetch --depth=1 origin <sha>` against Forgejo, and
|
FULL_SHA=$(git rev-parse HEAD)
|
||||||
# Forgejo's uploadpack.allowAnySHA1InWant only matches FULL
|
|
||||||
# SHAs — a 7-char abbreviation returns "couldn't find remote
|
|
||||||
# ref". Resolving here (where we have the full local clone
|
|
||||||
# via actions/checkout@v4 fetch-depth: 0) keeps the chart's
|
|
||||||
# downstream consumers free of git plumbing.
|
|
||||||
FULL_SHA=$(git rev-parse "$SHORT_SHA" 2>/dev/null)
|
|
||||||
if [ -z "$FULL_SHA" ]; then
|
|
||||||
echo "::error::could not resolve $SHORT_SHA to a full SHA via git rev-parse" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
TARGET_VERSION="${NEXT_STABLE}-beta-${FULL_SHA}"
|
TARGET_VERSION="${NEXT_STABLE}-beta-${FULL_SHA}"
|
||||||
BRANCHES="develop"
|
BRANCHES="develop"
|
||||||
TRIGGER_DESC="ZDDC beta cut"
|
TRIGGER_DESC="ZDDC beta cut"
|
||||||
|
|
|
||||||
55
build
55
build
|
|
@ -949,35 +949,64 @@ if [ -n "$RELEASE_CHANNEL" ]; then
|
||||||
verify_channel_links "$RELEASES_DIR"
|
verify_channel_links "$RELEASES_DIR"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- Release commit + tag (stable cut only) -------------------------------
|
# --- Embedded commit (stable + beta cuts) ---------------------------------
|
||||||
# On a stable cut, fold the regenerated embedded artifacts into a release
|
# On both stable and beta cuts, fold the regenerated embedded artifacts
|
||||||
# commit, then place all seven tool tags at that new commit. This is the
|
# into a single commit on main. Two reasons:
|
||||||
# fix for the previous tag-before-commit bug that left tags pointing at
|
|
||||||
# alpha-dirty source-side commits, baking alpha labels into prod binaries.
|
|
||||||
#
|
#
|
||||||
# Idempotent: if there are no embedded changes, no commit is made; tags
|
# 1. Stable: the next tag block needs HEAD to point at the bytes the
|
||||||
# are still verified to be at HEAD.
|
# stable binary will serve. Without this commit, tags would land on
|
||||||
if [ "$RELEASE_CHANNEL" = "stable" ]; then
|
# the source-side commit (with alpha-dirty embedded/*) and prod
|
||||||
|
# images compiled from `git checkout zddc-server-vX.Y.Z` would
|
||||||
|
# ship alpha bytes. (Original justification — preserved.)
|
||||||
|
#
|
||||||
|
# 2. Beta: the dev pipeline pins the chart's appVersion to a SHA
|
||||||
|
# (.forgejo/scripts/notify-chart-bump.sh reads HEAD). For that
|
||||||
|
# pin to point at a SHA where embedded/* matches what the binary
|
||||||
|
# will serve, HEAD has to advance past the source-side commit.
|
||||||
|
# Without this commit, the chart pin lags one commit and the dev
|
||||||
|
# image bakes the previous beta cut's bytes — exactly the failure
|
||||||
|
# mode that required manual chart-rebases on the v0.0.16-beta cuts.
|
||||||
|
#
|
||||||
|
# Idempotent: if there are no embedded changes, no commit is made.
|
||||||
|
if [ "$RELEASE_CHANNEL" = "stable" ] || [ "$RELEASE_CHANNEL" = "beta" ]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== Release commit + tag ==="
|
echo "=== Embedded commit ==="
|
||||||
|
|
||||||
# Stage the artifacts that are part of the release. dist/ is
|
# Stage the artifacts that are part of the release. dist/ is
|
||||||
# gitignored everywhere — none of the tools' dist/<tool>.html files
|
# gitignored everywhere — none of the tools' dist/<tool>.html files
|
||||||
# are tracked. The release commit only carries the bake-in artifacts
|
# are tracked. The release commit only carries the bake-in artifacts
|
||||||
# that the binary needs at //go:embed time + the embedded form
|
# that the binary needs at //go:embed time + the embedded form +
|
||||||
# template.
|
# tables templates.
|
||||||
git -C "$SCRIPT_DIR" add "$EMBED_DIR/" \
|
git -C "$SCRIPT_DIR" add "$EMBED_DIR/" \
|
||||||
"$SCRIPT_DIR/zddc/internal/handler/form.html" \
|
"$SCRIPT_DIR/zddc/internal/handler/form.html" \
|
||||||
"$SCRIPT_DIR/zddc/internal/handler/tables.html"
|
"$SCRIPT_DIR/zddc/internal/handler/tables.html"
|
||||||
|
|
||||||
if ! git -C "$SCRIPT_DIR" diff --cached --quiet; then
|
if ! git -C "$SCRIPT_DIR" diff --cached --quiet; then
|
||||||
|
if [ "$RELEASE_CHANNEL" = "stable" ]; then
|
||||||
git -C "$SCRIPT_DIR" commit -m "release: v${RELEASE_VERSION} lockstep"
|
git -C "$SCRIPT_DIR" commit -m "release: v${RELEASE_VERSION} lockstep"
|
||||||
echo " release commit created"
|
else
|
||||||
|
# Beta cuts don't carry an explicit version — derive the
|
||||||
|
# next-stable target from the lockstep helper so the commit
|
||||||
|
# message reflects "cut v<X.Y.Z>-beta" the same way
|
||||||
|
# versions.txt and the release-output filenames do.
|
||||||
|
_embed_ver=$(_coordinated_next_stable)
|
||||||
|
git -C "$SCRIPT_DIR" commit -m "chore(embedded): cut v${_embed_ver}-beta"
|
||||||
|
fi
|
||||||
|
echo " embedded commit created at HEAD: $(git -C "$SCRIPT_DIR" rev-parse --short HEAD)"
|
||||||
else
|
else
|
||||||
echo " no embedded changes to commit (re-run on same source state)"
|
echo " no embedded changes to commit (re-run on same source state)"
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Tag the seven artifacts at HEAD. Pre-flight already validated that
|
# --- Release tag (stable cut only) ----------------------------------------
|
||||||
|
# Beta channels never get tags — channel mirrors are by-design moving
|
||||||
|
# targets. Tags only exist for stable, where they pin a specific
|
||||||
|
# X.Y.Z to an immutable commit (the release commit produced above).
|
||||||
|
if [ "$RELEASE_CHANNEL" = "stable" ]; then
|
||||||
|
echo ""
|
||||||
|
echo "=== Release tag ==="
|
||||||
|
|
||||||
|
# Tag the nine artifacts at HEAD. Pre-flight already validated that
|
||||||
# any pre-existing tag is in HEAD's history, so this is safe.
|
# any pre-existing tag is in HEAD's history, so this is safe.
|
||||||
_head=$(git -C "$SCRIPT_DIR" rev-parse HEAD)
|
_head=$(git -C "$SCRIPT_DIR" rev-parse HEAD)
|
||||||
for _t in archive transmittal classifier mdedit landing form tables browse zddc-server; do
|
for _t in archive transmittal classifier mdedit landing form tables browse zddc-server; do
|
||||||
|
|
|
||||||
|
|
@ -187,8 +187,17 @@ compute_build_label() {
|
||||||
alpha | beta)
|
alpha | beta)
|
||||||
channel="$_arg"
|
channel="$_arg"
|
||||||
_date=$(date -u +"%Y-%m-%d")
|
_date=$(date -u +"%Y-%m-%d")
|
||||||
_sha=$(git -C "$root_dir" rev-parse --short=7 HEAD 2>/dev/null || echo "unknown")
|
# No SHA in the channel-cut label — the build's auto-commit
|
||||||
build_label="v${_next_stable}-${channel} · ${_date} · ${_sha}"
|
# block (build:971-995) advances HEAD when committing the
|
||||||
|
# regenerated embedded artifacts, which would shift the SHA
|
||||||
|
# and trigger the next run to commit again ad infinitum.
|
||||||
|
# Cosmetic SHA traceability is now via the chart's
|
||||||
|
# appVersion (full SHA, written by notify-chart-bump.sh on
|
||||||
|
# push) and the running binary's `--version` output —
|
||||||
|
# both of which are accurate where the embedded label
|
||||||
|
# SHA was not (it always pointed at HEAD-before-commit,
|
||||||
|
# off by one).
|
||||||
|
build_label="v${_next_stable}-${channel} · ${_date}"
|
||||||
_emit_build_label_sidecar "$_tool"
|
_emit_build_label_sidecar "$_tool"
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue