Three shell E2E tests created scratch files via `mktemp` but never
deleted them on early exit (assertion failure, SIGINT, errexit). Each
CI run leaked ~10-100 KB of /tmp into the runner; over ~200 runs/week
that's 20+ MB of accumulated cruft.
## Files
- **test_chat_attachments_e2e.sh** — was missing both trap and rm;
added per-run TMPDIR_E2E with `trap rm -rf … EXIT INT TERM`.
- **test_notify_attachments_e2e.sh** — had a `cleanup()` for the
workspace but didn't include the TMPF; only an unconditional
`rm -f` at the bottom (line 233) which doesn't fire on early exit.
Extended cleanup() to also rm the scratch + dropped the redundant
trailing rm.
- **test_chat_attachments_multiruntime_e2e.sh** — `round_trip()`
function had per-call `rm -f` only on the success path; failure
paths leaked. Switched to script-level TMPDIR_E2E + trap; per-call
rm dropped (the trap handles every return path including SIGINT).
Pattern: `mktemp -d -t prefix-XXX` for the dir, `mktemp <full-template>`
for files (portable across BSD/macOS + GNU coreutils — `-p` is
GNU-only and breaks Mac local-dev runs).
## Regression gate
New `tests/e2e/lint_cleanup_traps.sh` asserts every `*.sh` that calls
`mktemp` also has a `trap … EXIT` line in the file. Wired into the
existing Shellcheck (E2E scripts) CI step. Verified locally: passes
on the fixed state, fails-loud when one of the 3 fixes is reverted.
## Verification
- shellcheck --severity=warning clean on all 4 touched files
- lint_cleanup_traps.sh passes on the post-fix tree (6 mktemp users,
all have EXIT trap)
- Negative test: revert one fix → lint exits 1 with file:line +
suggested fix pattern in the error message (CI-grokkable
::error file=… annotation)
- Trap fires on SIGTERM mid-run (smoke-tested on macOS BSD mktemp)
- Trap fires on `exit 1` (smoke-tested)
## Bars met (7-axis)
- SSOT: trap pattern documented in lint message (one rule, one fix)
- Cleanup: this IS the cleanup hygiene fix
- 100% coverage: lint catches future regressions across all
`tests/e2e/*.sh` files, not just the 3 fixed today
- File-split: N/A (no files split)
- Plugin / abstract / modular: N/A (test infra, not product code)
Iteration 2 of RFC #2873.
41 lines
1.5 KiB
Bash
Executable File
41 lines
1.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# lint_cleanup_traps.sh — regression gate for the OSS-shape program's
|
|
# "all E2E tests must have proper cleanup" bar (RFC #2873).
|
|
#
|
|
# Asserts: every shell file under tests/e2e/ that calls `mktemp` ALSO
|
|
# installs an `EXIT` trap somewhere in the file. The trap is the
|
|
# minimum-viable guarantee that scratch files won't leak when an
|
|
# assertion or curl exits the script non-zero.
|
|
#
|
|
# Why this lints (instead of the test runner enforcing): shell scripts
|
|
# can't easily be wrapped by an outer harness without breaking the
|
|
# `WSID=… ./test_x.sh` invocation contract. Static gate is the cheap
|
|
# defense.
|
|
#
|
|
# Usage:
|
|
# tests/e2e/lint_cleanup_traps.sh
|
|
#
|
|
# Exits non-zero if any test_*.sh has unmatched mktemp/trap. CI invokes
|
|
# it from the existing Shellcheck (E2E scripts) workflow.
|
|
|
|
set -euo pipefail
|
|
|
|
cd "$(dirname "$0")"
|
|
|
|
violations=0
|
|
for f in test_*.sh; do
|
|
if grep -qE '\bmktemp\b' "$f"; then
|
|
if ! grep -qE 'trap[[:space:]]+.*EXIT' "$f"; then
|
|
echo "::error file=tests/e2e/$f::has 'mktemp' but no 'trap … EXIT' — scratch will leak when test exits non-zero. Pattern: TMPDIR_E2E=\$(mktemp -d -t prefix-XXX); trap 'rm -rf \"\$TMPDIR_E2E\"' EXIT INT TERM"
|
|
violations=$((violations + 1))
|
|
fi
|
|
fi
|
|
done
|
|
|
|
if [ "$violations" -gt 0 ]; then
|
|
echo "::error::$violations shell E2E file(s) leak scratch on early exit. See above."
|
|
exit 1
|
|
fi
|
|
|
|
echo "✓ all $(grep -lE '\bmktemp\b' test_*.sh | wc -l | tr -d ' ') shell E2E files with mktemp also install an EXIT trap"
|