Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,10 @@ releases-baseline-update: ## Grandfather the tags that shipped nothing and never
releases-published-test: ## Test the tag/release reconciliation rule (hermetic; not part of `gate`)
./scripts/test-releases-published.sh

.PHONY: guard-sigpipe-test
guard-sigpipe-test: ## Test that the gate guards survive a reader that closes their pipe early (#1815; hermetic; not part of `gate`)
./scripts/test-guard-sigpipe.sh

.PHONY: hooks
hooks: ## Install the pre-push gate hook (runs `make gate`, scoped to the diff, on every push)
git config core.hooksPath .githooks
Expand Down
32 changes: 26 additions & 6 deletions scripts/check-cargo-install-pins.sh
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,24 @@ if [ -z "$install_lines" ]; then
fi

fail=0

# The verdict is decided before anything is written (#1815). Failure lines are
# buffered while the scan runs and emitted in one final write: a guard that
# prints as it scans dies mid-report when its reader exits early, and under
# `set -euo pipefail` whatever partial state it had reached becomes the exit
# status. scripts/check-file-size.sh is the shape being copied.
report=""
note() { report="${report}$1"$'\n'; }

# Emission is best-effort: the verdict is already decided, so a reader that
# closed the pipe (`| head -1`, `| true`) must be able to change neither the
# report nor the exit code. SIGPIPE is ignored so a failed write surfaces as a
# discarded error instead of killing the script (#1815).
emit() {
trap '' PIPE
printf '%s' "$report" >&2 || true
}

while IFS= read -r line; do
[ -z "$line" ] && continue
file_and_rest="${line%%:*}"
Expand All @@ -71,7 +89,7 @@ while IFS= read -r line; do
\\) continue ;;
*@*) continue ;;
*)
echo "check-cargo-install-pins: $file_and_rest:$lineno installs '$tok' with no @version pin." >&2
note "check-cargo-install-pins: $file_and_rest:$lineno installs '$tok' with no @version pin."
fail=1
;;
esac
Expand All @@ -81,11 +99,13 @@ $install_lines
EOF

if [ "$fail" -ne 0 ]; then
echo >&2
echo "Pin each crate to an exact version, e.g.:" >&2
echo >&2
echo " run: cargo install --locked cargo-deny@0.20.2" >&2
note ""
note "Pin each crate to an exact version, e.g.:"
note ""
note " run: cargo install --locked cargo-deny@0.20.2"
emit
exit 1
fi

echo "check-cargo-install-pins: OK — every 'cargo install' crate is pinned to a version."
emit
echo "check-cargo-install-pins: OK — every 'cargo install' crate is pinned to a version." || true
73 changes: 47 additions & 26 deletions scripts/check-command-docs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,23 @@ exempt_file="scripts/command-docs-exempt.txt"

status=0

# The verdict is decided before anything is written (#1815). Failure lines are
# buffered while the checks run and emitted in one final write: a guard that
# prints as it scans dies mid-report when its reader exits early, and under
# `set -euo pipefail` whatever partial state it had reached becomes the exit
# status. scripts/check-file-size.sh is the shape being copied.
report=""
note() { report="${report}$1"$'\n'; }

# Emission is best-effort: the verdict is already decided, so a reader that
# closed the pipe (`| head -1`, `| true`) must be able to change neither the
# report nor the exit code. SIGPIPE is ignored so a failed write surfaces as a
# discarded error instead of killing the script (#1815).
emit() {
trap '' PIPE
printf '%s' "$report" >&2 || true
}

# Membership over a newline-delimited list, in pure shell.
#
# Deliberately NOT `printf ... | grep -qx`: under `set -o pipefail` that is a
Expand All @@ -66,16 +83,18 @@ $2
}

if [ ! -f "$enum_file" ]; then
echo "FAIL $enum_file not found." >&2
echo " This guard reads the top-level subcommand list from that file. If" >&2
echo " it moved, update \$enum_file in scripts/check-command-docs.sh in" >&2
echo " the same PR." >&2
note "FAIL $enum_file not found."
note " This guard reads the top-level subcommand list from that file. If"
note " it moved, update \$enum_file in scripts/check-command-docs.sh in"
note " the same PR."
emit
exit 1
fi

if [ ! -d "$docs_dir" ]; then
echo "FAIL $docs_dir not found." >&2
echo " This guard expects the command reference pages to live there." >&2
note "FAIL $docs_dir not found."
note " This guard expects the command reference pages to live there."
emit
exit 1
fi

Expand All @@ -96,10 +115,11 @@ variants="$(awk -v decl="$enum_decl" '
' "$enum_file")"

if [ -z "$variants" ]; then
echo "FAIL parsed zero variants from $enum_file." >&2
echo " Expected a block opening with: $enum_decl" >&2
echo " If the declaration changed, update \$enum_decl in" >&2
echo " scripts/check-command-docs.sh in the same PR." >&2
note "FAIL parsed zero variants from $enum_file."
note " Expected a block opening with: $enum_decl"
note " If the declaration changed, update \$enum_decl in"
note " scripts/check-command-docs.sh in the same PR."
emit
exit 1
fi

Expand Down Expand Up @@ -145,7 +165,7 @@ while IFS= read -r slug; do
[ -n "$slug" ] || continue
is_exempt "$slug" && continue
if [ ! -f "$docs_dir/$slug.mdx" ]; then
echo "FAIL \`stella $slug\` has no reference page: $docs_dir/$slug.mdx" >&2
note "FAIL \`stella $slug\` has no reference page: $docs_dir/$slug.mdx"
missing=$((missing + 1))
status=1
fi
Expand All @@ -154,9 +174,9 @@ $slugs
EOF

if [ "$missing" -ne 0 ]; then
echo "" >&2
echo " Write the page, or add the slug to $exempt_file with a reason." >&2
echo " An exemption is a decision on the record; a missing page is not." >&2
note ""
note " Write the page, or add the slug to $exempt_file with a reason."
note " An exemption is a decision on the record; a missing page is not."
fi

# --- 2. Every page is reachable from the sidebar ----------------------------
Expand All @@ -167,17 +187,17 @@ if [ -n "$sidebar" ]; then
is_exempt "$slug" && continue
[ -f "$docs_dir/$slug.mdx" ] || continue
if ! contains "$sidebar" "$slug"; then
echo "FAIL $docs_dir/$slug.mdx is not listed in $meta_file." >&2
note "FAIL $docs_dir/$slug.mdx is not listed in $meta_file."
unlisted=$((unlisted + 1))
status=1
fi
done <<EOF
$slugs
EOF
if [ "$unlisted" -ne 0 ]; then
echo "" >&2
echo " A page no sidebar lists is undocumented to anyone browsing." >&2
echo " Add the slug to the \"pages\" array under the right group." >&2
note ""
note " A page no sidebar lists is undocumented to anyone browsing."
note " Add the slug to the \"pages\" array under the right group."
fi
fi

Expand All @@ -186,7 +206,7 @@ orphans=0
while IFS= read -r page; do
[ -n "$page" ] || continue
if ! contains "$slugs" "$page"; then
echo "FAIL $docs_dir/$page.mdx documents no \`Command\` variant." >&2
note "FAIL $docs_dir/$page.mdx documents no \`Command\` variant."
orphans=$((orphans + 1))
status=1
fi
Expand All @@ -195,28 +215,29 @@ $pages
EOF

if [ "$orphans" -ne 0 ]; then
echo "" >&2
echo " The command was renamed or removed and its page outlived it." >&2
echo " Delete the page, or repoint it at the surface that replaced it." >&2
note ""
note " The command was renamed or removed and its page outlived it."
note " Delete the page, or repoint it at the surface that replaced it."
fi

# --- 4. No stale exemptions -------------------------------------------------
if [ -n "$exempt" ]; then
while IFS= read -r slug; do
[ -n "$slug" ] || continue
if ! contains "$slugs" "$slug"; then
echo "FAIL $exempt_file exempts \"$slug\", which is not a subcommand." >&2
echo " Remove the line: a stale exemption silently covers a future" >&2
echo " command that happens to reuse the name." >&2
note "FAIL $exempt_file exempts \"$slug\", which is not a subcommand."
note " Remove the line: a stale exemption silently covers a future"
note " command that happens to reuse the name."
status=1
fi
done <<EOF
$exempt
EOF
fi

emit
if [ "$status" -eq 0 ]; then
count="$(printf '%s\n' "$slugs" | wc -l | tr -d '[:blank:]')"
echo "check-command-docs: OK — $count subcommands, each with a listed reference page."
echo "check-command-docs: OK — $count subcommands, each with a listed reference page." || true
fi
exit "$status"
7 changes: 6 additions & 1 deletion scripts/check-file-size.sh
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,9 @@ if [ -n "$report" ]; then
fi

tracked=$(git ls-files "${RATCHET_PATHSPECS[@]}" | wc -l | tr -d ' ')
echo "check-file-size: OK — $tracked Rust/Python/shell files, none over $LIMIT lines except $(grep -cv '^#' "$baseline") grandfathered (none grew)."
# The verdict is already decided; the write is best-effort. SIGPIPE is ignored
# and the write's failure discarded, so a reader that closed the pipe
# (`| head -1`, `| true`) cannot turn a green verdict into a failure (#1815).
grandfathered=$(grep -cv '^#' "$baseline")
trap '' PIPE
echo "check-file-size: OK — $tracked Rust/Python/shell files, none over $LIMIT lines except $grandfathered grandfathered (none grew)." || true
26 changes: 23 additions & 3 deletions scripts/check-gate-parity.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,38 @@ agents="AGENTS.md"
contributing="CONTRIBUTING.md"

fail=0
note() { printf 'check-gate-parity: %s\n' "$1" >&2; }

# The verdict is decided before anything is written (#1815). Failure lines are
# buffered while the checks run and emitted in one final write: a guard that
# prints as it scans dies mid-report when its reader exits early, and under
# `set -euo pipefail` whatever partial state it had reached becomes the exit
# status. scripts/check-file-size.sh is the shape being copied.
report=""
note() { report="${report}check-gate-parity: $1"$'\n'; }

# Emission is best-effort: the verdict is already decided, so a reader that
# closed the pipe (`| head -1`, `| true`) must be able to change neither the
# report nor the exit code. SIGPIPE is ignored so a failed write surfaces as a
# discarded error instead of killing the script (#1815).
emit() {
trap '' PIPE
printf '%s' "$report" >&2 || true
}

# ── The truth ────────────────────────────────────────────────────────────────

if ! steps="$(make -s print-gate-steps 2>/dev/null)"; then
note "FAIL — could not read GATE_STEPS (\`make -s print-gate-steps\`)."
note " That target is what makes this guard derived rather than a"
note " second copy of the list. Restore it in the Makefile."
emit
exit 1
fi

count="$(printf '%s\n' "$steps" | wc -w | tr -d ' ')"
if [ "$count" -eq 0 ]; then
note "FAIL — GATE_STEPS is empty."
emit
exit 1
fi

Expand Down Expand Up @@ -167,8 +185,10 @@ if [ "$fail" -ne 0 ]; then
note "The gate's composition lives in GATE_STEPS in the Makefile, and these"
note "two documents restate it for readers. When you add or remove a guard,"
note "update all three in the same PR — that is what this guard is for."
emit
exit 1
fi

printf 'check-gate-parity: OK — all %s gate steps are named in %s and %s.\n' \
"$count" "$agents" "$contributing"
emit
printf 'check-gate-parity: OK — %s gate steps, named in %s and %s.\n' \
"$count" "$agents" "$contributing" || true
44 changes: 41 additions & 3 deletions scripts/check-god-files.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,30 @@ baseline="scripts/file-size-baseline.txt"
agents="AGENTS.md"

fail=0
note() { printf 'check-god-files: %s\n' "$1" >&2; }

# The verdict is decided before anything is written (#1815). Every line is
# buffered while the comparisons run and emitted in one final write, because a
# guard that prints as it scans dies mid-report the moment its reader exits
# early (`| head`, a `tail` that has seen enough) — and under `set -euo
# pipefail` whatever partial comparison state it had reached becomes the exit
# status: a false failure naming whichever crate the race landed on.
# scripts/check-file-size.sh is the shape being copied.
report=""
note() { report="${report}check-god-files: $1"$'\n'; }

# Emission is best-effort: the verdict is already decided, so a reader that
# closed the pipe (`| head -1`, `| true`) must be able to change neither the
# report nor the exit code. SIGPIPE is ignored so a failed write surfaces as a
# discarded error instead of killing the script (#1815).
emit() {
trap '' PIPE
printf '%s' "$report" >&2 || true
}

if [ ! -f "$baseline" ]; then
note "FAIL — $baseline does not exist. It is the only correct copy of the"
note " god-file set; this guard has nothing to compare prose against."
emit
exit 1
fi

Expand All @@ -73,6 +92,7 @@ grep -v '^#' "$baseline" |

if [ ! -s "$work/truth" ]; then
note "FAIL — no crates/ entries in $baseline; the extraction must have broken."
emit
exit 1
fi

Expand All @@ -84,8 +104,23 @@ all_crates="$(
LC_ALL=C sort
)"

# Membership over the newline-delimited crate list, in pure shell.
#
# Deliberately NOT `printf ... | grep -qx`: under `set -o pipefail` that is a
# race, not a test — `grep -q` exits the moment it matches, `printf` then dies
# of SIGPIPE, and pipefail reports the pipeline as failed, so a crate that IS
# present intermittently reads as absent. That race is #1815: it made this
# guard blame a different innocent crate on every piped run. Same shape as
# contains() in scripts/check-command-docs.sh.
has_god_files() {
printf '%s\n' "$crates_with_god_files" | grep -qx "$1"
case "
$crates_with_god_files
" in
*"
$1
"*) return 0 ;;
*) return 1 ;;
esac
}

# Backticked crate-relative source paths on stdin, one per line, sorted.
Expand Down Expand Up @@ -134,6 +169,7 @@ compare() {

if [ ! -f "$agents" ]; then
note "FAIL — $agents does not exist."
emit
exit 1
fi

Expand Down Expand Up @@ -201,10 +237,12 @@ if [ "$fail" -ne 0 ]; then
note "set that can stay correct, so the prose follows it, never the reverse."
note "After \`make file-size-update\`, update AGENTS.md's table and the affected"
note "crate README in the same PR."
emit
exit 1
fi

emit
printf 'check-god-files: OK — %d god file(s) across %d crate(s), named identically in %s and every crate README.\n' \
"$(wc -l <"$work/truth" | tr -d ' ')" \
"$(printf '%s\n' "$crates_with_god_files" | wc -l | tr -d ' ')" \
"$agents"
"$agents" || true
Loading