ci: run coverage-exempt heavy suites uninstrumented in parallel

The coverage lane's wall clock was pinned by a few compiler- and
subprocess-bound suites whose v8 instrumentation tax is a multiple of
their runtime while contributing nothing the per-file thresholds need:
typert generator fixtures (whole-workspace compiler analysis; its src is
threshold-excluded) and three scripts/ child-process fixture suites
(scripts/ sources are never coverage-measured; in-process imports are
covered by their owning package tests).

Split ci-coverage into two parallel gates: the instrumented run sets
DSH_COVERAGE_EXEMPT_HEAVY=1 and vitest.config.ts drops the exempt suites
from both projects (CLI --exclude cannot reach per-project include
resolution); a second uninstrumented gate runs exactly those suites, so
the aggregate still executes every test. Membership contract and the
filter/exclude pairs live in scripts/coverage-exempt.ts.

Local 6-worker A/B: instrumented gate 900s -> 260s wall; exempt gate
262s wall runs beside it, so the lane converges near the slower of the
two (~4.4min vs ~7min single-gate). DSH_GATE_CONCURRENCY now has two
schedulable gates in this lane.
This commit is contained in:
imccyu
2026-07-31 02:24:37 +08:00
parent 8a3b47ae5e
commit e9ed7193d8
3 changed files with 80 additions and 12 deletions

View File

@@ -1,5 +1,6 @@
import tsconfigPaths from 'vite-tsconfig-paths'
import { defineConfig } from 'vitest/config'
import { COVERAGE_EXEMPT_ENV, coverageExemptHeavySuites } from './scripts/coverage-exempt.ts'
// Resolution facade shared by every plugin instance below: tsconfig.base.json
// has no include, which vite-tsconfig-paths treats as match-all, so its paths
@@ -37,6 +38,12 @@ const testIncludes = [
'scripts/**/*.spec.ts',
]
// The instrumented coverage gate sets this env; the exempt heavy suites then
// run beside it uninstrumented (membership contract in scripts/coverage-exempt.ts).
const coverageExemptExcludes = process.env[COVERAGE_EXEMPT_ENV] === '1'
? coverageExemptHeavySuites.map(suite => suite.exclude)
: []
// These suites exercise process-global state, process APIs, or timing-sensitive process I/O
// that worker threads cannot isolate reliably under aggregate gate contention.
// Keep the narrow exception in forks while the rest of the inventory avoids per-file processes.
@@ -73,6 +80,7 @@ export default defineConfig({
exclude: [
...windowsUnsupportedPackages.map(path => `${path}/tests/**/*.spec.ts`),
...processBoundTests,
...coverageExemptExcludes,
],
},
},
@@ -83,7 +91,10 @@ export default defineConfig({
pool: 'forks',
setupFiles: ['./scripts/test-invariants.ts'],
include: processBoundTests,
exclude: windowsUnsupportedPackages.map(path => `${path}/tests/**/*.spec.ts`),
exclude: [
...windowsUnsupportedPackages.map(path => `${path}/tests/**/*.spec.ts`),
...coverageExemptExcludes,
],
},
},
],