diff --git a/shared/build-lib.sh b/shared/build-lib.sh index 7710e56..8f13165 100755 --- a/shared/build-lib.sh +++ b/shared/build-lib.sh @@ -131,6 +131,37 @@ _validate_semver() { case "$_v3" in '' | *[!0-9]*) _bad ;; esac } +# Walk backwards from HEAD until a non-auto-commit is found, return its +# short SHA. Auto-commits are recognised by their canonical commit- +# message prefixes: +# +# - "chore(embedded): cut v-beta" (beta auto-commit, build:993) +# - "release: v lockstep" (stable auto-commit, build:986) +# +# Used by compute_build_label to embed a stable source-SHA into beta +# labels without triggering the embedded-commit recursion: HEAD shifts +# when embedded bytes change, but the source SHA returned here stays +# the same as long as the underlying source hasn't moved. +# +# Falls back to plain `HEAD` short SHA when the walk doesn't find a +# non-auto-commit in the first 32 commits (defensive cap). +_source_commit_short_sha() { + _i=0 + _ref="HEAD" + while [ "$_i" -lt 32 ]; do + _msg=$(git -C "$root_dir" log -1 --format=%s "$_ref" 2>/dev/null || echo "") + case "$_msg" in + "chore(embedded): cut v"* | "release: v"*" lockstep") + _ref="${_ref}~1" + _i=$((_i + 1)) + continue + ;; + esac + break + done + git -C "$root_dir" rev-parse --short=7 "$_ref" 2>/dev/null || echo "unknown" +} + # Compute build label and channel. Reads positional args: # compute_build_label [--release []] # Sets global variables: @@ -187,17 +218,17 @@ compute_build_label() { alpha | beta) channel="$_arg" _date=$(date -u +"%Y-%m-%d") - # 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}" + # Resolve the *source* SHA — the most recent commit that is + # not itself an embedded-files auto-commit. This stays stable + # across the build's auto-commit step (build:971-995) which + # advances HEAD by one when embedded bytes change. Using + # HEAD directly here would create an infinite loop: each cut + # would update the embedded label with the new chore-commit + # SHA, which would in turn require another chore commit. + # The source SHA is invariant across embedded auto-commits + # so the second cut on the same source state is a no-op. + _src_sha=$(_source_commit_short_sha) + build_label="v${_next_stable}-${channel} · ${_date} · g${_src_sha}" _emit_build_label_sidecar "$_tool" return 0 ;;