@codyswann/lisa 4.47.0 → 4.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/configs/vitest/base.d.ts +71 -1
- package/dist/configs/vitest/base.d.ts.map +1 -1
- package/dist/configs/vitest/base.js +141 -23
- package/dist/configs/vitest/base.js.map +1 -1
- package/dist/configs/vitest/fleet-admission-global-setup.d.ts +34 -0
- package/dist/configs/vitest/fleet-admission-global-setup.d.ts.map +1 -0
- package/dist/configs/vitest/fleet-admission-global-setup.js +123 -0
- package/dist/configs/vitest/fleet-admission-global-setup.js.map +1 -0
- package/dist/configs/vitest/fleet-admission.d.ts +129 -0
- package/dist/configs/vitest/fleet-admission.d.ts.map +1 -0
- package/dist/configs/vitest/fleet-admission.js +239 -0
- package/dist/configs/vitest/fleet-admission.js.map +1 -0
- package/dist/configs/vitest/synthesis-scratch-reclaim.d.ts +86 -0
- package/dist/configs/vitest/synthesis-scratch-reclaim.d.ts.map +1 -0
- package/dist/configs/vitest/synthesis-scratch-reclaim.js +226 -0
- package/dist/configs/vitest/synthesis-scratch-reclaim.js.map +1 -0
- package/dist/core/host-rules-pointer.d.ts.map +1 -1
- package/dist/core/host-rules-pointer.js +30 -22
- package/dist/core/host-rules-pointer.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +9 -1
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/package.json +4 -4
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/scripts/cleanup-worktrees.sh +69 -16
|
@@ -167,18 +167,83 @@ export declare const MIN_FLEET_WORKERS = 2;
|
|
|
167
167
|
* before or after this run registers its own root, and both orders must give
|
|
168
168
|
* the same count. The result adds one back for this run.
|
|
169
169
|
* @param deps - Injectable seams so tests state a fleet instead of spawning one.
|
|
170
|
-
* @param deps.namespaceDir -
|
|
170
|
+
* @param deps.namespaceDir - One directory holding run roots; overrides the candidates.
|
|
171
|
+
* @param deps.namespaceDirs - Every directory worth counting.
|
|
171
172
|
* @param deps.readDir - Directory lister.
|
|
172
173
|
* @param deps.isAlive - Process-liveness probe.
|
|
173
174
|
* @param deps.self - This process's id.
|
|
175
|
+
* @param deps.selfBasename - This run's own root basename, when supervised.
|
|
174
176
|
* @returns Live run count including this one; 1 when nothing else is detected.
|
|
175
177
|
*/
|
|
176
178
|
export declare function discoverFleetConcurrency(deps?: {
|
|
177
179
|
namespaceDir?: () => string;
|
|
180
|
+
namespaceDirs?: () => readonly string[];
|
|
178
181
|
readDir?: (dir: string) => readonly string[];
|
|
179
182
|
isAlive?: (pid: number) => boolean;
|
|
180
183
|
self?: number;
|
|
184
|
+
selfBasename?: () => string | undefined;
|
|
181
185
|
}): number;
|
|
186
|
+
/**
|
|
187
|
+
* This run's own scratch root basename, when it is supervised.
|
|
188
|
+
*
|
|
189
|
+
* The config factory runs inside the PAYLOAD process, whose pid is not the pid
|
|
190
|
+
* the run root is named for — the supervisor allocates the root before it forks
|
|
191
|
+
* the bootstrap that launches the payload. So `process.pid` never matches the
|
|
192
|
+
* run's own root, and a pid-only exclusion counts this run's root as a sibling
|
|
193
|
+
* and then adds one more for itself. The lease is the only thing in the payload
|
|
194
|
+
* that names the root, so it is what identifies it.
|
|
195
|
+
* @returns The run root basename, or undefined when this run is unsupervised.
|
|
196
|
+
*/
|
|
197
|
+
export declare function ownRunRootBasename(): string | undefined;
|
|
198
|
+
/**
|
|
199
|
+
* Names of every live run root across the namespaces worth counting.
|
|
200
|
+
*
|
|
201
|
+
* Fails open per namespace rather than per call: one unreadable directory must
|
|
202
|
+
* not erase the count from a readable one. An empty result means "no evidence
|
|
203
|
+
* of siblings", which yields the floor — the behaviour that shipped.
|
|
204
|
+
* @param deps - Injected seams; every one defaults to the real machine.
|
|
205
|
+
* @param deps.namespaceDirs - Directories to enumerate.
|
|
206
|
+
* @param deps.readDir - Directory lister.
|
|
207
|
+
* @param deps.isAlive - Process-liveness probe.
|
|
208
|
+
* @returns Live run-root basenames, de-duplicated across namespaces.
|
|
209
|
+
*/
|
|
210
|
+
export declare function liveFleetRunRootNames(deps?: {
|
|
211
|
+
namespaceDirs?: () => readonly string[];
|
|
212
|
+
readDir?: (dir: string) => readonly string[];
|
|
213
|
+
isAlive?: (pid: number) => boolean;
|
|
214
|
+
}): readonly string[];
|
|
215
|
+
/**
|
|
216
|
+
* The pool one run gets when a fleet of the stated size shares the machine.
|
|
217
|
+
*
|
|
218
|
+
* Extracted so admission control can ask what a run would be given without
|
|
219
|
+
* re-deriving the arithmetic. Never below {@link MIN_FLEET_WORKERS}, which is
|
|
220
|
+
* exactly why the product `fleetShare(cores, fleet) * fleet` grows without
|
|
221
|
+
* bound in `fleet` and why pool sizing alone cannot bound the fleet
|
|
222
|
+
* (CodySwannGT/lisa#3941).
|
|
223
|
+
* @param cores - Logical cores available.
|
|
224
|
+
* @param fleet - How many runs share the machine.
|
|
225
|
+
* @returns Workers for one run.
|
|
226
|
+
*/
|
|
227
|
+
export declare function fleetShare(cores: number, fleet: number): number;
|
|
228
|
+
/**
|
|
229
|
+
* Why discovery no longer truncates its listing.
|
|
230
|
+
*
|
|
231
|
+
* A `.slice(0, 4_096)` used to bound the entries inspected, and the stated
|
|
232
|
+
* reason was that an unbounded `readdir` over a pathologically grown directory
|
|
233
|
+
* is the shape that once turned a shared temp directory into a 24.8 MB inode.
|
|
234
|
+
* The concern is real; the slice did not address it. `readdirSync` materialises
|
|
235
|
+
* the WHOLE listing before anything can slice it, so the expensive part had
|
|
236
|
+
* already happened — the bound removed only the per-name regex, and bought that
|
|
237
|
+
* saving by silently undercounting the fleet the moment the namespace held more
|
|
238
|
+
* than 4,096 entries. This namespace has been measured holding 3,730
|
|
239
|
+
* (CodySwannGT/lisa#3032), so the undercount was reachable rather than
|
|
240
|
+
* theoretical, and it undercounts hardest exactly when the fleet is largest.
|
|
241
|
+
*
|
|
242
|
+
* A bound that protects nothing and corrupts the count is worse than no bound,
|
|
243
|
+
* so the listing is now counted in full. The real protection against a grown
|
|
244
|
+
* namespace lives where the reading happens — `MAX_SCRATCH_NAMESPACE_SCAN_ENTRIES`
|
|
245
|
+
* in the scratch authority — not here.
|
|
246
|
+
*/
|
|
182
247
|
/**
|
|
183
248
|
* Resolves the Vitest worker-pool cap for this run.
|
|
184
249
|
*
|
|
@@ -243,6 +308,11 @@ export declare function resolveMaxWorkers(environment?: NodeJS.ProcessEnv, cores
|
|
|
243
308
|
export declare function scratchSetupFiles(): readonly string[];
|
|
244
309
|
/**
|
|
245
310
|
* Global setup that reclaims residue before a run and audits the namespace after it.
|
|
311
|
+
*
|
|
312
|
+
* Admission runs FIRST, and the order is load-bearing: it is the only hook that
|
|
313
|
+
* can decline to start work, and holding a run before the namespace sweep keeps
|
|
314
|
+
* a queued run from doing filesystem work it may not get to use. Vitest runs
|
|
315
|
+
* `globalSetup` entries in order, so the array order is the contract.
|
|
246
316
|
* @returns Absolute paths to spread into `test.globalSetup`.
|
|
247
317
|
*/
|
|
248
318
|
export declare function scratchGlobalSetup(): readonly string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../src/configs/vitest/base.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../src/configs/vitest/base.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,12 +12,14 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { existsSync, readdirSync } from "node:fs";
|
|
14
14
|
import { availableParallelism } from "node:os";
|
|
15
|
+
import { basename, join } from "node:path";
|
|
15
16
|
import { env } from "node:process";
|
|
16
17
|
import { fileURLToPath } from "node:url";
|
|
17
18
|
import { isUnitCoverageScope } from "../coverage-scope.js";
|
|
18
19
|
import { isInsideWorktree } from "../worktrees.js";
|
|
19
20
|
import { scratchNamespaceDir } from "./scratch-namespace-authority.js";
|
|
20
21
|
import { parseScratchRunRootName } from "./scratch-owner.js";
|
|
22
|
+
import { SCRATCH_SUPERVISION_LEASE_ENV, parseScratchSupervisionLease, } from "./scratch-supervision.js";
|
|
21
23
|
/**
|
|
22
24
|
* Default coverage thresholds used when not specified in project config.
|
|
23
25
|
* Projects can override via vitest.thresholds.json.
|
|
@@ -185,29 +187,129 @@ const positiveInteger = (raw) => {
|
|
|
185
187
|
* before or after this run registers its own root, and both orders must give
|
|
186
188
|
* the same count. The result adds one back for this run.
|
|
187
189
|
* @param deps - Injectable seams so tests state a fleet instead of spawning one.
|
|
188
|
-
* @param deps.namespaceDir -
|
|
190
|
+
* @param deps.namespaceDir - One directory holding run roots; overrides the candidates.
|
|
191
|
+
* @param deps.namespaceDirs - Every directory worth counting.
|
|
189
192
|
* @param deps.readDir - Directory lister.
|
|
190
193
|
* @param deps.isAlive - Process-liveness probe.
|
|
191
194
|
* @param deps.self - This process's id.
|
|
195
|
+
* @param deps.selfBasename - This run's own root basename, when supervised.
|
|
192
196
|
* @returns Live run count including this one; 1 when nothing else is detected.
|
|
193
197
|
*/
|
|
194
198
|
export function discoverFleetConcurrency(deps = {}) {
|
|
195
|
-
const { namespaceDir
|
|
199
|
+
const { namespaceDir, namespaceDirs = namespaceDir === undefined
|
|
200
|
+
? candidateNamespaceDirs
|
|
201
|
+
: () => [namespaceDir()], readDir = (dir) => readdirSync(dir), isAlive = defaultIsAlive, self = process.pid, selfBasename = ownRunRootBasename, } = deps;
|
|
202
|
+
return (liveFleetRunRootNames({ namespaceDirs, readDir, isAlive }).filter(name => !isOwnRunRoot(name, self, selfBasename())).length + 1);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Every namespace a sibling run might have registered itself in.
|
|
206
|
+
*
|
|
207
|
+
* `scratchNamespaceDir()` is built on `os.tmpdir()`, which is `TMPDIR` when the
|
|
208
|
+
* environment sets one. Two agents with different `TMPDIR` values therefore
|
|
209
|
+
* enumerate different directories and each sees only its own cohort — a real,
|
|
210
|
+
* measured undercount, and the largest of the four named in #3941. The fix is
|
|
211
|
+
* not to relocate the scratch namespace, which the authority owns and which
|
|
212
|
+
* other machinery depends on: it is to LOOK in more than one place when
|
|
213
|
+
* counting. Reading a namespace that does not exist costs one `ENOENT`.
|
|
214
|
+
*
|
|
215
|
+
* `/tmp` is the platform default `os.tmpdir()` falls back to when `TMPDIR` is
|
|
216
|
+
* unset, so it is where a run with a plain environment registers. Including it
|
|
217
|
+
* makes a `TMPDIR`-carrying run see a plain one and vice versa. Windows has no
|
|
218
|
+
* such stable second location, so there the list is the resolved one alone.
|
|
219
|
+
* @returns Namespace directories to count, without duplicates.
|
|
220
|
+
*/
|
|
221
|
+
function candidateNamespaceDirs() {
|
|
222
|
+
const resolved = scratchNamespaceDir();
|
|
223
|
+
if (process.platform === "win32")
|
|
224
|
+
return [resolved];
|
|
225
|
+
const fallback = join("/tmp", basename(resolved));
|
|
226
|
+
return resolved === fallback ? [resolved] : [resolved, fallback];
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* This run's own scratch root basename, when it is supervised.
|
|
230
|
+
*
|
|
231
|
+
* The config factory runs inside the PAYLOAD process, whose pid is not the pid
|
|
232
|
+
* the run root is named for — the supervisor allocates the root before it forks
|
|
233
|
+
* the bootstrap that launches the payload. So `process.pid` never matches the
|
|
234
|
+
* run's own root, and a pid-only exclusion counts this run's root as a sibling
|
|
235
|
+
* and then adds one more for itself. The lease is the only thing in the payload
|
|
236
|
+
* that names the root, so it is what identifies it.
|
|
237
|
+
* @returns The run root basename, or undefined when this run is unsupervised.
|
|
238
|
+
*/
|
|
239
|
+
export function ownRunRootBasename() {
|
|
240
|
+
const raw = env[SCRATCH_SUPERVISION_LEASE_ENV];
|
|
241
|
+
if (raw === undefined)
|
|
242
|
+
return undefined;
|
|
196
243
|
try {
|
|
197
|
-
|
|
198
|
-
.slice(0, MAX_DISCOVERY_ENTRIES)
|
|
199
|
-
.map(name => parseScratchRunRootName(name))
|
|
200
|
-
.filter(owner => owner !== undefined)
|
|
201
|
-
.filter(owner => owner.pid !== self)
|
|
202
|
-
.filter(owner => isAlive(owner.pid));
|
|
203
|
-
return siblings.length + 1;
|
|
244
|
+
return parseScratchSupervisionLease(raw).suiteRootBasename;
|
|
204
245
|
}
|
|
205
246
|
catch {
|
|
206
|
-
|
|
207
|
-
// probe. None of these is evidence of a fleet, and none may stop a run.
|
|
208
|
-
return 1;
|
|
247
|
+
return undefined;
|
|
209
248
|
}
|
|
210
249
|
}
|
|
250
|
+
/**
|
|
251
|
+
* Whether one run-root basename belongs to this run.
|
|
252
|
+
* @param name - Candidate run-root basename.
|
|
253
|
+
* @param self - This process's id.
|
|
254
|
+
* @param ownBasename - This run's own root basename, when it has one.
|
|
255
|
+
* @returns Whether the entry is this run rather than a sibling.
|
|
256
|
+
*/
|
|
257
|
+
function isOwnRunRoot(name, self, ownBasename) {
|
|
258
|
+
if (ownBasename !== undefined && name === ownBasename)
|
|
259
|
+
return true;
|
|
260
|
+
return parseScratchRunRootName(name)?.pid === self;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Names of every live run root across the namespaces worth counting.
|
|
264
|
+
*
|
|
265
|
+
* Fails open per namespace rather than per call: one unreadable directory must
|
|
266
|
+
* not erase the count from a readable one. An empty result means "no evidence
|
|
267
|
+
* of siblings", which yields the floor — the behaviour that shipped.
|
|
268
|
+
* @param deps - Injected seams; every one defaults to the real machine.
|
|
269
|
+
* @param deps.namespaceDirs - Directories to enumerate.
|
|
270
|
+
* @param deps.readDir - Directory lister.
|
|
271
|
+
* @param deps.isAlive - Process-liveness probe.
|
|
272
|
+
* @returns Live run-root basenames, de-duplicated across namespaces.
|
|
273
|
+
*/
|
|
274
|
+
export function liveFleetRunRootNames(deps = {}) {
|
|
275
|
+
const { namespaceDirs = candidateNamespaceDirs, readDir = (dir) => readdirSync(dir), isAlive = defaultIsAlive, } = deps;
|
|
276
|
+
const names = namespaceDirs().flatMap(dir => {
|
|
277
|
+
try {
|
|
278
|
+
return [...readDir(dir)];
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
// Absent namespace, unreadable directory, or a platform that refuses the
|
|
282
|
+
// probe. None of these is evidence of a fleet, and none may stop a run.
|
|
283
|
+
return [];
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
return [...new Set(names)].filter(name => {
|
|
287
|
+
const owner = parseScratchRunRootName(name);
|
|
288
|
+
if (owner === undefined)
|
|
289
|
+
return false;
|
|
290
|
+
try {
|
|
291
|
+
return isAlive(owner.pid);
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* The pool one run gets when a fleet of the stated size shares the machine.
|
|
300
|
+
*
|
|
301
|
+
* Extracted so admission control can ask what a run would be given without
|
|
302
|
+
* re-deriving the arithmetic. Never below {@link MIN_FLEET_WORKERS}, which is
|
|
303
|
+
* exactly why the product `fleetShare(cores, fleet) * fleet` grows without
|
|
304
|
+
* bound in `fleet` and why pool sizing alone cannot bound the fleet
|
|
305
|
+
* (CodySwannGT/lisa#3941).
|
|
306
|
+
* @param cores - Logical cores available.
|
|
307
|
+
* @param fleet - How many runs share the machine.
|
|
308
|
+
* @returns Workers for one run.
|
|
309
|
+
*/
|
|
310
|
+
export function fleetShare(cores, fleet) {
|
|
311
|
+
return Math.max(MIN_FLEET_WORKERS, Math.floor(cores / 2 / fleet));
|
|
312
|
+
}
|
|
211
313
|
/**
|
|
212
314
|
* Whether a pid names a live process.
|
|
213
315
|
*
|
|
@@ -227,15 +329,24 @@ function defaultIsAlive(pid) {
|
|
|
227
329
|
}
|
|
228
330
|
}
|
|
229
331
|
/**
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
332
|
+
* Why discovery no longer truncates its listing.
|
|
333
|
+
*
|
|
334
|
+
* A `.slice(0, 4_096)` used to bound the entries inspected, and the stated
|
|
335
|
+
* reason was that an unbounded `readdir` over a pathologically grown directory
|
|
336
|
+
* is the shape that once turned a shared temp directory into a 24.8 MB inode.
|
|
337
|
+
* The concern is real; the slice did not address it. `readdirSync` materialises
|
|
338
|
+
* the WHOLE listing before anything can slice it, so the expensive part had
|
|
339
|
+
* already happened — the bound removed only the per-name regex, and bought that
|
|
340
|
+
* saving by silently undercounting the fleet the moment the namespace held more
|
|
341
|
+
* than 4,096 entries. This namespace has been measured holding 3,730
|
|
342
|
+
* (CodySwannGT/lisa#3032), so the undercount was reachable rather than
|
|
343
|
+
* theoretical, and it undercounts hardest exactly when the fleet is largest.
|
|
344
|
+
*
|
|
345
|
+
* A bound that protects nothing and corrupts the count is worse than no bound,
|
|
346
|
+
* so the listing is now counted in full. The real protection against a grown
|
|
347
|
+
* namespace lives where the reading happens — `MAX_SCRATCH_NAMESPACE_SCAN_ENTRIES`
|
|
348
|
+
* in the scratch authority — not here.
|
|
237
349
|
*/
|
|
238
|
-
const MAX_DISCOVERY_ENTRIES = 4_096;
|
|
239
350
|
/**
|
|
240
351
|
* Resolves the Vitest worker-pool cap for this run.
|
|
241
352
|
*
|
|
@@ -294,8 +405,7 @@ export function resolveMaxWorkers(environment = env, cores = availableParallelis
|
|
|
294
405
|
const fleet = stated ?? discover();
|
|
295
406
|
if (fleet <= 1)
|
|
296
407
|
return DEFAULT_MAX_WORKERS;
|
|
297
|
-
|
|
298
|
-
return Math.max(MIN_FLEET_WORKERS, share);
|
|
408
|
+
return fleetShare(cores, fleet);
|
|
299
409
|
}
|
|
300
410
|
/**
|
|
301
411
|
* Resolves a sibling module of this file to an absolute path Vitest can load.
|
|
@@ -332,10 +442,18 @@ export function scratchSetupFiles() {
|
|
|
332
442
|
}
|
|
333
443
|
/**
|
|
334
444
|
* Global setup that reclaims residue before a run and audits the namespace after it.
|
|
445
|
+
*
|
|
446
|
+
* Admission runs FIRST, and the order is load-bearing: it is the only hook that
|
|
447
|
+
* can decline to start work, and holding a run before the namespace sweep keeps
|
|
448
|
+
* a queued run from doing filesystem work it may not get to use. Vitest runs
|
|
449
|
+
* `globalSetup` entries in order, so the array order is the contract.
|
|
335
450
|
* @returns Absolute paths to spread into `test.globalSetup`.
|
|
336
451
|
*/
|
|
337
452
|
export function scratchGlobalSetup() {
|
|
338
|
-
return [
|
|
453
|
+
return [
|
|
454
|
+
resolveSiblingModule("fleet-admission-global-setup"),
|
|
455
|
+
resolveSiblingModule("scratch-global-setup"),
|
|
456
|
+
];
|
|
339
457
|
}
|
|
340
458
|
/**
|
|
341
459
|
* Global setup that refuses a coverage run measuring an empty file set.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../../src/configs/vitest/base.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AA+
|
|
1
|
+
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../../src/configs/vitest/base.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type AdmissionVerdict } from "./fleet-admission.js";
|
|
2
|
+
/** Seams so a test can state a fleet and a clock instead of creating them. */
|
|
3
|
+
export interface AdmissionRuntime {
|
|
4
|
+
/** Live run-root basenames across every namespace worth counting. */
|
|
5
|
+
readonly liveNames: () => readonly string[];
|
|
6
|
+
/** This run's own root basename, when it is supervised. */
|
|
7
|
+
readonly selfBasename: () => string | undefined;
|
|
8
|
+
/** Logical cores available. */
|
|
9
|
+
readonly cores: () => number;
|
|
10
|
+
/** Wall clock in milliseconds. */
|
|
11
|
+
readonly now: () => number;
|
|
12
|
+
/** Wait for one poll interval. */
|
|
13
|
+
readonly wait: (ms: number) => Promise<void>;
|
|
14
|
+
/** Where a notice goes. */
|
|
15
|
+
readonly announce: (line: string) => void;
|
|
16
|
+
/** Environment holding the switches. */
|
|
17
|
+
readonly environment: NodeJS.ProcessEnv;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Hold this run until the fleet has room, or until the deadline expires.
|
|
21
|
+
*
|
|
22
|
+
* Any throw admits. This executes before a single test does, so an exception
|
|
23
|
+
* here would prevent a run rather than degrade one, and no load control is
|
|
24
|
+
* worth that.
|
|
25
|
+
* @param runtime - Injected seams; defaults to the real machine.
|
|
26
|
+
* @returns The verdict this run was admitted under.
|
|
27
|
+
*/
|
|
28
|
+
export declare function awaitFleetAdmission(runtime?: AdmissionRuntime): Promise<AdmissionVerdict>;
|
|
29
|
+
/**
|
|
30
|
+
* Vitest global setup hook.
|
|
31
|
+
* @returns A promise that settles once this run is admitted.
|
|
32
|
+
*/
|
|
33
|
+
export declare const setup: () => Promise<void>;
|
|
34
|
+
//# sourceMappingURL=fleet-admission-global-setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fleet-admission-global-setup.d.ts","sourceRoot":"","sources":["../../../src/configs/vitest/fleet-admission-global-setup.ts"],"names":[],"mappings":"AA8BA,OAAO,EAEL,KAAK,gBAAgB,EAOtB,MAAM,sBAAsB,CAAC;AAY9B,8EAA8E;AAC9E,MAAM,WAAW,gBAAgB;IAC/B,qEAAqE;IACrE,QAAQ,CAAC,SAAS,EAAE,MAAM,SAAS,MAAM,EAAE,CAAC;IAC5C,2DAA2D;IAC3D,QAAQ,CAAC,YAAY,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAChD,+BAA+B;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,MAAM,CAAC;IAC7B,kCAAkC;IAClC,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC;IAC3B,kCAAkC;IAClC,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,2BAA2B;IAC3B,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,wCAAwC;IACxC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC;CACzC;AAsED;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,GAAE,gBAAmC,GAC3C,OAAO,CAAC,gBAAgB,CAAC,CAM3B;AAED;;;GAGG;AACH,eAAO,MAAM,KAAK,QAAa,OAAO,CAAC,IAAI,CAE1C,CAAC"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vitest `globalSetup` that holds a run back until the fleet has room for it.
|
|
3
|
+
*
|
|
4
|
+
* This is the executable half of {@link module:configs/vitest/fleet-admission}.
|
|
5
|
+
* The decision itself is pure and lives there; this file supplies the clock, the
|
|
6
|
+
* directory listing and the sleep, and is deliberately the only part with side
|
|
7
|
+
* effects.
|
|
8
|
+
*
|
|
9
|
+
* ## Why globalSetup and not the config factory
|
|
10
|
+
*
|
|
11
|
+
* `resolveMaxWorkers` runs while Vitest is building its config, and that path is
|
|
12
|
+
* synchronous — a wait there would have to be a busy loop. `globalSetup` is
|
|
13
|
+
* async by contract and runs after the config resolves but before any worker
|
|
14
|
+
* executes a test file, which is exactly the moment the demand this bounds is
|
|
15
|
+
* about to be created. A run held here has one idle main process; a run not held
|
|
16
|
+
* here has a whole pool.
|
|
17
|
+
*
|
|
18
|
+
* ## Why this cannot wedge the fleet
|
|
19
|
+
*
|
|
20
|
+
* Admission is by rank among live run roots ordered oldest-first, so the
|
|
21
|
+
* `capacity` oldest runs always admit and the queue always drains. On top of
|
|
22
|
+
* that every unknown admits and the wait is bounded — the full argument is in
|
|
23
|
+
* the module docstring next door. The failure this is designed against is not
|
|
24
|
+
* "too many runs", it is "a control that stops a run from ever happening".
|
|
25
|
+
* @module configs/vitest/fleet-admission-global-setup
|
|
26
|
+
*/
|
|
27
|
+
import { availableParallelism } from "node:os";
|
|
28
|
+
import { env, stderr } from "node:process";
|
|
29
|
+
import { liveFleetRunRootNames, ownRunRootBasename } from "./base.js";
|
|
30
|
+
import { ADMISSION_POLL_MS, admissionDeadlineMs, admissionEnabled, admissionRank, admissionVerdict, fleetCapacity, readFleetRunRoots, } from "./fleet-admission.js";
|
|
31
|
+
/**
|
|
32
|
+
* Sleep for one poll interval.
|
|
33
|
+
* @param ms - Milliseconds to wait.
|
|
34
|
+
* @returns A promise that settles after the interval.
|
|
35
|
+
*/
|
|
36
|
+
const sleep = (ms) => new Promise(resolve => {
|
|
37
|
+
setTimeout(resolve, ms);
|
|
38
|
+
});
|
|
39
|
+
/**
|
|
40
|
+
* The runtime a real run uses.
|
|
41
|
+
* @returns Seams bound to this machine.
|
|
42
|
+
*/
|
|
43
|
+
const defaultRuntime = () => ({
|
|
44
|
+
liveNames: () => liveFleetRunRootNames(),
|
|
45
|
+
selfBasename: ownRunRootBasename,
|
|
46
|
+
cores: availableParallelism,
|
|
47
|
+
now: () => Date.now(),
|
|
48
|
+
wait: sleep,
|
|
49
|
+
announce: line => {
|
|
50
|
+
stderr.write(`${line}\n`);
|
|
51
|
+
},
|
|
52
|
+
environment: env,
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* Ask once whether this run may execute now.
|
|
56
|
+
* @param runtime - Injected seams.
|
|
57
|
+
* @param startedAt - When this run began waiting.
|
|
58
|
+
* @returns The verdict for this attempt.
|
|
59
|
+
*/
|
|
60
|
+
function attempt(runtime, startedAt) {
|
|
61
|
+
const roots = readFleetRunRoots(runtime.liveNames());
|
|
62
|
+
return admissionVerdict({
|
|
63
|
+
rank: admissionRank(roots, runtime.selfBasename()),
|
|
64
|
+
capacity: fleetCapacity(runtime.cores()),
|
|
65
|
+
liveRuns: roots.length,
|
|
66
|
+
elapsedMs: runtime.now() - startedAt,
|
|
67
|
+
deadlineMs: admissionDeadlineMs(runtime.environment),
|
|
68
|
+
enabled: admissionEnabled(runtime.environment),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Poll until this run is admitted, announcing only when the news changes.
|
|
73
|
+
*
|
|
74
|
+
* The queue notice is printed on the FIRST wait and the over-capacity notice on
|
|
75
|
+
* admission; the polling in between says nothing. An operator needs to know a
|
|
76
|
+
* run is queued and needs to know if it started anyway, and a line every two
|
|
77
|
+
* seconds would bury both.
|
|
78
|
+
*
|
|
79
|
+
* Recursive rather than looping: each call is separated by an `await`, so the
|
|
80
|
+
* synchronous stack does not grow, and the shape states "one attempt, then the
|
|
81
|
+
* rest" instead of carrying mutable poll state.
|
|
82
|
+
* @param runtime - Injected seams.
|
|
83
|
+
* @param startedAt - When this run began waiting.
|
|
84
|
+
* @param announced - Whether the queue notice has already been printed.
|
|
85
|
+
* @returns The verdict this run was admitted under.
|
|
86
|
+
*/
|
|
87
|
+
async function poll(runtime, startedAt, announced) {
|
|
88
|
+
const verdict = attempt(runtime, startedAt);
|
|
89
|
+
if (verdict.admitted) {
|
|
90
|
+
if (verdict.notice !== "")
|
|
91
|
+
runtime.announce(verdict.notice);
|
|
92
|
+
return verdict;
|
|
93
|
+
}
|
|
94
|
+
if (!announced)
|
|
95
|
+
runtime.announce(verdict.notice);
|
|
96
|
+
await runtime.wait(ADMISSION_POLL_MS);
|
|
97
|
+
return poll(runtime, startedAt, true);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Hold this run until the fleet has room, or until the deadline expires.
|
|
101
|
+
*
|
|
102
|
+
* Any throw admits. This executes before a single test does, so an exception
|
|
103
|
+
* here would prevent a run rather than degrade one, and no load control is
|
|
104
|
+
* worth that.
|
|
105
|
+
* @param runtime - Injected seams; defaults to the real machine.
|
|
106
|
+
* @returns The verdict this run was admitted under.
|
|
107
|
+
*/
|
|
108
|
+
export async function awaitFleetAdmission(runtime = defaultRuntime()) {
|
|
109
|
+
try {
|
|
110
|
+
return await poll(runtime, runtime.now(), false);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return { decision: "admit-unranked", admitted: true, notice: "" };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Vitest global setup hook.
|
|
118
|
+
* @returns A promise that settles once this run is admitted.
|
|
119
|
+
*/
|
|
120
|
+
export const setup = async () => {
|
|
121
|
+
await awaitFleetAdmission();
|
|
122
|
+
};
|
|
123
|
+
//# sourceMappingURL=fleet-admission-global-setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fleet-admission-global-setup.js","sourceRoot":"","sources":["../../../src/configs/vitest/fleet-admission-global-setup.ts"],"names":[],"mappings":"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"}
|