Find guards wired onto a helper that can never return non-zero

This commit is contained in:
Gmer4Lfe
2026-08-24 18:22:59 -04:00
parent 37017882c7
commit 367d0203b8
+171
View File
@@ -0,0 +1,171 @@
#!/bin/bash
# ══════════════════════════════════════════════════════════════════════════════════════════════
# PURPOSE
# Find guards that cannot fire. A script that writes
#
# do_the_work || STEP_OK=false
#
# is only telling the truth if do_the_work can actually return non-zero. When it cannot, the
# flag stays true no matter what happened, and the run reports a step it never completed.
#
# This is the most expensive bug shape in this repo — a summary that says ✅ is the thing the
# operator trusts instead of reading the log. adapter.sh already carries the scar: "every
# caller that wrote `platform_push_setup_state || X=false` was testing a constant."
#
# Written 2026-08-24. Its first run found partnership_offboard.sh Step 3 reporting a stack
# cleanup that could not fail and, on a second path, one that had not run at all.
#
# OPERATIONAL MODEL
# For every `cmd || VAR=false` in the tree, the command left of the || is resolved and
# classified:
#
# CANFAIL a shell function with an explicit non-zero exit path — the guard is real
# EXTERNAL chown, docker, ssh, rsync and friends — can fail, nothing to check
# IDIOM [[ … ]] && X=true || X=false — a conditional, not a guard
# NEVERFAILS a function with no non-zero path — the guard is decorative
# UNKNOWN could not be resolved; reported rather than assumed either way
#
# A function "can fail" if it contains `return` or `exit` with a non-zero literal or any
# variable. The variable case matters: cleanup_partner_containers ends `return "$_rc"`, and a
# pattern that only looked for digits reported a working guard as broken.
#
# UNKNOWN is a first-class verdict, not a failure. Deciding what a bare `done` returns means
# evaluating the last command of the last loop, and a checker that guesses at that would
# produce confident wrong answers in both directions.
#
# DESIGN PRINCIPLES
# Resolve the command, never word-split it.
# An early build stripped a leading "do" to handle `for x in …; do cmd || F=false; done`
# and turned do_final_sync into a phantom _final_sync that resolved to nothing. The
# inline-loop case is matched on "; do " with required trailing space; nothing else is
# trimmed from a command name.
#
# Comments are excluded before anything else.
# Two of the matches in this repo are prose describing the bug, in adapter.sh and
# rsync_stop.sh. A checker that reports the documentation of a fixed bug as the bug
# teaches the operator to skim its output.
#
# Every verdict names where it looked.
# A NEVERFAILS line carries the file and line of the function it judged, because the
# first question is always "which definition did it find" — a name can exist twice.
#
# OPERATIONAL SAFEGUARDS
# Read-only. Greps and reads; writes nothing, executes nothing it finds.
# Exits 1 when any NEVERFAILS is reported, 0 otherwise — safe to gate a commit on.
# UNKNOWN never fails the run. It is a prompt to look, not a defect claim.
#
# RUNTIME MODES
# ══════════════════════════════════════════════════════════════════════════════════════════════
#
# audit_guards.sh
# Classify every guard in the repo. Exits 1 if any guard cannot fire.
#
# audit_guards.sh --all
# Show every verdict, including the guards that are sound.
#
# audit_guards.sh --unknown
# Show only the guards that could not be resolved.
#
# ══════════════════════════════════════════════════════════════════════════════════════════════
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
SHOW_ALL=false
ONLY_UNKNOWN=false
for a in "$@"; do
case "$a" in
--all) SHOW_ALL=true ;;
--unknown) ONLY_UNKNOWN=true ;;
*) echo "unknown argument: $a" >&2; exit 2 ;;
esac
done
cd "$REPO_ROOT" || { echo "cannot reach repo root: $REPO_ROOT" >&2; exit 2; }
# Locate a function definition and print "file:line" followed by its body.
_fn_body() {
local fn="$1" loc f ln
loc=$(grep -rnE "^[[:space:]]*(function[[:space:]]+)?${fn}[[:space:]]*\(\)" \
--include='*.sh' . 2>/dev/null | grep -v '^./.git' | head -1)
[[ -z "$loc" ]] && return 1
f=${loc%%:*}
ln=$(echo "$loc" | cut -d: -f2)
echo "${f#./}:$ln"
awk -v s="$ln" 'NR>=s { print; if (NR > s && /^\}/) exit }' "$f"
}
dead=0 sound=0 unknown=0 other=0
while IFS= read -r hit; do
file=${hit%%:*}
lno=$(echo "$hit" | cut -d: -f2)
code=$(echo "$hit" | cut -d: -f3-)
# Everything left of the ||, with an inline loop header removed. The trailing space after
# "do" is required — without it this eats the prefix of do_final_sync.
cmd=$(echo "$code" | sed 's/[[:space:]]*||.*//' \
| sed 's/^[[:space:]]*//' \
| sed 's/^.*;[[:space:]]*do[[:space:]]\{1,\}//')
head=$(echo "$cmd" | awk '{print $1}')
verdict=""; detail=""
case "$head" in
'[['|'[' | test )
verdict=IDIOM; detail="conditional, not a guard" ;;
chown|chmod|find|rm|mv|cp|docker|ssh|rsync|systemctl|timeout|curl|git|mkdir|ln|tar )
verdict=EXTERNAL; detail="external command, can fail" ;;
-*|2\>*|1\>*|\>* )
# A guard written across several lines: the match landed on a continuation of an
# external command (find … -exec …, a redirect) rather than on its head. Reading
# back to the head would mean parsing line continuations; the classification is
# the same either way, so it is recorded as what it is.
verdict=EXTERNAL; detail="continuation of a multi-line external command" ;;
esac
if [[ -z "$verdict" ]]; then
if body=$(_fn_body "$head"); then
where=$(echo "$body" | head -1)
body=$(echo "$body" | tail -n +2)
# Non-zero literal, or any variable — quoted or bare.
if echo "$body" | grep -qE '^[[:space:]]*(return|exit)[[:space:]]+("?\$|[1-9])'; then
verdict=CANFAIL; detail="$where"
else
verdict=NEVERFAILS
detail="$where — no non-zero exit path"
fi
else
verdict=UNKNOWN; detail="could not resolve '$head'"
fi
fi
case "$verdict" in
NEVERFAILS) dead=$((dead+1)) ;;
CANFAIL) sound=$((sound+1)) ;;
UNKNOWN) unknown=$((unknown+1)) ;;
*) other=$((other+1)) ;;
esac
if $ONLY_UNKNOWN; then
show=false
[[ "$verdict" == UNKNOWN ]] && show=true
elif $SHOW_ALL || [[ "$verdict" == NEVERFAILS ]]; then
show=true
else
show=false
fi
if $show; then
printf '%-11s %s:%s\n %s\n %s\n' \
"$verdict" "$file" "$lno" "$(echo "$cmd" | cut -c1-76)" "$detail"
fi
done < <(grep -rnE '\|\|[[:space:]]*[A-Za-z_][A-Za-z0-9_]*=false' --include='*.sh' . 2>/dev/null \
| grep -v '^./.git' \
| grep -vE ':[0-9]+:[[:space:]]*#' \
| sed 's|^\./||')
echo
echo "──────────────────────────────────────────────────────────────"
printf 'sound %s external/idiom %s unresolved %s CANNOT FIRE %s\n' \
"$sound" "$other" "$unknown" "$dead"
[[ $dead -gt 0 ]] && exit 1
exit 0