@vzn/vx 0.0.60 → 0.0.62
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.62",
|
|
4
4
|
"description": "An open, extensible monorepo task runner.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
"node": ">=18"
|
|
18
18
|
},
|
|
19
19
|
"optionalDependencies": {
|
|
20
|
-
"@vzn/vx-linux-x64": "0.0.
|
|
21
|
-
"@vzn/vx-linux-arm64": "0.0.
|
|
22
|
-
"@vzn/vx-darwin-x64": "0.0.
|
|
23
|
-
"@vzn/vx-darwin-arm64": "0.0.
|
|
20
|
+
"@vzn/vx-linux-x64": "0.0.62",
|
|
21
|
+
"@vzn/vx-linux-arm64": "0.0.62",
|
|
22
|
+
"@vzn/vx-darwin-x64": "0.0.62",
|
|
23
|
+
"@vzn/vx-darwin-arm64": "0.0.62"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
package/src/cache/inputs.ts
CHANGED
|
@@ -386,6 +386,9 @@ async function runRuntimeCommand(
|
|
|
386
386
|
stdin: 'ignore',
|
|
387
387
|
stdout: 'pipe',
|
|
388
388
|
stderr: 'pipe',
|
|
389
|
+
// Its own group, so the probe's whole tree can be taken down with vx
|
|
390
|
+
// (`killProbesOnExit`).
|
|
391
|
+
detached: true,
|
|
389
392
|
})
|
|
390
393
|
} catch (err) {
|
|
391
394
|
// The probe runs through `sh -c` like a task: a box without sh names
|
|
@@ -397,11 +400,18 @@ async function runRuntimeCommand(
|
|
|
397
400
|
}
|
|
398
401
|
throw new UserError(`cache.inputs runtime command failed to spawn: ${command} (cwd: ${cwd})`)
|
|
399
402
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
403
|
+
liveProbes.add(proc)
|
|
404
|
+
killProbesOnExit()
|
|
405
|
+
let stdout, stderr, exitCode
|
|
406
|
+
try {
|
|
407
|
+
;[stdout, stderr, exitCode] = await Promise.all([
|
|
408
|
+
new Response(proc.stdout).text(),
|
|
409
|
+
new Response(proc.stderr).text(),
|
|
410
|
+
proc.exited,
|
|
411
|
+
])
|
|
412
|
+
} finally {
|
|
413
|
+
liveProbes.delete(proc)
|
|
414
|
+
}
|
|
405
415
|
const output = `${stdout}${stderr}`.trim()
|
|
406
416
|
if (exitCode !== 0) {
|
|
407
417
|
throw new UserError(
|
|
@@ -412,6 +422,31 @@ async function runRuntimeCommand(
|
|
|
412
422
|
return output
|
|
413
423
|
}
|
|
414
424
|
|
|
425
|
+
/**
|
|
426
|
+
* Runtime probes still running. A probe that outlives its answer's need — a
|
|
427
|
+
* run stopped by Ctrl-C, or ended by a refusal, while `node -e …` or a hung
|
|
428
|
+
* `git` still ran — was left behind: vx exited and the probe's shell and
|
|
429
|
+
* what it started ran on under init (A-9). Every probe is its own group, and
|
|
430
|
+
* the ones still listed when vx exits are SIGKILLed with their trees. A
|
|
431
|
+
* `kill -9` of vx runs no exit hook; that residual is the task groups' guard's
|
|
432
|
+
* (exec/kill-tree.ts), which probes are not on.
|
|
433
|
+
*/
|
|
434
|
+
const liveProbes = new Set<ReturnType<typeof Bun.spawn>>()
|
|
435
|
+
let probeExitHooked = false
|
|
436
|
+
function killProbesOnExit(): void {
|
|
437
|
+
if (probeExitHooked) return
|
|
438
|
+
probeExitHooked = true
|
|
439
|
+
process.on('exit', () => {
|
|
440
|
+
for (const p of liveProbes) {
|
|
441
|
+
try {
|
|
442
|
+
process.kill(-p.pid, 'SIGKILL')
|
|
443
|
+
} catch {
|
|
444
|
+
// the group is gone
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
})
|
|
448
|
+
}
|
|
449
|
+
|
|
415
450
|
/**
|
|
416
451
|
* Resolve a list of runtime-input commands to sorted [command, output]
|
|
417
452
|
* pairs. Dedups via the shared `memo` (Promise per key): the first
|
|
@@ -29,6 +29,8 @@ export interface SummarizeArgs {
|
|
|
29
29
|
totalMs: number
|
|
30
30
|
/** The run's verdict — the same value the CLI turns into the exit code. */
|
|
31
31
|
ok: boolean
|
|
32
|
+
/** The process's exit code where it is not `ok ? 0 : 1`: a stopping signal's. */
|
|
33
|
+
exitCode?: number
|
|
32
34
|
outcomes: readonly TaskOutcome[]
|
|
33
35
|
/** What `detectFlaky` found this run; the footer's Flaky section, typed. */
|
|
34
36
|
flaky?: readonly FlakyFinding[]
|
|
@@ -96,12 +98,13 @@ export async function writeRunSummary(args: SummarizeArgs): Promise<string> {
|
|
|
96
98
|
// parser must not be told less than a human is.
|
|
97
99
|
const aborted = args.outcomes.filter((o) => !isGroupTask(o.node) && o.status === 'aborted')
|
|
98
100
|
const flakyById = new Map((args.flaky ?? []).map((f) => [f.taskId, f]))
|
|
101
|
+
const exitCode = args.exitCode ?? (args.ok ? 0 : 1)
|
|
99
102
|
const payload = {
|
|
100
103
|
runId: args.runId,
|
|
101
104
|
// The run-level verdict, first: a consumer gating on this artifact must
|
|
102
105
|
// not have to re-derive it by re-implementing the bucket rules.
|
|
103
|
-
ok:
|
|
104
|
-
exitCode
|
|
106
|
+
ok: exitCode === 0,
|
|
107
|
+
exitCode,
|
|
105
108
|
startedAt: new Date(args.startedAtMs).toISOString(),
|
|
106
109
|
endedAt: new Date(args.endedAtMs).toISOString(),
|
|
107
110
|
totalMs: args.totalMs,
|
package/src/orchestrator/run.ts
CHANGED
|
@@ -58,6 +58,7 @@ import { prepareRun, type PreparedRun } from './prepare.js'
|
|
|
58
58
|
import { acquireRunLock } from './run-lock.js'
|
|
59
59
|
import {
|
|
60
60
|
forwardedSignal,
|
|
61
|
+
type StopSignal,
|
|
61
62
|
forwardSignals,
|
|
62
63
|
SIGNAL_SHUTDOWN_GRACE_MS,
|
|
63
64
|
terminateChildren,
|
|
@@ -456,7 +457,7 @@ async function runOnBus(
|
|
|
456
457
|
// handler exited before any outcome landed, and a Ctrl-C'd run then read
|
|
457
458
|
// in `vx last` as FAILED with 0 tasks (item 854). An embedder's abort is
|
|
458
459
|
// its own call and keeps its record.
|
|
459
|
-
let
|
|
460
|
+
let stoppedBy: StopSignal | undefined
|
|
460
461
|
let leftRun = (): void => {}
|
|
461
462
|
const runLeft = new Promise<void>((resolve) => {
|
|
462
463
|
leftRun = resolve
|
|
@@ -468,7 +469,7 @@ async function runOnBus(
|
|
|
468
469
|
liveChildren,
|
|
469
470
|
persistentRegistry,
|
|
470
471
|
stop: (signal) => {
|
|
471
|
-
|
|
472
|
+
stoppedBy = signal
|
|
472
473
|
stopRun.abort(signal)
|
|
473
474
|
},
|
|
474
475
|
done: runLeft,
|
|
@@ -903,7 +904,10 @@ async function runOnBus(
|
|
|
903
904
|
|
|
904
905
|
// Optional artifacts. Errors are surfaced to the user but don't
|
|
905
906
|
// change the run's exit code — the run already happened.
|
|
906
|
-
|
|
907
|
+
// Written again after the keep-alive wait: a kept server's crash or a
|
|
908
|
+
// Ctrl-C there is the process's exit, and the first write said ok.
|
|
909
|
+
const summarize = async (runOk: boolean): Promise<void> => {
|
|
910
|
+
if (options.summarize === undefined) return
|
|
907
911
|
try {
|
|
908
912
|
const wrote = await writeRunSummary({
|
|
909
913
|
target: options.summarize,
|
|
@@ -913,7 +917,8 @@ async function runOnBus(
|
|
|
913
917
|
startedAtMs: endedAtMsAtStart,
|
|
914
918
|
endedAtMs,
|
|
915
919
|
totalMs,
|
|
916
|
-
ok,
|
|
920
|
+
ok: runOk,
|
|
921
|
+
...(stoppedBy !== undefined && { exitCode: signalExitCode(stoppedBy) }),
|
|
917
922
|
outcomes: list,
|
|
918
923
|
flaky,
|
|
919
924
|
})
|
|
@@ -923,6 +928,7 @@ async function runOnBus(
|
|
|
923
928
|
log.status(`vx: failed to write summary: ${msg}`)
|
|
924
929
|
}
|
|
925
930
|
}
|
|
931
|
+
await summarize(ok)
|
|
926
932
|
if (options.profile !== undefined) {
|
|
927
933
|
try {
|
|
928
934
|
const wrote = await writeRunProfile({
|
|
@@ -964,7 +970,7 @@ async function runOnBus(
|
|
|
964
970
|
withTelemetry: telemetry !== undefined,
|
|
965
971
|
})
|
|
966
972
|
try {
|
|
967
|
-
if (
|
|
973
|
+
if (stoppedBy === undefined) cache.recordRunBundle(records)
|
|
968
974
|
} catch (err) {
|
|
969
975
|
// History is observability: a full cache disk at the very end must
|
|
970
976
|
// not turn a finished run's verdict into a stack and exit 1 (seen as
|
|
@@ -1105,6 +1111,7 @@ async function runOnBus(
|
|
|
1105
1111
|
)
|
|
1106
1112
|
}
|
|
1107
1113
|
await terminateChildren(() => keepAlive.children)
|
|
1114
|
+
await summarize(ok && first.code === 0)
|
|
1108
1115
|
return { ok: ok && first.code === 0, outcomes: list }
|
|
1109
1116
|
}
|
|
1110
1117
|
|