91 lines
3.7 KiB
Bash
Executable file
91 lines
3.7 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Compare current `npm run typecheck` output to the known tech-debt snapshot.
|
|
# Emits ONLY new errors introduced relative to the snapshot — does not dump
|
|
# the full output, so agents can read the result without burning context.
|
|
#
|
|
# Comparison is line/col-insensitive: each error's `(L,C):` location is masked
|
|
# to `(_,_):` before sorting + comm, so a pure line shift (e.g. one added/
|
|
# removed line above the error) doesn't trigger a phantom NEW + fixed pair.
|
|
# Re-run `npm run typecheck` to see real positions for any errors flagged here.
|
|
#
|
|
# Caveat: this checks the working tree, not the staged worktree. If you stage
|
|
# a fix but leave it unstaged, or vice versa, the diff reports the working-tree
|
|
# state. For a strict pre-commit gate, run from a clean stash-apply state.
|
|
#
|
|
# `npm run check:eslint` is now a normal green check (0 errors); no snapshot
|
|
# needed there. Run it directly if you want to see warnings.
|
|
#
|
|
# Usage:
|
|
# bash docs/known-tech-debt-lint/diff.sh
|
|
|
|
set -u
|
|
|
|
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
BASELINE_DIR="$ROOT/docs/known-tech-debt-lint"
|
|
TC_BASE="$BASELINE_DIR/typecheck.snapshot.txt"
|
|
|
|
# Temp files registered for cleanup on any exit path (success, error, ^C).
|
|
TMP_FILES=()
|
|
cleanup() { [ "${#TMP_FILES[@]}" -gt 0 ] && rm -f "${TMP_FILES[@]}"; }
|
|
trap cleanup EXIT INT TERM
|
|
|
|
# Tracks whether any new errors were introduced. Set to 1 by run_typecheck_diff
|
|
# when delta > 0; script exits with this code at end so CI / runbooks can use
|
|
# the script as a gate (e.g. `bash diff.sh && echo OK`).
|
|
NEW_ERRORS=0
|
|
|
|
run_typecheck_diff() {
|
|
echo "=== typecheck diff vs known-tech-debt snapshot ==="
|
|
local now tc_rc
|
|
now="$(mktemp)"
|
|
TMP_FILES+=("$now")
|
|
( cd "$ROOT" && npm run typecheck ) >"$now" 2>&1
|
|
tc_rc=$?
|
|
# Sanity-check: distinguish "tsc ran and reported errors" (rc=2, expected on
|
|
# this baseline) from "tsc/npm/node failed to run at all" (rc=other, broken
|
|
# toolchain). Without this guard a rc=127 "tsc: not found" or rc=1 npm error
|
|
# could produce stdout with no "error TS" lines and the diff would falsely
|
|
# report "no new errors" / "incidentally fixed: 33".
|
|
if [ "$tc_rc" -ne 0 ] && [ "$tc_rc" -ne 2 ] && ! grep -q "error TS" "$now"; then
|
|
echo " ERROR: 'npm run typecheck' did not run cleanly (exit=$tc_rc):"
|
|
sed 's/^/ /' "$now"
|
|
NEW_ERRORS=2
|
|
return
|
|
fi
|
|
# Mask `(line,col):` so a pure line shift doesn't change an error's identity.
|
|
# We compare on the masked form; for NEW lines we display the masked form (the
|
|
# real position is reproducible by running `npm run typecheck` directly).
|
|
# `sort` (NOT `sort -u`): we want to preserve cardinality so that two identical
|
|
# masked errors in the same file aren't collapsed to one — a regression that
|
|
# adds a duplicate error would otherwise be hidden by the first occurrence.
|
|
local mask_re='s/\([0-9]+,[0-9]+\):/(_,_):/'
|
|
local now_masked base_masked
|
|
now_masked="$(mktemp)"
|
|
base_masked="$(mktemp)"
|
|
TMP_FILES+=("$now_masked" "$base_masked")
|
|
sed -E "$mask_re" "$now" | grep -E "error TS" | sort > "$now_masked"
|
|
sed -E "$mask_re" "$TC_BASE" | grep -E "error TS" | sort > "$base_masked"
|
|
|
|
local new
|
|
new="$(comm -23 "$now_masked" "$base_masked")"
|
|
if [ -z "$new" ]; then
|
|
echo " no new typecheck errors"
|
|
else
|
|
local count
|
|
count="$(printf '%s\n' "$new" | wc -l)"
|
|
echo " NEW errors: $count (line/col masked — run \`npm run typecheck\` for real positions)"
|
|
printf '%s\n' "$new" | sed 's/^/ /'
|
|
NEW_ERRORS=1
|
|
fi
|
|
local fixed
|
|
fixed="$(comm -13 "$now_masked" "$base_masked")"
|
|
if [ -n "$fixed" ]; then
|
|
local fcount
|
|
fcount="$(printf '%s\n' "$fixed" | wc -l)"
|
|
echo " (incidentally fixed: $fcount)"
|
|
fi
|
|
}
|
|
|
|
run_typecheck_diff
|
|
|
|
exit "$NEW_ERRORS"
|