Skip to content

Commit

Permalink
Refactor a bunch of stuff (#938)
Browse files Browse the repository at this point in the history
This helps prepare for WebappComponentsRunnerPlugin.

* Move webapp-runner version number to the outer build configuration
    * Use sbt-buildinfo to propagate it it to WarPackageRunnerPlugin
* Fork tests
* Move .war contents concatenation from WarPackagePlugin to
  WebappComponentsPlugin
* Prefix WarPackageRunnerPlugin keys with `war`
* Move WarPackageRunnerPlugin settings out of War configuration
* Flip webapp contents map from `src -> dest` to `dest -> src`
  • Loading branch information
earldouglas authored Sep 26, 2024
1 parent 4b92d00 commit 9df0c9d
Show file tree
Hide file tree
Showing 13 changed files with 131 additions and 190 deletions.
12 changes: 12 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,20 @@ semanticdbEnabled := true
semanticdbVersion := scalafixSemanticdb.revision
scalacOptions += "-Ywarn-unused-import"

// webapp-runner
lazy val webappRunnerVersion =
settingKey[String]("webapp-runner version")
webappRunnerVersion := "9.0.68.1"
libraryDependencies += "com.heroku" % "webapp-runner" % webappRunnerVersion.value intransitive ()

// sbt-buildinfo
enablePlugins(BuildInfoPlugin)
buildInfoKeys := Seq[BuildInfoKey](webappRunnerVersion)
buildInfoPackage := "com.earldouglas.sbt.war"

// Testing
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % "test"
Test / fork := true

// Publish to Sonatype, https://www.scala-sbt.org/release/docs/Using-Sonatype.html
credentials := List(
Expand Down
1 change: 1 addition & 0 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ libraryDependencies += "org.scala-sbt" %% "scripted-plugin" % sbtVersion.value
addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.2.1")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2")
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.12.1")
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.12.0")
32 changes: 0 additions & 32 deletions src/main/scala/com/earldouglas/sbt/war/WarPackage.scala

This file was deleted.

18 changes: 5 additions & 13 deletions src/main/scala/com/earldouglas/sbt/war/WarPackagePlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,14 @@ object WarPackagePlugin extends AutoPlugin {

override def requires = WebappComponentsPlugin

val warContents: Initialize[Task[Seq[(File, String)]]] =
Def.task {

import WebappComponentsPlugin.autoImport._

WarPackage.getWarContents(
webappResources = webappResources.value,
webappClasses = webappClasses.value,
webappLib = webappLib.value
)
}

override lazy val projectSettings: Seq[Setting[_]] = {

val packageContents: Initialize[Task[Seq[(java.io.File, String)]]] =
WebappComponentsPlugin.webappContents
.map(_.toSeq.map({ case (k, v) => (v, k) }))

val packageTaskSettings: Seq[Setting[_]] =
Defaults.packageTaskSettings(pkg, warContents)
Defaults.packageTaskSettings(pkg, packageContents)

val packageArtifactSetting: Setting[_] =
pkg / artifact := Artifact(moduleName.value, "war", "war")
Expand Down
61 changes: 28 additions & 33 deletions src/main/scala/com/earldouglas/sbt/war/WarPackageRunnerPlugin.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.earldouglas.sbt.war

import sbt.Def.Initialize
import sbt.Def.settingKey
import sbt.Def.taskKey
import sbt.Keys._
Expand All @@ -16,10 +17,12 @@ object WarPackageRunnerPlugin extends AutoPlugin {

object autoImport {
lazy val War = config("war").hide
lazy val port = settingKey[Int]("container port")
lazy val start = taskKey[Unit]("start container")
lazy val join = taskKey[Option[Int]]("join container")
lazy val stop = taskKey[Unit]("stop container")
lazy val warPort = settingKey[Int]("container port")
lazy val warStart = taskKey[Unit]("start container")
lazy val warJoin = taskKey[Option[Int]]("join container")
lazy val warStop = taskKey[Unit]("stop container")
lazy val warForkOptions =
settingKey[ForkOptions]("container fork options")
lazy val webappRunnerVersion =
settingKey[String]("webapp-runner version")
}
Expand All @@ -30,16 +33,16 @@ object WarPackageRunnerPlugin extends AutoPlugin {
override val projectConfigurations: Seq[Configuration] = Seq(War)

private lazy val containerInstance =
new AtomicReference[Option[ScalaProcess]](
Option.empty[ScalaProcess]
)
new AtomicReference[Option[ScalaProcess]](None)

private val startTask: Def.Initialize[Task[Unit]] =
private val startWar: Initialize[Task[Unit]] =
Def.task {
stopContainerInstance()

val runners: Seq[File] =
Classpaths
.managedJars(
configuration.value,
War,
classpathTypes.value,
update.value
)
Expand All @@ -51,8 +54,8 @@ object WarPackageRunnerPlugin extends AutoPlugin {
streams.value.log.info("[sbt-war] Starting server")
val process: ScalaProcess =
Fork.java.fork(
(War / forkOptions).value,
Seq("-jar", r.file.getPath(), (War / pkg).value.getPath())
warForkOptions.value,
Seq("-jar", r.file.getPath(), pkg.value.getPath())
)
containerInstance.set(Some(process))
case _ :: _ =>
Expand All @@ -67,24 +70,22 @@ object WarPackageRunnerPlugin extends AutoPlugin {
}
}

private val joinTask: Def.Initialize[Task[Option[Int]]] =
private val joinWar: Initialize[Task[Option[Int]]] =
Def.task {
containerInstance.get.map { _.exitValue }
}

private def stopContainerInstance(): Unit = {
val oldProcess =
containerInstance
.getAndSet(Option.empty[ScalaProcess])
val oldProcess = containerInstance.getAndSet(None)
oldProcess.foreach(_.destroy())
}

private val stopTask: Def.Initialize[Task[Unit]] =
private val stopWar: Initialize[Task[Unit]] =
Def.task {
stopContainerInstance()
}

private val onLoadSetting: Def.Initialize[State => State] =
private val onLoadSetting: Initialize[State => State] =
Def.setting {
(Global / onLoad).value compose { state: State =>
state.addExitHook(stopContainerInstance())
Expand All @@ -93,20 +94,14 @@ object WarPackageRunnerPlugin extends AutoPlugin {

override lazy val projectSettings =
Seq(
inConfig(War)(
Seq(
port := 8080,
start := startTask.value,
join := joinTask.value,
stop := stopTask.value,
forkOptions := ForkOptions(),
webappRunnerVersion := "9.0.68.1"
)
),
Seq(
Global / onLoad := onLoadSetting.value,
libraryDependencies +=
("com.heroku" % "webapp-runner" % (War / webappRunnerVersion).value intransitive ()) % War
)
).flatten
warPort := 8080,
warStart := startWar.value,
warJoin := joinWar.value,
warStop := stopWar.value,
warForkOptions := ForkOptions(),
webappRunnerVersion := BuildInfo.webappRunnerVersion,
Global / onLoad := onLoadSetting.value,
libraryDependencies +=
("com.heroku" % "webapp-runner" % webappRunnerVersion.value intransitive ()) % War
)
}
33 changes: 18 additions & 15 deletions src/main/scala/com/earldouglas/sbt/war/WebappComponents.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,16 @@ object WebappComponents {
* find all the files it contains.
*
* @return
* a mapping from source to destination of webapp resources
* a mapping from destination to source of webapp resources
*/
def getResources(resourcesDir: File): Map[File, String] = {
def getResources(resourcesDir: File): Map[String, File] = {
(resourcesDir ** "*").get
.filter(_.exists())
.filter(_.isFile())
.flatMap(src =>
.flatMap(file =>
IO
.relativize(resourcesDir, src)
.map(dst => src -> dst)
.relativize(resourcesDir, file)
.map(path => path -> file)
)
.toMap
}
Expand All @@ -34,38 +35,40 @@ object WebappComponents {
* directories), traverse to find all the .class files.
*
* @return
* a mapping from source to destination of .class files
* a mapping from destination to source of .class files
*/
def getClasses(classpath: Seq[File]): Map[File, String] = {
def getClasses(classpath: Seq[File]): Map[String, File] = {

val classpathDirs: Seq[File] =
classpath
.filter(_.exists())
.filter(_.isDirectory())

val classesMappings: Seq[(File, File)] =
val classesMappings: Seq[(String, File)] =
for {
classpathDir <- classpathDirs
classFile <- (classpathDir ** "*").get
if classFile.exists()
if classFile.isFile()
relativeFile <- IO.relativizeFile(classpathDir, classFile)
} yield (classFile, relativeFile)
relativePath = s"WEB-INF/classes/${relativeFile.getPath()}"
} yield (relativePath, classFile)

classesMappings
.map({ case (src, dst) => src -> dst.getPath() })
.toMap
classesMappings.toMap
}

/** Given a classpath (potentially with both .jar files and classes
* directories), traverse to find all the .jar files.
*
* @return
* a mapping from source to destination of .jar files
* a mapping from destination to source of .jar files
*/
def getLib(classpath: Seq[File]): Map[File, String] = {
def getLib(classpath: Seq[File]): Map[String, File] = {
classpath
.filter(f => f.exists())
.filter(f => f.isFile())
.filter(f => f.getName().endsWith(".jar"))
.map(src => src -> src.getName())
.map(file => s"WEB-INF/lib/${file.getName()}" -> file)
.toMap
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ object WebappComponentsPlugin extends AutoPlugin {
object autoImport {

lazy val webappResources =
taskKey[Map[File, String]]("webapp resources")
taskKey[Map[String, File]]("webapp resources")

lazy val webappClasses =
taskKey[Map[File, String]]("webapp classes")
taskKey[Map[String, File]]("webapp classes")

lazy val webappLib =
taskKey[Map[File, String]]("webapp lib")
taskKey[Map[String, File]]("webapp lib")
}

import autoImport._
Expand All @@ -32,16 +32,16 @@ object WebappComponentsPlugin extends AutoPlugin {
val webappResourcesDir: Initialize[File] =
Def.setting((Compile / sourceDirectory).value / "webapp")

val webappResourcesTask: Initialize[Task[Map[File, String]]] =
val webappResourcesTask: Initialize[Task[Map[String, File]]] =
Def.task(WebappComponents.getResources(webappResourcesDir.value))

val classpathFiles: Initialize[Task[Seq[File]]] =
Def.task((Runtime / fullClasspath).value.files)

val webappClassesTask: Initialize[Task[Map[File, String]]] =
val webappClassesTask: Initialize[Task[Map[String, File]]] =
Def.task(WebappComponents.getClasses(classpathFiles.value))

val webappLibTask: Initialize[Task[Map[File, String]]] =
val webappLibTask: Initialize[Task[Map[String, File]]] =
Def.task(WebappComponents.getLib(classpathFiles.value))

Seq(
Expand All @@ -50,4 +50,11 @@ object WebappComponentsPlugin extends AutoPlugin {
webappLib := webappLibTask.value
)
}

lazy val webappContents: Initialize[Task[Map[String, File]]] =
Def.task {
webappResources.value ++
webappClasses.value ++
webappLib.value
}
}
24 changes: 14 additions & 10 deletions src/main/scala/com/earldouglas/xwp/WebappPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ object WebappPlugin extends AutoPlugin {
val resourceFiles: Set[File] =
WebappComponents
.getResources(webappResourcesDir)
.filterNot(x => x._1.isDirectory())
.map(_._1)
.map(_._2)
.filter(_.isFile())
.toSet

cacheify(
Expand Down Expand Up @@ -156,8 +156,12 @@ object WebappPlugin extends AutoPlugin {
(Runtime / fullClasspath).value
.map(_.data)

val webappClasses: Map[File, String] =
WebappComponents.getClasses(classpath)
val webappClasses: Map[String, File] =
WebappComponents
.getClasses(classpath)
.map({ case (path, file) =>
(path.replaceAll("^WEB-INF/classes/", ""), file)
})

// copy this project's classes directly to WEB-INF/classes
def classesAsClasses(): Set[File] = {
Expand All @@ -166,12 +170,12 @@ object WebappPlugin extends AutoPlugin {
"classes",
{ in =>
webappClasses
.find { case (src, dest) => src == in }
.map { case (src, dest) => webInfDir / "classes" / dest }
.find { case (_, file) => file == in }
.map { case (path, _) => webInfDir / "classes" / path }
},
webappClasses
.filter { case (src, dest) => !src.isDirectory }
.map { case (src, dest) => src }
.filter { case (_, file) => file.isFile() }
.map { case (_, file) => file }
.toSet,
taskStreams
)
Expand All @@ -186,7 +190,7 @@ object WebappPlugin extends AutoPlugin {
val outputJar = webappLibDir / jarFilename

Compat.jar(
sources = webappClasses,
sources = webappClasses.map({ case (k, v) => (v, k) }).toMap,
outputJar = outputJar,
manifest = new Manifest
)
Expand All @@ -204,7 +208,7 @@ object WebappPlugin extends AutoPlugin {
cacheify(
"lib-deps",
{ in => Some(webappTarget / "WEB-INF" / "lib" / in.getName()) },
WebappComponents.getLib(classpath).keySet,
WebappComponents.getLib(classpath).values.toSet,
taskStreams
)

Expand Down
Loading

0 comments on commit 9df0c9d

Please sign in to comment.