How to Keep sbt Incremental Compilation Working in CI
sbt uses Zinc to recompile only the Scala sources whose dependencies changed, but a clean CI runner throws that state away unless you cache the target and Coursier directories.
Zinc stores analysis in target/ that lets sbt recompile just what changed. In CI, restore target/, ~/.sbt, and ~/.cache/coursier keyed on your build files so incremental compilation resumes instead of a cold full compile.
Steps
- Cache
~/.sbt,~/.cache/coursier, and**/targetbetween runs. - Key the cache on the build definition and lock files.
- Run
sbt compile test; Zinc recompiles only affected sources.
Workflow
.github/workflows/ci.yml
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with: { distribution: temurin, java-version: 21 }
- uses: actions/cache@v4
with:
path: |
~/.sbt
~/.cache/coursier
**/target
key: sbt-${{ hashFiles('**/build.sbt', 'project/**') }}
restore-keys: sbt-
- run: sbt compile testGotchas
- Caching
target/can carry over stale class files if the Scala version or compiler flags change; include those in the cache key. - A corrupt Zinc analysis occasionally forces a full recompile; a
restore-keysfallback keeps you from a cold cache.
Related guides
How to Enable Incremental Gradle Builds in CISpeed up Gradle in CI with the build cache and up-to-date checks so only tasks with changed inputs re-run, re…
How to Use tsc --incremental in CIEnable TypeScript incremental type checking in CI with tsc --incremental and a cached .tsbuildinfo so only ch…
How to Cache Build Outputs for Incremental CIPersist build outputs (compiler caches, build directories) across CI runs with actions/cache so even affected…