Skip to content
Latchkey

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 **/target between 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 test

Gotchas

  • 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-keys fallback keeps you from a cold cache.

Related guides

Run this faster and cheaper on Latchkey managed runners. Start free →