#!/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"