Skip to content
This repository was archived by the owner on May 22, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ object ModificationCheck {
/**
* libraryDependencies in all projects and configurations
*/
def allLibraryDependencies(state: State): (State, Seq[ModuleID]) = {
def allLibraryDependencies(state: State): (State, Map[String, Seq[ModuleID]]) = {
val extracted = Project.extract(state)
extracted.runTask(SbtLockKeys.collectLockModuleIDs, state)
}
Expand Down
22 changes: 14 additions & 8 deletions src/main/scala/com/github/tkawachi/sbtlock/SbtLock.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ package com.github.tkawachi.sbtlock
import sbt._
import java.nio.charset.Charset

import sbt.Configurations.{ Optional, Provided }

object SbtLock {
private[sbtlock] val DEFAULT_LOCK_FILE_NAME = "lock.sbt"
private[sbtlock] val DEFAULT_SCOPES = Seq("compile", "runtime", "test", "provided")
private[sbtlock] val DEPS_HASH_PREFIX = "// LIBRARY_DEPENDENCIES_HASH "

case class Artifact(organization: String, name: String) {
Expand All @@ -13,23 +16,26 @@ object SbtLock {
}

def doLock(
allModules: Seq[ModuleID],
allModulesByScope: Map[String, Seq[ModuleID]],
depsHash: String,
lockFile: File,
log: Logger): File = {

val moduleLines = allModules
.filter(m => m.organization != "org.scala-lang")
.map(mod =>
s""""${mod.organization}" % "${mod.name}" % "${mod.extraAttributes.getOrElse("version", mod.revision)}"""")
.distinct
.sorted
val moduleLines = allModulesByScope.flatMap {
case (scope, allModules) =>
allModules
.filter(_.organization != "org.scala-lang")
.map {
mod =>
s""""${mod.organization}" % "${mod.name}" % "${mod.extraAttributes.getOrElse("version", mod.revision)}" % "$scope" """
}.distinct.sorted
}

val dependencyOverrides =
s"""// DON'T EDIT THIS FILE.
|// This file is auto generated by ${BuildInfo.name} ${BuildInfo.version}.
|// ${BuildInfo._url}
|dependencyOverrides in Compile ++= {
|dependencyOverrides in ThisBuild ++= {
| if (!(sbtLockHashIsUpToDate in ThisBuild).value && sbtLockIgnoreOverridesOnStaleHash.value) {
| ${Compat.dependencyOverridesType}.empty
| } else {
Expand Down
4 changes: 3 additions & 1 deletion src/main/scala/com/github/tkawachi/sbtlock/SbtLockKeys.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ object SbtLockKeys {
val sbtLockLockFile = settingKey[String]("A version locking file name. Must ends with `.sbt`.")
val lock = taskKey[File]("Create a version locking file for sbt-lock")
val unlock = taskKey[Unit]("Delete a version locking file for sbt-lock")
val collectLockModuleIDs = taskKey[Seq[ModuleID]]("Collect ModuleIDs to lock")
val collectLockModuleIDs = taskKey[Map[String, Seq[ModuleID]]]("Collect ModuleIDs to lock")
val checkLockUpdate = taskKey[Unit]("Check whether a version locking file needs update")
val sbtLockHashIsUpToDate = settingKey[Boolean]("lock.sbt file is up to date with libraryDependencies in the current project. " +
"(sbtLockHashIsUpToDate in ThisProject) does NOT consider the libraryDependencies of project dependencies! " +
"(sbtLockHashIsUpToDate in ThisBuild) considers the libraryDependencies across all projects.")
val sbtLockIgnoreOverridesOnStaleHash = settingKey[Boolean]("When libraryDependencies are changed, ignores the lock.sbt file.")
val sbtLockScopes = settingKey[Seq[String]]("all scope lock.")

}
88 changes: 68 additions & 20 deletions src/main/scala/com/github/tkawachi/sbtlock/SbtLockPlugin.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.github.tkawachi.sbtlock

import sbt._
import com.github.tkawachi.sbtlock.SbtLock.DEFAULT_LOCK_FILE_NAME
import sbt.Keys._
import SbtLock.DEFAULT_LOCK_FILE_NAME
import sbt._

import scala.collection.mutable

object SbtLockPlugin extends AutoPlugin {

Expand All @@ -15,6 +17,7 @@ object SbtLockPlugin extends AutoPlugin {
val checkLockUpdate: TaskKey[Unit] = SbtLockKeys.checkLockUpdate
val sbtLockHashIsUpToDate: SettingKey[Boolean] = SbtLockKeys.sbtLockHashIsUpToDate
val sbtLockIgnoreOverridesOnStaleHash: SettingKey[Boolean] = SbtLockKeys.sbtLockIgnoreOverridesOnStaleHash
val sbtLockScopes: SettingKey[Seq[String]] = SbtLockKeys.sbtLockScopes
}

import autoImport._
Expand All @@ -27,29 +30,68 @@ object SbtLockPlugin extends AutoPlugin {

override lazy val projectSettings = Seq(
SbtLockKeys.collectLockModuleIDs := {
val classpath: Seq[Attributed[File]] =
Classpaths.managedJars(Compile, classpathTypes.value, update.value)

val logger = streams.value.log

classpath.flatMap { entry =>
for {
art: Artifact <- entry.get(artifact.key)
mod: ModuleID <- entry.get(moduleID.key)
if !(excludeDependencies in lock).value.exists { exclude =>
exclude.organization == mod.organization && exclude.name == "*" && { logger.debug(s"""[Skipped] "${mod.organization}" % "${mod.name}" % "${mod.revision}" because of exclude by organization"""); true } ||
exclude.organization == mod.organization && exclude.name == mod.name && { logger.debug(s"""[Skipped] "${mod.organization}" % "${mod.name}" % "${mod.revision}" because of exclude by organization/name"""); true }
def getModules(config: Configuration) = {
val classpath: Seq[Attributed[File]] =
Classpaths.managedJars(config, classpathTypes.value, update.value)

classpath.flatMap { entry =>
for {
art: Artifact <- entry.get(artifact.key)
mod: ModuleID <- entry.get(moduleID.key)
if !(excludeDependencies in lock).value.exists { exclude =>
exclude.organization == mod.organization && exclude.name == "*" && {
logger.debug(s"""[Skipped] "${mod.organization}" % "${mod.name}" % "${mod.revision}" because of exclude by organization""");
true
} ||
exclude.organization == mod.organization && exclude.name == mod.name && {
logger.debug(s"""[Skipped] "${mod.organization}" % "${mod.name}" % "${mod.revision}" because of exclude by organization/name""");
true
}
}
} yield {
logger.debug(
s"""[Lock] "${mod.organization}" % "${mod.name}" % "${mod.revision}"""")
mod
}
} yield {
logger.debug(
s"""[Lock] "${mod.organization}" % "${mod.name}" % "${mod.revision}"""")
mod
}
}

val allScopes = SbtLockKeys.sbtLockScopes.value
// Configurations.default is sort from lower to higher scopes
val allConfigModules = Configurations.default.collect {
case config if allScopes.contains(config.name) => (config, getModules(config))
}

val allModules = allConfigModules.flatMap(_._2).distinct
if (allConfigModules.isEmpty) {
logger.debug(s"""[Lock] no scope match: $allScopes""")
Map.empty
} else {
// init map on 'natural' order
val moduleByScope: mutable.Map[String, Seq[ModuleID]] = mutable.LinkedHashMap[String, Seq[ModuleID]]()
allConfigModules.foreach {
case (config, _) => moduleByScope.put(config.name, Seq[ModuleID]())
}

allModules.map {
m =>
val lowerScope = allConfigModules.collectFirst {
case (c, ms) if ms.contains(m) => c.name
}.getOrElse(Configurations.default.last.name) // fallback should never append ;)

moduleByScope.put(lowerScope, moduleByScope(lowerScope) :+ m)
(m, lowerScope)
}
moduleByScope.toMap
}
},
lock := {
val lockFile = new File(baseDirectory.value, sbtLockLockFile.value)
val allModules = SbtLockKeys.collectLockModuleIDs.value
val depsHash = ModificationCheck.hash((libraryDependencies in Compile).value)
val depsHash = ModificationCheck.hash((libraryDependencies in Test).value)
SbtLock.doLock(allModules, depsHash, lockFile, streams.value.log)
},
unlock := {
Expand All @@ -63,7 +105,9 @@ object SbtLockPlugin extends AutoPlugin {
sbtLockHashIsUpToDate := {
val lockFile = new File(baseDirectory.value, sbtLockLockFile.value)
val maybeLockedHash = SbtLock.readDepsHash(lockFile)
def currentHash: String = ModificationCheck.hash((libraryDependencies in Compile).value)

def currentHash: String = ModificationCheck.hash((libraryDependencies in Test).value)

maybeLockedHash == Some(currentHash)
},
(sbtLockHashIsUpToDate in ThisBuild) := {
Expand All @@ -89,7 +133,7 @@ object SbtLockPlugin extends AutoPlugin {
checkLockUpdate := {
val lockFile = new File(baseDirectory.value, sbtLockLockFile.value)
val currentHash =
ModificationCheck.hash((libraryDependencies in Compile).value)
ModificationCheck.hash((libraryDependencies in Test).value)
val logger = streams.value.log
val ignoreOnStaleHash = sbtLockIgnoreOverridesOnStaleHash.value
val projectName = name.value
Expand Down Expand Up @@ -127,7 +171,11 @@ object SbtLockPlugin extends AutoPlugin {
})

override val globalSettings = Seq(
onLoad in Global ~= { _ compose SbtLock.checkDepUpdates },
onLoad in Global ~= {
_ compose SbtLock.checkDepUpdates
},
sbtLockLockFile := DEFAULT_LOCK_FILE_NAME,
excludeDependencies in lock := Seq.empty)
excludeDependencies in lock := Seq.empty,
sbtLockScopes := SbtLock.DEFAULT_SCOPES)

}
11 changes: 10 additions & 1 deletion src/sbt-test/sbt-lock/force/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ scalaVersion := "2.11.8"
libraryDependencies ++= Seq(
"org.yaml" % "snakeyaml" % "1.15",
"com.typesafe.play" %% "play" % "2.5.6", // use a guava 16
"com.google.guava" % "guava" % "15.0" force () //force older version of guava
"com.google.guava" % "guava" % "15.0" force (), //force older version of guava
"org.junit.jupiter" % "junit-jupiter-engine" % "5.5.2" % "test"
)

def check(module: Seq[String], dependencies: Set[ModuleID]): Boolean = {
Expand All @@ -24,3 +25,11 @@ InputKey[Unit]("checkExistsDependency") := {
exists,
s"${module.mkString(":")} should exist in dependencyOverrides: ${(dependencyOverrides in Compile).value}")
}

InputKey[Unit]("checkExistsTestDependency") := {
val module = complete.Parsers.spaceDelimited("").parsed
val exists = check(module, (dependencyOverrides in Test).value.toSet)
assert(
exists,
s"${module.mkString(":")} should exist in dependencyOverrides: ${(dependencyOverrides in Test).value}")
}
1 change: 1 addition & 0 deletions src/sbt-test/sbt-lock/force/test
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
> lock
> reload
> checkExistsDependency "com.google.guava" "guava" "15.0"
> checkExistsTestDependency "org.junit.jupiter" "junit-jupiter-engine" "5.5.2"