diff --git a/src/main/paradox/settings.md b/src/main/paradox/settings.md index cd2a558..cd81572 100644 --- a/src/main/paradox/settings.md +++ b/src/main/paradox/settings.md @@ -20,4 +20,10 @@ The different levels of checking available are: * `WarnOnError`: a check will be performed after `update`s, and a warning printed if there are any changes. * `FailOnError`: a check will be performed after `update`s, and the build will fail if there are any changes. * `AutoUpdate`: a check will be performed after `update`s, and the lockfile will be automatically updated if there are - any changes. \ No newline at end of file + any changes. + +### dependencyLockModuleFilter + +* **Description:** Excludes the specified dependencies from the lockfile. +* **Accepts:** `sbt.librarymanagement.ModuleFilter` +* **Default:** `DependencyFilter.fnToModuleFilter(_ => false)` (no exclusions) \ No newline at end of file diff --git a/src/main/scala/software/purpledragon/sbt/lock/DependencyLockPlugin.scala b/src/main/scala/software/purpledragon/sbt/lock/DependencyLockPlugin.scala index f1aca8f..5c07c9e 100644 --- a/src/main/scala/software/purpledragon/sbt/lock/DependencyLockPlugin.scala +++ b/src/main/scala/software/purpledragon/sbt/lock/DependencyLockPlugin.scala @@ -19,6 +19,7 @@ package software.purpledragon.sbt.lock import sbt._ import sbt.Keys._ import sbt.internal.util.ManagedLogger +import sbt.librarymanagement.{DependencyFilter, ModuleFilter} import software.purpledragon.sbt.lock.DependencyLockUpdateMode._ import software.purpledragon.sbt.lock.model.{DependencyLockFile, LockFileMatches} import software.purpledragon.sbt.lock.util.MessageUtil @@ -30,6 +31,7 @@ object DependencyLockPlugin extends AutoPlugin { val dependencyLockFile = settingKey[File]("lockfile to generate") val dependencyLockWrite = taskKey[File]("write dependencies to lockfile") val dependencyLockRead = taskKey[Option[DependencyLockFile]]("read dependencies from lockfile") + val dependencyLockModuleFilter = settingKey[ModuleFilter]("exclusion filter for dependencies") val dependencyLockCheck = taskKey[Unit]("check if dependency lock is up to date") @@ -45,12 +47,12 @@ object DependencyLockPlugin extends AutoPlugin { override def projectSettings: Seq[Def.Setting[_]] = Seq( dependencyLockFile := baseDirectory.value / "build.sbt.lock", - dependencyLockAutoCheck := DependencyLockUpdateMode.WarnOnError, dependencyLockWrite := { val dest = dependencyLockFile.value val updateReport = update.value + val exclusionFilter = dependencyLockModuleFilter.value - val lockFile = DependencyUtils.resolve(updateReport, thisProject.value.configurations.map(_.toConfigRef)) + val lockFile = DependencyUtils.resolve(updateReport, exclusionFilter, thisProject.value.configurations.map(_.toConfigRef)) val updateStatus = DependencyLockIO .readLockFile(dest) @@ -69,9 +71,10 @@ object DependencyLockPlugin extends AutoPlugin { dependencyLockCheck := { val logger: ManagedLogger = streams.value.log val updateReport: UpdateReport = update.value + val exclusionFilter = dependencyLockModuleFilter.value val currentFile = dependencyLockRead.value.getOrElse(sys.error(MessageUtil.formatMessage("lock.status.missing"))) - val updatedFile = DependencyUtils.resolve(updateReport, thisProject.value.configurations.map(_.toConfigRef)) + val updatedFile = DependencyUtils.resolve(updateReport, exclusionFilter, thisProject.value.configurations.map(_.toConfigRef)) val changes = currentFile.findChanges(updatedFile) @@ -89,13 +92,14 @@ object DependencyLockPlugin extends AutoPlugin { // check to see if the current command/task is one of our internal ones val skipCheck = state.value.currentCommand.map(_.commandLine).exists(PluginTasks.contains) val checkMode = dependencyLockAutoCheck.value + val exclusionFilter = dependencyLockModuleFilter.value if (checkMode != DependencyLockUpdateMode.CheckDisabled && !skipCheck) { logger.debug("Automatically checking lockfile") dependencyLockRead.value match { case Some(currentFile) => - val updatedFile = DependencyUtils.resolve(report, thisProject.value.configurations.map(_.toConfigRef)) + val updatedFile = DependencyUtils.resolve(report, exclusionFilter, thisProject.value.configurations.map(_.toConfigRef)) val changes = currentFile.findChanges(updatedFile) @@ -129,4 +133,9 @@ object DependencyLockPlugin extends AutoPlugin { } }.value ) + + override def globalSettings: Seq[Def.Setting[_]] = Seq( + dependencyLockAutoCheck := DependencyLockUpdateMode.WarnOnError, + dependencyLockModuleFilter := DependencyFilter.fnToModuleFilter(_ => false) + ) } diff --git a/src/main/scala/software/purpledragon/sbt/lock/DependencyUtils.scala b/src/main/scala/software/purpledragon/sbt/lock/DependencyUtils.scala index 00ae5f2..0725615 100644 --- a/src/main/scala/software/purpledragon/sbt/lock/DependencyUtils.scala +++ b/src/main/scala/software/purpledragon/sbt/lock/DependencyUtils.scala @@ -19,12 +19,13 @@ package software.purpledragon.sbt.lock import java.time.Instant import sbt._ +import sbt.librarymanagement.ModuleFilter import software.purpledragon.sbt.lock.model.{DependencyLockFile, DependencyRef, ResolvedArtifact, ResolvedDependency} import scala.collection.{immutable, mutable, SortedSet} object DependencyUtils { - def resolve(updateReport: UpdateReport, configs: Seq[ConfigRef]): DependencyLockFile = { + def resolve(updateReport: UpdateReport, exclusion: ModuleFilter, configs: Seq[ConfigRef]): DependencyLockFile = { val configurations: immutable.Seq[ConfigurationReport] = updateReport.configurations.filter(config => configs.contains(config.configuration)) @@ -34,7 +35,11 @@ object DependencyUtils { configurations.foldLeft(Map.empty[DependencyRef, ResolvedDependency]) { (acc, conf) => val configName = conf.configuration.name - conf.modules.foldLeft(acc) { (acc2, module) => + val filteredModules = conf.modules.filterNot { moduleReport => + exclusion(moduleReport.module) + } + + filteredModules.foldLeft(acc) { (acc2, module) => resolveModuleForConfig(acc2, configName, module, checksumCache) } } diff --git a/src/sbt-test/dependencyLockCheck/pass-with-exclusion/build.sbt b/src/sbt-test/dependencyLockCheck/pass-with-exclusion/build.sbt new file mode 100644 index 0000000..32ada5a --- /dev/null +++ b/src/sbt-test/dependencyLockCheck/pass-with-exclusion/build.sbt @@ -0,0 +1,9 @@ +scalaVersion := "2.12.10" + +libraryDependencies ++= Seq( + "org.apache.commons" % "commons-lang3" % "3.9", + "org.scalatest" %% "scalatest" % "3.0.8" % Test, +) + +// One ineffective filter and one for commons-lang3. +dependencyLockModuleFilter := moduleFilter(name = "fake") | moduleFilter(name = "commons-lang3") \ No newline at end of file diff --git a/src/sbt-test/dependencyLockCheck/pass-with-exclusion/build.sbt.lock b/src/sbt-test/dependencyLockCheck/pass-with-exclusion/build.sbt.lock new file mode 100644 index 0000000..ff69575 --- /dev/null +++ b/src/sbt-test/dependencyLockCheck/pass-with-exclusion/build.sbt.lock @@ -0,0 +1,85 @@ +{ + "lockVersion" : 1, + "timestamp" : "2022-08-11T17:47:26.677327Z", + "configurations" : [ + "compile", + "optional", + "provided", + "runtime", + "test" + ], + "dependencies" : [ + { + "org" : "org.scala-lang", + "name" : "scala-library", + "version" : "2.12.10", + "artifacts" : [ + { + "name" : "scala-library.jar", + "hash" : "sha1:3509860bc2e5b3da001ed45aca94ffbe5694dbda" + } + ], + "configurations" : [ + "test", + "compile", + "runtime" + ] + }, + { + "org" : "org.scala-lang", + "name" : "scala-reflect", + "version" : "2.12.10", + "artifacts" : [ + { + "name" : "scala-reflect.jar", + "hash" : "sha1:14cb7beb516cd8e07716133668c427792122c926" + } + ], + "configurations" : [ + "test" + ] + }, + { + "org" : "org.scala-lang.modules", + "name" : "scala-xml_2.12", + "version" : "1.2.0", + "artifacts" : [ + { + "name" : "scala-xml_2.12.jar", + "hash" : "sha1:5d38ac30beb8420dd395c0af447ba412158965e6" + } + ], + "configurations" : [ + "test" + ] + }, + { + "org" : "org.scalactic", + "name" : "scalactic_2.12", + "version" : "3.0.8", + "artifacts" : [ + { + "name" : "scalactic_2.12.jar", + "hash" : "sha1:b50559dfc4a691c1089f9c8812e1d6fd17f80277" + } + ], + "configurations" : [ + "test" + ] + }, + { + "org" : "org.scalatest", + "name" : "scalatest_2.12", + "version" : "3.0.8", + "artifacts" : [ + { + "name" : "scalatest_2.12.jar", + "hash" : "sha1:8493ffa579676977b810a7a9fdc23af9d3c8af7f" + } + ], + "configurations" : [ + "test" + ] + } + ] +} \ No newline at end of file diff --git a/src/sbt-test/dependencyLockCheck/pass-with-exclusion/project/build.properties b/src/sbt-test/dependencyLockCheck/pass-with-exclusion/project/build.properties new file mode 100644 index 0000000..22af262 --- /dev/null +++ b/src/sbt-test/dependencyLockCheck/pass-with-exclusion/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.7.1 diff --git a/src/sbt-test/dependencyLockCheck/pass-with-exclusion/project/plugins.sbt b/src/sbt-test/dependencyLockCheck/pass-with-exclusion/project/plugins.sbt new file mode 100644 index 0000000..85547d6 --- /dev/null +++ b/src/sbt-test/dependencyLockCheck/pass-with-exclusion/project/plugins.sbt @@ -0,0 +1,7 @@ +{ + val pluginVersion = System.getProperty("plugin.version") + if (pluginVersion == null) + throw new RuntimeException("""|The system property 'plugin.version' is not defined. + |Specify this property using the scriptedLaunchOpts -D.""".stripMargin) + else addSbtPlugin("software.purpledragon" % "sbt-dependency-lock" % pluginVersion) +} diff --git a/src/sbt-test/dependencyLockCheck/pass-with-exclusion/test b/src/sbt-test/dependencyLockCheck/pass-with-exclusion/test new file mode 100644 index 0000000..1d6433e --- /dev/null +++ b/src/sbt-test/dependencyLockCheck/pass-with-exclusion/test @@ -0,0 +1 @@ +> dependencyLockCheck \ No newline at end of file diff --git a/src/sbt-test/dependencyLockWrite/unchanged-with-exclusion/build.sbt b/src/sbt-test/dependencyLockWrite/unchanged-with-exclusion/build.sbt new file mode 100644 index 0000000..9c10975 --- /dev/null +++ b/src/sbt-test/dependencyLockWrite/unchanged-with-exclusion/build.sbt @@ -0,0 +1,9 @@ +scalaVersion := "2.12.10" + +libraryDependencies ++= Seq( + "org.apache.commons" % "commons-lang3" % "3.9", + "org.scalatest" %% "scalatest" % "3.0.8" % Test, +) + +// One ineffective filter and one for the org.scalatest organization. +dependencyLockModuleFilter := moduleFilter(organization = "org.fake") | moduleFilter(organization = "org.scalatest") \ No newline at end of file diff --git a/src/sbt-test/dependencyLockWrite/unchanged-with-exclusion/build.sbt.lock b/src/sbt-test/dependencyLockWrite/unchanged-with-exclusion/build.sbt.lock new file mode 100644 index 0000000..458593a --- /dev/null +++ b/src/sbt-test/dependencyLockWrite/unchanged-with-exclusion/build.sbt.lock @@ -0,0 +1,87 @@ +{ + "lockVersion" : 1, + "timestamp" : "2022-08-11T18:02:07.891395Z", + "configurations" : [ + "compile", + "optional", + "provided", + "runtime", + "test" + ], + "dependencies" : [ + { + "org" : "org.apache.commons", + "name" : "commons-lang3", + "version" : "3.9", + "artifacts" : [ + { + "name" : "commons-lang3.jar", + "hash" : "sha1:0122c7cee69b53ed4a7681c03d4ee4c0e2765da5" + } + ], + "configurations" : [ + "compile", + "runtime", + "test" + ] + }, + { + "org" : "org.scala-lang", + "name" : "scala-library", + "version" : "2.12.10", + "artifacts" : [ + { + "name" : "scala-library.jar", + "hash" : "sha1:3509860bc2e5b3da001ed45aca94ffbe5694dbda" + } + ], + "configurations" : [ + "compile", + "runtime", + "test" + ] + }, + { + "org" : "org.scala-lang", + "name" : "scala-reflect", + "version" : "2.12.10", + "artifacts" : [ + { + "name" : "scala-reflect.jar", + "hash" : "sha1:14cb7beb516cd8e07716133668c427792122c926" + } + ], + "configurations" : [ + "test" + ] + }, + { + "org" : "org.scala-lang.modules", + "name" : "scala-xml_2.12", + "version" : "1.2.0", + "artifacts" : [ + { + "name" : "scala-xml_2.12.jar", + "hash" : "sha1:5d38ac30beb8420dd395c0af447ba412158965e6" + } + ], + "configurations" : [ + "test" + ] + }, + { + "org" : "org.scalactic", + "name" : "scalactic_2.12", + "version" : "3.0.8", + "artifacts" : [ + { + "name" : "scalactic_2.12.jar", + "hash" : "sha1:b50559dfc4a691c1089f9c8812e1d6fd17f80277" + } + ], + "configurations" : [ + "test" + ] + } + ] +} \ No newline at end of file diff --git a/src/sbt-test/dependencyLockWrite/unchanged-with-exclusion/build.sbt.lock.orig b/src/sbt-test/dependencyLockWrite/unchanged-with-exclusion/build.sbt.lock.orig new file mode 100644 index 0000000..458593a --- /dev/null +++ b/src/sbt-test/dependencyLockWrite/unchanged-with-exclusion/build.sbt.lock.orig @@ -0,0 +1,87 @@ +{ + "lockVersion" : 1, + "timestamp" : "2022-08-11T18:02:07.891395Z", + "configurations" : [ + "compile", + "optional", + "provided", + "runtime", + "test" + ], + "dependencies" : [ + { + "org" : "org.apache.commons", + "name" : "commons-lang3", + "version" : "3.9", + "artifacts" : [ + { + "name" : "commons-lang3.jar", + "hash" : "sha1:0122c7cee69b53ed4a7681c03d4ee4c0e2765da5" + } + ], + "configurations" : [ + "compile", + "runtime", + "test" + ] + }, + { + "org" : "org.scala-lang", + "name" : "scala-library", + "version" : "2.12.10", + "artifacts" : [ + { + "name" : "scala-library.jar", + "hash" : "sha1:3509860bc2e5b3da001ed45aca94ffbe5694dbda" + } + ], + "configurations" : [ + "compile", + "runtime", + "test" + ] + }, + { + "org" : "org.scala-lang", + "name" : "scala-reflect", + "version" : "2.12.10", + "artifacts" : [ + { + "name" : "scala-reflect.jar", + "hash" : "sha1:14cb7beb516cd8e07716133668c427792122c926" + } + ], + "configurations" : [ + "test" + ] + }, + { + "org" : "org.scala-lang.modules", + "name" : "scala-xml_2.12", + "version" : "1.2.0", + "artifacts" : [ + { + "name" : "scala-xml_2.12.jar", + "hash" : "sha1:5d38ac30beb8420dd395c0af447ba412158965e6" + } + ], + "configurations" : [ + "test" + ] + }, + { + "org" : "org.scalactic", + "name" : "scalactic_2.12", + "version" : "3.0.8", + "artifacts" : [ + { + "name" : "scalactic_2.12.jar", + "hash" : "sha1:b50559dfc4a691c1089f9c8812e1d6fd17f80277" + } + ], + "configurations" : [ + "test" + ] + } + ] +} \ No newline at end of file diff --git a/src/sbt-test/dependencyLockWrite/unchanged-with-exclusion/project/plugins.sbt b/src/sbt-test/dependencyLockWrite/unchanged-with-exclusion/project/plugins.sbt new file mode 100644 index 0000000..85547d6 --- /dev/null +++ b/src/sbt-test/dependencyLockWrite/unchanged-with-exclusion/project/plugins.sbt @@ -0,0 +1,7 @@ +{ + val pluginVersion = System.getProperty("plugin.version") + if (pluginVersion == null) + throw new RuntimeException("""|The system property 'plugin.version' is not defined. + |Specify this property using the scriptedLaunchOpts -D.""".stripMargin) + else addSbtPlugin("software.purpledragon" % "sbt-dependency-lock" % pluginVersion) +} diff --git a/src/sbt-test/dependencyLockWrite/unchanged-with-exclusion/test b/src/sbt-test/dependencyLockWrite/unchanged-with-exclusion/test new file mode 100644 index 0000000..6dce62d --- /dev/null +++ b/src/sbt-test/dependencyLockWrite/unchanged-with-exclusion/test @@ -0,0 +1,3 @@ +> dependencyLockWrite +$ must-mirror build.sbt.lock build.sbt.lock.orig +> dependencyLockCheck \ No newline at end of file