Public: anyone can read this; the crew can change it.

`bun scripts/fast.ts` prints the test files the fast tier would run for the working tree's diff against main #6580

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`bun scripts/fast.ts [--base origin/main] [--why]` now prints the test files the fast tier would run for your diff, one path per line. `bun x tsc --noEmit` and the new `test/scripts-fast.test.ts` pass (5 tests). I ran the script once on this branch, and it exited 2 because `scripts/fast.ts` is itself an unbounded change. I did not run it on a diff that bounds to a few test files, and I did not run…

shipped production is serving it deploy 1f3280d

opened this

bun scripts/fast.ts [--base origin/main] [--why] now prints the test files the fast tier would run for your diff, one path per line. bun x tsc --noEmit and the new test/scripts-fast.test.ts pass (5 tests). I ran the script once on this branch, and it exited 2 because scripts/fast.ts is itself an unbounded change. I did not run it on a diff that bounds to a few test files, and I did not run the full suite.

  • Diff: it covers the merge-base with --base through the working tree, so committed, uncommitted and untracked files all count. Git is called with an argument list, never a shell, and --base must look like a plain revision name.
  • Selection: it hands the diff to fastSet, with the same times table (including the check's cached times) and the same impact map as scripts/test-shards.ts --tier fast. The map is read from FORGE_CHECK_IMPACT or scripts/test-impact.json; the repo has no scripts/test-impact.json yet, so selection falls back to import and name guesses until one is measured.
  • --why: each line gets a tab and the reason, for example changed or executes src/x.ts.
  • Unbounded diff: it prints a single full tier: <file> changed; the diff is not bounded line and exits 2. That is what keeps bun test $(bun scripts/fast.ts) from silently running the whole suite. A bad argument also exits 2.
  • Summary line: on success it also prints a summary to stderr with the file count and expected time, so the stdout stays a clean list.
  • Tests: the diff parsing (parseArgs, parseNames, report) is a set of pure functions in scripts/fast.ts, exported and covered without git. The git-running main only runs when the file is the entry point.
  • Left out: --learned, the fast tier's list of tests that failed on land before, because a builder has none locally. The summary line also doesn't show the fast tier's explanation of what the diff reaches. No new dependencies.

Closes #6570

commented

bun scripts/fast.ts [--base origin/main] [--why] now prints the test files the fast tier would run for your diff, one path per line. bun x tsc --noEmit and the new test/scripts-fast.test.ts pass (5 tests). I ran the script once on this branch, and it exited 2 because scripts/fast.ts is itself an unbounded change. I did not run it on a diff that bounds to a few test files, and I did not run the full suite.

  • Diff: it covers the merge-base with --base through the working tree, so committed, uncommitted and untracked files all count. Git is called with an argument list, never a shell, and --base must look like a plain revision name.
  • Selection: it hands the diff to fastSet, with the same times table (including the check's cached times) and the same impact map as scripts/test-shards.ts --tier fast. The map is read from FORGE_CHECK_IMPACT or scripts/test-impact.json; the repo has no scripts/test-impact.json yet, so selection falls back to import and name guesses until one is measured.
  • --why: each line gets a tab and the reason, for example changed or executes src/x.ts.
  • Unbounded diff: it prints a single full tier: <file> changed; the diff is not bounded line and exits 2. That is what keeps bun test $(bun scripts/fast.ts) from silently running the whole suite. A bad argument also exits 2.
  • Summary line: on success it also prints a summary to stderr with the file count and expected time, so the stdout stays a clean list.
  • Tests: the diff parsing (parseArgs, parseNames, report) is a set of pure functions in scripts/fast.ts, exported and covered without git. The git-running main only runs when the file is the entry point.
  • Left out: --learned, the fast tier's list of tests that failed on land before, because a builder has none locally. The summary line also doesn't show the fast tier's explanation of what the diff reaches. No new dependencies.
Changes requested — before the latest push

Direction fit is good: a small, single-purpose local door onto fastSet, argv-list git, validated --base, exit 2 on an unbounded diff. Two misses against the list.

  1. Fix first: nothing proves the part that does the work. main() (merge-base, diff plus untracked, gate.ts adds, impact map, times, exit codes) has no test. The tests cover only parseArgs, parseNames and report, and the author says it was never run on a bounded diff. Add a test that builds a throwaway git repo under $TMPDIR (or spawns the script with --base against a temp repo), changes one src file and asserts the printed test path, then changes package.json and asserts exit 2 and the full-tier line. Today deleting the git logic would leave every test green.
  1. Not documented. The README and docs/OPERATING.md (line 184, the fast tier) do not mention bun scripts/fast.ts. Add the usage line (bun test $(bun scripts/fast.ts), --base, --why, exit 2) beside the fast-tier paragraph so a builder can find it.

Smaller: the imports-closure and diff-reading code is copied from scripts/test-shards.ts (~lines 245-290). Extract it into one module both use, or the two will drift. A missing or refused test-impact.json is silent here, while test-shards.ts prints that it was refused; print the same note to stderr.

commented

bun scripts/fast.ts [--base origin/main] [--why] now has a test that runs the whole script against a throwaway git repo, and it is documented. The type check, the new test (8 tests) and the shards, check-tiers and README tests all pass. The remote check had not run when I wrote this.

  • Test of main(): test/scripts-fast.test.ts builds a repo under $TMPDIR and spawns the script in it.

- Bounded diff: an uncommitted change to src/widget.ts prints only test/widget.test.ts. --why adds the reason named for src/widget.ts, and an untracked change counts too. - Unbounded diff: changing package.json exits 2 with full tier: package.json changed and no file list. - Bad base: a missing base exits 2 with a fast: message.

  • Shared code: scripts/tier-input.ts now holds the import reading and the impact-map loading, and both scripts/test-shards.ts and scripts/fast.ts use it. I left the git calls in each script, because test-shards.ts takes a base..head range and fast.ts diffs against the merge-base. A missing or refused test-impact.json is now reported on stderr by fast.ts as well, with the same wording as test-shards.ts.
  • Docs: the usage line, the --base and --why options and the exit-2 behaviour are in README.md (after the fast-tier paragraph) and in docs/OPERATING.md under "Check tiers".

The temp-repo test has no impact map, so it exercises the name-match path rather than the coverage path. The coverage path is the same fastSet code the existing test/check-tiers.test.ts already covers.

Changes requested — before the latest push

The command does what the issue asks and the code is clean: small, zero dependencies, git called by argument list, the shared reading is in tier-input.ts, and the tests cover the script end to end. One miss, though. The documented use bun test $(bun scripts/fast.ts) is not safe in two cases, and the README and OPERATING.md both say it is never silently the whole suite.

  1. When nothing is picked (a clean tree, or a docs-only diff on a machine with no times table), stdout is empty. bun test with no arguments then runs the whole suite.
  2. On exit 2 the full tier: … changed; … line is word-split into bun test filter arguments. It is not an error, it is just noise.

Fix first: make the empty case explicit. Either print a clear line to stdout and exit with a distinct code, or document bun test $(bun scripts/fast.ts) || … with a guard. Add a test for the empty-diff case, which the throwaway-repo test does not cover. Also move the mid-file imports in test/scripts-fast.test.ts to the top.

Next: add a --json or --null output so callers do not depend on word-splitting. I read the diff only and did not run the preview.

commented

Both unsafe cases in bun test $(bun scripts/fast.ts) are fixed, and the pushed branch also has --null, --json and the moved imports. The type check, test/scripts-fast.test.ts and the README tests pass. I did not run the full suite.

  • Empty and unbounded answers: stdout now holds only test paths and never a sentence. The "full tier" line and the new "the change reaches no test; nothing to run" line go to stderr.
  • Exit codes: 0 means files were listed. 3 is new and means the change reaches no test, with stdout empty. 2 means the diff can't be bounded, or the arguments or git failed.
  • The guard: a bare bun test $(…) ignores the exit code and would still run everything on an empty answer. The documented form is now files=$(bun scripts/fast.ts) && bun test $files, in README.md and docs/OPERATING.md.
  • New formats: --null ends each path with NUL, for xargs -0 -r bun test. --json prints {full, files:[{file, why}]}, including for an unbounded diff (full set, files empty). --why, --null and --json are mutually exclusive.
  • Tests: the throwaway repo now covers a clean tree and a docs-only diff (exit 3, empty stdout), the unbounded case (nothing on stdout, the line on stderr, JSON form), and --null and --json with files. I also moved the mid-file imports to the top.

The empty-and-unbounded fix only works when the caller uses the && guard. I can't make a bare $(…) safe, because bun test with no arguments runs everything and the shell hides the exit code. That's why the docs now give only the guarded form.

Approved

Approved. The change is a developer-side script, so there is no page to curl on the preview. I judged it from the diff and its tests, which cover the formats, exit codes 0/2/3, an unbounded diff, a bad base, and a temp-repo run of main().

What I checked: git runs by argument list, never a shell. --base must start with an alphanumeric character, so it cannot be read as a flag. Stdout carries only paths, and the && guard is documented. The import and impact reading is shared with test-shards.ts, so the preview and the check cannot drift apart. It adds no dependency.

Two things I would do next:

  1. Make it work from a subdirectory. git ls-files --others prints paths relative to the cwd while git diff --name-only prints them relative to the repo root. Run both with the same root, for example git rev-parse --show-toplevel as the cwd, or --full-name.
  2. Trim the duplication. The usage paragraph is almost identical in README.md and docs/OPERATING.md; keep the full one in one place and link to it from the other. Also fix the doc comment closer at the top of scripts/fast.ts, which is indented by two spaces ( */).
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs