A cached build behind an uncached code generator could replay a stale result when the generator's env changed. Why vx moved inputs out of the cache block and onto the task, so every task is keyed whether it caches or not, and how a gitignored file can now be an input.
Caching
Caching
A cache key is only as good as what it can see. Until 0.0.640, vx put a
task’s inputs inside its cache block. A task that did not cache had
nowhere to say what it read, so vx keyed it on its whole project and its
env names, never their values.
That left a hole one task downstream. Take a code generator you do not
cache, because it is fast, and a build you do:
app/vx.config.ts
import { defineProject } from'@vzn/vx/config'
exportdefaultdefineProject({
tasks: {
codegen: {
exec: {
command: 'mkdir -p gen && echo "export const api = \"$API_URL\"" > gen/api.ts',
The build folds its dependency’s key. When that key held only env names,
changing API_URL changed nothing the build could see, and the next run
replayed a dist/ built against the old URL. A green run, wrong bytes.
inputs now sits on the task, beside exec, and keys the task whether
it caches or not. Turbo and Nx hash every task too. cache holds only
what caching adds: outputs and inputStability.
Change API_URL and the build misses, and vx why names the reason:
A task with a command and no inputs has no key at all. vx does not
guess one. Every task that depends on it misses and saves nothing, so a
missing declaration costs a re-run, never a stale hit:
The generator above reads .env.local, which git ignores. vx keys
tracked files from git’s own index, so an ignored file used to be
invisible, and a task could not name it at all.
Now a path named outright (.env.local), or a glob whose fixed part git
ignores (gen/**), is read from disk and hashed. A broad glob like
src/** still skips what git ignores, and node_modules is never
walked. Edit .env.local and the generator’s key moves, and the build’s
with it.
--affected still follows only what git sees, as Nx does: an ignored
input never selects a task on its own, but its key still moves when the
task runs.
Both changes were breaking. tasks.<name>.cache.inputs became
tasks.<name>.inputs, a cache block needs an exec, and the lock
went to version 4. vx is pre-1.0, so there is no alias: move the field
and run vx lock.
A remote cache hit used to wait for its whole download, even when the task itself would have finished first. How vx now weighs each download against the task it replaces, gives up a slow one, and runs the task here instead.
A fast run is mostly a run that skips work. Three jobs vx used to do on every run, and why each one went: housekeeping at exit, a walk of the shared store, and a "saved" figure that overstated what the cache saved.
Downloads from a remote cache used to run only as many at once as the run had workers, and uploads four at once. A transfer is a wait on a round trip, not work for a core. How vx now sizes each lane on its own, and why a missing key costs no request.
Turborepo, Nx and other product names are trademarks of their owners. vx is not affiliated with or endorsed by them.