fix: resume stranded merge-door publishes
Some checks failed
CI / action-exercise (pull_request) Successful in 12s
CI / self-guards (pull_request) Successful in 16s
CI / release-exercise (pull_request) Successful in 20s
CI / docs-sync-exercise (pull_request) Successful in 12s
labels / labels (pull_request) Successful in 13s
Refs guard / refs-not-closing (pull_request) Successful in 10s
CI / test (pull_request) Failing after 58s

This commit is contained in:
codex-bot-andresmgsl 2026-08-31 11:17:49 +00:00
parent 153a408e10
commit 7bd331a44d
2 changed files with 82 additions and 13 deletions

View file

@ -205,31 +205,40 @@ jobs:
fi
changelog_section CHANGELOG.md "$VER" > "$RUNNER_TEMP/notes.md"
cat "$RUNNER_TEMP/notes.md"
- name: nothing may exist yet — no tag, no release (re-runs refuse loudly)
- name: preflight — resume this merge, refuse every other collision
id: preflight
if: steps.decide.outputs.ceremony == 'yes'
env:
GH_TOKEN: ${{ github.token }}
VER: ${{ steps.facts.outputs.ver }}
# What makes a re-run of a completed ceremony refuse instead of
# clobber, and what catches a manual tag racing the merge.
MERGE_SHA: ${{ github.sha }}
# The pure table in lib/preflight.sh distinguishes a stranded run of
# this door from a completed release or a tag at another commit (#273).
run: |
if git ls-remote --exit-code origin "refs/tags/$VER" >/dev/null 2>&1; then
echo "tag '$VER' already exists — this release already happened, or a manual tag won the race; refusing to re-release, creating nothing." >&2
exit 1
fi
tag_read_rc=0
tag_refs="$(git ls-remote --exit-code origin "refs/tags/$VER" "refs/tags/$VER^{}")" || tag_read_rc=$?
case "$tag_read_rc" in
0) tag_shas="$(printf '%s\n' "$tag_refs" | awk 'NF { print $1 }')" ;;
2) tag_shas="" ;;
*)
echo "could not read tag '$VER' from origin (git ls-remote exit $tag_read_rc) — refusing rather than assuming it does not exist." >&2
exit 1
;;
esac
# shellcheck source=/dev/null
. "$CEREMONY_DIR/lib/forge.sh"
forge_select ""
if ! exists="$(forge_release_exists "$VER")"; then
if ! released="$(forge_release_exists "$VER")"; then
echo "could not read whether release '$VER' exists — refusing rather than assuming it does not (#191)." >&2
exit 1
fi
if [ "$exists" = yes ]; then
echo "release '$VER' already exists — refusing to re-release, creating nothing." >&2
exit 1
fi
# shellcheck source=/dev/null
. "$CEREMONY_DIR/lib/preflight.sh"
out="$(TAG_SHAS="$tag_shas" RELEASED="$released" release_preflight)"
printf '%s\n' "$out"
printf '%s\n' "$out" | grep '^resume=' >> "$GITHUB_OUTPUT"
- name: tag the merge commit — same job as the publish, on purpose
if: steps.decide.outputs.ceremony == 'yes'
if: steps.decide.outputs.ceremony == 'yes' && steps.preflight.outputs.resume != 'yes'
env:
GH_TOKEN: ${{ github.token }}
VER: ${{ steps.facts.outputs.ver }}

View file

@ -1185,6 +1185,66 @@ release_read 200 '{"id":41,"tag_name":"1.2.3","draft":false}'
forge_release_create 1.2.3 1.2.3 "$TMP/notes.md" >/dev/null 2>&1
check "a published same-tag release is never deleted" 1 "" grep -q '^DELETE ' "$WRITES"
# The merge door's preflight step is extracted and executed. Its network
# edges are stubbed at the boundary, while the real pure decision library
# consumes the gathered facts (#273).
MERGE_PREFLIGHT="$TMP/merge-preflight.sh"
{
printf '%s\n' '#!/usr/bin/env bash' 'set -e'
yq -r '.jobs.release-on-merge.steps[] | select(.id == "preflight") | .run' \
"$ROOT/.github/workflows/release.yml"
} >"$MERGE_PREFLIGHT"
chmod +x "$MERGE_PREFLIGHT"
mkdir -p "$TMP/merge-preflight-lib/lib" "$TMP/merge-preflight-bin"
ln -s "$ROOT/lib/preflight.sh" "$TMP/merge-preflight-lib/lib/preflight.sh"
# shellcheck disable=SC2016 # expanded when the generated helper runs
printf '%s\n' \
'forge_select() { :; }' \
'forge_release_exists() { case "$RELEASE_EXISTS" in error) return 1 ;; *) echo "$RELEASE_EXISTS" ;; esac; }' \
>"$TMP/merge-preflight-lib/lib/forge.sh"
# shellcheck disable=SC2016 # expanded when the PATH stub is executed
printf '%s\n' \
'#!/usr/bin/env bash' \
'case "$GIT_LS_REMOTE" in' \
' absent) exit 2 ;;' \
' error) exit 128 ;;' \
' *) printf "%s\n" "$GIT_LS_REMOTE" ;;' \
'esac' >"$TMP/merge-preflight-bin/git"
chmod +x "$TMP/merge-preflight-bin/git"
merge_preflight_extracted() { [ "$(wc -l <"$MERGE_PREFLIGHT")" -ge 8 ]; }
run_merge_preflight() {
: >"$TMP/merge-preflight-output"
CEREMONY_DIR="$TMP/merge-preflight-lib" \
GITHUB_OUTPUT="$TMP/merge-preflight-output" \
PATH="$TMP/merge-preflight-bin:$PATH" \
VER=1.2.3 MERGE_SHA=1111111111111111111111111111111111111111 \
RELEASE_EXISTS="$1" GIT_LS_REMOTE="$2" "$MERGE_PREFLIGHT"
}
merge_preflight_output_is() {
run_merge_preflight "$1" "$2" >/dev/null && \
[ "$(cat "$TMP/merge-preflight-output")" = "$3" ]
}
check "the merge door's preflight is extracted" 0 "" merge_preflight_extracted
check "the merge door refuses an unreadable release state" 1 \
"refusing rather than assuming" run_merge_preflight error absent
check "the merge door refuses an unreadable tag state" 1 \
"could not read tag '1.2.3'" run_merge_preflight no error
check "ls-remote exit 2 is tag absence and proceeds" 0 "" \
merge_preflight_output_is no absent resume=no
check "a matching peeled ref records a resume" 0 "" \
merge_preflight_output_is no \
$'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/tags/1.2.3\n1111111111111111111111111111111111111111\trefs/tags/1.2.3^{}' \
resume=yes
tag_step_uses_preflight() {
yq -r '.jobs.release-on-merge.steps[] | select(.name | test("tag the merge commit")) | .if' \
"$ROOT/.github/workflows/release.yml" | grep -q 'steps.preflight.outputs.resume'
}
check "the merge-door tag step is conditioned on the preflight output" 0 "" \
tag_step_uses_preflight
# The tag door's published-release guard is extracted and executed, like the
# other workflow-shell contracts in this repository. The tag itself is this
# door's premise, so only a published release refuses; adding the merge door's