168 non-green runs since the first workflow run were classified (docs/analysis/ci-failures-2026-10-06.md): 102 were tree checks that finish in under 25 s on the pushing machine, 40 were GitHub-side refusals nobody saw. tools/ci/pre-push.sh is the one list of fast checks; ci.yml's site job calls it with --ci and the pre-push hook with --hook (full gate for master and release-*, structural checks for other refs; never writes into the worktree). tools/ci/export-exclude.txt lists research documents outside the public export list, pruned by identity-check.sh and by the mirror's sync.sh (self-test: an excluded path may quote the patterns, an exported one may not); polish.md and this record are its first entries, which makes master green. tools/ci/windows-paths-check.sh (colon, trailing dot or space, reserved names, over 240 characters) runs as the pre-commit hook on staged paths and in the gate. tools/ci/red-watch.mjs plus the red job on the box's runner record one line per failed master or release-* run to /srv/ci-red/red.jsonl; igneum-ci-red.timer posts each once to the updates channel. pow and sims read IGNEUM_CI_RUNNER for the box. no-foreign-tree-writes.sh no longer exits silently on its warning pipeline under pipefail. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
43 lines
3.4 KiB
Bash
Executable file
43 lines
3.4 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# A script writes only under its own repository (git rev-parse --show-toplevel of its own path), the downloads folder
|
|
# and the scratch dirs. It never builds a target path from another worktree's name, from a list of worktrees or from a
|
|
# walk over $HOME/Projects. Ruled 6 October 2026: five worktrees held 0.3.14's site rows as uncommitted edits to tracked
|
|
# files after the pre-push hook's site build fetched the live downloads index and rewrote its snapshot in whatever tree
|
|
# the push ran from (site/build.mjs now writes the snapshot only on SITE_DOWNLOADS_REFRESH=1 or in CI). Runs in CI and
|
|
# locally; --self-test shows it firing.
|
|
set -euo pipefail
|
|
cd "$(git rev-parse --show-toplevel)"
|
|
# a path built from a worktree list or a Projects walk: `git worktree list` piped into a loop with a write, or
|
|
# $HOME/Projects, ~/Projects, /Users/*/Projects used in a path (comments and docs excluded; strings in tests excluded)
|
|
PAT='(\$HOME|~|/Users/[a-z]+)/Projects/igneum-wt-|(\$HOME|~|/Users/[a-z]+)/Projects/(\*|igneum\*|igneum-wt-\*)|git worktree list[^|]*\|[^#]*(cp|mv|tee|>|writeFileSync|install )'
|
|
# the shared checkout as an absolute default (/Users/<user>/Projects/igneum/...) is the lesser class: a read of a binary
|
|
# or a script there, overridable by an environment variable. Listed as a warning; the row of 6 October 2026 moves each
|
|
# to an env-only default (no fallback path) and this pattern then joins PAT.
|
|
WARN='/Users/[a-z]+/Projects/igneum/'
|
|
check_file() {
|
|
local f="$1" bad=0
|
|
while IFS= read -r line; do
|
|
local code="${line%%#*}"
|
|
[[ "$code" =~ ^[[:space:]]*(//|\*|/\*) ]] && continue
|
|
[[ "$code" =~ $PAT ]] || continue
|
|
echo "foreign-tree: $f builds a path into another worktree or a Projects walk: ${line:0:140}"; bad=1
|
|
done < "$f"
|
|
return $bad
|
|
}
|
|
if [ "${1:-}" = "--self-test" ]; then
|
|
t="$(mktemp -d)"; trap 'rm -rf "$t"' EXIT
|
|
printf 'cp out.json "$HOME/Projects/igneum-wt-other/site/downloads.json"\nfor d in ~/Projects/igneum*/; do echo "$d"; done\n' > "$t/bad.sh"
|
|
printf 'for w in $(git worktree list | cut -d" " -f1); do cp x "$w/site/x"; done\n' > "$t/bad2.sh"
|
|
printf 'ROOT="$(git rev-parse --show-toplevel)"; cp out.json "$ROOT/site/downloads.json"\n# ~/Projects/igneum is fine in a comment\n' > "$t/good.sh"
|
|
check_file "$t/bad.sh" && { echo "self-test failed: bad.sh passed"; exit 1; }
|
|
check_file "$t/bad2.sh" && { echo "self-test failed: bad2.sh passed"; exit 1; }
|
|
check_file "$t/good.sh" || { echo "self-test failed: good.sh flagged"; exit 1; }
|
|
echo "self-test passed: a Projects path fails, a worktree-list loop with a write fails, an own-toplevel write passes"; exit 0
|
|
fi
|
|
fail=0
|
|
# `|| true`: with pipefail a file without a warning hit fails this pipeline, and set -e then ends the script silently with
|
|
# exit 1 (bash 3.2 on the Mac; the two CI runs right after this check landed on 6 October 2026 died the same way)
|
|
while IFS= read -r f; do grep -nE "$WARN" "$f" | grep -vE '^[0-9]+:\s*(#|//)' | sed "s|^|foreign-tree (warning, env-only default owed): $f:|" | cut -c1-200 || true; done < <(git ls-files 'tools/**' 'packaging/**' 'infra/**' 'relay/**' | grep -E '\.(sh|mjs|js|py|ps1)$' | grep -v '^tools/ci/no-foreign-tree-writes.sh$')
|
|
while IFS= read -r f; do check_file "$f" || fail=1; done < <(git ls-files 'tools/**' 'packaging/**' 'site/*.mjs' 'infra/**' 'relay/**' | grep -E '\.(sh|mjs|js|py|ps1)$' | grep -v '^tools/ci/no-foreign-tree-writes.sh$')
|
|
[ "$fail" = 0 ] && echo "foreign-tree: every script writes under its own toplevel"
|
|
exit $fail
|