Overview

sbt-dynver-ci is a thin sbt-dynver policy for early-effect builds. It keeps jar names stable across CI commits so sbt 2 action-cache digests (CompileInputs2) hit instead of miss.

The problem

Stock dynver encodes the current commit into the version (for example 0.0.0+1-<sha>). Those version strings land in jar paths, which are hashed into compile input digests. Every push produces a new digest, so restoring ~/.cache/sbt does not help: every compile looks like a miss.

The policy

On a clean checkout of a version tag such as v0.2.0, the version is the release itself: 0.2.0. Anywhere else after that tag, including dirty trees and commits that are not the tag tip, the version is the last release with a fixed suffix: 0.2.0-ci. If the repository has no tags yet, the version is 0.0.0-ci.

Between tags, jar names stay fixed, so action-cache digests can match across CI pushes. Cutting the next tag starts a new cache generation on purpose.

{
  def format(isCleanAfterTag: Boolean, version: String, suffix: String): String =
    if isCleanAfterTag then version else version + suffix

  (
    format(true, "0.2.0", "-ci"),
    format(false, "0.2.0", "-ci"),
    format(false, "0.0.0", "-ci"),
  )
}
(0.2.0,0.2.0-ci,0.0.0-ci)