@codyswann/lisa 4.44.0 → 4.45.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/all/copy-overwrite/scripts/lib/gate-failure-diagnosis.mjs +156 -14
- package/dist/configs/vitest/coverage-reports-directory.d.ts +70 -0
- package/dist/configs/vitest/coverage-reports-directory.d.ts.map +1 -1
- package/dist/configs/vitest/coverage-reports-directory.js +180 -2
- package/dist/configs/vitest/coverage-reports-directory.js.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +4 -0
- package/dist/core/lisa-owned-hash-ledger.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 +2 -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
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
* measurement when the suite that produced it finished.
|
|
30
30
|
* @module lib/gate-failure-diagnosis
|
|
31
31
|
*/
|
|
32
|
-
import { readFileSync } from "node:fs";
|
|
33
|
-
import { availableParallelism, loadavg } from "node:os";
|
|
32
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
33
|
+
import { availableParallelism, loadavg, tmpdir } from "node:os";
|
|
34
34
|
|
|
35
35
|
/** Default load-average source for {@link machineLoad}. */
|
|
36
36
|
const osLoadavg = () => loadavg();
|
|
@@ -288,13 +288,10 @@ const TAIL_LINES = 3;
|
|
|
288
288
|
* @param {string[]} items Every item found.
|
|
289
289
|
* @returns {string[]} At most `MAX_EVIDENCE + 1` lines.
|
|
290
290
|
*/
|
|
291
|
-
function capped(items) {
|
|
291
|
+
function capped(items, limit = MAX_EVIDENCE) {
|
|
292
292
|
const unique = [...new Set(items)];
|
|
293
|
-
if (unique.length <=
|
|
294
|
-
return [
|
|
295
|
-
...unique.slice(0, MAX_EVIDENCE),
|
|
296
|
-
`…and ${unique.length - MAX_EVIDENCE} more`,
|
|
297
|
-
];
|
|
293
|
+
if (unique.length <= limit) return unique;
|
|
294
|
+
return [...unique.slice(0, limit), `…and ${unique.length - limit} more`];
|
|
298
295
|
}
|
|
299
296
|
|
|
300
297
|
/**
|
|
@@ -349,18 +346,37 @@ function tailLines(output) {
|
|
|
349
346
|
|
|
350
347
|
/**
|
|
351
348
|
* A timeout verdict, worded so it can never be mistaken for a coverage miss.
|
|
349
|
+
*
|
|
350
|
+
* Carries the shared temp root's population, because a blown wall-clock budget
|
|
351
|
+
* is the one signature that gives no hint of its own cause. A saturated
|
|
352
|
+
* platform temp root makes every `mkdtemp` on the box slow, and the lane that
|
|
353
|
+
* pays is whichever one happened to create fixtures — so it presents as a
|
|
354
|
+
* single flaky-looking test in a suite that has nothing to do with the
|
|
355
|
+
* producer. The reading is attached here and nowhere else for that reason: on
|
|
356
|
+
* an assertion failure or a coverage miss it would be noise.
|
|
352
357
|
* @param {{count: number, budgets: number[]}} timeouts What was found.
|
|
353
358
|
* @param {string[]} suites Suites the output named as failing.
|
|
359
|
+
* @param {TempRootReading|null} [tempRoot] The temp root's population at
|
|
360
|
+
* diagnosis time. Omitted or null when it could not be read.
|
|
354
361
|
* @returns {Diagnosis} The verdict.
|
|
355
362
|
*/
|
|
356
|
-
function timeoutVerdict(timeouts, suites) {
|
|
363
|
+
function timeoutVerdict(timeouts, suites, tempRoot) {
|
|
357
364
|
const budget = Math.max(...timeouts.budgets);
|
|
365
|
+
// Reserve the temp-root line's slot BEFORE capping the suites, so this
|
|
366
|
+
// verdict carries the same MAX_EVIDENCE + 1 ceiling as every other one.
|
|
367
|
+
// Appending after the cap made this the only verdict that could reach
|
|
368
|
+
// MAX_EVIDENCE + 2, and the cap test could not see it because that test
|
|
369
|
+
// suppresses the reading.
|
|
370
|
+
const tempEvidence = tempRootEvidence(tempRoot ?? null);
|
|
358
371
|
return {
|
|
359
372
|
kind: DIAGNOSIS.TIMEOUT,
|
|
360
373
|
summary:
|
|
361
374
|
`${timeouts.count} test(s)/hook(s) exceeded the ${budget}ms budget, ` +
|
|
362
375
|
`so the suite did not finish — this is NOT a coverage shortfall`,
|
|
363
|
-
evidence:
|
|
376
|
+
evidence: [
|
|
377
|
+
...capped(suites, MAX_EVIDENCE - tempEvidence.length),
|
|
378
|
+
...tempEvidence,
|
|
379
|
+
],
|
|
364
380
|
};
|
|
365
381
|
}
|
|
366
382
|
|
|
@@ -627,6 +643,102 @@ function loadEvidence(load) {
|
|
|
627
643
|
];
|
|
628
644
|
}
|
|
629
645
|
|
|
646
|
+
/**
|
|
647
|
+
* List the platform temp root's direct children.
|
|
648
|
+
*
|
|
649
|
+
* Deliberately NOT recursive. The cost being measured is the width of one
|
|
650
|
+
* directory — what `mkdtemp` and every `readdir` on that path must walk — and
|
|
651
|
+
* descending into tens of thousands of entries to diagnose a failure would
|
|
652
|
+
* itself be the expensive operation this line exists to warn about.
|
|
653
|
+
* @returns {string[]} Direct children of the platform temp root.
|
|
654
|
+
*/
|
|
655
|
+
function defaultTempRootEntries() {
|
|
656
|
+
return readdirSync(tmpdir());
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Read the platform temp root's own inode size.
|
|
661
|
+
* @returns {number} Size in bytes of the directory inode itself.
|
|
662
|
+
*/
|
|
663
|
+
function defaultTempRootInodeBytes() {
|
|
664
|
+
return statSync(tmpdir()).size;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* How crowded the shared platform temp root is, or `null` when unreadable.
|
|
669
|
+
* @typedef {object} TempRootReading
|
|
670
|
+
* @property {number} entries Direct children of the platform temp root.
|
|
671
|
+
* @property {number} inodeBytes Size of the directory's own inode.
|
|
672
|
+
*/
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Measure the shared platform temp root.
|
|
676
|
+
*
|
|
677
|
+
* Both numbers are taken because they answer different questions and only one
|
|
678
|
+
* of them is repairable by deleting things. The ENTRY COUNT is the current
|
|
679
|
+
* population. The INODE SIZE is what that population did to the directory, and
|
|
680
|
+
* a directory inode does not shrink when its entries are removed — so a root
|
|
681
|
+
* that once held tens of thousands of names stays expensive to walk after a
|
|
682
|
+
* prune, and a report that showed only the count would say "cleaned up" about
|
|
683
|
+
* a directory that is still slow.
|
|
684
|
+
*
|
|
685
|
+
* Injected for the same reason {@link machineLoad} is: a test must be able to
|
|
686
|
+
* state a population rather than inherit whatever the test machine's temp root
|
|
687
|
+
* happened to contain, which is shared with every other process on the box.
|
|
688
|
+
* @param {() => string[]} [readEntries] Lists the temp root's children.
|
|
689
|
+
* @param {() => number} [readInodeBytes] Reads the temp root's own inode size.
|
|
690
|
+
* @returns {TempRootReading|null} The reading, or null when either source fails.
|
|
691
|
+
*/
|
|
692
|
+
export function tempRootPopulation(
|
|
693
|
+
readEntries = defaultTempRootEntries,
|
|
694
|
+
readInodeBytes = defaultTempRootInodeBytes
|
|
695
|
+
) {
|
|
696
|
+
try {
|
|
697
|
+
const entries = readEntries().length;
|
|
698
|
+
const inodeBytes = readInodeBytes();
|
|
699
|
+
if (!Number.isFinite(entries) || !Number.isFinite(inodeBytes)) return null;
|
|
700
|
+
return { entries, inodeBytes };
|
|
701
|
+
} catch {
|
|
702
|
+
return null;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* Turn a temp-root reading into the line that lets an operator rule crowding
|
|
708
|
+
* in or out.
|
|
709
|
+
*
|
|
710
|
+
* REPORTS, NEVER JUDGES — and that is a measured decision rather than caution.
|
|
711
|
+
* A crowded platform temp root makes `mkdtemp` pathologically slow, which
|
|
712
|
+
* surfaces as one slow test in an unrelated lane and never as "the filesystem
|
|
713
|
+
* is the problem", so the number belongs beside a timeout. But the threshold
|
|
714
|
+
* at which it starts costing anything is NOT KNOWN: 16.5k entries measured at
|
|
715
|
+
* a 1.0x `mkdtemp` penalty against a nested directory — i.e. none at all — on
|
|
716
|
+
* the same platform where ~46k was reported harmful. Nothing measured in
|
|
717
|
+
* between.
|
|
718
|
+
*
|
|
719
|
+
* Guessing a boundary in that gap would produce a detector that fires on the
|
|
720
|
+
* ordinary state of a busy workstation, and a check that cries wolf on a
|
|
721
|
+
* healthy machine is worse than no check: it trains its own readers to skip
|
|
722
|
+
* the line. So this prints what it saw and names both calibration points,
|
|
723
|
+
* leaving the reader to decide — and accumulates the evidence a later change
|
|
724
|
+
* can set a real threshold from.
|
|
725
|
+
* @param {TempRootReading|null} tempRoot The reading, or null when unavailable.
|
|
726
|
+
* @returns {string[]} Zero or one evidence line.
|
|
727
|
+
*/
|
|
728
|
+
function tempRootEvidence(tempRoot) {
|
|
729
|
+
if (tempRoot === null) return [];
|
|
730
|
+
const kb = Math.round(tempRoot.inodeBytes / 1024);
|
|
731
|
+
return [
|
|
732
|
+
`shared temp root at diagnosis: ${tempRoot.entries} entries, ` +
|
|
733
|
+
`${kb} KB directory inode. A crowded platform temp root slows every ` +
|
|
734
|
+
`mkdtemp on the box and shows up as one slow test in an unrelated ` +
|
|
735
|
+
`lane, so this is a candidate cause to rule in or out — not a verdict. ` +
|
|
736
|
+
`No threshold is asserted because none is known: ~16.5k entries ` +
|
|
737
|
+
`measured NO penalty and ~46k was reported harmful, with nothing ` +
|
|
738
|
+
`measured between. The inode size is the part a prune does not fix.`,
|
|
739
|
+
];
|
|
740
|
+
}
|
|
741
|
+
|
|
630
742
|
/**
|
|
631
743
|
* Runnable work per core above which the box is treated as saturated.
|
|
632
744
|
*
|
|
@@ -707,7 +819,7 @@ function emptyTranscriptVerdict() {
|
|
|
707
819
|
* @param {LoadReading|null} load The machine's load at diagnosis time.
|
|
708
820
|
* @returns {object} What the failure was, before it is attributed.
|
|
709
821
|
*/
|
|
710
|
-
function classify(output, code, load, read) {
|
|
822
|
+
function classify(output, code, load, read, tempRoot) {
|
|
711
823
|
if (wasKilled(code)) return killedVerdict(code ?? null, load);
|
|
712
824
|
|
|
713
825
|
if (typeof output !== "string") return unavailableVerdict();
|
|
@@ -754,7 +866,16 @@ function classify(output, code, load, read) {
|
|
|
754
866
|
const failures = findFailures(output);
|
|
755
867
|
const misses = findThresholdMisses(output);
|
|
756
868
|
|
|
757
|
-
if (timeouts.count > 0)
|
|
869
|
+
if (timeouts.count > 0)
|
|
870
|
+
return timeoutVerdict(
|
|
871
|
+
timeouts,
|
|
872
|
+
failures.suites,
|
|
873
|
+
// Resolved HERE and nowhere else: this is the only consumer, and it is
|
|
874
|
+
// the last point at which `undefined` (measure it) and `null` (suppress
|
|
875
|
+
// it) are still distinguishable. Do not "simplify" this to `??` — that
|
|
876
|
+
// collapses the two and makes every suppressed test read the real box.
|
|
877
|
+
tempRoot === undefined ? tempRootPopulation() : tempRoot
|
|
878
|
+
);
|
|
758
879
|
|
|
759
880
|
if ((failures.tally ?? 0) > 0 || failures.suites.length > 0) {
|
|
760
881
|
const count = failures.tally ?? failures.suites.length;
|
|
@@ -1067,14 +1188,35 @@ function findTerminatedComments(output, read) {
|
|
|
1067
1188
|
* @param {(path: string) => string|null} [read] Reads a source file the
|
|
1068
1189
|
* transcript named, returning null when it cannot be read. Defaults to the
|
|
1069
1190
|
* real filesystem; injected by tests so a fixture needs no files on disk.
|
|
1191
|
+
* @param {TempRootReading|null} [tempRoot] The shared temp root's population,
|
|
1192
|
+
* for a timeout's evidence line. OMIT to measure this machine — but the
|
|
1193
|
+
* measurement is taken lazily, only on the timeout path that consumes it.
|
|
1194
|
+
* Pass `null` to suppress the line, or a fixed reading to make output
|
|
1195
|
+
* deterministic.
|
|
1196
|
+
*
|
|
1197
|
+
* This is deliberately NOT a default parameter. A default is evaluated on
|
|
1198
|
+
* every call that omits the argument, so `tempRoot = tempRootPopulation()`
|
|
1199
|
+
* ran a `readdirSync` plus a `statSync` over the shared temp root for every
|
|
1200
|
+
* assertion failure, coverage miss and kill — none of which use the reading.
|
|
1201
|
+
* On a box whose temp root holds tens of thousands of entries that is not
|
|
1202
|
+
* free, and the irony is total: this was added by the change about temp-root
|
|
1203
|
+
* churn. Nothing in the suite could catch it, because cost is not asserted.
|
|
1204
|
+
*
|
|
1205
|
+
* Making the default lazier does NOT work, and that is the trap: a default
|
|
1206
|
+
* parameter cannot distinguish OMITTED from EXPLICITLY NULL, because both
|
|
1207
|
+
* arrive as `undefined`/`null` at different points and only `undefined`
|
|
1208
|
+
* triggers a default. `null` is the documented suppression that keeps test
|
|
1209
|
+
* output deterministic, so it must survive. Hence the resolution moved to
|
|
1210
|
+
* the single consuming call site, where the two are still distinguishable.
|
|
1070
1211
|
* @returns {Diagnosis} What the failure was, and whose it was.
|
|
1071
1212
|
*/
|
|
1072
1213
|
export function diagnoseFailure(
|
|
1073
1214
|
output,
|
|
1074
1215
|
code,
|
|
1075
1216
|
load = machineLoad(),
|
|
1076
|
-
read = readSourceFile
|
|
1217
|
+
read = readSourceFile,
|
|
1218
|
+
tempRoot
|
|
1077
1219
|
) {
|
|
1078
|
-
const verdict = classify(output, code, load, read);
|
|
1220
|
+
const verdict = classify(output, code, load, read, tempRoot);
|
|
1079
1221
|
return { ...verdict, proves: ATTRIBUTION[verdict.kind] ?? null };
|
|
1080
1222
|
}
|
|
@@ -30,6 +30,76 @@ export declare const COVERAGE_REPORTS_DIR_ENV = "LISA_COVERAGE_REPORTS_DIR";
|
|
|
30
30
|
* @returns Root-relative path, POSIX-separated as Vitest's own default is.
|
|
31
31
|
*/
|
|
32
32
|
export declare function coverageRunDirectory(owner: number, startedAt: number, suffix: string): string;
|
|
33
|
+
/**
|
|
34
|
+
* Publish one finished run's artifacts to the stable directory, then drop it.
|
|
35
|
+
*
|
|
36
|
+
* ## Why isolation alone is not enough
|
|
37
|
+
*
|
|
38
|
+
* `reportsDirectory` is the only lever the provider offers, so isolating the
|
|
39
|
+
* scratch necessarily moves the REPORTS with it. An unpinned run therefore
|
|
40
|
+
* writes `coverage/.runs/<run>/lcov.info` and never writes `coverage/lcov.info`
|
|
41
|
+
* again.
|
|
42
|
+
*
|
|
43
|
+
* A project upgrading to that behaviour does not get a missing-file error. Its
|
|
44
|
+
* Sonar keeps reading **the file left there before the upgrade**, indefinitely,
|
|
45
|
+
* and reports a coverage number that has quietly stopped moving — a wrong value
|
|
46
|
+
* that looks like a real one, which is the same failure isolation removed from
|
|
47
|
+
* concurrent runs, relocated one layer out to consumers.
|
|
48
|
+
*
|
|
49
|
+
* ## Why this is safe where a mid-run shared directory is not
|
|
50
|
+
*
|
|
51
|
+
* Ordering. The provider runs `reportCoverage` -> `generateReports` ->
|
|
52
|
+
* `cleanAfterRun`, and this runs later still, at process exit. It therefore
|
|
53
|
+
* moves COMPLETED files, never files a writer is partway through — which is
|
|
54
|
+
* precisely the distinction that makes the shared-directory `rm` destructive
|
|
55
|
+
* and this move benign.
|
|
56
|
+
*
|
|
57
|
+
* Two concurrent runs publishing together is last-writer-wins per file, and can
|
|
58
|
+
* leave `lcov.info` from one beside `index.html` from another. That cost is
|
|
59
|
+
* accepted deliberately: each run still reported its own correct number, and
|
|
60
|
+
* nothing here can delete a directory another run is writing into.
|
|
61
|
+
* @param directory - Reports directory as {@link coverageReportsDirectory} returned it
|
|
62
|
+
* @param expectedName - The run-directory basename this process minted
|
|
63
|
+
* @returns True when this run's own directory was published and removed
|
|
64
|
+
*/
|
|
65
|
+
export declare function publishCoverageRunDirectory(directory: string, expectedName: string): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Remove a run directory this process minted, and nothing else.
|
|
68
|
+
*
|
|
69
|
+
* The fallback when publishing could not happen, and the guard is the same one:
|
|
70
|
+
* the basename must be the name this process generated, so a mis-wired caller
|
|
71
|
+
* removes nothing rather than removing a directory another run is writing into.
|
|
72
|
+
* @param directory - Reports directory as {@link coverageReportsDirectory} returned it
|
|
73
|
+
* @param expectedName - The run-directory basename this process minted
|
|
74
|
+
* @returns True when the path was this run's own
|
|
75
|
+
*/
|
|
76
|
+
export declare function removeOwnCoverageRunDirectory(directory: string, expectedName: string): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Remove run directories belonging to runs that are no longer alive.
|
|
79
|
+
*
|
|
80
|
+
* Publishing happens at process exit, so an ordinary run leaves nothing behind.
|
|
81
|
+
* A run that is `SIGKILL`ed does — and on a contended machine that is routine
|
|
82
|
+
* rather than exceptional, so without this `coverage/.runs/` accrues one
|
|
83
|
+
* directory per killed run with nothing ever removing them.
|
|
84
|
+
*
|
|
85
|
+
* ## Why liveness is legitimate here and not in a lock
|
|
86
|
+
*
|
|
87
|
+
* Reclaiming an abandoned directory is not adjudicating ownership of a
|
|
88
|
+
* contended resource, and the asymmetry is what makes it safe: a false KEEP is
|
|
89
|
+
* inert debris, and a false REMOVE is impossible, because a live run's own pid
|
|
90
|
+
* always probes alive. The lock this module refuses has the opposite asymmetry
|
|
91
|
+
* — there, a wrong answer releases a lock somebody is holding.
|
|
92
|
+
*
|
|
93
|
+
* It introduces no second authority. {@link parseRunRootName} and
|
|
94
|
+
* {@link isProcessAlive} are the scratch namespace's existing parser and
|
|
95
|
+
* liveness probe, and these directories are named by {@link runRootName}, so
|
|
96
|
+
* that parser reads them unchanged. Anything it does not recognise is left
|
|
97
|
+
* alone.
|
|
98
|
+
* @param runsRoot - Directory holding the run directories
|
|
99
|
+
* @param alive - Liveness probe; defaults to the shared one
|
|
100
|
+
* @returns Basenames of the directories removed
|
|
101
|
+
*/
|
|
102
|
+
export declare function reclaimAbandonedCoverageRunDirectories(runsRoot?: string, alive?: (owner: number) => boolean): readonly string[];
|
|
33
103
|
/**
|
|
34
104
|
* Resolves the coverage reports directory for this run.
|
|
35
105
|
* @param environment - Environment to read; defaults to this process's.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coverage-reports-directory.d.ts","sourceRoot":"","sources":["../../../src/configs/vitest/coverage-reports-directory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"coverage-reports-directory.d.ts","sourceRoot":"","sources":["../../../src/configs/vitest/coverage-reports-directory.ts"],"names":[],"mappings":"AAuEA;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,UAAU,CAAC;AAEzC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,wBAAwB,8BAA8B,CAAC;AAkBpE;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,MAAM,CAER;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACnB,OAAO,CAyBT;AAED;;;;;;;;;GASG;AACH,wBAAgB,6BAA6B,CAC3C,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACnB,OAAO,CAWT;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,sCAAsC,CACpD,QAAQ,GAAE,MAAoD,EAC9D,KAAK,GAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAwB,GACjD,SAAS,MAAM,EAAE,CAenB;AAYD;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,WAAW,GAAE,MAAM,CAAC,UAAgB,GACnC,MAAM,CAcR;AAkCD;;;;;;GAMG;AACH,wBAAgB,6BAA6B,IAAI,IAAI,CAGpD"}
|
|
@@ -59,8 +59,11 @@
|
|
|
59
59
|
* @module configs/vitest/coverage-reports-directory
|
|
60
60
|
*/
|
|
61
61
|
import { randomBytes } from "node:crypto";
|
|
62
|
+
import * as fs from "node:fs";
|
|
63
|
+
import * as path from "node:path";
|
|
62
64
|
import { env, pid } from "node:process";
|
|
63
|
-
import { runRootName } from "./scratch-namespace-authority.js";
|
|
65
|
+
import { parseRunRootName, runRootName, } from "./scratch-namespace-authority.js";
|
|
66
|
+
import { isProcessAlive } from "./scratch-sweep.js";
|
|
64
67
|
/**
|
|
65
68
|
* Directory under `coverage/` holding one run's isolated output.
|
|
66
69
|
*
|
|
@@ -84,6 +87,17 @@ export const COVERAGE_RUNS_DIR = ".runs";
|
|
|
84
87
|
export const COVERAGE_REPORTS_DIR_ENV = "LISA_COVERAGE_REPORTS_DIR";
|
|
85
88
|
/** Bytes of randomness disambiguating two runs started in the same millisecond. */
|
|
86
89
|
const SUFFIX_BYTES = 4;
|
|
90
|
+
/** The stable directory a checkout's coverage artifacts are read from. */
|
|
91
|
+
const COVERAGE_BASE = "coverage";
|
|
92
|
+
/**
|
|
93
|
+
* The provider's own scratch subdirectory, named so it is never published.
|
|
94
|
+
*
|
|
95
|
+
* `cleanAfterRun` removes it before the process exits, so in practice it is
|
|
96
|
+
* already gone by the time anything publishes. Skipping it explicitly means a
|
|
97
|
+
* run killed between report generation and exit cannot copy a half-written
|
|
98
|
+
* `coverage-<n>.json` up to the stable path, where another run would read it.
|
|
99
|
+
*/
|
|
100
|
+
const COVERAGE_SCRATCH_DIR = ".tmp";
|
|
87
101
|
/**
|
|
88
102
|
* Builds the relative reports directory for one run.
|
|
89
103
|
*
|
|
@@ -97,6 +111,134 @@ const SUFFIX_BYTES = 4;
|
|
|
97
111
|
export function coverageRunDirectory(owner, startedAt, suffix) {
|
|
98
112
|
return `coverage/${COVERAGE_RUNS_DIR}/${runRootName(owner, startedAt, suffix)}`;
|
|
99
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Publish one finished run's artifacts to the stable directory, then drop it.
|
|
116
|
+
*
|
|
117
|
+
* ## Why isolation alone is not enough
|
|
118
|
+
*
|
|
119
|
+
* `reportsDirectory` is the only lever the provider offers, so isolating the
|
|
120
|
+
* scratch necessarily moves the REPORTS with it. An unpinned run therefore
|
|
121
|
+
* writes `coverage/.runs/<run>/lcov.info` and never writes `coverage/lcov.info`
|
|
122
|
+
* again.
|
|
123
|
+
*
|
|
124
|
+
* A project upgrading to that behaviour does not get a missing-file error. Its
|
|
125
|
+
* Sonar keeps reading **the file left there before the upgrade**, indefinitely,
|
|
126
|
+
* and reports a coverage number that has quietly stopped moving — a wrong value
|
|
127
|
+
* that looks like a real one, which is the same failure isolation removed from
|
|
128
|
+
* concurrent runs, relocated one layer out to consumers.
|
|
129
|
+
*
|
|
130
|
+
* ## Why this is safe where a mid-run shared directory is not
|
|
131
|
+
*
|
|
132
|
+
* Ordering. The provider runs `reportCoverage` -> `generateReports` ->
|
|
133
|
+
* `cleanAfterRun`, and this runs later still, at process exit. It therefore
|
|
134
|
+
* moves COMPLETED files, never files a writer is partway through — which is
|
|
135
|
+
* precisely the distinction that makes the shared-directory `rm` destructive
|
|
136
|
+
* and this move benign.
|
|
137
|
+
*
|
|
138
|
+
* Two concurrent runs publishing together is last-writer-wins per file, and can
|
|
139
|
+
* leave `lcov.info` from one beside `index.html` from another. That cost is
|
|
140
|
+
* accepted deliberately: each run still reported its own correct number, and
|
|
141
|
+
* nothing here can delete a directory another run is writing into.
|
|
142
|
+
* @param directory - Reports directory as {@link coverageReportsDirectory} returned it
|
|
143
|
+
* @param expectedName - The run-directory basename this process minted
|
|
144
|
+
* @returns True when this run's own directory was published and removed
|
|
145
|
+
*/
|
|
146
|
+
export function publishCoverageRunDirectory(directory, expectedName) {
|
|
147
|
+
if (path.basename(directory) !== expectedName)
|
|
148
|
+
return false;
|
|
149
|
+
const from = path.resolve(process.cwd(), directory);
|
|
150
|
+
// Two levels up from `<base>/coverage/.runs/<run>` is `<base>/coverage`.
|
|
151
|
+
// Derived from the run directory rather than from `process.cwd()` so the
|
|
152
|
+
// pair travels together: a caller that states one has stated both, and a
|
|
153
|
+
// test can exercise this against a temporary tree without changing
|
|
154
|
+
// directory.
|
|
155
|
+
const stable = path.dirname(path.dirname(from));
|
|
156
|
+
try {
|
|
157
|
+
if (!fs.existsSync(from))
|
|
158
|
+
return false;
|
|
159
|
+
for (const entry of fs.readdirSync(from)) {
|
|
160
|
+
if (entry === COVERAGE_SCRATCH_DIR)
|
|
161
|
+
continue;
|
|
162
|
+
const target = path.join(stable, entry);
|
|
163
|
+
fs.rmSync(target, { recursive: true, force: true });
|
|
164
|
+
fs.renameSync(path.join(from, entry), target);
|
|
165
|
+
}
|
|
166
|
+
fs.rmSync(from, { recursive: true, force: true });
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// Best effort. A run that cannot publish its artifacts has still measured
|
|
171
|
+
// and reported its coverage correctly, and failing over it would turn a
|
|
172
|
+
// tidiness problem into the red gate this module exists to prevent.
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Remove a run directory this process minted, and nothing else.
|
|
178
|
+
*
|
|
179
|
+
* The fallback when publishing could not happen, and the guard is the same one:
|
|
180
|
+
* the basename must be the name this process generated, so a mis-wired caller
|
|
181
|
+
* removes nothing rather than removing a directory another run is writing into.
|
|
182
|
+
* @param directory - Reports directory as {@link coverageReportsDirectory} returned it
|
|
183
|
+
* @param expectedName - The run-directory basename this process minted
|
|
184
|
+
* @returns True when the path was this run's own
|
|
185
|
+
*/
|
|
186
|
+
export function removeOwnCoverageRunDirectory(directory, expectedName) {
|
|
187
|
+
if (path.basename(directory) !== expectedName)
|
|
188
|
+
return false;
|
|
189
|
+
try {
|
|
190
|
+
fs.rmSync(path.resolve(process.cwd(), directory), {
|
|
191
|
+
recursive: true,
|
|
192
|
+
force: true,
|
|
193
|
+
});
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Remove run directories belonging to runs that are no longer alive.
|
|
202
|
+
*
|
|
203
|
+
* Publishing happens at process exit, so an ordinary run leaves nothing behind.
|
|
204
|
+
* A run that is `SIGKILL`ed does — and on a contended machine that is routine
|
|
205
|
+
* rather than exceptional, so without this `coverage/.runs/` accrues one
|
|
206
|
+
* directory per killed run with nothing ever removing them.
|
|
207
|
+
*
|
|
208
|
+
* ## Why liveness is legitimate here and not in a lock
|
|
209
|
+
*
|
|
210
|
+
* Reclaiming an abandoned directory is not adjudicating ownership of a
|
|
211
|
+
* contended resource, and the asymmetry is what makes it safe: a false KEEP is
|
|
212
|
+
* inert debris, and a false REMOVE is impossible, because a live run's own pid
|
|
213
|
+
* always probes alive. The lock this module refuses has the opposite asymmetry
|
|
214
|
+
* — there, a wrong answer releases a lock somebody is holding.
|
|
215
|
+
*
|
|
216
|
+
* It introduces no second authority. {@link parseRunRootName} and
|
|
217
|
+
* {@link isProcessAlive} are the scratch namespace's existing parser and
|
|
218
|
+
* liveness probe, and these directories are named by {@link runRootName}, so
|
|
219
|
+
* that parser reads them unchanged. Anything it does not recognise is left
|
|
220
|
+
* alone.
|
|
221
|
+
* @param runsRoot - Directory holding the run directories
|
|
222
|
+
* @param alive - Liveness probe; defaults to the shared one
|
|
223
|
+
* @returns Basenames of the directories removed
|
|
224
|
+
*/
|
|
225
|
+
export function reclaimAbandonedCoverageRunDirectories(runsRoot = path.join(COVERAGE_BASE, COVERAGE_RUNS_DIR), alive = isProcessAlive) {
|
|
226
|
+
const root = path.resolve(process.cwd(), runsRoot);
|
|
227
|
+
try {
|
|
228
|
+
const abandoned = fs.readdirSync(root).filter(name => {
|
|
229
|
+
const owner = parseRunRootName(name);
|
|
230
|
+
return owner !== undefined && !alive(owner.pid);
|
|
231
|
+
});
|
|
232
|
+
for (const name of abandoned)
|
|
233
|
+
fs.rmSync(path.join(root, name), { recursive: true, force: true });
|
|
234
|
+
return abandoned;
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
// An absent or unreadable runs directory is not an error: nothing to
|
|
238
|
+
// reclaim is the ordinary state on a checkout that has never run coverage.
|
|
239
|
+
return [];
|
|
240
|
+
}
|
|
241
|
+
}
|
|
100
242
|
/**
|
|
101
243
|
* The path this process's coverage output belongs in.
|
|
102
244
|
*
|
|
@@ -115,9 +257,44 @@ export function coverageReportsDirectory(environment = env) {
|
|
|
115
257
|
const override = environment[COVERAGE_REPORTS_DIR_ENV];
|
|
116
258
|
if (override !== undefined && override.trim() !== "")
|
|
117
259
|
return override;
|
|
118
|
-
pinned.current
|
|
260
|
+
if (pinned.current === undefined) {
|
|
261
|
+
const name = runRootName(pid, Date.now(), randomBytes(SUFFIX_BYTES).toString("hex"));
|
|
262
|
+
pinned.current = `${COVERAGE_BASE}/${COVERAGE_RUNS_DIR}/${name}`;
|
|
263
|
+
registerRunLifecycle(pinned.current, name);
|
|
264
|
+
}
|
|
119
265
|
return pinned.current;
|
|
120
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Whether this process has already armed its reclaim and publish steps.
|
|
269
|
+
*
|
|
270
|
+
* Field named `current` to match {@link pinned}, and not by accident: the
|
|
271
|
+
* project's `functional/immutable-data` rule permits mutation only through
|
|
272
|
+
* `*.current`, `*.value` and `*.displayName`. Renaming it makes the file
|
|
273
|
+
* stop linting.
|
|
274
|
+
*/
|
|
275
|
+
const lifecycle = { current: false };
|
|
276
|
+
/**
|
|
277
|
+
* Reclaim abandoned directories now, and publish this run's own at exit.
|
|
278
|
+
*
|
|
279
|
+
* Armed once per process, from the same place the directory is minted, so the
|
|
280
|
+
* two cannot disagree about which run they belong to. A pinned run never
|
|
281
|
+
* reaches here: the pinned directory belongs to whoever set the pin, and is
|
|
282
|
+
* left exactly as it was found.
|
|
283
|
+
* @param directory - This run's reports directory
|
|
284
|
+
* @param name - Its basename, which is the ownership proof both steps check
|
|
285
|
+
*/
|
|
286
|
+
function registerRunLifecycle(directory, name) {
|
|
287
|
+
if (lifecycle.current)
|
|
288
|
+
return;
|
|
289
|
+
lifecycle.current = true;
|
|
290
|
+
// Before minting anything of our own: this run's directory does not exist
|
|
291
|
+
// yet and its pid is alive regardless, so it can never be its own victim.
|
|
292
|
+
reclaimAbandonedCoverageRunDirectories();
|
|
293
|
+
process.once("exit", () => {
|
|
294
|
+
if (!publishCoverageRunDirectory(directory, name))
|
|
295
|
+
removeOwnCoverageRunDirectory(directory, name);
|
|
296
|
+
});
|
|
297
|
+
}
|
|
121
298
|
/**
|
|
122
299
|
* Discards the memoized value.
|
|
123
300
|
*
|
|
@@ -127,5 +304,6 @@ export function coverageReportsDirectory(environment = env) {
|
|
|
127
304
|
*/
|
|
128
305
|
export function resetCoverageReportsDirectory() {
|
|
129
306
|
pinned.current = undefined;
|
|
307
|
+
lifecycle.current = false;
|
|
130
308
|
}
|
|
131
309
|
//# sourceMappingURL=coverage-reports-directory.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coverage-reports-directory.js","sourceRoot":"","sources":["../../../src/configs/vitest/coverage-reports-directory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAExC,OAAO,
|
|
1
|
+
{"version":3,"file":"coverage-reports-directory.js","sourceRoot":"","sources":["../../../src/configs/vitest/coverage-reports-directory.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lisa-owned-hash-ledger.d.ts","sourceRoot":"","sources":["../../src/core/lisa-owned-hash-ledger.ts"],"names":[],"mappings":"AACA,6EAA6E;AAE7E;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAC3C,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"lisa-owned-hash-ledger.d.ts","sourceRoot":"","sources":["../../src/core/lisa-owned-hash-ledger.ts"],"names":[],"mappings":"AACA,6EAA6E;AAE7E;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAC3C,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAikCjC,CAAC;AAEH;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,+BAA+B,EAAE,QAAQ,CACpD,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAo+BjC,CAAC"}
|
|
@@ -358,8 +358,10 @@ export const LISA_OWNED_HASH_LEDGER = Object.freeze({
|
|
|
358
358
|
"041094be579c3aefdf9f7b384e24ca23cd860c984c9094ffeb55b1542829bc39",
|
|
359
359
|
"28600bd3ce643d80aefcc696c4e9607f83410ab926738d9baa7db694d78563ea",
|
|
360
360
|
"3e9a5911c9b4b4f3c0dd6aef4ecc7accb762edf6e62eea66252248f7c02684f4",
|
|
361
|
+
"43dc203de647ebdcd0e3e1878240423e3895b588372002c79ffed3348e4f08a3",
|
|
361
362
|
"4503ddbe7bd54cd553db2b5952972d5319d6b6eb39a258b7c58577b61c7e7ce7",
|
|
362
363
|
"503b51e9239411d07ce6b51dbe2b3079bb12e94bf7c0310e76247af19ff667e6",
|
|
364
|
+
"5d0aee00b95dba43cae499f338358ad4b0286f869c993508c1146448e92bd777",
|
|
363
365
|
"6742cc12ea14fc99b7bef31ff03df68d5c4610b56fad43107790372e6d777eb1",
|
|
364
366
|
"6b72a6a71078c838fdb0531e74898a5e36d942cad424b20480e2cd370b5264eb",
|
|
365
367
|
"7ffcd3f6a0595ae26c697bb0f77044b8a100200985774ad7059ebc3dc2e6bedd",
|
|
@@ -1440,8 +1442,10 @@ export const LISA_OWNED_HASH_HISTORY_DERIVED = Object.freeze({
|
|
|
1440
1442
|
"041094be579c3aefdf9f7b384e24ca23cd860c984c9094ffeb55b1542829bc39",
|
|
1441
1443
|
"28600bd3ce643d80aefcc696c4e9607f83410ab926738d9baa7db694d78563ea",
|
|
1442
1444
|
"3e9a5911c9b4b4f3c0dd6aef4ecc7accb762edf6e62eea66252248f7c02684f4",
|
|
1445
|
+
"43dc203de647ebdcd0e3e1878240423e3895b588372002c79ffed3348e4f08a3",
|
|
1443
1446
|
"4503ddbe7bd54cd553db2b5952972d5319d6b6eb39a258b7c58577b61c7e7ce7",
|
|
1444
1447
|
"503b51e9239411d07ce6b51dbe2b3079bb12e94bf7c0310e76247af19ff667e6",
|
|
1448
|
+
"5d0aee00b95dba43cae499f338358ad4b0286f869c993508c1146448e92bd777",
|
|
1445
1449
|
"6742cc12ea14fc99b7bef31ff03df68d5c4610b56fad43107790372e6d777eb1",
|
|
1446
1450
|
"7ffcd3f6a0595ae26c697bb0f77044b8a100200985774ad7059ebc3dc2e6bedd",
|
|
1447
1451
|
"9bce79d2cdcf709be40fee1bec469796bee2bb06a4cd38b11905677b97c12bc3",
|