Fallout-build / Fallout-build/Fallout

Content-addressed target caching with declared inputs and outputs

Open
#647 1 comment 0 reactions 0 assignees View on GitHub
enhancement target/backlog
Dominant language
C#
Stars
154
Forks
19
Avg merge
1d 22h
Merged PRs (30d)
15

Description

### Problem

Fallout re-runs every target in the execution plan on every invocation. There is no target-level cache. MSBuild's own incremental build is timestamp-based, works only inside one workspace, and does not survive a fresh CI runner. Gradle, Bazel and Nx all cache at this level; NUKE never did.

A full rebuild on every iteration is the main limit on how many edit-build-check cycles fit into a working session. That applies to a person at a keyboard and to an automated caller equally.

### Outcome

A target declares its inputs and outputs. Fallout hashes the inputs, and when the hash matches a stored entry it restores the outputs and skips the target.

### Acceptance criteria

- [ ] Targets can declare input and output sets; a target that declares neither is never cached
- [ ] Input hashing uses file content, never modification time
- [ ] The cache key includes the .NET SDK version from `global.json`, the configuration, the target framework or RID, the Fallout version, and every parameter the target reads
- [ ] A cache hit restores the declared outputs and reports the target as skipped, with the reason
- [ ] `--no-cache` bypasses the cache entirely
- [ ] A verify mode runs a target both cached and uncached and reports any difference in the outputs
- [ ] The cache store sits behind an interface, with a local filesystem implementation first

### Making it work on CI, and on GitHub Actions in particular

This has to survive ephemeral runners, which is where most of the design risk sits.

- Absolute paths, timestamps and machine identity all differ between runs. Hash content, keep paths relative inside the cache key, and require deterministic build settings (`ContinuousIntegrationBuild`, `Deterministic`, `PathMap`) so outputs can move between machines.
- `actions/cache` stores one immutable archive per key and cannot update an existing key. A first pass can restore by key prefix and save under a new key at the end of the job. Fallout's workflows already cache `.fallout/temp`, so a cache directory there needs no new workflow step.
- Entry-level caching needs the runner's cache service API (`ACTIONS_RESULTS_URL` and `ACTIONS_RUNTIME_TOKEN`), which supports per-entry read and write. This is how sccache, Gradle and Turborepo do remote caching on GitHub Actions. It is the argument for a pluggable cache store rather than a hard dependency on `actions/cache`.
- The cache limit is 10 GB per repository, evicted least-recently-used. Per-entry eviction starts to matter once build outputs are stored.
- Pull requests from forks receive a read-only cache token, so an untrusted pull request cannot write cache entries.
- Adjacent and independent of this work: the cache key in `.github/workflows/build.yml` has no `restore-keys`, so editing any `.csproj` discards the whole NuGet package cache.

### Notes

A wrong cache hit is worse than no cache, so caching stays opt-in per target until the key provably covers everything that affects output. Suggested order: local hashing and cache first, pluggable store second, GitHub Actions cache-service backend third.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.