138 lines
4 KiB
Bash
138 lines
4 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
# Contract tests for .github/scripts/self-ref-check.sh (issue #9; #1 D3) —
|
||
|
|
# the pin rules, driven against constructed fixture trees. The CI step runs
|
||
|
|
# the same script against the real tree. set -u, not -e: failing commands
|
||
|
|
# are behavior for the harness to inspect.
|
||
|
|
set -u
|
||
|
|
|
||
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
|
|
# shellcheck source=test/harness.sh
|
||
|
|
. "$ROOT/test/harness.sh"
|
||
|
|
|
||
|
|
CHECK="$ROOT/.github/scripts/self-ref-check.sh"
|
||
|
|
|
||
|
|
TMP="$(mktemp -d)"
|
||
|
|
trap 'rm -rf "$TMP"' EXIT
|
||
|
|
|
||
|
|
# tree <name> <pin...> — a fixture tree whose workflows carry the pins (one
|
||
|
|
# workflow file per pin, mimicking release.yml + labels.yml).
|
||
|
|
tree() {
|
||
|
|
local name="$1"
|
||
|
|
shift
|
||
|
|
mkdir -p "$TMP/$name/.github/workflows"
|
||
|
|
local i=0 pin
|
||
|
|
for pin in "$@"; do
|
||
|
|
i=$((i + 1))
|
||
|
|
printf 'name: w%s\nenv:\n CEREMONY_SELF_REF: "%s"\n' "$i" "$pin" \
|
||
|
|
>"$TMP/$name/.github/workflows/w$i.yml"
|
||
|
|
done
|
||
|
|
}
|
||
|
|
|
||
|
|
run_check() {
|
||
|
|
bash "$CHECK" "$TMP/$1"
|
||
|
|
}
|
||
|
|
|
||
|
|
# --- the pre-dogfood window (no VERSION yet — the tree #9 lands on) ---------
|
||
|
|
|
||
|
|
tree pre-dogfood 0.1.0
|
||
|
|
check "no VERSION: pin unchecked, loud notice, green" 0 "cannot bind yet" \
|
||
|
|
run_check pre-dogfood
|
||
|
|
|
||
|
|
# --- a bare tree: pin must equal VERSION -------------------------------------
|
||
|
|
|
||
|
|
tree bare-good 0.3.0
|
||
|
|
printf '0.3.0\n' >"$TMP/bare-good/VERSION"
|
||
|
|
check "bare tree, pin == VERSION passes" 0 "agrees" run_check bare-good
|
||
|
|
|
||
|
|
tree bare-stale 0.2.0
|
||
|
|
printf '0.3.0\n' >"$TMP/bare-stale/VERSION"
|
||
|
|
check "bare tree, stale pin fails" 1 "must be '0.3.0'" run_check bare-stale
|
||
|
|
|
||
|
|
# --- a -dev tree: pin must equal the newest stamped heading ------------------
|
||
|
|
|
||
|
|
tree dev-good 0.3.0
|
||
|
|
printf '0.4.0-dev\n' >"$TMP/dev-good/VERSION"
|
||
|
|
cat >"$TMP/dev-good/CHANGELOG.md" <<'EOF'
|
||
|
|
# Changelog
|
||
|
|
|
||
|
|
## Unreleased
|
||
|
|
|
||
|
|
- Pending entry.
|
||
|
|
|
||
|
|
## 0.3.0 — 2026-07-20
|
||
|
|
|
||
|
|
- The shipped entry.
|
||
|
|
|
||
|
|
## 0.2.0 — 2026-07-01
|
||
|
|
|
||
|
|
- Older entry.
|
||
|
|
EOF
|
||
|
|
check "-dev tree, pin == newest stamped heading passes" 0 "agrees" \
|
||
|
|
run_check dev-good
|
||
|
|
|
||
|
|
tree dev-stale 0.2.0
|
||
|
|
printf '0.4.0-dev\n' >"$TMP/dev-stale/VERSION"
|
||
|
|
cp "$TMP/dev-good/CHANGELOG.md" "$TMP/dev-stale/CHANGELOG.md"
|
||
|
|
check "-dev tree, pin behind the last release fails" 1 "must be '0.3.0'" \
|
||
|
|
run_check dev-stale
|
||
|
|
|
||
|
|
# Whole-field match: an rc heading never satisfies the bare X.Y.Z shape —
|
||
|
|
# the newest BARE heading below it is the last release.
|
||
|
|
tree dev-rc 0.3.0
|
||
|
|
printf '0.4.0-dev\n' >"$TMP/dev-rc/VERSION"
|
||
|
|
cat >"$TMP/dev-rc/CHANGELOG.md" <<'EOF'
|
||
|
|
# Changelog
|
||
|
|
|
||
|
|
## Unreleased
|
||
|
|
|
||
|
|
## 0.4.0-rc1 — 2026-07-21
|
||
|
|
|
||
|
|
- The candidate's entry.
|
||
|
|
|
||
|
|
## 0.3.0 — 2026-07-20
|
||
|
|
|
||
|
|
- The shipped entry.
|
||
|
|
EOF
|
||
|
|
check "-dev tree: an rc heading is skipped, the bare one governs" 0 "agrees" \
|
||
|
|
run_check dev-rc
|
||
|
|
|
||
|
|
# --- the pre-first-release tree: pin == VERSION with -dev stripped -----------
|
||
|
|
|
||
|
|
tree first-good 0.1.0
|
||
|
|
printf '0.1.0-dev\n' >"$TMP/first-good/VERSION"
|
||
|
|
printf '# Changelog\n\n## Unreleased\n\n- Everything so far.\n' \
|
||
|
|
>"$TMP/first-good/CHANGELOG.md"
|
||
|
|
check "pre-first-release: pin == VERSION minus -dev passes" 0 "agrees" \
|
||
|
|
run_check first-good
|
||
|
|
|
||
|
|
tree first-stale 0.0.1
|
||
|
|
printf '0.1.0-dev\n' >"$TMP/first-stale/VERSION"
|
||
|
|
check "pre-first-release: any other pin fails" 1 "must be '0.1.0'" \
|
||
|
|
run_check first-stale
|
||
|
|
|
||
|
|
# --- degenerate trees --------------------------------------------------------
|
||
|
|
|
||
|
|
mkdir -p "$TMP/no-pin/.github/workflows"
|
||
|
|
printf 'name: w\non: push\n' >"$TMP/no-pin/.github/workflows/w.yml"
|
||
|
|
check "no pin anywhere fails — the guard must have something to guard" 1 \
|
||
|
|
"nothing to guard" run_check no-pin
|
||
|
|
|
||
|
|
tree split-pin 0.3.0 0.2.0
|
||
|
|
printf '0.3.0\n' >"$TMP/split-pin/VERSION"
|
||
|
|
check "disagreeing pins across workflows fail" 1 "disagree" run_check split-pin
|
||
|
|
|
||
|
|
tree empty-version 0.1.0
|
||
|
|
: >"$TMP/empty-version/VERSION"
|
||
|
|
check "an empty VERSION fails loudly" 1 "is empty" run_check empty-version
|
||
|
|
|
||
|
|
# --- the real tree -----------------------------------------------------------
|
||
|
|
|
||
|
|
# Whatever state the repo is in (pre-dogfood today, versioned after #11),
|
||
|
|
# the guard must hold on it — this is the CI step's exact invocation.
|
||
|
|
real_check() {
|
||
|
|
(cd "$ROOT" && bash "$CHECK")
|
||
|
|
}
|
||
|
|
check "the real tree passes its own guard" 0 "" real_check
|
||
|
|
|
||
|
|
summary
|