From 5a16e42ba6f8b5d351b4b5113b9f76026c1bb888 Mon Sep 17 00:00:00 2001 From: Joseph Mearman Date: Tue, 27 Oct 2026 17:49:54 +0000 Subject: [PATCH] chore(build): run only the tests related to a push in the pre-push hook The hook ran the whole suite, which CI runs again on the pull request, so every push attempt, and every failed one, repeated every test file. It now runs lint, typecheck and the cached frontend build as before, then only the tests vitest relates to the files changed since the push diverged from origin/main, with workers bounded by the cores the run queue leaves free. Closes #463 --- .husky/pre-push | 8 +++++++- turbo.json | 9 ++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.husky/pre-push b/.husky/pre-push index 5595d082..394f72e2 100644 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1 +1,7 @@ -pnpm run prepush 2>&1 +# Run lint, typecheck and the frontend build, then the tests related to what the push changes. The full suite is CI's job: it runs there on every pull request, so repeating it here only repeats the same work for each push attempt, and again for each attempt that fails. +# The related tests are those vitest finds from the module graph for the files changed since the push diverged from origin/main, and a push that changes no source file has none to run, which is a pass. A change to the vitest config, package.json or the lockfile still reruns everything, as vitest treats those as triggers. +# Workers are the cores the machine's run queue leaves free, and at least one: vitest starts a pool the size of the core count by default, so several pushes at once on a shared machine each oversubscribe it, and every test slows until the guards against a hang fire on tests that did nothing wrong. +base=$(git merge-base origin/main HEAD) && +workers=$(node -p "Math.max(1, os.availableParallelism() - Math.ceil(os.loadavg()[0]))") && +pnpm run prepush 2>&1 && +pnpm exec vitest run --changed "$base" --passWithNoTests --maxWorkers="$workers" 2>&1 diff --git a/turbo.json b/turbo.json index 15da1c9c..f18faa41 100644 --- a/turbo.json +++ b/turbo.json @@ -17,11 +17,14 @@ "inputs": ["$TURBO_DEFAULT$", "eslint.config.ts"] }, "_prepush": { - "dependsOn": ["_lint", "_typecheck", "_test"] + // The checks the pre-push hook runs before its related-tests run. The build is here because the web-bridge + // integration tests the hook may select need it (see _test), and turbo caches it, so it costs nothing when + // the frontend sources are unchanged. + "dependsOn": ["_lint", "_typecheck", "_build:frontend"] }, "_test": { - // The web-bridge integration tests request the frontend routes, which serve src/bridges/user/web/dist - // -- without this dependency, a fresh checkout with no prior build (a new worktree's pre-push hook, in + // The web-bridge integration tests request the frontend routes, which serve src/bridges/user/web/dist. + // Without this dependency, a fresh checkout with no prior build (a new worktree running the tests, in // particular) gets 503 from those routes and fails those suites rather than actually exercising them. "dependsOn": ["_build:frontend"] },