#!/usr/bin/env bash # The swallowed-defaults class (6 October 2026, twice in one evening): a comment appended to a line of shell assignments # (`JOBS=...; # ... OUT=""; CARGO_ARGS=()`) turns every assignment after the `#` into comment text; the script still parses, # bash -n and shellcheck say nothing, and the first use of the undeclared array dies under the Mac's bash 3.2 with # "unbound variable" (build-remote.sh at 19:5x UTC, cross-remote.sh at 21:xx UTC: the shipper's default cross-build never ran # and its chain kept the previous exes). Rule: no `#` comment on a line that carries shell assignments after it. # The scan (python3, which every CI host has) first drops quoted strings, ${...} expansions, $# and [^#] so a `#` inside them # is not a comment, then flags a line where a comment start (start of line or whitespace, then #) is followed by `NAME=`. # # tools/ci/defaults-line-check.sh # exit 1 with file:line on a hit # tools/ci/defaults-line-check.sh --self-test # fires on the swallowed shape, passes clean ones (including ${a#b} and sed s#x#y#) set -euo pipefail cd "$(dirname "$0")/../.." scan() { # file names as arguments (python3 - takes its script from stdin, so stdin cannot carry the list), file:line per hit, exit 1 if any python3 - "$@" <<'PY' import re, sys strip = [ (re.compile(r"\$\{[^}]*\}"), ""), # ${var#pat}, ${var%pat}, ${!i} (re.compile(r"\$#"), ""), # the argument count (re.compile(r"\[\^#\]"), ""), # a bracket expression excluding # (re.compile(r"'[^']*'"), "''"), # single-quoted strings (re.compile(r'"(?:[^"\\]|\\.)*"'), '""'), # double-quoted strings (re.compile(r"\\#"), ""), # an escaped # ] # the swallowed shape is an assignment LIST after the comment start: `NAME=...;` or `NAME=()`; a prose mention such as # "(access public|private, private only in NET_MODE=private)" or "OTA_SKIP=1 (the default now)" has neither hit = re.compile(r"(^|\s)#.*\s[A-Za-z_][A-Za-z0-9_]*=(\(\)|[^;()]*;)") bad = 0 for path in sys.argv[1:]: try: lines = open(path, encoding="utf-8", errors="replace").read().split("\n") except OSError: continue for n, line in enumerate(lines, 1): s = line for rx, rep in strip: s = rx.sub(rep, s) if s.lstrip().startswith("#"): continue # a whole-line comment may say anything if hit.search(s): print(f"defaults-line: {path}:{n}: a comment swallows assignments after it: {line.strip()[:140]}"); bad = 1 sys.exit(bad) PY } if [ "${1:-}" = --self-test ]; then t=$(mktemp -d); trap 'rm -rf "$t"' EXIT printf '%s\n' 'JOBS="${JOBS:-}"; # empty = the box decides OUT=""; CARGO_ARGS=()' > "$t/bad.sh" printf '%s\n' 'JOBS="${JOBS:-}"; OUT=""; CARGO_ARGS=() # one line, nothing assigned after the comment' \ 'ver=${tarball#node-}; ver=${ver%-linux-x64.tar.xz}' \ "epoch=\$(sed -n 's/^#define X \"\\(.*\\)\"/\\1/p' \"\$ph\"); day=\$(sed -n 's/^#define Y/x/p')" \ 'rel="${a#"$TARGET_DIR"/}"; dest="$OUT/$rel"' \ 'for ((i=1;i<=$#;i++)); do fee="${!i}"; done' \ "A=\"\$(find . | sed 's#^\\./##' | sort)\"; B=1" \ 'hetzner_create_one() { # name type location [role] [access] (access public|private, private only in NET_MODE=private)' \ 'if [ "${OTA_SKIP:-}" = 0 ]; then SIGN=1; fi # the old spelling; OTA_SKIP=1 (the default now) leaves the manifest alone' \ '# a whole-line comment with OUT=""; CARGO_ARGS=() in it' > "$t/good.sh" if scan "$t/bad.sh" >/dev/null; then echo "defaults-line self-test: the swallowed shape did NOT fire"; exit 1; fi if scan "$t/good.sh"; then echo "defaults-line self-test: fires on the swallowed shape, passes the clean shapes"; exit 0; else echo "defaults-line self-test: a clean shape fired"; exit 1; fi fi # shellcheck disable=SC2046 # paths in this tree carry no spaces if scan $(git ls-files 'tools/*.sh' 'tools/**/*.sh' 'infra/**/*.sh' 'proto-cuda/**/*.sh' 'packaging/**/*.sh'); then echo "defaults-line: no assignment hides behind a comment"; else exit 1; fi