Files
improvise/scripts/release-preflight-test.sh
T
Edward Langley 708d335fa7 ci: the preflight repair relocated its blind spot; close it
REVIEW OF THE PREVIOUS COMMIT, NOT NEW MACHINERY. The diagnosis it states was
re-verified in both directions before anything here was written: `dist plan
--tag=v0.1.0-rc2` gets past the tag/version check (stopping at the pinned
cargo-dist-version, as expected) and `--tag=v0.1.0-rc3` reproduces the CI error
verbatim, naming `--tag=v0.1.0-rc2` as the fix in its own help text. The
workflow was never broken. Both claimed fault injections also reproduce exactly:
the comparison replaced by `if false;`, and `expected` derived from the tag, each
produce four failures and exit 1 -- exit code read unpiped, since a pager's
status would mask it.

WHAT THE TEST COULD NOT SEE. Disabling the dirty-tree guard outright left all
six checks green and the script exiting 0. That guard is not decoration: it is
what stops a tag naming a commit that was never tested, and its failure is
irreversible in the same way the version mismatch is.

The cause is the previous repair. Moving the negative cases to a clean throwaway
checkout is what made the tag comparison reachable at all -- and it thereby
removed the only circumstance in which the test could observe the tree guard.
The guard whose PRECEDENCE caused the original vacuity became the one thing the
suite could not see. The vacuity and its repair are not two events; the repair
relocated the blind spot rather than closing it.

So the new check is aimed the other way: a tag that MATCHES the version, run
against a dirtied checkout, so the comparison passes and a refusal can only be
the tree. Verified discriminating -- with the guard disabled it fails with exit
1, and the two faults are independent in both directions (comparison disabled
leaves the tree check passing, guard disabled leaves the three tag checks
passing), so the seventh check is not a restatement of the other six.

Also recorded, rather than guarded: the `dist plan` second opinion can be
disabled with the suite green, because every input the test feeds it has already
been refused by the textual check. That follows from the arm being optional by
design -- pinning it would mean installing a dist matching cargo-dist-version,
the very mismatch the script tolerates on purpose. Stated so the silence is not
mistaken for coverage.

Two corrections of fact. The CI step lives in the `kb` job beside
pr-watch-test.sh, where this repo keeps its check_the_checkers -- not in `test`;
tooling.md now says so. And the awk version parser was probed rather than
hardened: `version.workspace = true` yields empty, which the root path refuses
loudly and the per-crate loop skips; a non-first [package] block and build
metadata both parse correctly; a trailing comment on the version line mis-parses
but fails CLOSED and prints the mis-parsed value in the refusal. No manifest in
this workspace has one, so nothing was changed for it.

No version bumped, no tag cut.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
2026-09-21 22:25:37 -07:00

92 lines
4.1 KiB
Bash
Executable File

#!/bin/sh
# check_the_checker for scripts/release-preflight.sh.
#
# It exists for the reason scripts/assemble-stamp-test.sh does: the guard is the
# whole value, and a guard that silently stopped guarding is invisible from the
# outside -- a preflight that accepted everything would print the same reassuring
# lines it prints today.
#
# IT ASSERTS THE REASON, NOT THE EXIT CODE, and that is the point. A first version
# checked only "did it refuse", and every negative case passed while the tag
# comparison was disabled -- because the script refuses a DIRTY WORKING TREE first,
# and this repo's tree is usually dirty (bd auto-exports .beads/issues.jsonl). Four
# of five checks were answering "is the tree dirty" while claiming to answer "does
# it catch a bad tag". Injecting `if false;` into the comparison is what exposed it.
# See context/design-principles.md section 7 -- a check adjacent to the thing it names.
set -eu
root=$(cd "$(dirname "$0")/.." && pwd)
script="$root/scripts/release-preflight.sh"
fails=0
# Refuses, AND the refusal names the tag/version mismatch rather than the tree.
refuses_for_the_tag() {
what=$1; tag=$2
out=$(sh "$script" "$tag" 2>&1) && { echo "FAIL: $what -- accepted" >&2; fails=$((fails+1)); return; }
case "$out" in
*"Cargo.toml has"*) echo " ok: $what (refused, naming the version)" ;;
*"uncommitted changes"*) echo "FAIL: $what -- refused for the DIRTY TREE, so this check proved nothing" >&2; fails=$((fails+1)) ;;
*) echo "FAIL: $what -- refused for an unrecognised reason:" >&2; echo "$out" | head -3 >&2; fails=$((fails+1)) ;;
esac
}
version=$(awk '/^\[package\]/{p=1;next} /^\[/{p=0} p&&/^version *=/{gsub(/[" ]/,"");sub(/^version=/,"");print;exit}' \
"$root/Cargo.toml")
test -n "$version" || { echo "FAIL: could not read the root version" >&2; exit 1; }
echo "release-preflight-test: root version is $version"
# The dirty-tree guard runs first, so the negative cases are exercised against a
# CLEAN checkout of this tree rather than the working copy. Without this the test
# cannot reach the comparison it exists to check.
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
git -C "$root" archive HEAD | (cd "$tmp" && tar x)
cp -f "$script" "$tmp/scripts/release-preflight.sh"
cp -f "$root/Cargo.toml" "$tmp/Cargo.toml"
(cd "$tmp" && git init -q . && git add -A && git -c user.email=t@t -c user.name=t commit -qm t)
script="$tmp/scripts/release-preflight.sh"
if sh "$script" --self-test >/dev/null 2>&1; then
echo " ok: --self-test (accepted)"
else
echo "FAIL: --self-test refused" >&2; fails=$((fails+1))
fi
if sh "$script" >/dev/null 2>&1; then
echo "FAIL: no argument should be a usage error" >&2; fails=$((fails+1))
else
echo " ok: no argument is a usage error (refused)"
fi
refuses_for_the_tag "a tag one character off" "v${version}x"
refuses_for_the_tag "the shape that actually broke CI" "v0.1.0-rc99"
refuses_for_the_tag "a bare version with no v" "$version"
# The positive direction, which only a clean tree can exercise.
if sh "$script" "v$version" >/dev/null 2>&1; then
echo " ok: the matching tag v$version (accepted)"
else
echo "FAIL: the matching tag v$version was refused" >&2; fails=$((fails+1))
fi
# The dirty-tree guard, which every check above is deliberately blind to: they
# run on a CLEAN checkout precisely so the tag comparison is reachable at all.
# The tag here MATCHES the version, so the comparison passes and a refusal can
# only be the tree -- which is what separates this check from the ones above
# instead of duplicating them. Disabling the guard leaves all six of those green.
echo "dirtied by release-preflight-test" >> "$tmp/README.md"
if out=$(sh "$script" "v$version" 2>&1); then
echo "FAIL: a dirty tree -- accepted" >&2; fails=$((fails+1))
else
case "$out" in
*"uncommitted changes"*) echo " ok: a dirty tree (refused, naming the tree)" ;;
*) echo "FAIL: a dirty tree -- refused, but not for the tree:" >&2; echo "$out" | head -3 >&2; fails=$((fails+1)) ;;
esac
fi
if [ "$fails" -ne 0 ]; then
echo "release-preflight-test: $fails failure(s)" >&2
exit 1
fi
echo "release-preflight-test: all checks passed"