@vzn/vx 0.0.637 → 0.0.639
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/docs/caching.md +84 -28
- package/docs/cli.md +273 -408
- package/docs/execution.md +19 -44
- package/docs/features.md +13 -15
- package/docs/patterns.md +5 -7
- package/docs/schema.md +68 -61
- package/package.json +5 -5
- package/schemas/last.json +7 -4
- package/schemas/plan.json +2 -2
- package/skills/vx/SKILL.md +19 -6
- package/src/bin.ts +13 -7
- package/src/cache/cache.ts +60 -11
- package/src/cache/git-inputs.ts +6 -0
- package/src/cache/index.ts +10 -2
- package/src/cache/inputs.ts +0 -24
- package/src/cache/layer.ts +32 -12
- package/src/cache/layered-cache.ts +112 -315
- package/src/cache/plugin-cache.ts +240 -0
- package/src/cache/policy.ts +9 -0
- package/src/cache/remote-lane.ts +383 -0
- package/src/cache/run-history.ts +92 -8
- package/src/cache/run-store.ts +77 -0
- package/src/cache/schema.ts +44 -13
- package/src/cache/scoped-cache.ts +22 -2
- package/src/cli/cache.ts +33 -16
- package/src/cli/completions.ts +11 -7
- package/src/cli/foreign-flags.ts +10 -22
- package/src/cli/help.ts +22 -38
- package/src/cli/index.ts +25 -11
- package/src/cli/info.ts +8 -6
- package/src/cli/init.ts +14 -15
- package/src/cli/last.ts +20 -10
- package/src/cli/lock.ts +39 -27
- package/src/cli/plan-format.ts +10 -61
- package/src/cli/plugin-commands.ts +77 -6
- package/src/cli/plugin-templates.ts +1 -1
- package/src/cli/run.ts +31 -191
- package/src/cli/select.ts +18 -9
- package/src/cli/show.ts +72 -40
- package/src/cli/task-verb.ts +4 -4
- package/src/cli/upgrade.ts +132 -39
- package/src/cli/watch-cycle.ts +2 -1
- package/src/cli/watch-filter.ts +7 -2
- package/src/cli/watch-judge.ts +6 -1
- package/src/cli/watch.ts +73 -34
- package/src/cli/why.ts +14 -5
- package/src/cli/workspace-config.ts +2 -2
- package/src/config.ts +0 -6
- package/src/exec/executor.ts +1 -1
- package/src/exec/sandbox-runtime.ts +1 -1
- package/src/exec/sandbox-violations.ts +3 -1
- package/src/graph/index.ts +9 -1
- package/src/graph/priorities.ts +1 -1
- package/src/graph/scheduler.ts +12 -16
- package/src/graph/task-graph.ts +119 -21
- package/src/graph/task-status.ts +95 -0
- package/src/{orchestrator → history}/failure-mode.ts +20 -11
- package/src/{orchestrator → history}/history.ts +61 -5
- package/src/history/index.ts +48 -0
- package/src/{orchestrator → history}/metrics.ts +14 -23
- package/src/{orchestrator → history}/miss-reason.ts +1 -1
- package/src/index.ts +21 -29
- package/src/orchestrator/affected-tasks.ts +8 -3
- package/src/orchestrator/doctor.ts +2 -2
- package/src/orchestrator/execute-args.ts +80 -0
- package/src/orchestrator/execute-task.ts +47 -429
- package/src/orchestrator/fingerprint-watch.ts +2 -2
- package/src/orchestrator/index.ts +3 -74
- package/src/orchestrator/miss-save.ts +33 -3
- package/src/orchestrator/options.ts +1 -36
- package/src/orchestrator/output-host.ts +4 -3
- package/src/orchestrator/persistent-task.ts +321 -0
- package/src/orchestrator/persistent.ts +130 -3
- package/src/orchestrator/placement.ts +3 -3
- package/src/orchestrator/plan.ts +18 -5
- package/src/orchestrator/plugin-host.ts +77 -47
- package/src/orchestrator/plugin.ts +86 -14
- package/src/orchestrator/prepare.ts +74 -31
- package/src/orchestrator/projects.ts +3 -0
- package/src/orchestrator/remote-prefetch.ts +23 -6
- package/src/orchestrator/run-artifacts.ts +5 -177
- package/src/orchestrator/run-context.ts +55 -0
- package/src/orchestrator/run-failures.ts +15 -3
- package/src/orchestrator/run-records.ts +36 -4
- package/src/orchestrator/run-report.ts +81 -179
- package/src/orchestrator/run-settings.ts +1 -16
- package/src/orchestrator/run-stop.ts +153 -0
- package/src/orchestrator/run.ts +313 -555
- package/src/orchestrator/sandbox-fix.ts +0 -16
- package/src/orchestrator/signals.ts +14 -6
- package/src/orchestrator/stable-keys.ts +107 -3
- package/src/orchestrator/steps.ts +50 -0
- package/src/orchestrator/task-hash.ts +3 -16
- package/src/orchestrator/task-log-buffer.ts +1 -1
- package/src/orchestrator/task-process.ts +94 -0
- package/src/orchestrator/telemetry-host.ts +1 -1
- package/src/orchestrator/telemetry.ts +6 -83
- package/src/{orchestrator → report}/events.ts +41 -8
- package/src/{orchestrator → report}/forecast.ts +35 -2
- package/src/{orchestrator → report}/framed-output.ts +39 -34
- package/src/report/index.ts +36 -0
- package/src/{orchestrator → report}/logger.ts +166 -21
- package/src/{orchestrator → report}/status-line.ts +84 -7
- package/src/{orchestrator → report}/summary.ts +179 -63
- package/src/report/tally.ts +83 -0
- package/src/settings.ts +13 -23
- package/src/{orchestrator → util}/colors.ts +49 -4
- package/src/util/config-frame.ts +31 -2
- package/src/util/errors.ts +32 -0
- package/src/util/git-ignored.ts +143 -0
- package/src/util/index.ts +25 -3
- package/src/util/refusal-notice.ts +111 -0
- package/src/util/report.ts +39 -0
- package/src/util/settle.ts +14 -39
- package/src/util/tail.ts +1 -1
- package/src/util/ui.ts +197 -0
- package/src/workspace/affected.ts +20 -23
- package/src/workspace/dependency-rules.ts +12 -4
- package/src/workspace/migration.ts +87 -91
- package/src/workspace/workspace.ts +5 -4
- package/schemas/summary.json +0 -133
- package/src/cache/chained-cache.ts +0 -276
- package/src/orchestrator/tally.ts +0 -112
- /package/src/{orchestrator → report}/path-links.ts +0 -0
- /package/src/{orchestrator → report}/plain-output.ts +0 -0
- /package/src/{orchestrator → report}/terminal-signals.ts +0 -0
|
@@ -15,11 +15,13 @@ import { statSync } from 'node:fs'
|
|
|
15
15
|
import path from 'node:path'
|
|
16
16
|
// The keyed-run predicate lives beside the schema in cache/cache.ts, so a
|
|
17
17
|
// rule written once cannot drift.
|
|
18
|
-
import { KEYED_RUNS_SQL } from '../cache/index.js'
|
|
19
|
-
import { splitTaskId } from '../graph/index.js'
|
|
18
|
+
import { KEYED_RUNS_SQL, type TaskInputComponent } from '../cache/index.js'
|
|
19
|
+
import { isPassStatus, splitTaskId } from '../graph/index.js'
|
|
20
20
|
import { clampInt, MASKED, secretNamed } from '../util/index.js'
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
|
|
22
|
+
// A recorded `cache_policy` is the `--cache-policy` spec (`local:rw,remote:r`).
|
|
23
|
+
const readsCache = (spec: string): boolean => /(?:^|,)(?:local|remote):w?r/.test(spec)
|
|
24
|
+
const writesCache = (spec: string): boolean => /(?:^|,)(?:local|remote):r?w/.test(spec)
|
|
23
25
|
|
|
24
26
|
// ---------------------------------------------------------------------------
|
|
25
27
|
// Run listing + detail
|
|
@@ -53,6 +55,8 @@ export interface RunSummaryRow {
|
|
|
53
55
|
notReady: 'timeout' | 'exited' | 'spawn' | null
|
|
54
56
|
/** The task ran with the run's forwarded args (`-- …`): a requested one under a run that had some. */
|
|
55
57
|
forwarded: boolean
|
|
58
|
+
/** Each remote read or write of its key that failed, `upload: <message>`, `; `-joined (v40). */
|
|
59
|
+
remoteError: string | null
|
|
56
60
|
}
|
|
57
61
|
|
|
58
62
|
export interface ListRunsArgs {
|
|
@@ -113,7 +117,7 @@ export function listRuns(db: Database, args: ListRunsArgs = {}): RunSummaryRow[]
|
|
|
113
117
|
wallclock_start_ns AS wallclockStartNs, wallclock_end_ns AS wallclockEndNs,
|
|
114
118
|
blocked_by AS blockedBy, timed_out AS timedOut,
|
|
115
119
|
sandbox_violations AS sandboxViolations, not_ready AS notReady,
|
|
116
|
-
forward_args IS NOT NULL AS forwarded
|
|
120
|
+
forward_args IS NOT NULL AS forwarded, remote_error AS remoteError
|
|
117
121
|
FROM runs ${clause} ORDER BY id DESC LIMIT ?`,
|
|
118
122
|
)
|
|
119
123
|
.all(...params, limit) as RawRow[]
|
|
@@ -131,8 +135,7 @@ export function listRuns(db: Database, args: ListRunsArgs = {}): RunSummaryRow[]
|
|
|
131
135
|
|
|
132
136
|
/**
|
|
133
137
|
* One `vx run` invocation header — every `invocations` column, camelCased,
|
|
134
|
-
* `
|
|
135
|
-
* surfaced as booleans. The SPA's
|
|
138
|
+
* `dirty`/`ci`/`exitOk` surfaced as booleans. The SPA's
|
|
136
139
|
* existing fields (runId / startedAt / endedAt / taskCount / failedCount /
|
|
137
140
|
* hitCount / totalDurationMs) are all present, so existing views keep working.
|
|
138
141
|
*/
|
|
@@ -166,7 +169,6 @@ export interface InvocationDetail {
|
|
|
166
169
|
os: string | null
|
|
167
170
|
arch: string | null
|
|
168
171
|
vxVersion: string
|
|
169
|
-
tags: Record<string, string>
|
|
170
172
|
}
|
|
171
173
|
|
|
172
174
|
interface InvocationRawRow {
|
|
@@ -197,7 +199,6 @@ interface InvocationRawRow {
|
|
|
197
199
|
os: string | null
|
|
198
200
|
arch: string | null
|
|
199
201
|
vxVersion: string
|
|
200
|
-
tags: string
|
|
201
202
|
}
|
|
202
203
|
|
|
203
204
|
const INVOCATION_COLUMNS = `
|
|
@@ -210,7 +211,7 @@ const INVOCATION_COLUMNS = `
|
|
|
210
211
|
restored_remote_count AS restoredRemoteCount,
|
|
211
212
|
exit_ok AS exitOk,
|
|
212
213
|
commit_sha AS commitSha, branch, dirty, ci, ci_provider AS ciProvider,
|
|
213
|
-
host, os, arch, vx_version AS vxVersion
|
|
214
|
+
host, os, arch, vx_version AS vxVersion`
|
|
214
215
|
|
|
215
216
|
function mapInvocation(r: InvocationRawRow): InvocationDetail {
|
|
216
217
|
let requestedTasks: string[] = []
|
|
@@ -218,15 +219,6 @@ function mapInvocation(r: InvocationRawRow): InvocationDetail {
|
|
|
218
219
|
const parsed = JSON.parse(r.requestedTasks) as unknown
|
|
219
220
|
if (Array.isArray(parsed)) requestedTasks = parsed.map(String)
|
|
220
221
|
} catch {}
|
|
221
|
-
let tags: Record<string, string> = {}
|
|
222
|
-
try {
|
|
223
|
-
const parsed = JSON.parse(r.tags) as unknown
|
|
224
|
-
if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
225
|
-
tags = Object.fromEntries(
|
|
226
|
-
Object.entries(parsed as Record<string, unknown>).map(([k, v]) => [k, String(v)]),
|
|
227
|
-
)
|
|
228
|
-
}
|
|
229
|
-
} catch {}
|
|
230
222
|
return {
|
|
231
223
|
runId: r.runId,
|
|
232
224
|
command: r.command,
|
|
@@ -255,7 +247,6 @@ function mapInvocation(r: InvocationRawRow): InvocationDetail {
|
|
|
255
247
|
os: r.os,
|
|
256
248
|
arch: r.arch,
|
|
257
249
|
vxVersion: r.vxVersion,
|
|
258
|
-
tags,
|
|
259
250
|
}
|
|
260
251
|
}
|
|
261
252
|
|
|
@@ -412,7 +403,7 @@ function unchangedKeyNote(
|
|
|
412
403
|
p: string
|
|
413
404
|
} | null
|
|
414
405
|
)?.p
|
|
415
|
-
if (policy !== undefined && !policy
|
|
406
|
+
if (policy !== undefined && !readsCache(policy)) {
|
|
416
407
|
return 'cache key unchanged — re-executed because this run did not read the cache (--force, or a --cache-policy without read)'
|
|
417
408
|
}
|
|
418
409
|
// `get` answers null, not undefined, for no row.
|
|
@@ -438,7 +429,7 @@ function unchangedKeyNote(
|
|
|
438
429
|
const row = db
|
|
439
430
|
.query('SELECT cache_policy AS p, failed_count AS f FROM invocations WHERE run_id = ?')
|
|
440
431
|
.get(id) as { p: string; f: number } | null
|
|
441
|
-
const writes = row
|
|
432
|
+
const writes = row !== null && writesCache(row.p)
|
|
442
433
|
return writes ? (row?.f ?? 0) : 0
|
|
443
434
|
}
|
|
444
435
|
const before = failedWithWrites(prev.runId)
|
|
@@ -472,7 +463,7 @@ function unchangedKeyNote(
|
|
|
472
463
|
p: string
|
|
473
464
|
} | null
|
|
474
465
|
)?.p
|
|
475
|
-
if (prevPolicy !== undefined && !prevPolicy
|
|
466
|
+
if (prevPolicy !== undefined && !writesCache(prevPolicy)) {
|
|
476
467
|
return 'cache key unchanged — the previous run on this key did not write the cache (--no-cache, or a --cache-policy without write), so there was nothing to hit'
|
|
477
468
|
}
|
|
478
469
|
return 'cache key unchanged — the previous run on this key executed but no entry for it is in the cache (its save failed, or it was pruned since), so there was nothing to hit'
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import type { Database } from 'bun:sqlite'
|
|
10
10
|
import type { InputChanges } from '../graph/index.js'
|
|
11
11
|
import { diffKeyComponents, loadEntryInputs } from './metrics.js'
|
|
12
|
-
import type { TaskInputComponent } from '
|
|
12
|
+
import type { TaskInputComponent } from '../cache/index.js'
|
|
13
13
|
|
|
14
14
|
/** How many changed components a miss names; `count` says how many there were. */
|
|
15
15
|
const NAMED = 10
|
package/src/index.ts
CHANGED
|
@@ -56,13 +56,9 @@ export { run, planRun, prepareRun } from './orchestrator/index.js'
|
|
|
56
56
|
// The per-task duration history a `schedule` plugin learns from
|
|
57
57
|
// (`@vzn/vx-schedule-history` does; a plugin package reaches core only
|
|
58
58
|
// through this façade, which is what put these here).
|
|
59
|
-
export { currentBranch
|
|
60
|
-
export
|
|
61
|
-
|
|
62
|
-
HistoryTable,
|
|
63
|
-
TaskHistory,
|
|
64
|
-
TaskResources,
|
|
65
|
-
} from './orchestrator/index.js'
|
|
59
|
+
export { currentBranch } from './orchestrator/index.js'
|
|
60
|
+
export { LocalHistoryProvider } from './history/index.js'
|
|
61
|
+
export type { HistoryProvider, HistoryTable, TaskHistory, TaskResources } from './history/index.js'
|
|
66
62
|
export type { PreparedRun } from './orchestrator/index.js'
|
|
67
63
|
// `RunOptions.affected`: what `--affected`'s diff changed, per project.
|
|
68
64
|
export type { AffectedChanges } from './workspace/index.js'
|
|
@@ -72,14 +68,8 @@ export type { AffectedChanges } from './workspace/index.js'
|
|
|
72
68
|
// docs/cli.md § Programmatic API listed all three (item 387, 2026-09-19).
|
|
73
69
|
export type { RunPlan, PlannedTask } from './orchestrator/index.js'
|
|
74
70
|
export type { CiContext, GitContext, HostContext, WorkspaceIdentity } from './orchestrator/index.js'
|
|
75
|
-
export type {
|
|
76
|
-
|
|
77
|
-
CachePolicy,
|
|
78
|
-
Logger,
|
|
79
|
-
OutputView,
|
|
80
|
-
RunOptions,
|
|
81
|
-
RunSummary,
|
|
82
|
-
} from './orchestrator/index.js'
|
|
71
|
+
export type { CacheFlags, CachePolicy, RunOptions, RunSummary } from './orchestrator/index.js'
|
|
72
|
+
export type { Logger, OutputView } from './report/index.js'
|
|
83
73
|
// `splitTaskId` is on the façade because the alternative is what happened:
|
|
84
74
|
// with only `taskId()` to JOIN an id and nothing exported to SPLIT one,
|
|
85
75
|
// consumers roll their own `split('#', 2)` and drift from the graph, which
|
|
@@ -89,7 +79,13 @@ export type { TaskNode, TaskOutcome, TaskStatus } from './graph/index.js'
|
|
|
89
79
|
|
|
90
80
|
// Cache classes + the layer interface (the `cache` capability's currency).
|
|
91
81
|
export { Cache } from './cache/index.js'
|
|
92
|
-
export type {
|
|
82
|
+
export type {
|
|
83
|
+
CacheLayer,
|
|
84
|
+
PluginCache,
|
|
85
|
+
RemoteCacheLayer,
|
|
86
|
+
RunRecord,
|
|
87
|
+
InvocationRecord,
|
|
88
|
+
} from './cache/index.js'
|
|
93
89
|
|
|
94
90
|
// Workspace discovery + the project/config catalog surface — an
|
|
95
91
|
// out-of-process service/CLI needs these. `loadProjectConfig` is the RAW
|
|
@@ -118,6 +114,8 @@ export {
|
|
|
118
114
|
quoteTsLiteral,
|
|
119
115
|
taskNameProblem,
|
|
120
116
|
} from './workspace/index.js'
|
|
117
|
+
// Output components: a plugin draws its lines with core's, so they look alike.
|
|
118
|
+
export { ui } from './util/index.js'
|
|
121
119
|
export type {
|
|
122
120
|
ApplyMigrationArgs,
|
|
123
121
|
MigrationFormat,
|
|
@@ -166,10 +164,12 @@ export type {
|
|
|
166
164
|
DiscoverContext,
|
|
167
165
|
DiscoveredProject,
|
|
168
166
|
PluginSetupContext,
|
|
167
|
+
RunReport,
|
|
168
|
+
RunReportTask,
|
|
169
169
|
PluginHookName,
|
|
170
170
|
PluginHookHandlers,
|
|
171
|
-
RunStartInfo,
|
|
172
171
|
} from './orchestrator/index.js'
|
|
172
|
+
export type { RunStartInfo } from './report/index.js'
|
|
173
173
|
// The per-task execution contract a plugin's `executor` capability returns.
|
|
174
174
|
// (`runCommand` / `runSandboxed`, the local executor's own primitives, left
|
|
175
175
|
// the façade 2026-09-10: no executor plugin built on them — `@vzn/vx-reapi`
|
|
@@ -201,15 +201,8 @@ export type {
|
|
|
201
201
|
// consumer rolls its own Set of status literals — and a Set has no
|
|
202
202
|
// compile-time tripwire when the union gains a member, so it silently answers
|
|
203
203
|
// "no" for the new one.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
// task names core does, and the cloud job summary shipped without the escape.
|
|
207
|
-
export {
|
|
208
|
-
escapeMarkdownCell,
|
|
209
|
-
TELEMETRY_SCHEMA_VERSION,
|
|
210
|
-
isCacheHit,
|
|
211
|
-
isPassStatus,
|
|
212
|
-
} from './orchestrator/index.js'
|
|
204
|
+
export { type CacheSource, isCacheHit, isPassStatus } from './graph/index.js'
|
|
205
|
+
export { TELEMETRY_SCHEMA_VERSION } from './orchestrator/index.js'
|
|
213
206
|
// `TaskLogBuffer` is on the façade on the same demonstrated need: EVERY
|
|
214
207
|
// telemetry sink that ships build output has to bound it, and the retention
|
|
215
208
|
// rules (per-task tail, per-run budget, failures never evicted by successes,
|
|
@@ -221,7 +214,6 @@ export { LOG_WIRE_VERSION, TaskLogBuffer } from './orchestrator/index.js'
|
|
|
221
214
|
// say `128 + SIGKILL`; the decode is the runner's convention, one copy.
|
|
222
215
|
export { exitSignal } from './orchestrator/index.js'
|
|
223
216
|
export type {
|
|
224
|
-
CacheSource,
|
|
225
217
|
RunContextRecord,
|
|
226
218
|
RunSummaryRecord,
|
|
227
219
|
TaskLogBundle,
|
|
@@ -232,5 +224,5 @@ export type {
|
|
|
232
224
|
TelemetrySink,
|
|
233
225
|
} from './orchestrator/index.js'
|
|
234
226
|
|
|
235
|
-
// The serializable
|
|
236
|
-
export type {
|
|
227
|
+
// The serializable per-task views.
|
|
228
|
+
export type { TaskView, OutcomeView } from './report/index.js'
|
|
@@ -62,13 +62,15 @@ function wholeProjects(
|
|
|
62
62
|
* `input`, a changed `file` is one of its declared inputs; `project`, its
|
|
63
63
|
* project changed and the task reads it whole (an uncached task, a path no
|
|
64
64
|
* cached task declares, or no single `file`: a lockfile claim, a manifest
|
|
65
|
-
* edge, a config import); `
|
|
65
|
+
* edge, a config import); `workspace`, a workspace-wide `file` (the
|
|
66
|
+
* workspace config, a root file a plugin claims) reached every task of its
|
|
67
|
+
* project; `package`, a `^` edge passes through `project`, a
|
|
66
68
|
* changed package with no such task; `named`, asked as `pkg#task`;
|
|
67
69
|
* `selected`, another filter selected its project outright. `via` is the
|
|
68
70
|
* `dependsOn` chain, nearest first, to the task the change seeded.
|
|
69
71
|
*/
|
|
70
72
|
export interface AffectedReason {
|
|
71
|
-
kind: 'input' | 'project' | 'package' | 'named' | 'selected'
|
|
73
|
+
kind: 'input' | 'project' | 'workspace' | 'package' | 'named' | 'selected'
|
|
72
74
|
/** Workspace-relative. */
|
|
73
75
|
file?: string
|
|
74
76
|
project?: string
|
|
@@ -102,7 +104,10 @@ export function affectedRoots(
|
|
|
102
104
|
const seeds = explain === undefined ? null : new Map<string, AffectedReason>()
|
|
103
105
|
const through = explain === undefined ? null : new Map<string, string>()
|
|
104
106
|
const inProject = (name: string, rel: string | undefined): AffectedReason => {
|
|
105
|
-
if (rel === undefined)
|
|
107
|
+
if (rel === undefined) {
|
|
108
|
+
const file = changes.wholeBy?.get(name)
|
|
109
|
+
return file === undefined ? { kind: 'project' } : { kind: 'workspace', file }
|
|
110
|
+
}
|
|
106
111
|
const dir = path.relative(explain!.workspaceRoot, projects.get(name)!.dir)
|
|
107
112
|
return { kind: 'project', file: dir === '' ? rel : path.posix.join(dir, rel) }
|
|
108
113
|
}
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
resolveCacheDir,
|
|
31
31
|
resolveStoreRoot,
|
|
32
32
|
} from '../workspace/index.js'
|
|
33
|
-
import { flakyTasks, type FlakyTask } from '
|
|
33
|
+
import { flakyTasks, type FlakyTask } from '../history/index.js'
|
|
34
34
|
import type { VxPlugin } from './plugin.js'
|
|
35
35
|
import { discoverProjects, loadProjects, loadWorkspacePlugins, lockReader } from './projects.js'
|
|
36
36
|
import { atPath, envName, resolveSetting, SETTINGS, type SettingSource } from '../settings.js'
|
|
@@ -384,7 +384,7 @@ function gitVersion(): string | null {
|
|
|
384
384
|
function settingsFact(config: WorkspaceConfig | null): InfoFacts['settings'] {
|
|
385
385
|
const out: InfoFacts['settings'] = {}
|
|
386
386
|
for (const s of SETTINGS) {
|
|
387
|
-
const env = process.env[envName(s.path)]
|
|
387
|
+
const env = s.env === false ? undefined : process.env[envName(s.path)]
|
|
388
388
|
const declared = s.config === false ? undefined : atPath(config, s.path)
|
|
389
389
|
out[s.path] =
|
|
390
390
|
env !== undefined && env !== ''
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// What a dispatch hands executeTask: the run's half, built once, and the
|
|
2
|
+
// task's half, looked up per dispatch. The run's half is one fixed-shape
|
|
3
|
+
// object every task spreads; the task's optional fields are assigned only
|
|
4
|
+
// when present: twelve conditional spreads per task cost ~18 ms of object
|
|
5
|
+
// copying on a 1,090-package warm run (X-149).
|
|
6
|
+
|
|
7
|
+
import type { TaskNode, TaskOutcome } from '../graph/index.js'
|
|
8
|
+
import type { Logger } from '../report/index.js'
|
|
9
|
+
import type { DownloadMode } from './download-policy.js'
|
|
10
|
+
import type { ExecuteArgs } from './execute-task.js'
|
|
11
|
+
import type { ShortCircuit } from './local-shortcircuit.js'
|
|
12
|
+
import { UNPLACED_EXECUTOR, type Placements } from './placement.js'
|
|
13
|
+
|
|
14
|
+
type TaskField =
|
|
15
|
+
| 'node'
|
|
16
|
+
| 'upstream'
|
|
17
|
+
| 'log'
|
|
18
|
+
| 'executor'
|
|
19
|
+
| 'nestedProjectDirs'
|
|
20
|
+
| 'remoteOnlyNoop'
|
|
21
|
+
| 'remoteOnly'
|
|
22
|
+
| 'download'
|
|
23
|
+
| 'preProbed'
|
|
24
|
+
| 'upfrontKey'
|
|
25
|
+
| 'upfrontGroupKey'
|
|
26
|
+
| 'taintedUpstream'
|
|
27
|
+
| 'noDependants'
|
|
28
|
+
| 'terminal'
|
|
29
|
+
|
|
30
|
+
/** What every task of a run shares; an absent optional stays absent. */
|
|
31
|
+
export type RunScope = Omit<ExecuteArgs, TaskField>
|
|
32
|
+
|
|
33
|
+
/** Where the task's half is read from, per task id. */
|
|
34
|
+
export interface TaskFacts {
|
|
35
|
+
log: (node: TaskNode) => Logger
|
|
36
|
+
placements: Placements
|
|
37
|
+
nestedDirsByProject: ReadonlyMap<string, string[]>
|
|
38
|
+
downloadModeOf: ReadonlyMap<string, DownloadMode>
|
|
39
|
+
shortCircuit: Pick<ShortCircuit, 'preProbed' | 'uncachedKeys' | 'groupKeys'>
|
|
40
|
+
/** Tasks some other task depends on. */
|
|
41
|
+
dependedOn: ReadonlySet<string>
|
|
42
|
+
/** Tasks that hold the terminal (`terminalHolders`). */
|
|
43
|
+
holders: ReadonlySet<string>
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The dispatch builder. `reuseProbe: false` takes the task's key again
|
|
48
|
+
* instead of the up-front probe (admission's retry after a dropped probe);
|
|
49
|
+
* `tainted` is the run's taint verdict for this dispatch.
|
|
50
|
+
*/
|
|
51
|
+
export function executeArgsOf(
|
|
52
|
+
scope: RunScope,
|
|
53
|
+
facts: TaskFacts,
|
|
54
|
+
): (node: TaskNode, upstream: TaskOutcome[], reuseProbe: boolean, tainted: boolean) => ExecuteArgs {
|
|
55
|
+
const { placements, shortCircuit } = facts
|
|
56
|
+
return (node, upstream, reuseProbe, tainted) => {
|
|
57
|
+
const id = node.id
|
|
58
|
+
const a: ExecuteArgs = {
|
|
59
|
+
...scope,
|
|
60
|
+
node,
|
|
61
|
+
upstream,
|
|
62
|
+
log: facts.log(node),
|
|
63
|
+
executor: placements.executors.get(id) ?? UNPLACED_EXECUTOR,
|
|
64
|
+
nestedProjectDirs: facts.nestedDirsByProject.get(node.projectName) ?? [],
|
|
65
|
+
}
|
|
66
|
+
const probe = reuseProbe ? shortCircuit.preProbed.get(id) : undefined
|
|
67
|
+
const upfrontKey = shortCircuit.uncachedKeys.get(id)
|
|
68
|
+
const upfrontGroupKey = shortCircuit.groupKeys.get(id)
|
|
69
|
+
if (facts.downloadModeOf.get(id) === 'deferred') a.download = 'deferred'
|
|
70
|
+
if (placements.remoteOnlyNoop.has(id)) a.remoteOnlyNoop = true
|
|
71
|
+
if (placements.remoteOnly.has(id)) a.remoteOnly = true
|
|
72
|
+
if (probe !== undefined) a.preProbed = probe
|
|
73
|
+
if (upfrontKey !== undefined) a.upfrontKey = upfrontKey
|
|
74
|
+
if (upfrontGroupKey !== undefined) a.upfrontGroupKey = upfrontGroupKey
|
|
75
|
+
if (tainted) a.taintedUpstream = true
|
|
76
|
+
if (!facts.dependedOn.has(id)) a.noDependants = true
|
|
77
|
+
if (facts.holders.has(id)) a.terminal = true
|
|
78
|
+
return a
|
|
79
|
+
}
|
|
80
|
+
}
|