@vzn/vx 0.0.642 → 0.0.644
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/schemas/info.json +0 -11
- package/skills/vx/SKILL.md +1 -1
- package/src/cache/archive.ts +153 -2
- package/src/cache/cache.ts +417 -183
- package/src/cache/file-hashes.ts +2 -14
- package/src/cache/fold-memo.ts +7 -3
- package/src/cache/index.ts +10 -2
- package/src/cache/key-fold.ts +4 -1
- package/src/cache/layer.ts +11 -4
- package/src/cache/layered-cache.ts +82 -8
- package/src/cache/plugin-cache.ts +51 -43
- package/src/cache/remote-lane.ts +18 -13
- package/src/cache/run-store.ts +4 -1
- package/src/cache/scoped-cache.ts +74 -239
- package/src/cache/tar-stream.ts +12 -7
- package/src/cli/format.ts +1 -2
- package/src/cli/help.ts +8 -4
- package/src/cli/info.ts +2 -19
- package/src/cli/plugin-commands.ts +11 -6
- package/src/cli/run.ts +7 -12
- package/src/cli/select.ts +7 -9
- package/src/cli/upgrade.ts +38 -8
- package/src/cli/workspace-config.ts +8 -2
- package/src/config.ts +22 -26
- package/src/exec/sandbox-paths.ts +2 -2
- package/src/graph/scheduler.ts +3 -1
- package/src/graph/task-graph.ts +15 -11
- package/src/index.ts +5 -1
- package/src/orchestrator/affected-tasks.ts +23 -2
- package/src/orchestrator/doctor.ts +0 -17
- package/src/orchestrator/execute-task.ts +1 -1
- package/src/orchestrator/index.ts +4 -0
- package/src/orchestrator/options.ts +15 -1
- package/src/orchestrator/plugin-host.ts +11 -4
- package/src/orchestrator/plugin.ts +23 -2
- package/src/orchestrator/prepare.ts +81 -18
- package/src/orchestrator/projects.ts +26 -5
- package/src/orchestrator/remote-prefetch.ts +11 -4
- package/src/orchestrator/run-context.ts +85 -0
- package/src/orchestrator/run-settings.ts +3 -0
- package/src/orchestrator/run.ts +9 -26
- package/src/orchestrator/task-hash.ts +6 -0
- package/src/report/logger.ts +0 -4
- package/src/report/summary.ts +3 -11
- package/src/settings.ts +54 -25
- package/src/util/index.ts +1 -0
- package/src/util/paths.ts +42 -33
- package/src/util/size.ts +4 -5
- package/src/workspace/affected.ts +120 -63
- package/src/workspace/config-schema.ts +44 -52
- package/src/workspace/index.ts +3 -0
|
@@ -19,8 +19,6 @@ import {
|
|
|
19
19
|
CORE_VERBS,
|
|
20
20
|
nearest,
|
|
21
21
|
normalizeGlob,
|
|
22
|
-
parseDuration,
|
|
23
|
-
parseSize,
|
|
24
22
|
UserError,
|
|
25
23
|
BUN_GLOB_WILDCARDS,
|
|
26
24
|
EXTGLOB,
|
|
@@ -44,14 +42,39 @@ import { nonJsonMessage, nonJsonPaths } from './json-data.js'
|
|
|
44
42
|
// declared no plugins and ran the workspace bare, `cacheDirectory` left the
|
|
45
43
|
// cache where it was — a config that loads and quietly does nothing it says.
|
|
46
44
|
const CACHE_SCOPE_RULE =
|
|
47
|
-
"must be a scope name of letters, digits and . _ - / @ (at most 128), like '
|
|
45
|
+
"must be a scope name of letters, digits and . _ - / @ : (at most 128), like 'vznjs/vx:feat-x'"
|
|
48
46
|
|
|
49
|
-
/**
|
|
47
|
+
/** A cache scope's rule, for `VX_CACHE_SCOPE` and a plugin's `setBranch`. */
|
|
50
48
|
export function isCacheScope(v: unknown): v is string {
|
|
51
|
-
return typeof v === 'string' && /^[\w
|
|
49
|
+
return typeof v === 'string' && /^[\w.@/:-]{1,128}$/.test(v)
|
|
52
50
|
}
|
|
53
51
|
|
|
54
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* A branch's cache scope, `<owner>/<repo>:<branch>`: what CI writes for a
|
|
54
|
+
* branch and a laptop on it reads (owner, 2026-10-17). The repository's
|
|
55
|
+
* identity, not a remote's local name, so a clone whose `origin` is a fork
|
|
56
|
+
* names the fork. A name may hold characters a scope may not; each
|
|
57
|
+
* becomes `_`.
|
|
58
|
+
*/
|
|
59
|
+
const BASE_BRANCH_RULE = "must be a branch name like 'main'"
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* A branch name git takes after `origin/`: a leading '-' would reach git
|
|
63
|
+
* as an option, and whitespace, `..`, `~^:?*[\\` are no ref's.
|
|
64
|
+
*/
|
|
65
|
+
function isBaseBranch(value: unknown): value is string {
|
|
66
|
+
return (
|
|
67
|
+
typeof value === 'string' &&
|
|
68
|
+
/^[^-\s\0~^:?*[\\][^\s\0~^:?*[\\]*$/.test(value) &&
|
|
69
|
+
!value.includes('..')
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function branchCacheScope(repo: string, branch: string): string {
|
|
74
|
+
return `${repo.toLowerCase()}:${branch}`.replace(/[^\w.@/:-]/g, '_').slice(0, 128)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Why a `VX_CACHE_SCOPE` value is refused. */
|
|
55
78
|
export function cacheScopeError(source = 'VX_CACHE_SCOPE'): string {
|
|
56
79
|
return `${source} ${CACHE_SCOPE_RULE}`
|
|
57
80
|
}
|
|
@@ -61,6 +84,15 @@ export function cacheScopeEnvError(): UserError {
|
|
|
61
84
|
return new UserError(cacheScopeError())
|
|
62
85
|
}
|
|
63
86
|
|
|
87
|
+
/** `baseBranch`: `VX_BASE_BRANCH`, else the workspace's; undefined lets git name it. */
|
|
88
|
+
export function configuredBaseBranch(workspace: WorkspaceConfig | null): string | undefined {
|
|
89
|
+
const env = settingEnv('baseBranch')
|
|
90
|
+
if (env !== undefined && !isBaseBranch(env)) {
|
|
91
|
+
throw new UserError(`VX_BASE_BRANCH ${BASE_BRANCH_RULE}`, 'VX_E_USAGE')
|
|
92
|
+
}
|
|
93
|
+
return (env as string | undefined) ?? workspace?.baseBranch
|
|
94
|
+
}
|
|
95
|
+
|
|
64
96
|
const STRATEGIES = ['immutable', 'mutable', 'unchecked'] as const
|
|
65
97
|
const SEVERITIES = ['warn', 'error'] as const
|
|
66
98
|
const INPUT_STABILITY_FIELDS = new Set(['strategy', 'severity'])
|
|
@@ -98,7 +130,7 @@ export function inputStabilityEnv(): InputStability {
|
|
|
98
130
|
}
|
|
99
131
|
}
|
|
100
132
|
|
|
101
|
-
const WORKSPACE_FIELDS = new Set<string>([...SETTING_GROUPS, 'plugins'])
|
|
133
|
+
const WORKSPACE_FIELDS = new Set<string>([...SETTING_GROUPS, 'baseBranch', 'plugins'])
|
|
102
134
|
|
|
103
135
|
/**
|
|
104
136
|
* `.vx/workspace`'s settings, as `src/settings.ts` lists them: each group's
|
|
@@ -126,7 +158,7 @@ function validateSettingsAt(value: unknown, at: string, configPath: string): voi
|
|
|
126
158
|
if (at === 'cache')
|
|
127
159
|
// Turbo's and Nx's workspace `cache: true` is a task's own `cache` in vx (D-98).
|
|
128
160
|
throw new UserError(
|
|
129
|
-
`${where} must be an object like { dir, policy
|
|
161
|
+
`${where} must be an object like { dir, policy }; a task caches through its own \`cache\` in its project's vx.config`,
|
|
130
162
|
)
|
|
131
163
|
throw new UserError(`${where} must be an object (fields: ${[...fields].join(', ')})`)
|
|
132
164
|
}
|
|
@@ -169,15 +201,6 @@ function validateDependencyRules(value: unknown, where: string): void {
|
|
|
169
201
|
/** `value` as the setting at `path` takes it, refused in `where`'s words. */
|
|
170
202
|
function validateSettingValue(value: unknown, path: string, where: string): void {
|
|
171
203
|
const { kind } = setting(path)
|
|
172
|
-
// The bound `vx cache clean` refuses too: each run would empty the cache,
|
|
173
|
-
// the entries it just saved included, so nothing ever hits (item 969).
|
|
174
|
-
if (
|
|
175
|
-
path === 'cache.retention.olderThan' &&
|
|
176
|
-
typeof value === 'string' &&
|
|
177
|
-
parseDuration(value) === 0
|
|
178
|
-
) {
|
|
179
|
-
throw new UserError(`${where} of 0 evicts every entry after every run`)
|
|
180
|
-
}
|
|
181
204
|
// A file's values are typed: a count or a switch is no string (only a
|
|
182
205
|
// share of the cores is), as a variable's or a flag's must be.
|
|
183
206
|
const untyped =
|
|
@@ -197,19 +220,12 @@ function validateSettingsExtra(config: WorkspaceConfig, configPath: string): voi
|
|
|
197
220
|
// unwritable workspace.
|
|
198
221
|
throw new UserError(`${configPath}: \`cache.dir\` holds a NUL, which no path can carry`)
|
|
199
222
|
}
|
|
200
|
-
const base = config.
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
if (base !== undefined && !/^[^-\s\0][^\s\0]*$/.test(base)) {
|
|
204
|
-
throw new UserError(`${configPath}: \`run.affectedBase\` must be a git ref like 'origin/main'`)
|
|
223
|
+
const base = config.baseBranch
|
|
224
|
+
if (base !== undefined && !isBaseBranch(base)) {
|
|
225
|
+
throw new UserError(`${configPath}: \`baseBranch\` ${BASE_BRANCH_RULE}`)
|
|
205
226
|
}
|
|
206
227
|
// `policy`'s grammar is the cache module's (`parseCachePolicy`), which
|
|
207
228
|
// this module may not import: the run parses it before any task (prepare.ts).
|
|
208
|
-
const scope = config.cache?.scope
|
|
209
|
-
if (scope !== undefined && !isCacheScope(scope)) {
|
|
210
|
-
throw new UserError(`${configPath}: \`cache.scope\` ${CACHE_SCOPE_RULE}`)
|
|
211
|
-
}
|
|
212
|
-
if (config.cache?.retention !== undefined) validateRetention(config.cache.retention, configPath)
|
|
213
229
|
}
|
|
214
230
|
|
|
215
231
|
/**
|
|
@@ -222,30 +238,6 @@ function shown(v: unknown): string {
|
|
|
222
238
|
return typeof v === 'bigint' ? `${v}n` : (JSON.stringify(v) ?? String(v))
|
|
223
239
|
}
|
|
224
240
|
|
|
225
|
-
function validateRetention(
|
|
226
|
-
retention: NonNullable<NonNullable<WorkspaceConfig['cache']>['retention']>,
|
|
227
|
-
configPath: string,
|
|
228
|
-
): void {
|
|
229
|
-
const { olderThan, maxSize } = retention
|
|
230
|
-
// An empty object would load and evict nothing, which reads as a policy.
|
|
231
|
-
if (olderThan === undefined && maxSize === undefined) {
|
|
232
|
-
throw new UserError(
|
|
233
|
-
`${configPath}: \`cache.retention\` names neither \`olderThan\` nor \`maxSize\``,
|
|
234
|
-
)
|
|
235
|
-
}
|
|
236
|
-
// As for olderThan (item 969).
|
|
237
|
-
if (maxSize !== undefined && parseSize(maxSize) === 0) {
|
|
238
|
-
throw new UserError(
|
|
239
|
-
`${configPath}: \`cache.retention.maxSize\` of 0 evicts every entry after every run`,
|
|
240
|
-
)
|
|
241
|
-
}
|
|
242
|
-
if (maxSize !== undefined && /^\d+$/.test(maxSize)) {
|
|
243
|
-
throw new UserError(
|
|
244
|
-
`${configPath}: \`cache.retention.maxSize\` '${maxSize}' reads as ${maxSize} bytes — give a unit (e.g. '${maxSize}MB', '${maxSize}GB')`,
|
|
245
|
-
)
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
241
|
/** A `plugins` entry that stands for no plugin: `undefined`, `null` or `false`. */
|
|
250
242
|
export function isSkippedPlugin(p: unknown): p is undefined | null | false {
|
|
251
243
|
return p === undefined || p === null || p === false
|
|
@@ -1103,7 +1095,7 @@ const FOREIGN_FIELDS: ReadonlyMap<ReadonlySet<string>, Readonly<Record<string, s
|
|
|
1103
1095
|
'a cache plugin in `plugins` (`turboCache()` or `nxCache()` from `@vzn/vx-migrate`)',
|
|
1104
1096
|
parallel: '`run.concurrency`',
|
|
1105
1097
|
cacheDirectory: '`cache.dir`',
|
|
1106
|
-
defaultBase: '`
|
|
1098
|
+
defaultBase: '`baseBranch`',
|
|
1107
1099
|
tasksRunnerOptions: 'a cache plugin in `plugins` (`nxCache()` from `@vzn/vx-migrate`)',
|
|
1108
1100
|
globalPassThroughEnv: '`exec.env.passThrough` on the tasks it passes to',
|
|
1109
1101
|
// pnpm-workspace.yaml's, Lerna's and the package.json's own keys
|
package/src/workspace/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ export {
|
|
|
6
6
|
type AffectedChanges,
|
|
7
7
|
affectedProjects,
|
|
8
8
|
defaultAffectedBase,
|
|
9
|
+
branchChanges,
|
|
9
10
|
type FingerprintClaims,
|
|
10
11
|
refIsHead,
|
|
11
12
|
} from './affected.js'
|
|
@@ -35,8 +36,10 @@ export {
|
|
|
35
36
|
WORKSPACE_CONFIG_FILENAMES,
|
|
36
37
|
} from './project-loader.js'
|
|
37
38
|
export {
|
|
39
|
+
branchCacheScope,
|
|
38
40
|
cacheScopeEnvError,
|
|
39
41
|
cacheScopeError,
|
|
42
|
+
configuredBaseBranch,
|
|
40
43
|
inputStabilityEnv,
|
|
41
44
|
isCacheScope,
|
|
42
45
|
taskNameProblem,
|