mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-27 23:21:53 +02:00
v1.90.2.0 perf: halve local free-suite time and preserve coverage (#2972)
* perf: remove repeated test work and preserve AUQ execution budgets * fix: validate native evaluation fixture evidence at its actual boundaries * fix: clarify deployment approval and recovery state transitions * chore: document coverage and release v1.90.2.0 * test: preserve Windows scheduling and native no-change consent * test: recognize verified reads through fixture symlinks * test: isolate alias-name installation from runtime assets
This commit is contained in:
+13
-10
@@ -91,10 +91,13 @@ import {
|
||||
installChildSignalForwarding,
|
||||
isTerminationRequested,
|
||||
killProcessGroup,
|
||||
normalizeRelativePath,
|
||||
strictTestExitCode,
|
||||
stripAnsiLine,
|
||||
} from './test-strict-output';
|
||||
|
||||
export { normalizeRelativePath } from './test-strict-output';
|
||||
|
||||
const ROOT = path.resolve(import.meta.dir, '..');
|
||||
// design/test was silently absent from BOTH the package.json test script and
|
||||
// this list — design tests (including a teardown bomb) never ran in any CI
|
||||
@@ -420,10 +423,14 @@ export function wallTimeoutForPackedShard(predictedMs: number, baseMs = DEFAULT_
|
||||
return Math.max(baseMs, Math.ceil(predictedMs * 3), fileCount * PER_FILE_WALL_MS);
|
||||
}
|
||||
/**
|
||||
* Full-suite parallelism: leave RESERVED_CPUS cores for the parent runner +
|
||||
* OS, cap at MAX_FULL_SUITE_JOBS — beyond ~6 concurrent bun processes the
|
||||
* playwright-heavy shards contend on browser launches instead of finishing
|
||||
* sooner (measured on an M-series dev box).
|
||||
* Full-suite parallelism: use all available CPUs, with a floor of one and a
|
||||
* cap of MAX_FULL_SUITE_JOBS. Shards stay serial internally; separate shard
|
||||
* processes can overlap subprocess and I/O waits without a fixed CPU reserve.
|
||||
* Prefer availableParallelism() to honor CPU affinity, falling back to cpus()
|
||||
* on runtimes without it. Keep the existing cap: beyond ~6 concurrent bun
|
||||
* processes, playwright-heavy shards contended on browser launches in the
|
||||
* original M-series measurement. More shards are not a guaranteed speedup;
|
||||
* compare complete-suite runs before raising the default further.
|
||||
*
|
||||
* GSTACK_FREE_JOBS overrides the computed count (the free runner's analogue
|
||||
* of the paid runner's EVALS_JOBS). Exists for syscall-supervised sandboxes:
|
||||
@@ -437,7 +444,6 @@ export function wallTimeoutForPackedShard(predictedMs: number, baseMs = DEFAULT_
|
||||
* beefy box can also raise it deliberately.
|
||||
*/
|
||||
export const MAX_FULL_SUITE_JOBS = 6;
|
||||
export const RESERVED_CPUS = 2;
|
||||
|
||||
export function fullSuiteJobs(): number {
|
||||
const raw = process.env.GSTACK_FREE_JOBS;
|
||||
@@ -449,7 +455,8 @@ export function fullSuiteJobs(): number {
|
||||
}
|
||||
return Number.parseInt(raw, 10);
|
||||
}
|
||||
return Math.max(1, Math.min(MAX_FULL_SUITE_JOBS, os.cpus().length - RESERVED_CPUS));
|
||||
const availableCpus = os.availableParallelism?.() ?? os.cpus().length;
|
||||
return Math.max(1, Math.min(MAX_FULL_SUITE_JOBS, availableCpus));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -486,10 +493,6 @@ export const WORKER_HOSTILE: Record<string, string> = {
|
||||
*/
|
||||
export const TREE_MUTATING: Record<string, string> = {};
|
||||
|
||||
export function normalizeRelativePath(filePath: string): string {
|
||||
return filePath.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
export function isFreeTestFile(relativePath: string): boolean {
|
||||
const normalized = normalizeRelativePath(relativePath);
|
||||
if (!TEST_FILE_REGEX.test(normalized)) return false;
|
||||
|
||||
@@ -54,12 +54,12 @@ import * as fs from 'node:fs';
|
||||
import * as os from 'node:os';
|
||||
import * as path from 'node:path';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { normalizeRelativePath } from './test-free-shards';
|
||||
import {
|
||||
BunTestOutputClassifier,
|
||||
exactTestFileSelectors,
|
||||
forwardAndClassify,
|
||||
isTerminationRequested,
|
||||
normalizeRelativePath,
|
||||
runShardChild,
|
||||
strictTestExitCode,
|
||||
} from './test-strict-output';
|
||||
|
||||
@@ -92,6 +92,11 @@ function matches(file: string, patterns: readonly string[]): boolean {
|
||||
return patterns.some(pattern => matchGlob(file, pattern));
|
||||
}
|
||||
|
||||
export const FREE_ONLY_PR_FILES = [
|
||||
'scripts/test-free-shards.ts',
|
||||
'test/helpers/auq-parallel-worker.ts',
|
||||
] as const;
|
||||
|
||||
function knownNonBehaviorFile(file: string): boolean {
|
||||
// A mapped dependency still wins over these exemptions. New helper/fixture,
|
||||
// runtime, dependency, or workflow files are deliberately not exempted.
|
||||
@@ -99,6 +104,7 @@ function knownNonBehaviorFile(file: string): boolean {
|
||||
// Hermetic skill views exclude checkout instructions; these are maintained
|
||||
// by free doc/generation checks and are not copied into paid fixtures.
|
||||
|| ['AGENTS.md', 'CLAUDE.md', 'agents-digest/gstack-AGENTS.md'].includes(file)
|
||||
|| FREE_ONLY_PR_FILES.some(freeOnly => freeOnly === file)
|
||||
|| (file.startsWith('test/') && /\.test\.tsx?$/.test(file) && !isPaidTestFile(file));
|
||||
}
|
||||
|
||||
|
||||
@@ -310,6 +310,10 @@ export function strictTestExitCode(
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function normalizeRelativePath(filePath: string): string {
|
||||
return filePath.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Bun treats positional test paths as substring filters. Resolve every
|
||||
* canonical relative path before spawning so `test/foo.test.ts` cannot also
|
||||
|
||||
Reference in New Issue
Block a user