diff --git a/.forgejo/scripts/notify-chart-bump.sh b/.forgejo/scripts/notify-chart-bump.sh index 2720650..0f8a111 100755 --- a/.forgejo/scripts/notify-chart-bump.sh +++ b/.forgejo/scripts/notify-chart-bump.sh @@ -60,37 +60,27 @@ case "$CHANNEL" in PAT=$(echo "${LATEST_STABLE#zddc-server-v}" | cut -d. -f3) NEXT_STABLE="$MAJ.$MIN.$((PAT + 1))" - # Use the SHA baked into the embedded files (third field of - # versions.txt: "= · · "), NOT - # `git rev-parse HEAD`. This matters because `./build beta` - # runs locally at HEAD=N, then the operator commits the - # generated embed files as N+1; the embed label encodes N - # 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 - # users see in the served website (N) — confusing on its - # face when triaging "is this image current?". Reading from - # versions.txt guarantees they line up. - VERSIONS_FILE="zddc/internal/apps/embedded/versions.txt" - SHORT_SHA=$(awk -F' · ' '/^[a-z]+=/ { print $NF; exit }' "$VERSIONS_FILE" \ - | tr -d '[:space:]') - if [ -z "$SHORT_SHA" ]; then - echo "::error::could not parse SHA from $VERSIONS_FILE" >&2 - cat "$VERSIONS_FILE" >&2 - exit 1 - fi - # Expand the short SHA from versions.txt to its full 40-char - # form. The chart's downstream Dockerfile fetches the SHA via - # `git fetch --depth=1 origin ` against Forgejo, and - # 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 + # Pin to HEAD. The dev pipeline's Dockerfile fetches this SHA + # via `git fetch --depth=1 origin ` and runs `go build` + # against it; //go:embed at build time bakes whatever + # zddc/internal/apps/embedded/* and zddc/internal/handler/{form, + # tables}.html are at THAT commit. + # + # Since `./build beta` (build:952-995) now auto-commits the + # regenerated embedded artifacts before push, HEAD always + # contains the bytes the binary will serve. Earlier this script + # read the SHA from embedded/versions.txt to keep the served + # HTML's build label cosmetically matched to the chart's + # appVersion — but that read pinned the chart at the source- + # side commit (HEAD-1), which is the commit BEFORE the + # embedded refresh. The Dockerfile would then bake the previous + # cut's bytes. Manual chart-rebases were required on every beta + # cut. HEAD is the right anchor: substantively correct, even + # if the build-label SHA in the served HTML is one commit + # behind cosmetically (operators triaging "is this image + # current?" should compare chart appVersion to the running + # binary's `--version` output, not the HTML footer). + FULL_SHA=$(git rev-parse HEAD) TARGET_VERSION="${NEXT_STABLE}-beta-${FULL_SHA}" BRANCHES="develop" TRIGGER_DESC="ZDDC beta cut" diff --git a/build b/build index b81e86a..bd462be 100755 --- a/build +++ b/build @@ -949,35 +949,64 @@ if [ -n "$RELEASE_CHANNEL" ]; then verify_channel_links "$RELEASES_DIR" fi -# --- Release commit + tag (stable cut only) ------------------------------- -# On a stable cut, fold the regenerated embedded artifacts into a release -# commit, then place all seven tool tags at that new commit. This is the -# fix for the previous tag-before-commit bug that left tags pointing at -# alpha-dirty source-side commits, baking alpha labels into prod binaries. +# --- Embedded commit (stable + beta cuts) --------------------------------- +# On both stable and beta cuts, fold the regenerated embedded artifacts +# into a single commit on main. Two reasons: # -# Idempotent: if there are no embedded changes, no commit is made; tags -# are still verified to be at HEAD. -if [ "$RELEASE_CHANNEL" = "stable" ]; then +# 1. Stable: the next tag block needs HEAD to point at the bytes the +# stable binary will serve. Without this commit, tags would land on +# 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 "=== Release commit + tag ===" + echo "=== Embedded commit ===" # Stage the artifacts that are part of the release. dist/ is # gitignored everywhere — none of the tools' dist/.html files # are tracked. The release commit only carries the bake-in artifacts - # that the binary needs at //go:embed time + the embedded form - # template. + # that the binary needs at //go:embed time + the embedded form + + # tables templates. git -C "$SCRIPT_DIR" add "$EMBED_DIR/" \ "$SCRIPT_DIR/zddc/internal/handler/form.html" \ "$SCRIPT_DIR/zddc/internal/handler/tables.html" if ! git -C "$SCRIPT_DIR" diff --cached --quiet; then - git -C "$SCRIPT_DIR" commit -m "release: v${RELEASE_VERSION} lockstep" - echo " release commit created" + if [ "$RELEASE_CHANNEL" = "stable" ]; then + git -C "$SCRIPT_DIR" commit -m "release: v${RELEASE_VERSION} lockstep" + 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-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 echo " no embedded changes to commit (re-run on same source state)" 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. _head=$(git -C "$SCRIPT_DIR" rev-parse HEAD) for _t in archive transmittal classifier mdedit landing form tables browse zddc-server; do diff --git a/shared/build-lib.sh b/shared/build-lib.sh index 2808d51..7710e56 100755 --- a/shared/build-lib.sh +++ b/shared/build-lib.sh @@ -187,8 +187,17 @@ compute_build_label() { alpha | beta) channel="$_arg" _date=$(date -u +"%Y-%m-%d") - _sha=$(git -C "$root_dir" rev-parse --short=7 HEAD 2>/dev/null || echo "unknown") - build_label="v${_next_stable}-${channel} · ${_date} · ${_sha}" + # No SHA in the channel-cut label — the build's auto-commit + # 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" return 0 ;;