Fallout-build / Fallout-build/Fallout
Content-addressed target caching with declared inputs and outputs
- 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
Assessment
This issue has not been assessed yet.