Skip to main content

fyrer Build Caching: Skip Tasks with Unchanged Inputs

fyrer can skip tasks whose inputs haven't changed since the last successful run. Cached tasks report ⚡ Cached and have their declared outputs restored instantly — no recompilation, no rebundling.

Enabling the cache

To enable caching for a task, set cache: true and declare its inputs. You also need to enable a cache provider at the top level of fyrer.yml:

version: 1
cache:
provider: local

local is the only available provider today. It stores cache artifacts on disk under .fyrer/cache/ in your monorepo root.

A minimal cached task looks like this:

tasks:
build:
cmd: bun run build
inputs:
- src/**
- package.json
outputs:
- dist/**
cache: true

How the cache key is computed

The cache key is a blake3 hash of the following inputs, all resolved at run time:

  1. The fully-qualified task ID (package:task)
  2. The task's cmd string
  3. The task's working directory
  4. The fully resolved environment (all env layers merged in precedence order)
  5. The contents of every file matched by the task's inputs globs

Outputs are archived as tar.zst under .fyrer/cache/<key>/ relative to the workspace root and restored on a hit. An additional output digest (blake3 of outputs) decides whether hydration is needed — unchanged outputs are left in place.

i

The cache key does not include transitive dependency keys. If app:build depends on shared:build and shared changes, app:build will not be invalidated unless its own inputs also cover the change. Add the shared outputs or a version file to inputs if you need cross-package invalidation, or rely on watch restarts during development.

Cache storage

Outputs are archived under .fyrer/cache/ in the monorepo root. On a cache hit:

  1. fyrer restores the archived outputs to their declared locations.
  2. The task is skipped and reported as ⚡ Cached.
  3. No process is spawned for the task.

You should add .fyrer/ to your .gitignore to avoid committing cache artifacts:

# .gitignore
.fyrer/

inputs, outputs, and ignore

Three glob-based fields control what the cache considers and what it preserves:

FieldPurpose
inputsFiles the task reads. A change in any matched file causes a cache miss.
outputsFiles the task produces. Archived on a cache write; restored on a cache hit.
ignoreFiles excluded from both inputs and outputs.

All glob paths are relative to the package root. Here is a full example:

build:
cmd: bun build src/index.ts --outdir dist
inputs:
- src/**
- package.json
outputs:
- dist/**
ignore:
- node_modules/**
cache: true

Restrictions

A few combinations are not allowed and will be rejected at startup:

  • cache: true + persistent: true — Dev servers run indefinitely and never produce a stable final output, so they cannot be cached.
  • cache: true + watch: true — Watched tasks restart on input changes and have no single deterministic "done" state to cache.
  • Tasks without inputs — If no inputs are declared, the cache key is computed from the task config alone (ID, command, environment). Any file change in the package will not invalidate the cache. Always declare inputs for tasks that read source files.