Allow customization of remote cache artifacts

This commit is contained in:
Maksim Ochenashko 2020-06-20 14:05:41 +03:00
parent 9f9bb9daa2
commit 0c07fa1851
2 changed files with 162 additions and 51 deletions

View File

@ -345,6 +345,8 @@ object Keys {
val remoteCacheId = taskKey[String]("Unique identifier for the remote cache.") val remoteCacheId = taskKey[String]("Unique identifier for the remote cache.")
val remoteCacheProjectId = taskKey[ModuleID]("ModuleID used for remote cache JARs.") val remoteCacheProjectId = taskKey[ModuleID]("ModuleID used for remote cache JARs.")
val remoteCacheIdCandidates = taskKey[Seq[String]]("Remote cache ids to pull.") val remoteCacheIdCandidates = taskKey[Seq[String]]("Remote cache ids to pull.")
val remoteCacheArtifacts = taskKey[Seq[RemoteCacheArtifact]]("Remote cache artifact definitions.")
val remoteCacheArtifact = taskKey[RemoteCacheArtifact]("The remote cache artifact definition.")
val pullRemoteCache = taskKey[Unit]("Retrieve remote cache.") val pullRemoteCache = taskKey[Unit]("Retrieve remote cache.")
val pushRemoteCache = taskKey[Unit]("Push remote cache to the cache server.") val pushRemoteCache = taskKey[Unit]("Push remote cache to the cache server.")
val pushRemoteCacheArtifact = settingKey[Boolean]("Enables publishing an artifact to remote cache.") val pushRemoteCacheArtifact = settingKey[Boolean]("Enables publishing an artifact to remote cache.")

View File

@ -25,11 +25,12 @@ import sbt.util.Logger
object RemoteCache { object RemoteCache {
final val cachedCompileClassifier = "cached-compile" final val cachedCompileClassifier = "cached-compile"
final val cachedTestClasifier = "cached-test" final val cachedTestClassifier = "cached-test"
final val commitLength = 10 final val commitLength = 10
def gitCommitId: String = def gitCommitId: String =
scala.sys.process.Process("git rev-parse HEAD").!!.trim.take(commitLength) scala.sys.process.Process("git rev-parse HEAD").!!.trim.take(commitLength)
def gitCommitIds(n: Int): List[String] = def gitCommitIds(n: Int): List[String] =
scala.sys.process scala.sys.process
.Process("git log -n " + n.toString + " --format=%H") .Process("git log -n " + n.toString + " --format=%H")
@ -41,7 +42,7 @@ object RemoteCache {
lazy val globalSettings: Seq[Def.Setting[_]] = Seq( lazy val globalSettings: Seq[Def.Setting[_]] = Seq(
remoteCacheId := gitCommitId, remoteCacheId := gitCommitId,
remoteCacheIdCandidates := gitCommitIds(5), remoteCacheIdCandidates := gitCommitIds(5),
pushRemoteCacheTo :== None, pushRemoteCacheTo :== None
) )
lazy val projectSettings: Seq[Def.Setting[_]] = (Seq( lazy val projectSettings: Seq[Def.Setting[_]] = (Seq(
@ -54,12 +55,21 @@ object RemoteCache {
ModuleID(o, m, v).cross(c) ModuleID(o, m, v).cross(c)
}, },
pushRemoteCacheConfiguration / publishMavenStyle := true, pushRemoteCacheConfiguration / publishMavenStyle := true,
pushRemoteCacheConfiguration / artifacts := artifactDefs(defaultArtifactTasks).value, pushRemoteCacheConfiguration / packagedArtifacts := Def.taskDyn {
pushRemoteCacheConfiguration / packagedArtifacts := packaged(defaultArtifactTasks).value, val artifacts = (pushRemoteCacheConfiguration / remoteCacheArtifacts).value
artifacts
.map(a => a.packaged.map(file => (a.artifact, file)))
.join
.apply(_.join.map(_.toMap))
}.value,
pushRemoteCacheConfiguration / remoteCacheArtifacts := {
enabledOnly(remoteCacheArtifact.toSettingKey, defaultArtifactTasks).apply(_.join).value
},
Compile / packageCache / pushRemoteCacheArtifact := true, Compile / packageCache / pushRemoteCacheArtifact := true,
Test / packageCache / pushRemoteCacheArtifact := true, Test / packageCache / pushRemoteCacheArtifact := true,
Compile / packageCache / artifact := Artifact(moduleName.value, cachedCompileClassifier), Compile / packageCache / artifact := Artifact(moduleName.value, cachedCompileClassifier),
Test / packageCache / artifact := Artifact(moduleName.value, cachedTestClasifier), Test / packageCache / artifact := Artifact(moduleName.value, cachedTestClassifier),
remoteCachePom / pushRemoteCacheArtifact := true, remoteCachePom / pushRemoteCacheArtifact := true,
pushRemoteCacheConfiguration := { pushRemoteCacheConfiguration := {
Classpaths.publishConfig( Classpaths.publishConfig(
@ -75,19 +85,17 @@ object RemoteCache {
) )
}, },
pullRemoteCache := { pullRemoteCache := {
val s = streams.value val log = streams.value.log
val smi = scalaModuleInfo.value val smi = scalaModuleInfo.value
val dr = (pullRemoteCache / dependencyResolution).value val dr = (pullRemoteCache / dependencyResolution).value
val is = (pushRemoteCache / ivySbt).value val is = (pushRemoteCache / ivySbt).value
val t = crossTarget.value / "cache-download" val t = crossTarget.value / "cache-download"
val p = remoteCacheProjectId.value val p = remoteCacheProjectId.value
val ids = remoteCacheIdCandidates.value val ids = remoteCacheIdCandidates.value
val compileAf = (Compile / compileAnalysisFile).value val artifacts = (pushRemoteCacheConfiguration / remoteCacheArtifacts).value
val compileOutput = (Compile / classDirectory).value val applicable = artifacts.filterNot(RemoteCacheArtifact.isPomArtifact)
val testAf = (Test / compileAnalysisFile).value val classifiers = applicable.flatMap(_.artifact.classifier).toVector
val testOutput = (Test / classDirectory).value
val testStreams = (Test / test / streams).value
val testResult = Defaults.succeededFile(testStreams.cacheDirectory)
var found = false var found = false
ids foreach { ids foreach {
id: String => id: String =>
@ -95,19 +103,25 @@ object RemoteCache {
val modId = p.withRevision(v) val modId = p.withRevision(v)
if (found) () if (found) ()
else else
pullFromMavenRepo0(modId, smi, is, dr, t, s.log) match { pullFromMavenRepo0(modId, classifiers, smi, is, dr, t, log) match {
case Right(xs0) => case Right(xs0) =>
val xs = xs0.distinct val jars = xs0.distinct
xs.find(_.toString.endsWith(s"$v-$cachedCompileClassifier.jar")) foreach {
jar: File => applicable.foreach { art =>
extractCache(jar, compileOutput, compileAf, None) val classifier = art.artifact.classifier
}
xs.find(_.toString.endsWith(s"$v-$cachedTestClasifier.jar")) foreach { jar: File => findJar(classifier, v, jars) match {
extractCache(jar, testOutput, testAf, Some(testResult)) case Some(jar) =>
extractJar(art, jar)
log.info(s"remote cache artifact extracted for $classifier")
case None =>
log.info(s"remote cache artifact not found for $classifier")
}
} }
found = true found = true
case Left(unresolvedWarning) => case Left(unresolvedWarning) =>
s.log.info(s"remote cache not found for ${v}") log.info(s"remote cache not found for ${v}")
} }
} }
}, },
@ -118,7 +132,9 @@ object RemoteCache {
publisher.makePomFile((pushRemoteCache / ivyModule).value, config, s.log) publisher.makePomFile((pushRemoteCache / ivyModule).value, config, s.log)
config.file.get config.file.get
}, },
remoteCachePom / packagedArtifact := ((makePom / artifact).value -> remoteCachePom.value), remoteCachePom / remoteCacheArtifact := {
RemoteCacheArtifact.Pom((makePom / artifact).value, remoteCachePom)
}
) ++ inTask(pushRemoteCache)( ) ++ inTask(pushRemoteCache)(
Seq( Seq(
ivyConfiguration := { ivyConfiguration := {
@ -146,7 +162,7 @@ object RemoteCache {
val s = streams.value val s = streams.value
val config = pushRemoteCacheConfiguration.value val config = pushRemoteCacheConfiguration.value
IvyActions.publish(ivyModule.value, config, s.log) IvyActions.publish(ivyModule.value, config, s.log)
} tag (Tags.Publish, Tags.Network)).value, } tag (Tags.Publish, Tags.Network)).value
) )
) ++ inTask(pullRemoteCache)( ) ++ inTask(pullRemoteCache)(
Seq( Seq(
@ -157,10 +173,12 @@ object RemoteCache {
.withResolvers(rs) .withResolvers(rs)
} }
) )
) ++ inConfig(Compile)(packageCacheSettings) ) ++ inConfig(Compile)(packageCacheSettings(compileArtifact(Compile, cachedCompileClassifier)))
++ inConfig(Test)(packageCacheSettings)) ++ inConfig(Test)(packageCacheSettings(testArtifact(Test, cachedTestClassifier))))
def packageCacheSettings: Seq[Def.Setting[_]] = private def packageCacheSettings[A <: RemoteCacheArtifact](
cacheArtifact: Def.Initialize[Task[A]]
): Seq[Def.Setting[_]] =
inTask(packageCache)( inTask(packageCache)(
Seq( Seq(
packageCache.in(Defaults.TaskZero) := { packageCache.in(Defaults.TaskZero) := {
@ -180,15 +198,42 @@ object RemoteCache {
// } // }
artp artp
}, },
remoteCacheArtifact := cacheArtifact.value,
packagedArtifact := (artifact.value -> packageCache.value), packagedArtifact := (artifact.value -> packageCache.value),
artifactPath := Defaults.artifactPathSetting(artifact).value, artifactPath := Defaults.artifactPathSetting(artifact).value
) )
) )
def compileArtifact(
configuration: Configuration,
classifier: String
): Def.Initialize[Task[RemoteCacheArtifact.Compile]] = Def.task {
RemoteCacheArtifact.Compile(
Artifact(moduleName.value, classifier),
configuration / packageCache,
(configuration / classDirectory).value,
(configuration / compileAnalysisFile).value
)
}
def testArtifact(
configuration: Configuration,
classifier: String
): Def.Initialize[Task[RemoteCacheArtifact.Test]] = Def.task {
RemoteCacheArtifact.Test(
Artifact(moduleName.value, classifier),
configuration / packageCache,
(configuration / classDirectory).value,
(configuration / compileAnalysisFile).value,
Defaults.succeededFile((configuration / test / streams).value.cacheDirectory)
)
}
private def toVersion(v: String): String = s"0.0.0-$v" private def toVersion(v: String): String = s"0.0.0-$v"
private def pullFromMavenRepo0( private def pullFromMavenRepo0(
modId: ModuleID, modId: ModuleID,
classifiers: Vector[String],
smi: Option[ScalaModuleInfo], smi: Option[ScalaModuleInfo],
is: IvySbt, is: IvySbt,
dr: DependencyResolution, dr: DependencyResolution,
@ -202,46 +247,68 @@ object RemoteCache {
.withScalaModuleInfo(smi) .withScalaModuleInfo(smi)
.withDependencies(deps) .withDependencies(deps)
} }
val deps = val deps = classifiers.map(modId.classifier)
Vector(modId.classifier(cachedCompileClassifier), modId.classifier(cachedTestClasifier))
val mconfig = dummyModule(deps) val mconfig = dummyModule(deps)
val m = new is.Module(mconfig) val m = new is.Module(mconfig)
dr.retrieve(m, cacheDir, log) dr.retrieve(m, cacheDir, log)
} }
private def extractCache( private def findJar(classifier: Option[String], ver: String, jars: Vector[File]): Option[File] = {
jar: File, val suffix = classifier.fold(ver)(c => s"$ver-$c.jar")
output: File, jars.find(_.toString.endsWith(suffix))
analysisFile: File, }
testResult: Option[File]
private def extractJar(cacheArtifact: RemoteCacheArtifact, jar: File): Unit =
cacheArtifact match {
case RemoteCacheArtifact.Compile(_, _, extractDirectory, analysisFile) =>
extractCache(jar, extractDirectory, preserveLastModified = true) { output =>
extractAnalysis(output, analysisFile)
}
case RemoteCacheArtifact.Test(_, _, extractDirectory, analysisFile, testResult) =>
extractCache(jar, extractDirectory, preserveLastModified = true) { output =>
extractAnalysis(output, analysisFile)
extractTestResult(output, testResult)
}
case RemoteCacheArtifact.Custom(_, _, extractDirectory, preserveLastModified) =>
extractCache(jar, extractDirectory, preserveLastModified)(_ => ())
case RemoteCacheArtifact.Pom(_, _) =>
()
}
private def extractCache(jar: File, output: File, preserveLastModified: Boolean)(
processOutput: File => Unit
): Unit = { ): Unit = {
IO.delete(output) IO.delete(output)
IO.unzip(jar, output) IO.unzip(jar, output, preserveLastModified = preserveLastModified)
processOutput(output)
IO.delete(output / "META-INF")
}
private def extractAnalysis(output: File, analysisFile: File): Unit = {
val metaDir = output / "META-INF" val metaDir = output / "META-INF"
val expandedAnalysis = metaDir / "inc_compile.zip" val expandedAnalysis = metaDir / "inc_compile.zip"
if (expandedAnalysis.exists) { if (expandedAnalysis.exists) {
IO.move(expandedAnalysis, analysisFile) IO.move(expandedAnalysis, analysisFile)
} }
IO.delete(metaDir) IO.delete(metaDir)
// testResult match { }
// case Some(r) =>
// val expandedTestResult = output / "META-INF" / "succeeded_tests" private def extractTestResult(output: File, testResult: File): Unit = {
// if (expandedTestResult.exists) { //val expandedTestResult = output / "META-INF" / "succeeded_tests"
// IO.move(expandedTestResult, r) //if (expandedTestResult.exists) {
// } // IO.move(expandedTestResult, testResult)
// case _ => () //}
// }
()
} }
private def defaultArtifactTasks: Seq[TaskKey[File]] = private def defaultArtifactTasks: Seq[TaskKey[File]] =
Seq(remoteCachePom, Compile / packageCache, Test / packageCache) Seq(
remoteCachePom,
private def packaged(pkgTasks: Seq[TaskKey[File]]): Def.Initialize[Task[Map[Artifact, File]]] = Compile / packageCache,
enabledOnly(packagedArtifact.toSettingKey, pkgTasks) apply (_.join.map(_.toMap)) Test / packageCache
)
private def artifactDefs(pkgTasks: Seq[TaskKey[File]]): Def.Initialize[Seq[Artifact]] =
enabledOnly(artifact, pkgTasks)
private def enabledOnly[A]( private def enabledOnly[A](
key: SettingKey[A], key: SettingKey[A],
@ -252,3 +319,45 @@ object RemoteCache {
case (a, true) => a case (a, true) => a
}) })
} }
sealed trait RemoteCacheArtifact {
def artifact: Artifact
def packaged: TaskKey[File]
}
object RemoteCacheArtifact {
final case class Pom(
artifact: Artifact,
packaged: TaskKey[File]
) extends RemoteCacheArtifact
final case class Compile(
artifact: Artifact,
packaged: TaskKey[File],
extractDirectory: File,
analysisFile: File
) extends RemoteCacheArtifact
final case class Test(
artifact: Artifact,
packaged: TaskKey[File],
extractDirectory: File,
analysisFile: File,
testResult: File
) extends RemoteCacheArtifact
final case class Custom(
artifact: Artifact,
packaged: TaskKey[File],
extractDirectory: File,
preserveLastModified: Boolean
) extends RemoteCacheArtifact
def isPomArtifact(artifact: RemoteCacheArtifact): Boolean =
artifact match {
case _: RemoteCacheArtifact.Pom => true
case _ => false
}
}