search term:

enforcing Semantic Versioning with sbt-strict-update

Rob wrote:

I want to tell sbt “this specific version breaks binary compatibility, so don’t resolve it via eviction, fail the build instead.” How do I do this? Complete answers only, I’m done trying to figure it out by following clues.

I wrote a small sbt plugin sbt-strict-update to do this.

Add this to project/plugins.sbt:

addSbtPlugin("com.eed3si9n" % "sbt-strict-update" % "0.1.0")

and then add this to build.sbt:

ThisBuild / libraryDependencySchemes += "org.typelevel" %% "cats-effect" % "early-semver"

That’s it.

ThisBuild / scalaVersion := "2.13.3"
ThisBuild / libraryDependencySchemes += "org.typelevel" %% "cats-effect" % "early-semver"

lazy val root = (project in file("."))
    name := "demo",
    libraryDependencies ++= List(
      "org.http4s" %% "http4s-blaze-server" % "0.21.11",
      "org.typelevel" %% "cats-effect" % "3.0-8096649",

Now if Rob tries to compile this build, he should get:

sbt:demo> compile
[warn] There may be incompatibilities among your library dependencies; run 'evicted' to see detailed eviction warnings.
[error] stack trace is suppressed; run last update for the full output
[error] (update) found version conflict(s) in library dependencies; some are suspected to be binary incompatible:
[error]   * org.typelevel:cats-effect_2.13:3.0-8096649 (early-semver) is selected over {2.2.0, 2.0.0, 2.0.0, 2.2.0}
[error]       +- demo:demo_2.13:0.1.0-SNAPSHOT                      (depends on 3.0-8096649)
[error]       +- org.http4s:http4s-core_2.13:0.21.11                (depends on 2.2.0)
[error]       +- io.chrisdavenport:vault_2.13:2.0.0                 (depends on 2.0.0)
[error]       +- io.chrisdavenport:unique_2.13:2.0.0                (depends on 2.0.0)
[error]       +- co.fs2:fs2-core_2.13:2.4.5                         (depends on 2.2.0)
[error] Total time: 0 s, completed Dec 13, 2020 11:53:31 PM

strict resolution

There’s been multiple attempts at making the dependency resolution more strict. Thus far none of them have worked.

One of my own attempt at calling on the user’s attention about potential incompatibility was eviction warning. However, I can’t think of a more unpopular feature than eviction warning in practice because despite its well intention, there are too many false positives.

We can actually fix this by removing all guessing completely. During my summer of Scala Center collaboration I added ThisBuild / versionScheme. This information could be used to improve eviction warning, but in general we should either not warn anything or fail the build if we know the incompatibility for sure.

sbt-strict-update reuses the eviction warning facility, but it’ll stay quiet until it knows there’s an error for sure. Since versionScheme is likely not used yet, I added libraryDependencySchemes key similar to Scala Center’s sbt-eviction-rules so the app users can specify the libraries’ version schemes.

set ThisBuild / versionScheme

If you’re a library author, please start setting ThisBuild / versionScheme. See Publishing for details.

next steps

In sbt 1.5.0, we should remove eviction warning as already suggested in #5976, and replace it with this.