mirror of https://github.com/sbt/sbt.git
Replace EvictionWarning with EvictionError
Fixes https://github.com/sbt/sbt/issues/5976 Ref https://eed3si9n.com/enforcing-semver-with-sbt-strict-update This removes the guess-based EvictionWarning, and runs EvictionError instead. EvictionError uses the `ThisBuild / versionScheme` information supplied by the library authors in addition to `libraryDependencySchemes` that the build users could provide: ```scala ThisBuild / libraryDependencySchemes += "com.example" %% "name" % "early-semver" ``` as the version scheme "early-semver", "semver-spec", "pvp", "strict", or "always" may be used. Here's an example of `update` failure: ``` [error] * org.typelevel:cats-effect_2.13:3.0.0-M4 (early-semver) is selected over {2.2.0, 2.0.0, 2.0.0, 2.2.0} [error] +- com.example:use2_2.13:0.1.0-SNAPSHOT (depends on 3.0.0-M4) [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] [error] [error] this can be overridden using libraryDependencySchemes or evictionErrorLevel ``` This catches the violation of cats-effect_2.13:3.0.0-M4 version scheme (early-semver) without user setting additional overrides. If the user wants to opt-out of this, the user can do so per module: ```scala ThisBuild / libraryDependencySchemes += "org.typelevel" %% "cats-effect" % "always" ``` or globally as: ``` ThisBuild / evictionErrorLevel := Level.Info ```
This commit is contained in:
parent
3c955abea6
commit
1111ed09ad
|
|
@ -2689,6 +2689,8 @@ object Classpaths {
|
||||||
defaultConfiguration :== Some(Configurations.Compile),
|
defaultConfiguration :== Some(Configurations.Compile),
|
||||||
dependencyOverrides :== Vector.empty,
|
dependencyOverrides :== Vector.empty,
|
||||||
libraryDependencies :== Nil,
|
libraryDependencies :== Nil,
|
||||||
|
libraryDependencySchemes :== Nil,
|
||||||
|
evictionErrorLevel :== Level.Error,
|
||||||
excludeDependencies :== Nil,
|
excludeDependencies :== Nil,
|
||||||
ivyLoggingLevel := (// This will suppress "Resolving..." logs on Jenkins and Travis.
|
ivyLoggingLevel := (// This will suppress "Resolving..." logs on Jenkins and Travis.
|
||||||
if (insideCI.value)
|
if (insideCI.value)
|
||||||
|
|
@ -3460,13 +3462,7 @@ object Classpaths {
|
||||||
.withMetadataDirectory(dependencyCacheDirectory.value)
|
.withMetadataDirectory(dependencyCacheDirectory.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
val evictionOptions = Def.taskDyn {
|
val extracted = Project.extract(state0)
|
||||||
if (executionRoots.value.exists(_.key == evicted.key))
|
|
||||||
Def.task(EvictionWarningOptions.empty)
|
|
||||||
else Def.task((evictionWarningOptions in update).value)
|
|
||||||
}.value
|
|
||||||
|
|
||||||
val extracted = (Project extract state0)
|
|
||||||
val isPlugin = sbtPlugin.value
|
val isPlugin = sbtPlugin.value
|
||||||
val thisRef = thisProjectRef.value
|
val thisRef = thisProjectRef.value
|
||||||
val label =
|
val label =
|
||||||
|
|
@ -3486,7 +3482,8 @@ object Classpaths {
|
||||||
force = shouldForce,
|
force = shouldForce,
|
||||||
depsUpdated = transitiveUpdate.value.exists(!_.stats.cached),
|
depsUpdated = transitiveUpdate.value.exists(!_.stats.cached),
|
||||||
uwConfig = (unresolvedWarningConfiguration in update).value,
|
uwConfig = (unresolvedWarningConfiguration in update).value,
|
||||||
ewo = evictionOptions,
|
evictionLevel = evictionErrorLevel.value,
|
||||||
|
versionSchemeOverrides = libraryDependencySchemes.value,
|
||||||
mavenStyle = publishMavenStyle.value,
|
mavenStyle = publishMavenStyle.value,
|
||||||
compatWarning = compatibilityWarningOptions.value,
|
compatWarning = compatibilityWarningOptions.value,
|
||||||
includeCallers = includeCallers,
|
includeCallers = includeCallers,
|
||||||
|
|
|
||||||
|
|
@ -455,6 +455,7 @@ object Keys {
|
||||||
val updateFull = taskKey[UpdateReport]("Resolves and optionally retrieves dependencies, producing a full report with callers.").withRank(CTask)
|
val updateFull = taskKey[UpdateReport]("Resolves and optionally retrieves dependencies, producing a full report with callers.").withRank(CTask)
|
||||||
val evicted = taskKey[EvictionWarning]("Display detailed eviction warnings.").withRank(CTask)
|
val evicted = taskKey[EvictionWarning]("Display detailed eviction warnings.").withRank(CTask)
|
||||||
val evictionWarningOptions = settingKey[EvictionWarningOptions]("Options on eviction warnings after resolving managed dependencies.").withRank(DSetting)
|
val evictionWarningOptions = settingKey[EvictionWarningOptions]("Options on eviction warnings after resolving managed dependencies.").withRank(DSetting)
|
||||||
|
val evictionErrorLevel = settingKey[Level.Value]("The log level for detected eviction errors. Level.Error will throw an error.")
|
||||||
val transitiveUpdate = taskKey[Seq[UpdateReport]]("UpdateReports for the internal dependencies of this project.").withRank(DTask)
|
val transitiveUpdate = taskKey[Seq[UpdateReport]]("UpdateReports for the internal dependencies of this project.").withRank(DTask)
|
||||||
val updateClassifiers = TaskKey[UpdateReport]("updateClassifiers", "Resolves and optionally retrieves classified artifacts, such as javadocs and sources, for dependency definitions, transitively.", BPlusTask, update)
|
val updateClassifiers = TaskKey[UpdateReport]("updateClassifiers", "Resolves and optionally retrieves classified artifacts, such as javadocs and sources, for dependency definitions, transitively.", BPlusTask, update)
|
||||||
val transitiveClassifiers = settingKey[Seq[String]]("List of classifiers used for transitively obtaining extra artifacts for sbt or declared dependencies.").withRank(BSetting)
|
val transitiveClassifiers = settingKey[Seq[String]]("List of classifiers used for transitively obtaining extra artifacts for sbt or declared dependencies.").withRank(BSetting)
|
||||||
|
|
@ -533,6 +534,7 @@ object Keys {
|
||||||
val checksums = settingKey[Seq[String]]("The list of checksums to generate and to verify for dependencies.").withRank(BSetting)
|
val checksums = settingKey[Seq[String]]("The list of checksums to generate and to verify for dependencies.").withRank(BSetting)
|
||||||
val forceUpdatePeriod = settingKey[Option[FiniteDuration]]("Duration after which to force a full update to occur").withRank(CSetting)
|
val forceUpdatePeriod = settingKey[Option[FiniteDuration]]("Duration after which to force a full update to occur").withRank(CSetting)
|
||||||
val versionScheme = settingKey[Option[String]]("""Version scheme used for the subproject: Supported values are Some("early-semver"), Some("pvp"), and Some("semver-spec")""").withRank(BSetting)
|
val versionScheme = settingKey[Option[String]]("""Version scheme used for the subproject: Supported values are Some("early-semver"), Some("pvp"), and Some("semver-spec")""").withRank(BSetting)
|
||||||
|
val libraryDependencySchemes = settingKey[Seq[ModuleID]]("""Version scheme to use for specific modules set as "org" %% "name" % "<scheme>": Supported values are "early-semver", "pvp", "semver-spec", "always", and "strict".""").withRank(BSetting)
|
||||||
|
|
||||||
val classifiersModule = taskKey[GetClassifiersModule]("classifiers-module").withRank(CTask)
|
val classifiersModule = taskKey[GetClassifiersModule]("classifiers-module").withRank(CTask)
|
||||||
val compatibilityWarningOptions = settingKey[CompatibilityWarningOptions]("Configures warnings around Maven incompatibility.").withRank(CSetting)
|
val compatibilityWarningOptions = settingKey[CompatibilityWarningOptions]("Configures warnings around Maven incompatibility.").withRank(CSetting)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import sbt.io.IO
|
||||||
import sbt.io.syntax._
|
import sbt.io.syntax._
|
||||||
import sbt.Project.richInitializeTask
|
import sbt.Project.richInitializeTask
|
||||||
import sjsonnew.JsonFormat
|
import sjsonnew.JsonFormat
|
||||||
|
import scala.compat.Platform.EOL
|
||||||
|
|
||||||
private[sbt] object LibraryManagement {
|
private[sbt] object LibraryManagement {
|
||||||
implicit val linter: sbt.dsl.LinterLevel.Ignore.type = sbt.dsl.LinterLevel.Ignore
|
implicit val linter: sbt.dsl.LinterLevel.Ignore.type = sbt.dsl.LinterLevel.Ignore
|
||||||
|
|
@ -36,7 +37,8 @@ private[sbt] object LibraryManagement {
|
||||||
force: Boolean,
|
force: Boolean,
|
||||||
depsUpdated: Boolean,
|
depsUpdated: Boolean,
|
||||||
uwConfig: UnresolvedWarningConfiguration,
|
uwConfig: UnresolvedWarningConfiguration,
|
||||||
ewo: EvictionWarningOptions,
|
evictionLevel: Level.Value,
|
||||||
|
versionSchemeOverrides: Seq[ModuleID],
|
||||||
mavenStyle: Boolean,
|
mavenStyle: Boolean,
|
||||||
compatWarning: CompatibilityWarningOptions,
|
compatWarning: CompatibilityWarningOptions,
|
||||||
includeCallers: Boolean,
|
includeCallers: Boolean,
|
||||||
|
|
@ -61,9 +63,19 @@ private[sbt] object LibraryManagement {
|
||||||
val report1 = transform(report)
|
val report1 = transform(report)
|
||||||
|
|
||||||
// Warn of any eviction and compatibility warnings
|
// Warn of any eviction and compatibility warnings
|
||||||
val ew = EvictionWarning(module, ewo, report1)
|
val evictionError = EvictionError(report1, module, versionSchemeOverrides)
|
||||||
ew.lines.foreach(log.warn(_))
|
if (evictionError.incompatibleEvictions.isEmpty) ()
|
||||||
ew.infoAllTheThings.foreach(log.info(_))
|
else
|
||||||
|
evictionLevel match {
|
||||||
|
case Level.Error =>
|
||||||
|
val msgs = List(
|
||||||
|
"",
|
||||||
|
"this can be overridden using libraryDependencySchemes or evictionErrorLevel"
|
||||||
|
)
|
||||||
|
sys.error((evictionError.lines ++ msgs).mkString(EOL))
|
||||||
|
case _ =>
|
||||||
|
evictionError.lines.foreach(log.log(evictionLevel, _: String))
|
||||||
|
}
|
||||||
CompatibilityWarning.run(compatWarning, module, mavenStyle, log)
|
CompatibilityWarning.run(compatWarning, module, mavenStyle, log)
|
||||||
val report2 = transformDetails(report1, includeCallers, includeDetails)
|
val report2 = transformDetails(report1, includeCallers, includeDetails)
|
||||||
report2
|
report2
|
||||||
|
|
@ -245,11 +257,6 @@ private[sbt] object LibraryManagement {
|
||||||
// logical clock is folded into UpdateConfiguration
|
// logical clock is folded into UpdateConfiguration
|
||||||
conf1.withLogicalClock(LogicalClock(state0.hashCode))
|
conf1.withLogicalClock(LogicalClock(state0.hashCode))
|
||||||
}
|
}
|
||||||
val evictionOptions = Def.taskDyn {
|
|
||||||
if (executionRoots.value.exists(_.key == evicted.key))
|
|
||||||
Def.task(EvictionWarningOptions.empty)
|
|
||||||
else Def.task((evictionWarningOptions in update).value)
|
|
||||||
}.value
|
|
||||||
cachedUpdate(
|
cachedUpdate(
|
||||||
// LM API
|
// LM API
|
||||||
lm = lm,
|
lm = lm,
|
||||||
|
|
@ -263,7 +270,8 @@ private[sbt] object LibraryManagement {
|
||||||
force = shouldForce,
|
force = shouldForce,
|
||||||
depsUpdated = transitiveUpdate.value.exists(!_.stats.cached),
|
depsUpdated = transitiveUpdate.value.exists(!_.stats.cached),
|
||||||
uwConfig = (unresolvedWarningConfiguration in update).value,
|
uwConfig = (unresolvedWarningConfiguration in update).value,
|
||||||
ewo = evictionOptions,
|
evictionLevel = Level.Debug,
|
||||||
|
versionSchemeOverrides = Nil,
|
||||||
mavenStyle = publishMavenStyle.value,
|
mavenStyle = publishMavenStyle.value,
|
||||||
compatWarning = compatibilityWarningOptions.value,
|
compatWarning = compatibilityWarningOptions.value,
|
||||||
includeCallers = false,
|
includeCallers = false,
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,10 @@
|
||||||
// ThisBuild / useCoursier := false
|
|
||||||
ThisBuild / organization := "com.example"
|
ThisBuild / organization := "com.example"
|
||||||
ThisBuild / scalaVersion := "2.12.12"
|
ThisBuild / scalaVersion := "2.13.3"
|
||||||
ThisBuild / versionScheme := Some("semver-spec")
|
ThisBuild / versionScheme := Some("semver-spec")
|
||||||
ThisBuild / csrCacheDirectory := (ThisBuild / baseDirectory).value / "coursier-cache"
|
ThisBuild / csrCacheDirectory := (ThisBuild / baseDirectory).value / "coursier-cache"
|
||||||
|
|
||||||
def commonSettings: Seq[Def.Setting[_]] =
|
def commonSettings: Seq[Def.Setting[_]] =
|
||||||
Seq(
|
Seq(
|
||||||
ivyPaths := IvyPaths(
|
|
||||||
(ThisBuild / baseDirectory).value,
|
|
||||||
Some((LocalRootProject / target).value / "ivy-cache")
|
|
||||||
),
|
|
||||||
fullResolvers := fullResolvers.value.filterNot(_.name == "inter-project"),
|
fullResolvers := fullResolvers.value.filterNot(_.name == "inter-project"),
|
||||||
publishTo := Some(MavenCache("local-maven", (LocalRootProject / target).value / "local-maven")),
|
publishTo := Some(MavenCache("local-maven", (LocalRootProject / target).value / "local-maven")),
|
||||||
resolvers += MavenCache("local-maven", (LocalRootProject / target).value / "local-maven"),
|
resolvers += MavenCache("local-maven", (LocalRootProject / target).value / "local-maven"),
|
||||||
|
|
@ -56,7 +51,17 @@ val use = project
|
||||||
}
|
}
|
||||||
log.info(s"extraAttributes = $extraAttributes")
|
log.info(s"extraAttributes = $extraAttributes")
|
||||||
assert(extraAttributes.nonEmpty, s"$extraAttributes is empty")
|
assert(extraAttributes.nonEmpty, s"$extraAttributes is empty")
|
||||||
val ew = EvictionWarning(ivyModule.value, (evicted / evictionWarningOptions).value, report)
|
|
||||||
assert(ew.directEvictions.isEmpty, s"${ew.directEvictions} is not empty")
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val use2 = project
|
||||||
|
.settings(commonSettings)
|
||||||
|
.settings(
|
||||||
|
name := "use2",
|
||||||
|
libraryDependencies ++= Seq(
|
||||||
|
"org.http4s" %% "http4s-blaze-server" % "0.21.11",
|
||||||
|
// https://repo1.maven.org/maven2/org/typelevel/cats-effect_2.13/3.0.0-M4/cats-effect_2.13-3.0.0-M4.pom
|
||||||
|
// is published with early-semver
|
||||||
|
"org.typelevel" %% "cats-effect" % "3.0.0-M4",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -3,3 +3,4 @@
|
||||||
> middle/publish
|
> middle/publish
|
||||||
|
|
||||||
> use/check
|
> use/check
|
||||||
|
-> use2/update
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue