@vzn/vx 0.0.607 → 0.0.609

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.607",
3
+ "version": "0.0.609",
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.607",
25
- "@vzn/vx-linux-arm64": "0.0.607",
26
- "@vzn/vx-darwin-x64": "0.0.607",
27
- "@vzn/vx-darwin-arm64": "0.0.607"
24
+ "@vzn/vx-linux-x64": "0.0.609",
25
+ "@vzn/vx-linux-arm64": "0.0.609",
26
+ "@vzn/vx-darwin-x64": "0.0.609",
27
+ "@vzn/vx-darwin-arm64": "0.0.609"
28
28
  },
29
29
  "dependencies": {
30
30
  "@anthropic-ai/sandbox-runtime": "^0.0.76"
package/src/cli/run.ts CHANGED
@@ -11,7 +11,6 @@ import {
11
11
  run as runOrchestrator,
12
12
  shellQuote,
13
13
  type RunOptions,
14
- type RunResult,
15
14
  } from '../orchestrator/index.js'
16
15
  import type { ContinueMode, TaskOutcome } from '../graph/index.js'
17
16
  import {
@@ -721,6 +720,12 @@ export async function runCmd(args: readonly string[]): Promise<number> {
721
720
  return 1
722
721
  }
723
722
  if (plan.tasks.length === 0) {
723
+ // The run's own line: a dry run that guessed from the scope alone
724
+ // said no affected project declared a task one did (X-141).
725
+ if (plan.noneAffected !== undefined) {
726
+ process.stderr.write(`${plan.noneAffected}\n`)
727
+ return 0
728
+ }
724
729
  if (opts.selectedByDiff === true) {
725
730
  process.stderr.write(`vx run: no affected project declares task(s): ${tasks.join(', ')}.\n`)
726
731
  return 0
@@ -82,6 +82,8 @@ export interface RunPlan {
82
82
  unresolvedTasks?: readonly string[]
83
83
  /** What `vx run` appends to that refusal: the nearest declared names, or how to declare one. */
84
84
  unresolvedHint?: string
85
+ /** Set when `--affected` left nothing to run: the line the run would print, and why. */
86
+ noneAffected?: string
85
87
  /**
86
88
  * Producers `--download` WANTED to defer but could not, mapped to why.
87
89
  * Without this the gate is invisible: a user asks for `--download=none`,
@@ -9,6 +9,7 @@ import type { ProjectConfig, TaskConfig } from '../config.js'
9
9
  import {
10
10
  frozenProjectConfigs,
11
11
  loadProjectConfigs,
12
+ LOCKFILE_NAME,
12
13
  validateProjectConfig,
13
14
  type LoadProjectConfigOptions,
14
15
  type LoadReads,
@@ -243,6 +244,21 @@ export async function loadProjects(args: LoadProjectsArgs): Promise<LoadedProjec
243
244
  // alike. With no root project it stays, and the graph's refusal says so.
244
245
  const root = path.resolve(workspaceRoot)
245
246
  const rootName = args.projectMetas.find((m) => path.resolve(m.dir) === root)?.name
247
+ // A project the lock holds whose config file is gone ran nothing under
248
+ // --frozen, green, though the lock promises what runs is what was
249
+ // locked (X-144); a run never even asks such a project for a task.
250
+ // Refused as a moved config is (D-88). With no lock at all, nothing was
251
+ // locked: a repo of config-less projects needs none.
252
+ if (lock) {
253
+ const bare = args.projectMetas.filter((m) => m.configPath === null)
254
+ const held = bare.length === 0 ? null : await lock().catch(() => null)
255
+ const gone = held === null ? undefined : bare.find((m) => Object.hasOwn(held.projects, m.name))
256
+ if (gone !== undefined && held !== null)
257
+ throw new UserError(
258
+ `${LOCKFILE_NAME} locks "${gone.name}" at ${held.projects[gone.name]!.configPath}, but it has no config now — ` +
259
+ `run \`vx lock\` to refresh, or delete ${LOCKFILE_NAME}`,
260
+ )
261
+ }
246
262
  const projects = new Map<string, ProjectEntry>()
247
263
  while (pending.length > 0) {
248
264
  const round = pending.splice(0, pending.length)
@@ -404,20 +404,7 @@ async function runOnBus(
404
404
  return { ok: false, outcomes: [], refused }
405
405
  }
406
406
  if (prepared.empty === 'none-affected') {
407
- // A changed project declares the task, so `--affected`'s task walk is
408
- // what left nothing (affected-tasks.ts), not the scope.
409
- const inScope = new Set(options.projects ?? [])
410
- const reachedNone =
411
- options.affected !== undefined &&
412
- [...prepared.projects.values()].some(
413
- (p) =>
414
- inScope.has(p.name) && options.tasks.some((t) => declaredTask(p.config, t) !== undefined),
415
- )
416
- log.status(
417
- reachedNone
418
- ? `Nothing affected: the change reaches no ${options.tasks.join(', ')} task.`
419
- : `No affected project declares task(s): ${options.tasks.join(', ')}.`,
420
- )
407
+ log.status(noneAffected(prepared, options))
421
408
  await teardown()
422
409
  prepared.cache.close()
423
410
  return { ok: true, outcomes: [] }
@@ -1516,6 +1503,22 @@ async function applyCacheRetention(prepared: PreparedRun): Promise<void> {
1516
1503
  }
1517
1504
  }
1518
1505
 
1506
+ /** Why an `--affected` run has nothing to run, said alike by the run and its `--dry` plan. */
1507
+ function noneAffected(prepared: PreparedRun, options: RunOptions): string {
1508
+ // A changed project declares the task, so `--affected`'s task walk is
1509
+ // what left nothing (affected-tasks.ts), not the scope.
1510
+ const inScope = new Set(options.projects ?? [])
1511
+ const reachedNone =
1512
+ options.affected !== undefined &&
1513
+ [...prepared.projects.values()].some(
1514
+ (p) =>
1515
+ inScope.has(p.name) && options.tasks.some((t) => declaredTask(p.config, t) !== undefined),
1516
+ )
1517
+ return reachedNone
1518
+ ? `Nothing affected: the change reaches no ${options.tasks.join(', ')} task.`
1519
+ : `No affected project declares task(s): ${options.tasks.join(', ')}.`
1520
+ }
1521
+
1519
1522
  /**
1520
1523
  * Planning mode. Same setup as `run()` — workspace discovery, config
1521
1524
  * load, package graph, task graph — but stops short of execution.
@@ -1548,6 +1551,8 @@ export async function planRun(options: RunOptions): Promise<RunPlan> {
1548
1551
  unresolvedHint: elsewhereHint(prepared.declaredElsewhere),
1549
1552
  }
1550
1553
  }
1554
+ if (prepared.empty === 'none-affected')
1555
+ return { tasks: [], noneAffected: noneAffected(prepared, options) }
1551
1556
  if (prepared.empty !== null) return { tasks: [] }
1552
1557
  // Its own mark: a dry run's plan (every task's hash, the cache lookups,
1553
1558
  // the history p50s) was booked under `close`, the next mark, and read