From ad6806ed2c04889e7d01518cce9ad1f7ef7e8902 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Wed, 10 Jun 2020 15:10:46 +0200 Subject: [PATCH 1/3] Rename compatibilityReconciliations to compatibilityRules --- README.md | 8 ++++---- .../scala/sbtcompatibility/SbtCompatibilityKeys.scala | 4 +++- .../scala/sbtcompatibility/SbtCompatibilitySettings.scala | 4 ++-- src/sbt-test/sbt-compatibility/in-this-build/build.sbt | 2 +- src/sbt-test/sbt-compatibility/simple/build.sbt | 4 ++-- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 4037008..8cbdffc 100644 --- a/README.md +++ b/README.md @@ -45,11 +45,11 @@ The previously compatible version is computed from `version` the following way: By default, `compatibilityPreviousArtifacts` relies on `mimaPreviousArtifacts` from sbt-mima, so that only setting / changing `mimaPreviousArtifacts` is enough for both sbt-mima and sbt-compatibility. -## `compatibilityReconciliations` +## `compatibilityRules` -`compatibilityReconciliations` allows to specify whether some version bumps are allowed or not, like +`compatibilityRules` allows to specify whether some version bumps are allowed or not, like ```scala -compatibilityReconciliations += "org.scala-lang.modules" %% "scala-xml" % "semver" +compatibilityRules += "org.scala-lang.modules" %% "scala-xml" % "semver" ``` The following compatility types are available: @@ -58,7 +58,7 @@ The following compatility types are available: - `always`: assumes all versions of the matched modules are compatible with each other, - `strict`: requires exact matches between the wanted and the selected versions of the matched modules. -If no rule for a module is found in `compatibilityReconciliations`, `compatibilityDefaultReconciliation` is used +If no rule for a module is found in `compatibilityRules`, `compatibilityDefaultReconciliation` is used as a compatibility type. It's default value is `VersionCompatibility.PackVer` (package versioning policy). ## Acknowledgments diff --git a/src/main/scala/sbtcompatibility/SbtCompatibilityKeys.scala b/src/main/scala/sbtcompatibility/SbtCompatibilityKeys.scala index 3c86d6b..6330522 100644 --- a/src/main/scala/sbtcompatibility/SbtCompatibilityKeys.scala +++ b/src/main/scala/sbtcompatibility/SbtCompatibilityKeys.scala @@ -11,7 +11,9 @@ trait SbtCompatibilityKeys { final val compatibilityReportDependencyIssues = taskKey[Unit]("") final val compatibilityCheck = taskKey[Unit]("Runs both compatibilityReportDependencyIssues and mimaReportBinaryIssues") - final val compatibilityReconciliations = taskKey[Seq[ModuleID]]("") + final val compatibilityRules = taskKey[Seq[ModuleID]]("") + @deprecated("Use compatibilityRules instead", "0.0.8") + final def compatibilityReconciliations = compatibilityRules final val compatibilityIgnored = taskKey[Seq[OrganizationArtifactName]]("") final val compatibilityDetailedReconciliations = taskKey[Seq[(ModuleMatchers, VersionCompatibility)]]("") final val compatibilityCheckDirection = taskKey[Direction]("") diff --git a/src/main/scala/sbtcompatibility/SbtCompatibilitySettings.scala b/src/main/scala/sbtcompatibility/SbtCompatibilitySettings.scala index 0e41ae2..5300d43 100644 --- a/src/main/scala/sbtcompatibility/SbtCompatibilitySettings.scala +++ b/src/main/scala/sbtcompatibility/SbtCompatibilitySettings.scala @@ -37,7 +37,7 @@ object SbtCompatibilitySettings { compatibilityCheckDirection := Direction.backward, compatibilityIgnoreSbtDefaultReconciliations := true, compatibilityUseCsrConfigReconciliations := true, - compatibilityReconciliations := Seq.empty, + compatibilityRules := Seq.empty, compatibilityIgnored := Seq.empty, compatibilityDefaultReconciliation := VersionCompatibility.PackVer ) @@ -46,7 +46,7 @@ object SbtCompatibilitySettings { compatibilityDetailedReconciliations := { val sv = scalaVersion.value val sbv = scalaBinaryVersion.value - compatibilityReconciliations.value.map { mod => + compatibilityRules.value.map { mod => val rec = VersionCompatibility(mod.revision) match { case Some(r) => r case None => sys.error(s"Unrecognized reconciliation '${mod.revision}' in $mod") diff --git a/src/sbt-test/sbt-compatibility/in-this-build/build.sbt b/src/sbt-test/sbt-compatibility/in-this-build/build.sbt index 49baf7d..6c62b0a 100644 --- a/src/sbt-test/sbt-compatibility/in-this-build/build.sbt +++ b/src/sbt-test/sbt-compatibility/in-this-build/build.sbt @@ -19,6 +19,6 @@ lazy val b = project inThisBuild(List( scalaVersion := "2.12.11", organization := "io.github.alexarchambault.sbtcompatibility.test2", - compatibilityReconciliations += "org.scala-lang.modules" %% "scala-xml" % "semver" + compatibilityRules += "org.scala-lang.modules" %% "scala-xml" % "semver" )) diff --git a/src/sbt-test/sbt-compatibility/simple/build.sbt b/src/sbt-test/sbt-compatibility/simple/build.sbt index 0a25abb..7e2194b 100644 --- a/src/sbt-test/sbt-compatibility/simple/build.sbt +++ b/src/sbt-test/sbt-compatibility/simple/build.sbt @@ -85,7 +85,7 @@ lazy val f = project libraryDependencies ++= Seq( "com.chuusai" %% "shapeless" % "2.3.3" ), - compatibilityReconciliations += "com.chuusai" %% "shapeless" % "strict", + compatibilityRules += "com.chuusai" %% "shapeless" % "strict", version := "0.1.1" ) @@ -96,7 +96,7 @@ lazy val g = project libraryDependencies ++= Seq( "com.chuusai" %% "shapeless" % "2.3.3" ), - compatibilityReconciliations += "com.chuusai" %% "shapeless" % "pvp", + compatibilityRules += "com.chuusai" %% "shapeless" % "pvp", version := "0.1.1" ) From e4fe77f752614f70fae5794092abad2d2b9f7faf Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Wed, 10 Jun 2020 15:14:27 +0200 Subject: [PATCH 2/3] Don't expose some internal keys by default These can still be accessed via compatibilityInternal --- .../SbtCompatibilityInternalKeys.scala | 21 +++++++++++++++++++ .../SbtCompatibilityKeys.scala | 19 ++++------------- .../SbtCompatibilitySettings.scala | 1 + .../sbt-compatibility/run-mima/build.sbt | 3 +-- 4 files changed, 27 insertions(+), 17 deletions(-) create mode 100644 src/main/scala/sbtcompatibility/SbtCompatibilityInternalKeys.scala diff --git a/src/main/scala/sbtcompatibility/SbtCompatibilityInternalKeys.scala b/src/main/scala/sbtcompatibility/SbtCompatibilityInternalKeys.scala new file mode 100644 index 0000000..450dc73 --- /dev/null +++ b/src/main/scala/sbtcompatibility/SbtCompatibilityInternalKeys.scala @@ -0,0 +1,21 @@ +package sbtcompatibility + +import coursier.version.{ModuleMatchers, VersionCompatibility} +import lmcoursier.CoursierConfiguration +import sbt._ +import sbt.librarymanagement.{DependencyResolution, ScalaModuleInfo, UpdateConfiguration, UnresolvedWarningConfiguration} + +trait SbtCompatibilityInternalKeys { + final val compatibilityCsrConfiguration = taskKey[CoursierConfiguration]("CoursierConfiguration instance to use to fetch previous versions dependencies") + final val compatibilityDependencyResolution = taskKey[DependencyResolution]("DependencyResolution instance to use to fetch previous versions dependencies") + final val compatibilityUpdateConfiguration = taskKey[UpdateConfiguration]("") + final val compatibilityUnresolvedWarningConfiguration = taskKey[UnresolvedWarningConfiguration]("") + final val compatibilityScalaModuleInfo = taskKey[Option[ScalaModuleInfo]]("") + + final val compatibilityIgnoreSbtDefaultReconciliations = taskKey[Boolean]("") + final val compatibilityUseCsrConfigReconciliations = taskKey[Boolean]("") + + final val compatibilityAutoPreviousArtifacts = taskKey[Seq[ModuleID]]("") + final val compatibilityPreviousArtifactsFromMima = taskKey[Seq[ModuleID]]("") + final val compatibilityPreviousVersions = taskKey[Seq[String]]("") +} diff --git a/src/main/scala/sbtcompatibility/SbtCompatibilityKeys.scala b/src/main/scala/sbtcompatibility/SbtCompatibilityKeys.scala index 6330522..b31f388 100644 --- a/src/main/scala/sbtcompatibility/SbtCompatibilityKeys.scala +++ b/src/main/scala/sbtcompatibility/SbtCompatibilityKeys.scala @@ -1,9 +1,7 @@ package sbtcompatibility import coursier.version.{ModuleMatchers, VersionCompatibility} -import lmcoursier.CoursierConfiguration import sbt._ -import sbt.librarymanagement.{DependencyResolution, ScalaModuleInfo, UpdateConfiguration, UnresolvedWarningConfiguration} import sbt.librarymanagement.DependencyBuilders.OrganizationArtifactName trait SbtCompatibilityKeys { @@ -20,17 +18,8 @@ trait SbtCompatibilityKeys { final val compatibilityFindDependencyIssues = taskKey[Seq[(ModuleID, DependencyCheckReport)]]("") - final val compatibilityCsrConfiguration = taskKey[CoursierConfiguration]("CoursierConfiguration instance to use to fetch previous versions dependencies") - final val compatibilityDependencyResolution = taskKey[DependencyResolution]("DependencyResolution instance to use to fetch previous versions dependencies") - final val compatibilityUpdateConfiguration = taskKey[UpdateConfiguration]("") - final val compatibilityUnresolvedWarningConfiguration = taskKey[UnresolvedWarningConfiguration]("") - final val compatibilityScalaModuleInfo = taskKey[Option[ScalaModuleInfo]]("") + final val compatibilityDefaultReconciliation = taskKey[VersionCompatibility]("") - final val compatibilityIgnoreSbtDefaultReconciliations = taskKey[Boolean]("") - final val compatibilityUseCsrConfigReconciliations = taskKey[Boolean]("") - final val compatibilityDefaultReconciliation = taskKey[VersionCompatibility]("") - - final val compatibilityAutoPreviousArtifacts = taskKey[Seq[ModuleID]]("") - final val compatibilityPreviousArtifactsFromMima = taskKey[Seq[ModuleID]]("") - final val compatibilityPreviousVersions = taskKey[Seq[String]]("") -} \ No newline at end of file + final val compatibilityInternal: SbtCompatibilityInternalKeys = + new SbtCompatibilityInternalKeys {} +} diff --git a/src/main/scala/sbtcompatibility/SbtCompatibilitySettings.scala b/src/main/scala/sbtcompatibility/SbtCompatibilitySettings.scala index 5300d43..e630743 100644 --- a/src/main/scala/sbtcompatibility/SbtCompatibilitySettings.scala +++ b/src/main/scala/sbtcompatibility/SbtCompatibilitySettings.scala @@ -15,6 +15,7 @@ object SbtCompatibilitySettings { private val keys: SbtCompatibilityKeys = new SbtCompatibilityKeys {} import keys._ + import keys.compatibilityInternal._ def updateSettings = Def.settings( compatibilityCsrConfiguration := csrConfiguration.value diff --git a/src/sbt-test/sbt-compatibility/run-mima/build.sbt b/src/sbt-test/sbt-compatibility/run-mima/build.sbt index faf9f63..e4cd099 100644 --- a/src/sbt-test/sbt-compatibility/run-mima/build.sbt +++ b/src/sbt-test/sbt-compatibility/run-mima/build.sbt @@ -9,6 +9,5 @@ lazy val core = project libraryDependencies ++= Seq( "com.chuusai" %% "shapeless" % "2.3.3", "io.argonaut" %% "argonaut" % "6.1a" - ), - compatibilityPreviousArtifacts := compatibilityAutoPreviousArtifacts.value + ) ) From 5b3733a034019e4ea90865ff2282d22ff4319053 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Wed, 10 Jun 2020 15:38:42 +0200 Subject: [PATCH 3/3] Add fallback rules mimicking the current sbt evicted behavior --- .../SbtCompatibilityInternalKeys.scala | 3 + .../SbtCompatibilityKeys.scala | 6 +- .../SbtCompatibilitySettings.scala | 41 +++++++++++-- .../sbt-compatibility/defaults/build.sbt | 59 +++++++++++++++++++ .../defaults/project/plugins.sbt | 1 + src/sbt-test/sbt-compatibility/defaults/test | 5 ++ .../sbt-compatibility/simple/build.sbt | 3 +- 7 files changed, 108 insertions(+), 10 deletions(-) create mode 100644 src/sbt-test/sbt-compatibility/defaults/build.sbt create mode 100644 src/sbt-test/sbt-compatibility/defaults/project/plugins.sbt create mode 100644 src/sbt-test/sbt-compatibility/defaults/test diff --git a/src/main/scala/sbtcompatibility/SbtCompatibilityInternalKeys.scala b/src/main/scala/sbtcompatibility/SbtCompatibilityInternalKeys.scala index 450dc73..d4728a6 100644 --- a/src/main/scala/sbtcompatibility/SbtCompatibilityInternalKeys.scala +++ b/src/main/scala/sbtcompatibility/SbtCompatibilityInternalKeys.scala @@ -18,4 +18,7 @@ trait SbtCompatibilityInternalKeys { final val compatibilityAutoPreviousArtifacts = taskKey[Seq[ModuleID]]("") final val compatibilityPreviousArtifactsFromMima = taskKey[Seq[ModuleID]]("") final val compatibilityPreviousVersions = taskKey[Seq[String]]("") + + final val compatibilityDetailedReconciliations = taskKey[Seq[(ModuleMatchers, VersionCompatibility)]]("") + final val compatibilityFallbackReconciliations = taskKey[Seq[(ModuleMatchers, VersionCompatibility)]]("") } diff --git a/src/main/scala/sbtcompatibility/SbtCompatibilityKeys.scala b/src/main/scala/sbtcompatibility/SbtCompatibilityKeys.scala index b31f388..51fda41 100644 --- a/src/main/scala/sbtcompatibility/SbtCompatibilityKeys.scala +++ b/src/main/scala/sbtcompatibility/SbtCompatibilityKeys.scala @@ -1,6 +1,6 @@ package sbtcompatibility -import coursier.version.{ModuleMatchers, VersionCompatibility} +import coursier.version.VersionCompatibility import sbt._ import sbt.librarymanagement.DependencyBuilders.OrganizationArtifactName @@ -13,12 +13,12 @@ trait SbtCompatibilityKeys { @deprecated("Use compatibilityRules instead", "0.0.8") final def compatibilityReconciliations = compatibilityRules final val compatibilityIgnored = taskKey[Seq[OrganizationArtifactName]]("") - final val compatibilityDetailedReconciliations = taskKey[Seq[(ModuleMatchers, VersionCompatibility)]]("") final val compatibilityCheckDirection = taskKey[Direction]("") final val compatibilityFindDependencyIssues = taskKey[Seq[(ModuleID, DependencyCheckReport)]]("") - final val compatibilityDefaultReconciliation = taskKey[VersionCompatibility]("") + final val compatibilityDefaultRules = taskKey[Seq[ModuleID]]("") + final val compatibilityDefaultReconciliation = taskKey[Option[VersionCompatibility]]("") final val compatibilityInternal: SbtCompatibilityInternalKeys = new SbtCompatibilityInternalKeys {} diff --git a/src/main/scala/sbtcompatibility/SbtCompatibilitySettings.scala b/src/main/scala/sbtcompatibility/SbtCompatibilitySettings.scala index e630743..7595a93 100644 --- a/src/main/scala/sbtcompatibility/SbtCompatibilitySettings.scala +++ b/src/main/scala/sbtcompatibility/SbtCompatibilitySettings.scala @@ -2,7 +2,7 @@ package sbtcompatibility import com.typesafe.tools.mima.plugin.MimaPlugin import coursier.version.{ModuleMatcher, ModuleMatchers, VersionCompatibility} -import sbt.{Compile, Def} +import sbt._ import sbt.Keys._ import sbt.librarymanagement.CrossVersion import lmcoursier.CoursierDependencyResolution @@ -34,16 +34,47 @@ object SbtCompatibilitySettings { compatibilityScalaModuleInfo := scalaModuleInfo.value ) + // Trying to mimick the current default behavior of evicted in sbt, that is + // - assume scala libraries follow PVP, + // - assume Java libraries follow semver. + private def defaultRules = Seq( + ("*" % "*" % "pvp").cross(CrossVersion.full), + "*" %% "*" % "pvp", + "*" % "*" % "semver" + ) + def reconciliationBuildSettings = Def.settings( compatibilityCheckDirection := Direction.backward, compatibilityIgnoreSbtDefaultReconciliations := true, compatibilityUseCsrConfigReconciliations := true, compatibilityRules := Seq.empty, + compatibilityDefaultRules := defaultRules, compatibilityIgnored := Seq.empty, - compatibilityDefaultReconciliation := VersionCompatibility.PackVer + compatibilityDefaultReconciliation := None ) def reconciliationSettings = Def.settings( + compatibilityFallbackReconciliations := { + val sv = scalaVersion.value + val sbv = scalaBinaryVersion.value + val defaultReconciliationOpt = compatibilityDefaultReconciliation.value + val (fallbackRules, fallbackMatchers) = { + val rules: Seq[ModuleID] = compatibilityDefaultRules.value + defaultReconciliationOpt match { + case None => (rules, Nil) + case Some(default) => (Nil, Seq(ModuleMatchers.all -> default)) + } + } + fallbackRules.map { mod => + val rec = VersionCompatibility(mod.revision) match { + case Some(r) => r + case None => sys.error(s"Unrecognized reconciliation '${mod.revision}' in $mod") + } + val name = CrossVersion(mod.crossVersion, sv, sbv).fold(mod.name)(_(mod.name)) + val matchers = ModuleMatchers.only(mod.organization, name) + (matchers, rec) + } ++ fallbackMatchers + }, compatibilityDetailedReconciliations := { val sv = scalaVersion.value val sbv = scalaBinaryVersion.value @@ -133,10 +164,10 @@ object SbtCompatibilitySettings { } val ours = compatibilityDetailedReconciliations.value + val fallback = compatibilityFallbackReconciliations.value - ours ++ fromCsrConfig0 + ours ++ fromCsrConfig0 ++ fallback } - val defaultReconciliation = compatibilityDefaultReconciliation.value val currentModules = DependencyCheck.modulesOf(compileReport, sv, sbv, log) @@ -148,7 +179,7 @@ object SbtCompatibilitySettings { currentModules, previousModuleId, reconciliations, - defaultReconciliation, + VersionCompatibility.Strict, sv, sbv, depRes, diff --git a/src/sbt-test/sbt-compatibility/defaults/build.sbt b/src/sbt-test/sbt-compatibility/defaults/build.sbt new file mode 100644 index 0000000..0298cd8 --- /dev/null +++ b/src/sbt-test/sbt-compatibility/defaults/build.sbt @@ -0,0 +1,59 @@ +lazy val v1 = project + .settings( + check := {}, + name := "defaults-test", + libraryDependencies ++= Seq( + "org.scala-lang.modules" %% "scala-xml" % "1.0.6", + "com.fasterxml.jackson.core" % "jackson-databind" % "2.10.4" + ), + version := "0.1.0" + ) + +lazy val bumpScala = project + .settings( + name := "defaults-test", + libraryDependencies ++= Seq( + "org.scala-lang.modules" %% "scala-xml" % "1.2.0", + "com.fasterxml.jackson.core" % "jackson-databind" % "2.10.4" + ), + version := "0.1.1", + checkFails + ) + +lazy val bumpJava = project + .settings( + name := "defaults-test", + libraryDependencies ++= Seq( + "org.scala-lang.modules" %% "scala-xml" % "1.0.6", + "com.fasterxml.jackson.core" % "jackson-databind" % "2.11.0" + ), + version := "0.1.1" + ) + +lazy val bumpScalaAndAddRule = project + .settings( + name := "defaults-test", + libraryDependencies ++= Seq( + "org.scala-lang.modules" %% "scala-xml" % "1.2.0", + "com.fasterxml.jackson.core" % "jackson-databind" % "2.10.4" + ), + version := "0.1.1", + compatibilityRules += "org.scala-lang.modules" %% "*" % "semver" + ) + +inThisBuild(List( + scalaVersion := "2.12.11", + organization := "io.github.alexarchambault.sbtcompatibility.test", +)) + +lazy val check = taskKey[Unit]("") + +lazy val checkFails = Def.settings( + check := { + val direction = compatibilityCheckDirection.value + val reports = compatibilityFindDependencyIssues.value + val failed = reports.exists(!_._2.validated(direction)) + assert(failed, s"Expected a failed report in $reports") + } +) + diff --git a/src/sbt-test/sbt-compatibility/defaults/project/plugins.sbt b/src/sbt-test/sbt-compatibility/defaults/project/plugins.sbt new file mode 100644 index 0000000..9087a86 --- /dev/null +++ b/src/sbt-test/sbt-compatibility/defaults/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("io.github.alexarchambault.sbt" % "sbt-compatibility" % sys.props("plugin.version")) diff --git a/src/sbt-test/sbt-compatibility/defaults/test b/src/sbt-test/sbt-compatibility/defaults/test new file mode 100644 index 0000000..50691de --- /dev/null +++ b/src/sbt-test/sbt-compatibility/defaults/test @@ -0,0 +1,5 @@ +> v1/publishLocal +-> bumpScala/compatibilityReportDependencyIssues +> bumpScala/check +> bumpJava/compatibilityReportDependencyIssues +> bumpScalaAndAddRule/compatibilityReportDependencyIssues diff --git a/src/sbt-test/sbt-compatibility/simple/build.sbt b/src/sbt-test/sbt-compatibility/simple/build.sbt index 7e2194b..e4dbd4c 100644 --- a/src/sbt-test/sbt-compatibility/simple/build.sbt +++ b/src/sbt-test/sbt-compatibility/simple/build.sbt @@ -58,7 +58,7 @@ lazy val d = project libraryDependencies ++= Seq( "com.chuusai" %% "shapeless" % "2.3.3" ), - compatibilityDefaultReconciliation := VersionCompatibility.Strict, + compatibilityDefaultReconciliation := Some(VersionCompatibility.Strict), checkFails, checkMimaPreviousArtifactsSet, version := "0.1.1" @@ -108,7 +108,6 @@ inThisBuild(List( lazy val check = taskKey[Unit]("") lazy val shared = Def.settings( - compatibilityPreviousArtifacts := compatibilityAutoPreviousArtifacts.value, check := {} )