@vzn/vx 0.0.21 → 0.0.22
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/README.md +21 -18
- package/package.json +6 -6
- package/src/bin.ts +52 -3
- package/src/cache/archive.ts +170 -21
- package/src/cache/cache.ts +667 -288
- package/src/cache/chained-cache.ts +14 -2
- package/src/cache/config-evals.ts +29 -6
- package/src/cache/file-hashes.ts +63 -35
- package/src/cache/git-inputs.ts +398 -69
- package/src/cache/index.ts +8 -1
- package/src/cache/inputs.ts +403 -102
- package/src/cache/key-fold.ts +232 -0
- package/src/cache/layer.ts +81 -11
- package/src/cache/layered-cache.ts +199 -32
- package/src/cache/output-index.ts +109 -10
- package/src/cache/run-history.ts +8 -3
- package/src/cache/zstd.ts +53 -30
- package/src/cli/cache.ts +51 -21
- package/src/cli/format.ts +4 -15
- package/src/cli/help.ts +13 -3
- package/src/cli/index.ts +35 -9
- package/src/cli/info.ts +49 -7
- package/src/cli/init.ts +17 -5
- package/src/cli/last.ts +96 -21
- package/src/cli/lock.ts +34 -17
- package/src/cli/plan-format.ts +4 -4
- package/src/cli/plugin-commands.ts +20 -5
- package/src/cli/run.ts +55 -12
- package/src/cli/select.ts +23 -13
- package/src/cli/show.ts +9 -4
- package/src/cli/upgrade.ts +134 -14
- package/src/cli/watch.ts +611 -102
- package/src/cli/why.ts +8 -16
- package/src/cli/workspace-config.ts +21 -9
- package/src/config.ts +46 -9
- package/src/exec/executor.ts +51 -5
- package/src/exec/index.ts +10 -0
- package/src/exec/kill-tree.ts +281 -0
- package/src/exec/local-executor.ts +1 -1
- package/src/exec/runner.ts +327 -82
- package/src/exec/sandbox-binds.ts +34 -5
- package/src/exec/sandbox-paths.ts +22 -2
- package/src/exec/sandbox-runtime.ts +627 -102
- package/src/exec/sandbox-violations.ts +36 -23
- package/src/graph/dependency-spec.ts +14 -14
- package/src/graph/index.ts +12 -2
- package/src/graph/priorities.ts +130 -0
- package/src/graph/scheduler.ts +182 -134
- package/src/graph/task-graph.ts +552 -188
- package/src/index.ts +16 -2
- package/src/orchestrator/admission.ts +41 -17
- package/src/orchestrator/colors.ts +8 -3
- package/src/orchestrator/deferred-outputs.ts +22 -7
- package/src/orchestrator/doctor.ts +122 -14
- package/src/orchestrator/events.ts +71 -2
- package/src/orchestrator/excluded-keys.ts +155 -0
- package/src/orchestrator/execute-task.ts +331 -37
- package/src/orchestrator/failure-recap.ts +121 -0
- package/src/orchestrator/fingerprint-watch.ts +89 -0
- package/src/orchestrator/framed-output.ts +81 -14
- package/src/orchestrator/history.ts +6 -7
- package/src/orchestrator/hit-restore.ts +82 -18
- package/src/orchestrator/index.ts +7 -1
- package/src/orchestrator/keyed-projects.ts +89 -0
- package/src/orchestrator/local-shortcircuit.ts +83 -23
- package/src/orchestrator/lockfile-claim.ts +152 -32
- package/src/orchestrator/logger.ts +116 -20
- package/src/orchestrator/metrics.ts +156 -60
- package/src/orchestrator/miss-save.ts +106 -43
- package/src/orchestrator/options.ts +30 -4
- package/src/orchestrator/persistent.ts +41 -21
- package/src/orchestrator/placement.ts +55 -25
- package/src/orchestrator/plan.ts +24 -11
- package/src/orchestrator/plugin-host.ts +17 -23
- package/src/orchestrator/plugin.ts +31 -8
- package/src/orchestrator/prepare.ts +99 -13
- package/src/orchestrator/projects.ts +19 -8
- package/src/orchestrator/remote-prefetch.ts +21 -1
- package/src/orchestrator/run-artifacts.ts +9 -0
- package/src/orchestrator/run-context.ts +19 -4
- package/src/orchestrator/run-lock.ts +349 -0
- package/src/orchestrator/run-records.ts +6 -23
- package/src/orchestrator/run-report.ts +4 -2
- package/src/orchestrator/run.ts +264 -57
- package/src/orchestrator/sandbox-request.ts +388 -62
- package/src/orchestrator/save-lane.ts +4 -7
- package/src/orchestrator/shell-verdict.ts +117 -0
- package/src/orchestrator/signals.ts +86 -30
- package/src/orchestrator/stable-keys.ts +202 -10
- package/src/orchestrator/status-line.ts +33 -1
- package/src/orchestrator/summary.ts +49 -2
- package/src/orchestrator/task-hash.ts +87 -7
- package/src/orchestrator/task-log-buffer.ts +7 -7
- package/src/orchestrator/telemetry-host.ts +16 -7
- package/src/orchestrator/telemetry.ts +69 -28
- package/src/orchestrator/upstream.ts +91 -45
- package/src/util/bun-version.ts +43 -0
- package/src/util/errors.ts +86 -0
- package/src/util/hash.ts +10 -1
- package/src/util/index.ts +32 -3
- package/src/util/paths.ts +147 -5
- package/src/util/procfs.ts +20 -0
- package/src/util/settle.ts +4 -2
- package/src/util/size.ts +33 -0
- package/src/util/task-id.ts +20 -0
- package/src/util/timing.ts +11 -1
- package/src/util/which.ts +48 -0
- package/src/workspace/affected.ts +272 -64
- package/src/workspace/config-cache.ts +262 -32
- package/src/workspace/config-eval.ts +49 -12
- package/src/workspace/config-imports.ts +126 -5
- package/src/workspace/config-schema.ts +237 -41
- package/src/workspace/filter.ts +33 -18
- package/src/workspace/fingerprint.ts +29 -11
- package/src/workspace/index.ts +7 -0
- package/src/workspace/json-data.ts +90 -0
- package/src/workspace/load-reads.ts +32 -0
- package/src/workspace/lockfile.ts +33 -11
- package/src/workspace/migrate-scripts.ts +60 -7
- package/src/workspace/migration.ts +40 -1
- package/src/workspace/package-graph.ts +154 -16
- package/src/workspace/project-loader.ts +227 -77
- package/src/workspace/workspace.ts +173 -23
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// `--exclude-dependencies` changes what RUNS, never what a key is (nx#35234).
|
|
2
|
+
// The graph module takes the dropped edges out of the schedule
|
|
3
|
+
// (`excludeDependencies`); this derives the key each dropped dependency
|
|
4
|
+
// would have, without running it, and hands it to the dependant as
|
|
5
|
+
// `excludedUpstream`, which every key site folds next to the live outcomes
|
|
6
|
+
// (`keyUpstream`). A dependant's key is then the one a full run derives.
|
|
7
|
+
//
|
|
8
|
+
// Folding the key makes the dependant's own entry say "built against that
|
|
9
|
+
// dependency at its current inputs", and nothing here proves the
|
|
10
|
+
// dependency's outputs on disk are the ones those inputs produce: it did
|
|
11
|
+
// not run. So a task whose key folds a dropped dependency, and everything
|
|
12
|
+
// built on it, runs and may hit but does not SAVE (`excludedTaint` seeds
|
|
13
|
+
// the run's taint): a save would file stale bytes under the key the next
|
|
14
|
+
// full run looks up.
|
|
15
|
+
|
|
16
|
+
import type { CacheLayer, GitFilesCache } from '../cache/index.js'
|
|
17
|
+
import { isGroupTask, type TaskNode, type TaskOutcome } from '../graph/index.js'
|
|
18
|
+
import { computeGroupHash, computeTaskHash, type HashCache } from './task-hash.js'
|
|
19
|
+
import { filterUpstreamHashes } from './upstream.js'
|
|
20
|
+
|
|
21
|
+
export interface KeyExcludedArgs {
|
|
22
|
+
/** The scheduled graph, after `excludeDependencies`. */
|
|
23
|
+
nodes: Map<string, TaskNode>
|
|
24
|
+
keyOnly: ReadonlyMap<string, TaskNode>
|
|
25
|
+
dropped: ReadonlyMap<string, readonly string[]>
|
|
26
|
+
cache: CacheLayer
|
|
27
|
+
workspaceRoot: string
|
|
28
|
+
workspaceFingerprint: string
|
|
29
|
+
forwardArgs?: readonly string[] | undefined
|
|
30
|
+
nestedDirsByProject: ReadonlyMap<string, string[]>
|
|
31
|
+
gitFilesCache: GitFilesCache
|
|
32
|
+
hashCache: HashCache
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Set `excludedUpstream` on every scheduled task that lost an edge. Keys
|
|
37
|
+
* are derived over the whole graph as it stands at the start of the run,
|
|
38
|
+
* the way `deriveStableKeys` derives them: a dropped task's own
|
|
39
|
+
* dependencies, scheduled or not, fold theirs.
|
|
40
|
+
*/
|
|
41
|
+
export async function keyExcludedDependencies(args: KeyExcludedArgs): Promise<void> {
|
|
42
|
+
const nodeOf = (id: string): TaskNode => args.nodes.get(id) ?? args.keyOnly.get(id)!
|
|
43
|
+
const fullDeps = (node: TaskNode): readonly string[] => [
|
|
44
|
+
...node.deps,
|
|
45
|
+
...(args.dropped.get(node.id) ?? []),
|
|
46
|
+
]
|
|
47
|
+
// Each key's promise is made after its dependencies' promises, in a
|
|
48
|
+
// post-order walk on an explicit stack, so nothing recurses once per edge:
|
|
49
|
+
// a dropped chain is as deep as the graph, and the builder takes 50,000
|
|
50
|
+
// (item 737). The keys still resolve concurrently.
|
|
51
|
+
const keys = new Map<string, Promise<string | undefined>>()
|
|
52
|
+
const outcomes = (ids: readonly string[]): Promise<TaskOutcome[]> =>
|
|
53
|
+
Promise.all(ids.map(async (id) => synthetic(nodeOf(id), await keys.get(id)!)))
|
|
54
|
+
const derive = async (node: TaskNode): Promise<string | undefined> => {
|
|
55
|
+
const upstream = await outcomes(fullDeps(node))
|
|
56
|
+
if (isGroupTask(node)) return computeGroupHash(upstream)
|
|
57
|
+
// A persistent task has no key on the live path, so none here either.
|
|
58
|
+
if (node.config.exec?.persistent !== undefined) return undefined
|
|
59
|
+
return computeTaskHash({
|
|
60
|
+
node,
|
|
61
|
+
upstream,
|
|
62
|
+
workspaceRoot: args.workspaceRoot,
|
|
63
|
+
workspaceFingerprint: args.workspaceFingerprint,
|
|
64
|
+
cache: args.cache,
|
|
65
|
+
forwardArgs: args.forwardArgs,
|
|
66
|
+
nestedProjectDirs: args.nestedDirsByProject.get(node.projectName) ?? [],
|
|
67
|
+
gitFilesCache: args.gitFilesCache,
|
|
68
|
+
hashCache: args.hashCache,
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
const stack: Array<[id: string, depsMade: boolean]> = []
|
|
72
|
+
for (const lost of args.dropped.values()) for (const id of lost) stack.push([id, false])
|
|
73
|
+
while (stack.length > 0) {
|
|
74
|
+
const [id, depsMade] = stack.pop()!
|
|
75
|
+
if (keys.has(id)) continue
|
|
76
|
+
const node = nodeOf(id)
|
|
77
|
+
if (depsMade) {
|
|
78
|
+
keys.set(id, derive(node))
|
|
79
|
+
continue
|
|
80
|
+
}
|
|
81
|
+
stack.push([id, true])
|
|
82
|
+
for (const dep of fullDeps(node)) if (!keys.has(dep)) stack.push([dep, false])
|
|
83
|
+
}
|
|
84
|
+
for (const [id, lost] of args.dropped) {
|
|
85
|
+
args.nodes.get(id)!.excludedUpstream = await outcomes(lost)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The taint `--exclude-dependencies` seeds: every scheduled task whose key
|
|
91
|
+
* folds a dropped dependency's key, and how many cacheable tasks the taint
|
|
92
|
+
* reaches from there (the run's one line says so; a persistent task or one
|
|
93
|
+
* with no `cache` saves nothing anyway, so it is not counted, though the
|
|
94
|
+
* taint still passes through it).
|
|
95
|
+
*/
|
|
96
|
+
export function excludedTaint(nodes: ReadonlyMap<string, TaskNode>): {
|
|
97
|
+
seeds: ReadonlySet<string>
|
|
98
|
+
unsaved: number
|
|
99
|
+
} {
|
|
100
|
+
const seeds = new Set<string>()
|
|
101
|
+
for (const node of nodes.values()) if (foldsExcludedKey(node)) seeds.add(node.id)
|
|
102
|
+
if (seeds.size === 0) return { seeds, unsaved: 0 }
|
|
103
|
+
// One walk over the dependant edges; re-scanning every node until nothing
|
|
104
|
+
// grew was a pass per hop, quadratic on a chain seeded at its bottom.
|
|
105
|
+
const dependants = new Map<string, string[]>()
|
|
106
|
+
for (const node of nodes.values()) {
|
|
107
|
+
for (const dep of node.deps) {
|
|
108
|
+
const list = dependants.get(dep)
|
|
109
|
+
if (list === undefined) dependants.set(dep, [node.id])
|
|
110
|
+
else list.push(node.id)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const reached = new Set(seeds)
|
|
114
|
+
const stack = [...seeds]
|
|
115
|
+
while (stack.length > 0) {
|
|
116
|
+
for (const up of dependants.get(stack.pop()!) ?? []) {
|
|
117
|
+
if (reached.has(up)) continue
|
|
118
|
+
reached.add(up)
|
|
119
|
+
stack.push(up)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
let unsaved = 0
|
|
123
|
+
for (const id of reached) {
|
|
124
|
+
const cfg = nodes.get(id)!.config
|
|
125
|
+
if (cfg.cache !== undefined && cfg.exec?.persistent === undefined) unsaved++
|
|
126
|
+
}
|
|
127
|
+
return { seeds, unsaved }
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Whether `node`'s key folds a dropped dependency's key: a group folds all
|
|
132
|
+
* of its dependencies, an exec task what its `cache.inputs.tasks` selects.
|
|
133
|
+
* A task with `tasks: []` folds none, so its entry claims nothing about a
|
|
134
|
+
* dependency and it saves as usual.
|
|
135
|
+
*/
|
|
136
|
+
function foldsExcludedKey(node: TaskNode): boolean {
|
|
137
|
+
const excluded = node.excludedUpstream
|
|
138
|
+
if (excluded === undefined) return false
|
|
139
|
+
if (isGroupTask(node)) return true
|
|
140
|
+
return (
|
|
141
|
+
filterUpstreamHashes(excluded, node.config.cache?.inputs?.tasks, node.projectName, node.id)
|
|
142
|
+
.length > 0
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** A dependency as a key reads it: only `node` and `hash` matter. */
|
|
147
|
+
function synthetic(node: TaskNode, hash: string | undefined): TaskOutcome {
|
|
148
|
+
return {
|
|
149
|
+
node,
|
|
150
|
+
status: 'success',
|
|
151
|
+
exitCode: 0,
|
|
152
|
+
durationMs: 0,
|
|
153
|
+
...(hash !== undefined ? { hash } : {}),
|
|
154
|
+
}
|
|
155
|
+
}
|