Skip to content
vxvx
GitHubBlueskydev.toRSS
Releases

No partial build is saved, no stale install runs

A build an incremental tool cut short is not saved, and a run refuses an install the lockfile does not name. vx lock names an ignored .env file and a variable the command never gets. Workers plan from their own warm-ups.

vx 0.0.655: No partial build is saved, no stale install runs

This release closes the ways an everyday workspace cached a wrong build under a green run.

A build that leftover state cut short is not saved. A run refuses an install the lockfile does not name.

vx lock says what a key misses, vx init writes a config a plain tsc builds, and workers plan from their own warm-ups.

This release is breaking. Read the breaking changes first.

In this release

tsc -b keeps its state in tsconfig.tsbuildinfo, beside the sources and outside dist/.

After an edit, vx emptied dist/ before the miss. The state still said the rest was built, so tsc emitted one file and the partial dist/ was saved.

Now a miss stamps such files before its command: a file git ignores, or a .tsbuildinfo, that the task declares neither input nor output.

If one changed, the result stands but is not saved, and the line names the fix.

Terminal window
$ vx run build --filter packages/lib
[vx] packages/lib#build: tsconfig.tsbuildinfo changed while the task ran, but the task declares it neither input nor output — the result stands, but is not saved: a tool that reads such a file back (an incremental build's state) builds from it, not from the inputs; declare it in outputs.files ('tsconfig.tsbuildinfo'), so a miss starts without it and a hit restores it

With the file in outputs.files, a miss starts without it and a hit restores it. A state file that claims no built output is left alone: Vite’s and create-vue’s noEmit builds keep caching.

See Caching.

The key hashes the lockfile, never node_modules.

A git pull that brought a lockfile change, run before the install, built against the old packages and saved the output under the new lockfile’s key.

Now a run whose cached tasks key on the lockfile refuses while node_modules is not the install the lockfile names. It names the package and the install that fixes it.

Terminal window
$ vx run build --all
✗ vx run plugin '@vzn/vx-lockfile' failed in key: node_modules was installed from a different bun.lock (packages/ui's is-number is 7.0.0, the lockfile names 5.0.0) — run `bun install`

pnpm(), bun(), npm() and yarn() each read the record their manager leaves of its install. A match is remembered, so a warm run compares nothing.

See Lockfile plugins.

A glob reads only what git lists. inputs: { files: ['**'] } over a Vite or Next app keyed everything but the ignored .env.local the build reads, with no word.

inputs.env only keys a variable; the command gets what exec.env.passThrough gives it. A build keyed on VITE_API_URL alone moved its key and built every bundle without the value.

vx lock now names both.

Terminal window
$ vx lock
✓ vx lock 2 project configs → .vx/projects.lock.json
! vx lock packages/web#build: inputs leave out git-ignored .env.local (matched by **) — a glob reads only what git lists; name it as a literal input ('.env.local') to key on it
! vx lock packages/web#build: inputs.env keys VITE_API_URL, which a bundler writes into its output, but the command never gets it — add it to exec.env.passThrough

The .env note also prints on the miss. A glob over vx’s own vx.lock.json no longer takes it for an ignored file.

See vx lock.

A default NestJS app broke twice on the vx.config.ts that vx init writes.

tsc on moduleResolution: node reads no exports map, so @vzn/vx/config did not resolve. A tsconfig with no include took the config into the build, and dist/main.js became dist/src/main.js.

Now the import resolves there, and vx init and a migration name each tsconfig that would compile the config they write.

Terminal window
$ vx init
vx init 0.0.655
from no turbo.json or nx.json
files .vx/workspace.ts
vx.config.ts
.vx/.gitignore
.vx/.gitattributes
note tsconfig.json names no "include", so tsc compiles vx.config.ts too and its output moves (dist/main.js becomes dist/src/main.js): add "vx.config.ts" to "exclude" there
lock 1 project config → .vx/projects.lock.json
next bunx @vzn/vx-migrate to make package.json scripts tasks, or declare a task in vx.config.ts, then vx lock && vx run <task>

The cache block a migration suggests now keys the whole package, inputs: { files: ['**'] }, and says to narrow it.

See vx init.

A distributed run kept each worker’s warm-up time in a cache entry, which a fresh cache lost.

Now a warm-up is timed like a task and kept in history, which @vzn/vx-history shares. The next run’s plan counts what a worker of that size took to join.

Terminal window
$ VX_DTE_RUN=$(uuidgen) vx run build --all
workers linux.small#1 left 1 done · warm-up 0.3s · packages/app#build
linux.small#2 left 2 done · warm-up 0.2s · packages/ui#build, packages/lib#build
asked 2 at 0.1s for 3 tasks (3 measured), plan 1.5s
ideal 1.3s: each task as long as it took, placed perfectly, holding 4.2 core-s at least
drift +0.1s past ideal
total cpu utilization machine 16% · tasks 0% · worker 6% · held 5.6 core-s

The plan also seeks the earliest end that packs onto fewer workers, books every task at least one core, and keeps an idle worker over one the CI has not started.

See Distributed task execution.

  • A sandboxed task in the root project runs; before, the sandbox hid the workspace from it.
  • A persistent task never outlives vx, and a sandboxed e2e task reaches its dev server.
  • A SIGTERM sent to the whole process group under a relaying parent is one stop, not a kill of every task mid-cleanup.
  • A run is refused when its cache directory sits under a task’s outputs.
  • A secret printed one character at a time, or two secrets that overlap, are masked in every copy of the log.
  • A hit gives a standing output directory its mode back.
  • A distributed task that defines its own PATH runs with it on the worker.
  • vx cache clean no longer fails a run waiting on the same cache directory.
  • Restores skip more work on the first run after a build, and files under text or eol attributes are no longer hashed on every run.
  • @vzn/vx-migrate accepts Nx 22 to 24; sandboxed nx-exec tasks run on Nx 24.
  • @vzn/vx-lockfile keys linked folders each manager left out of its lockfile.
Terminal window
vx upgrade

Run vx lock after upgrading. Every cached task misses once.

  • Cache format → vx-cache-v56: the local cache starts empty once.
  • @vzn/vx-lockfile digests changed: vx lock --check fails on the old lock.
  • TaskExecutor keys → grouped under capabilities and distribution; any other key refuses the run.
  • TaskOutcome fields → grouped under cache, timing, usage, placement, attempts and sandbox.
  • Cache memo methods → cache.memo.<name>; its record* methods are removed.
  • lockfileClaim’s importers → links.
  • PLUGIN_HOOKS gains measures, before schedule.
  • @vzn/vx-dte-server’s token exports → @vzn/vx-devtools.
  • @vzn/vx no longer ships index.ts or config/index.ts at its root.

See also Upgrading.

Turborepo, Nx and other product names are trademarks of their owners. vx is not affiliated with or endorsed by them.