@vzn/vx 0.0.580 → 0.0.582
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 +5 -5
- package/src/cli/lock.ts +5 -5
- package/src/graph/task-graph.ts +39 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.582",
|
|
4
4
|
"description": "The fastest task runner and build cache for JavaScript monorepos.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
"node": ">=18"
|
|
22
22
|
},
|
|
23
23
|
"optionalDependencies": {
|
|
24
|
-
"@vzn/vx-linux-x64": "0.0.
|
|
25
|
-
"@vzn/vx-linux-arm64": "0.0.
|
|
26
|
-
"@vzn/vx-darwin-x64": "0.0.
|
|
27
|
-
"@vzn/vx-darwin-arm64": "0.0.
|
|
24
|
+
"@vzn/vx-linux-x64": "0.0.582",
|
|
25
|
+
"@vzn/vx-linux-arm64": "0.0.582",
|
|
26
|
+
"@vzn/vx-darwin-x64": "0.0.582",
|
|
27
|
+
"@vzn/vx-darwin-arm64": "0.0.582"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@anthropic-ai/sandbox-runtime": "^0.0.76"
|
package/src/cli/lock.ts
CHANGED
|
@@ -90,8 +90,8 @@ async function writeLock(root: string, metas: ConfiguredMeta[], bare: number): P
|
|
|
90
90
|
if (leaks.length > 0) {
|
|
91
91
|
process.stderr.write(
|
|
92
92
|
`vx lock: ${LOCKFILE_NAME} is committed, and these configs evaluated to a secret value:\n` +
|
|
93
|
-
leaks.map((l) => ` ${l}\n`).join('') +
|
|
94
|
-
`let the shell expand it (
|
|
93
|
+
leaks.map((l) => ` ${l.at}\n`).join('') +
|
|
94
|
+
`let the shell expand it ($${leaks[0]!.name} in the command, the name in exec.env.passThrough) instead of reading process.env in the config\n`,
|
|
95
95
|
)
|
|
96
96
|
return 1
|
|
97
97
|
}
|
|
@@ -111,7 +111,7 @@ async function writeLock(root: string, metas: ConfiguredMeta[], bare: number): P
|
|
|
111
111
|
* such a value would be published, and masking it would freeze a `***`
|
|
112
112
|
* that `--frozen` runs (L-42).
|
|
113
113
|
*/
|
|
114
|
-
function secretsIn(projects: Record<string, LockfileEntry>): string
|
|
114
|
+
function secretsIn(projects: Record<string, LockfileEntry>): Array<{ at: string; name: string }> {
|
|
115
115
|
const named = new Set<string>()
|
|
116
116
|
for (const entry of Object.values(projects)) {
|
|
117
117
|
for (const task of Object.values(entry.config.tasks ?? {}))
|
|
@@ -123,11 +123,11 @@ function secretsIn(projects: Record<string, LockfileEntry>): string[] {
|
|
|
123
123
|
secrets.push([value, name])
|
|
124
124
|
}
|
|
125
125
|
if (secrets.length === 0) return []
|
|
126
|
-
const found: string
|
|
126
|
+
const found: Array<{ at: string; name: string }> = []
|
|
127
127
|
const walk = (project: string, at: string, v: unknown): void => {
|
|
128
128
|
if (typeof v === 'string') {
|
|
129
129
|
for (const [value, name] of secrets)
|
|
130
|
-
if (v.includes(value)) found.push(`${project}: ${at} holds $${name}
|
|
130
|
+
if (v.includes(value)) found.push({ at: `${project}: ${at} holds $${name}`, name })
|
|
131
131
|
} else if (Array.isArray(v)) {
|
|
132
132
|
v.forEach((x, i) => walk(project, `${at}[${i}]`, x))
|
|
133
133
|
} else if (v !== null && typeof v === 'object') {
|
package/src/graph/task-graph.ts
CHANGED
|
@@ -553,6 +553,7 @@ export function checkGraph(
|
|
|
553
553
|
delete node.addsToOutputsOf
|
|
554
554
|
delete node.outputsAddedToBy
|
|
555
555
|
}
|
|
556
|
+
for (const node of nodes.values()) refuseSelfClean(node)
|
|
556
557
|
detectCycle(nodes)
|
|
557
558
|
detectOutputCollisions(nodes, workspaceRoot, rules?.exclusiveOutputs !== false)
|
|
558
559
|
if (rules?.upfrontKeys !== false) detectInputOverlaps(nodes, workspaceRoot)
|
|
@@ -1123,6 +1124,44 @@ export function outputTakenBack(output: string, negatives: readonly string[]): b
|
|
|
1123
1124
|
)
|
|
1124
1125
|
}
|
|
1125
1126
|
|
|
1127
|
+
/**
|
|
1128
|
+
* A task whose input entry its own outputs take back whole cleans its own
|
|
1129
|
+
* sources: vx removes a task's outputs before it runs, so a formatter
|
|
1130
|
+
* declaring `src/**` as both deleted every committed file under `src`,
|
|
1131
|
+
* and its key read nothing (hunt 8). Always refused, whatever the rules.
|
|
1132
|
+
*/
|
|
1133
|
+
function refuseSelfClean(node: TaskNode): void {
|
|
1134
|
+
const cache = node.config.cache
|
|
1135
|
+
if (cache === undefined) return
|
|
1136
|
+
for (const field of ['files', 'workspaceFiles'] as const) {
|
|
1137
|
+
const inputs = splitNegations(cache.inputs[field] ?? [])
|
|
1138
|
+
const out = splitNegations(cache.outputs[field] ?? [])
|
|
1139
|
+
const outputs = out.positive
|
|
1140
|
+
if (outputs.length === 0) continue
|
|
1141
|
+
const roots = outputs.map((g) => rootless(staticPrefix(g)))
|
|
1142
|
+
for (const gi of inputs.positive) {
|
|
1143
|
+
// Only an output rooted at or above the input's prefix can cover it;
|
|
1144
|
+
// the prefix test keeps the glob matchers off the common disjoint pair.
|
|
1145
|
+
const pi = rootless(staticPrefix(gi))
|
|
1146
|
+
const near = outputs.filter((_, k) => {
|
|
1147
|
+
const r = roots[k]!
|
|
1148
|
+
return r === '' || pi === r || pi.startsWith(`${r}/`)
|
|
1149
|
+
})
|
|
1150
|
+
// Taken back by the task's own `!` inputs, or by an output `!` (no
|
|
1151
|
+
// output, so never cleaned: A-44), the entry reads what it says.
|
|
1152
|
+
if (near.length === 0 || outputTakenBack(gi, inputs.negative)) continue
|
|
1153
|
+
if (outputTakenBack(gi, out.negative)) continue
|
|
1154
|
+
if (!outputTakenBack(gi, near)) continue
|
|
1155
|
+
throw new UserError(
|
|
1156
|
+
`${node.id}: every file ${JSON.stringify(gi)} in cache.inputs.${field} selects is also ` +
|
|
1157
|
+
`its own output — vx removes a task's outputs before it runs, so the task would ` +
|
|
1158
|
+
`delete its own sources. A task that rewrites files in place (a formatter) declares ` +
|
|
1159
|
+
`no outputs.`,
|
|
1160
|
+
)
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1126
1165
|
/** One task's inputs (`reads`) or outputs in one namespace, for `detectInputOverlaps`. */
|
|
1127
1166
|
interface Side {
|
|
1128
1167
|
node: TaskNode
|