fix(exercise): pin the rehearsal to the backend its stub speaks
All checks were successful
CI / test (pull_request) Successful in 1m27s
CI / release-exercise (pull_request) Successful in 9s
CI / self-guards (pull_request) Successful in 5s
CI / action-exercise (pull_request) Successful in 4s
CI / docs-sync-exercise (pull_request) Successful in 5s
labels / labels (pull_request) Successful in 1m26s

release-exercise stubs `gh` to answer the one API fact the ceremony path
consults. Since #191 facts.sh selects a backend, and on this Forgejo runner
it selected the forgejo one — which speaks curl and walked straight past the
stub to the live instance, read the fixture's SHA against the real
repository, found no merged release-labeled PR behind it and refused.

The exercise rehearses the WIRING — facts → decide → notes through the real
$GITHUB_OUTPUT plumbing. Which backend answers is lib/forge.sh's own
contract and is covered in test/forge*.test.sh. So the facts step now pins
CEREMONY_FORGE=github, the backend its stub is shaped for, and the stub
returns the array shape the new label read expects.

Refs #191

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
cluade-reviewer-andresmgsl 2026-08-04 11:42:28 +00:00
parent 87cc7d5aa5
commit c63a55067e

View file

@ -94,12 +94,25 @@ jobs:
# consumes the tool end to end instead of hand-writing its output. # consumes the tool end to end instead of hand-writing its output.
# Same shape as test/release-chain.test.sh. The gh stub answers the # Same shape as test/release-chain.test.sh. The gh stub answers the
# one API fact the ceremony path consults (the merged # one API fact the ceremony path consults (the merged
# release-labeled PR) so nothing here talks to GitHub. # release-labeled PR) so nothing here talks to a forge.
#
# The stub is gh-shaped, so the facts step below pins
# CEREMONY_FORGE=github: since #191 facts.sh selects a backend, and
# on a Forgejo runner it would otherwise pick the forgejo backend,
# which speaks curl and would walk straight past this stub to the
# real instance — reading the exercise's fixture SHA against the
# live repository and refusing it. The exercise rehearses the
# WIRING; which backend answers is lib/forge.sh's own contract,
# covered in test/forge*.test.sh.
run: | run: |
mkdir -p "$RUNNER_TEMP/stub" mkdir -p "$RUNNER_TEMP/stub"
cat > "$RUNNER_TEMP/stub/gh" <<'EOF' cat > "$RUNNER_TEMP/stub/gh" <<'EOF'
#!/usr/bin/env bash #!/usr/bin/env bash
if [ "$1" = api ]; then echo true; exit 0; fi # The label read is GET commits/{sha}/pulls — a JSON array (#191).
if [ "$1" = api ]; then
echo '[{"merged_at":"2026-01-01T00:00:00Z","labels":[{"name":"release"}]}]'
exit 0
fi
echo "gh stub: unexpected call: gh $*" >&2 echo "gh stub: unexpected call: gh $*" >&2
exit 97 exit 97
EOF EOF
@ -136,6 +149,9 @@ jobs:
# back to the merge commit's first parent (#1 constraint 10). # back to the merge commit's first parent (#1 constraint 10).
EVENT_BEFORE: "" EVENT_BEFORE: ""
VERSION_SOURCE: file VERSION_SOURCE: file
# The stub above is gh-shaped; pin the backend that uses it.
CEREMONY_FORGE: github
GITHUB_REPOSITORY: fixture/fixture
# release.yml's step verbatim — same invocation, same # release.yml's step verbatim — same invocation, same
# $GITHUB_OUTPUT plumbing — cwd'd at the fixture instead of the # $GITHUB_OUTPUT plumbing — cwd'd at the fixture instead of the
# workspace (the one thing a replay cannot inherit). # workspace (the one thing a replay cannot inherit).