aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/xyz.driver.sbt/SbtSettings.scala
diff options
context:
space:
mode:
authorJakob Odersky <jakob@driver.xyz>2018-06-11 17:54:44 -0700
committerJakob Odersky <jakob@driver.xyz>2018-06-12 22:12:08 -0700
commitbe29a4964284f94d624ba25e4078fc382e80ac9a (patch)
tree958939d487aeb5bdb2e83c75349995b79ea06c93 /src/main/scala/xyz.driver.sbt/SbtSettings.scala
parentd511f3c109becb8a8e52e20826f9cb991d46bf9c (diff)
downloadsbt-settings-jo/plugins.tar.gz
sbt-settings-jo/plugins.tar.bz2
sbt-settings-jo/plugins.zip
Refactor settings to use autopluginsv2.0.0-RC1jo/plugins
**Overview and motivation** This consolidates settings that were previously implemented as functions and/or implicit conversions into a suite of sbt autoplugins. The rationale is that this is well-defined pattern by sbt and allows easy build introspection with standard sbt functionality (for example, `sbt plugins` will list all active plugins in a build). Furthermore, it makes it very easy to disable certain features when required, such as removing linting during development. **Migration from current version** All features from the previous version should still be provided by the changes proposed here. The migration path is quite straight-forward: - Replace `project.driverService(name)` with `project.enablePlugins(Service)` (same goes for libraries) and make sure the project's name corresponds to the service's name - Linting, which was previously enabled by adding `lintingSettings` and `formatSettings` to a project, is automatically enabled. It may be removed by disabling the plugin: `project.dsiablePlugin(Linting)` All tasks and settings provided by sbt-settings should remain the same. **Additional features** An additional feature is that versioning is now handled the same way between libraries and services; that is, the version is derived from the latest git tag. Since services may be deployed from the same tag mutliple times, it is required that versions can be explicitly set to include additional information, such as a build number from a CI system. This was previously done interactively, using sbt's `set` command: ``` export TAG="$(sbt -no-colors version | tail -1 | awk '{ print $2 }').$TRAVIS_BUILD_NUMBER" sbt "set version := \"$TAG\"" docker:publishLocal ``` While this approach works, it has the downsides of requiring mutliple sbt invocations. The changes proposed in this PR will read the version from a VERSION environment variable first, defaulting to git if unavailable. Therefore, the additional sbt invocation can be removed with a CI script similar to the following: ``` export VERSION="$(git describe).$TRAVIS_BUILD_NUMBER"" sbt docker:publishLocal // use version in other steps ``` Using an autoplugin-based approach may also make it easier to cross-compile projects to ScalaJS and Native in the future, as support for them is built into sbt-crossproject.
Diffstat (limited to 'src/main/scala/xyz.driver.sbt/SbtSettings.scala')
-rw-r--r--src/main/scala/xyz.driver.sbt/SbtSettings.scala390
1 files changed, 0 insertions, 390 deletions
diff --git a/src/main/scala/xyz.driver.sbt/SbtSettings.scala b/src/main/scala/xyz.driver.sbt/SbtSettings.scala
deleted file mode 100644
index 96ee267..0000000
--- a/src/main/scala/xyz.driver.sbt/SbtSettings.scala
+++ /dev/null
@@ -1,390 +0,0 @@
-package xyz.driver.sbt
-
-import com.lucidchart.sbt.scalafmt.ScalafmtCorePlugin.autoImport._
-import com.typesafe.sbt.SbtGit.git
-import com.typesafe.sbt.SbtNativePackager.autoImport._
-import com.typesafe.sbt.packager.archetypes._
-import com.typesafe.sbt.packager.docker.Cmd
-import com.typesafe.sbt.packager.docker.DockerPlugin.autoImport._
-import com.typesafe.sbt.{GitBranchPrompt, GitVersioning}
-import org.scalastyle.sbt.ScalastylePlugin.autoImport._
-import sbt.Keys._
-import sbt.{Project, State, _}
-import sbtassembly.AssemblyKeys._
-import sbtassembly._
-import sbtbuildinfo.BuildInfoPlugin
-import sbtbuildinfo.BuildInfoPlugin.autoImport.{BuildInfoKey, BuildInfoOption, _}
-import sbtrelease.ReleasePlugin.autoImport.ReleaseKeys._
-import sbtrelease.ReleasePlugin.autoImport.ReleaseTransformations._
-import sbtrelease.ReleasePlugin.autoImport._
-import sbtrelease.{Version, _}
-import scala.collection.JavaConverters._
-import IntegrationTestPackaging.autoImport.IntegrationTest
-
-// we hide the existing definition for setReleaseVersion to replace it with our own
-import sbtrelease.ReleaseStateTransformations.{setReleaseVersion => recordReleaseVersion, inquireVersions => _}
-
-/**
- * @see https://engineering.sharethrough.com/blog/2015/09/23/capturing-common-config-with-an-sbt-parent-plugin/
- */
-object SbtSettings extends AutoPlugin {
-
- val JMX_PORT = 8686
-
- object autoImport {
- lazy val formatSettings = {
- val generateScalafmtConfTask = Def.task {
- val scalafmtConfStream = getClass.getClassLoader.getResourceAsStream("scalafmt.conf")
- val formatConfFile = file(".scalafmt.conf")
- IO.write(formatConfFile, IO.readBytes(scalafmtConfStream))
- formatConfFile
- }
-
- Seq(
- scalafmtConfig := generateScalafmtConfTask.value,
- scalafmt in Compile := {
- (scalafmt in Compile).dependsOn(scalafmt in Test).value
- },
- // scalafmt::test -> tests scalafmt format in src/main + src/test (added behavior)
- test in scalafmt in Compile := {
- (test in scalafmt in Compile).dependsOn(test in scalafmt in Test).value
- },
- test in Test := {
- (test in scalafmt in Compile).value
- (test in Test).value
- }
- )
- }
-
- lazy val testScalastyle = taskKey[Unit]("testScalastyle")
-
- lazy val scalastyleSettings = {
- val generateScalastyleConfTask = Def.task {
- val stream = getClass.getClassLoader.getResourceAsStream("scalastyle-config.xml")
- val styleFile = file("scalastyle-config.xml")
- IO.write(styleFile, IO.readBytes(stream))
- Seq(styleFile)
- }
- Seq(
- resourceGenerators in Compile += generateScalastyleConfTask.taskValue,
- scalastyleConfig := file("scalastyle-config.xml"),
- testScalastyle := scalastyle.in(Compile).toTask("").value,
- testScalastyle in (Test, test) := {
- generateScalastyleConfTask.value
- (testScalastyle in (Test, test)).value
- },
- testExecution in (Test, test) := {
- generateScalastyleConfTask.value
- (testScalastyle in Compile).value
- (testScalastyle in Test).value
- (testExecution in (Test, test)).value
- }
- )
- }
-
- val scalacLintingSettings = Seq(
- scalacOptions ++= {
- Seq(
- "-Xlint:_,-unused,-missing-interpolator",
- "-Ywarn-numeric-widen",
- "-Ywarn-dead-code",
- "-Ywarn-unused:_,-explicits,-implicits"
- )
- },
- compile in Compile := {
- val compiled = (compile in Compile).value
- val problems = compiled.readSourceInfos().getAllSourceInfos.asScala.flatMap {
- case (_, info) =>
- info.getReportedProblems
- }
-
- val deprecationsOnly = problems.forall { problem =>
- problem.message().contains("is deprecated")
- }
-
- if (!deprecationsOnly) sys.error("Fatal warnings: some warnings other than deprecations were found.")
- compiled
- }
- )
-
- lazy val lintingSettings = scalacLintingSettings ++ scalastyleSettings
-
- lazy val repositoriesSettings: Seq[Setting[_]] = {
- Seq(
- resolvers += "releases" at "https://drivergrp.jfrog.io/drivergrp/releases",
- resolvers += "snapshots" at "https://drivergrp.jfrog.io/drivergrp/snapshots"
- )
- }
-
- lazy val publicationSettings: Seq[Setting[_]] = Seq(
- publishTo := {
- val jfrog = "https://drivergrp.jfrog.io/drivergrp/"
-
- if (isSnapshot.value) Some("snapshots" at jfrog + "snapshots;build.timestamp=" + new java.util.Date().getTime)
- else Some("releases" at jfrog + "releases")
- }
- )
-
- private def setVersionOnly(selectVersion: Versions => String): ReleaseStep = { st: State =>
- val vs = st
- .get(ReleaseKeys.versions)
- .getOrElse(sys.error("No versions are set! Was this release part executed before inquireVersions?"))
- val selected = selectVersion(vs)
-
- st.log.info("Setting version to '%s'." format selected)
- val useGlobal = Project.extract(st).get(releaseUseGlobalVersion)
-
- reapply(Seq(
- if (useGlobal) version in ThisBuild := selected else version := selected
- ),
- st)
- }
-
- lazy val setReleaseVersion: ReleaseStep = setVersionOnly(_._1)
-
- // Remove the prompt for next version
- lazy val inquireVersions: ReleaseStep = { st: State =>
- val extracted = Project.extract(st)
-
- val useDefs = st.get(useDefaults).getOrElse(false)
- val currentV = extracted.get(version)
-
- val (_, releaseFunc) = extracted.runTask(releaseVersion, st)
- val suggestedReleaseV = releaseFunc(currentV)
-
- // flatten the Option[Option[String]] as the get returns an Option, and the value inside is an Option
- val releaseV =
- readVersion(suggestedReleaseV, "Release version [%s] : ", useDefs, st.get(commandLineReleaseVersion).flatten)
- val nextV = releaseV
-
- st.put(versions, (releaseV, nextV))
-
- }
-
- def ServiceReleaseProcess = {
- Seq[ReleaseStep](
- checkSnapshotDependencies,
- inquireVersions,
- runTest,
- recordReleaseVersion, // set release version and persistent in version.sbt
- commitReleaseVersion, // performs the initial git checks
- tagRelease,
- pushChanges // also checks that an upstream branch is properly configured
- )
- }
-
- def LibraryReleaseProcess = {
- Seq[ReleaseStep](
- checkSnapshotDependencies,
- inquireVersions,
- setReleaseVersion,
- runTest,
- tagRelease,
- publishArtifacts,
- pushChanges // also checks that an upstream branch is properly configured
- )
- }
-
- def releaseSettings(releaseProcessSteps: Seq[ReleaseStep]): Seq[Setting[_]] = {
-
- val showReleaseVersion = taskKey[String]("the future version once releaseNextVersion has been applied to it")
- Seq(
- releaseCrossBuild := true,
- releaseIgnoreUntrackedFiles := true,
- // Check http://blog.byjean.eu/2015/07/10/painless-release-with-sbt.html for details
- releaseVersionBump := sbtrelease.Version.Bump.Bugfix,
- releaseVersion := {
- case ver @ snapshotVersion if snapshotVersion.endsWith("-SNAPSHOT") =>
- Version(ver).map(_.withoutQualifier.string).getOrElse(versionFormatError)
- case ver =>
- Version(ver).map(_.bumpBugfix.withoutQualifier.string).getOrElse(versionFormatError)
- },
- showReleaseVersion := {
- releaseVersion.value(version.value)
- },
- releaseProcess := releaseProcessSteps
- )
- }
-
- implicit class driverConfigurations(project: Project) {
-
- def gitPluginConfiguration: Project = {
- val VersionRegex = "v([0-9]+.[0-9]+.[0-9]+)-?(.*)?".r
-
- project
- .enablePlugins(GitVersioning, GitBranchPrompt)
- .settings(
- git.useGitDescribe := true,
- git.baseVersion := "0.0.0",
- git.gitTagToVersionNumber := {
- case VersionRegex(v, "SNAPSHOT") => // There are not committed changes at tagged commit
- val ver = Version(v)
- .map(_.withoutQualifier)
- .map(_.bump(sbtrelease.Version.Bump.Bugfix).string)
- .getOrElse(versionFormatError)
-
- Some(s"$ver-SNAPSHOT")
-
- case VersionRegex(v, "") =>
- Some(v)
-
- case VersionRegex(v, s) => // Commit is ahead of the last tagged commit
- val ver = Version(v)
- .map(_.withoutQualifier)
- .map(_.bump(sbtrelease.Version.Bump.Bugfix).string)
- .getOrElse(versionFormatError)
-
- Some(s"$ver-$s-SNAPSHOT")
-
- case _ => None
- }
- )
- }
-
- def buildInfoConfiguration(packageName: String = "xyz.driver"): Project = {
- project
- .enablePlugins(BuildInfoPlugin)
- .settings(
- buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion, git.gitHeadCommit),
- buildInfoPackage := packageName,
- buildInfoOptions += BuildInfoOption.BuildTime
- )
- }
-
- def packagingConfiguration: Project = {
- project
- .settings( // for assembly plugin
- test in assembly := {},
- assemblyMergeStrategy in assembly := {
- case PathList("org", "slf4j", "impl", xs @ _*) => MergeStrategy.first
- case "logback.xml" => MergeStrategy.first
- case strategy: String =>
- val oldStrategy = (assemblyMergeStrategy in assembly).value
- oldStrategy(strategy)
- }
- )
- }
-
- def dockerConfiguration(imageName: String,
- repository: String,
- exposedPorts: Seq[Int],
- baseImage: String = "java:8",
- customCommands: List[String] = List.empty[String],
- aggregateSubprojects: Boolean = false): Project = {
- import com.typesafe.sbt.packager.Keys._
-
- project
- .enablePlugins(JavaAppPackaging)
- .settings(
- // Settings reference http://www.scala-sbt.org/sbt-native-packager/formats/docker.html
- packageName in Docker := imageName,
- version in Docker := version.value.stripSuffix("-SNAPSHOT"),
- dockerRepository := Some(repository),
- maintainer := "Driver Inc. <info@driver.xyz>",
- dockerUpdateLatest := true, // to automatic update the latest tag
- dockerExposedPorts := exposedPorts,
- dockerBaseImage := baseImage,
- daemonUser in Docker := "root",
- dockerCommands := dockerCommands.value
- .flatMap { // @see http://blog.codacy.com/2015/07/16/dockerizing-scala/
- case cmd @ Cmd("FROM", _) => cmd :: customCommands.map(customCommand => Cmd("RUN", customCommand))
- case other => List(other)
- },
- aggregate in Docker := aggregateSubprojects // to include subprojects
- )
-
- // And then you can run "sbt docker:publish"
- }
-
- def deploymentConfiguration(imageName: String,
- exposedPorts: Seq[Int] = Seq(8080),
- clusterName: String = "sand-uw1a-1",
- clusterZone: String = "us-west1-a",
- gCloudProject: String = "driverinc-sandbox",
- baseImage: String = "java:8",
- dockerCustomCommands: List[String] = List.empty[String],
- aggregateSubprojects: Boolean = false) = {
-
- val repositoryName = "gcr.io/" + gCloudProject
-
- val keytoolCommand =
- "keytool -import -alias driverincInternal -keystore $JAVA_HOME/jre/lib/security/cacerts " +
- s"-file /etc/$imageName/ssl/issuing_ca -storepass changeit -noprompt"
-
- // If issuing_ca exists, import it into the internal default ca store
- val importTrustStoreCommand =
- s"if [ -f /etc/$imageName/ssl/issuing_ca ] ; then " + keytoolCommand + "; else echo \"No truststore customization.\"; fi"
-
- val dockerCommands = dockerCustomCommands // :+ importTrustStoreCommand
-
- val allExposedPorts = exposedPorts ++ Seq(JMX_PORT)
-
- dockerConfiguration(imageName, repositoryName, allExposedPorts, baseImage, dockerCommands, aggregateSubprojects)
- .settings(NativePackagerKeys.bashScriptExtraDefines += importTrustStoreCommand)
- .settings(NativePackagerKeys.bashScriptExtraDefines ++= Seq(
- """addJava "-Dcom.sun.management.jmxremote"""",
- s"""addJava "-Dcom.sun.management.jmxremote.port=$JMX_PORT"""",
- """addJava "-Dcom.sun.management.jmxremote.local.only=false"""",
- """addJava "-Dcom.sun.management.jmxremote.authenticate=false"""",
- """addJava "-Dcom.sun.management.jmxremote.ssl=false""""
- ))
- }
-
- def driverLibrary(libraryName: String): Project = {
- project
- .settings(name := libraryName)
- .gitPluginConfiguration
- .settings(repositoriesSettings ++ publicationSettings ++ releaseSettings(LibraryReleaseProcess))
- }
-
- def driverService(appName: String,
- exposedPorts: Seq[Int] = Seq(8080),
- clusterName: String = "sand-uw1a-1",
- clusterZone: String = "us-west1-a",
- gCloudProject: String = "driverinc-sandbox",
- baseImage: String = "java:8",
- dockerCustomCommands: List[String] = List.empty[String],
- aggregateSubprojects: Boolean = false): Project = {
- project
- .settings(name := appName)
- .settings(repositoriesSettings ++ releaseSettings(ServiceReleaseProcess))
- .buildInfoConfiguration()
- .deploymentConfiguration(appName,
- exposedPorts,
- clusterName,
- clusterZone,
- gCloudProject,
- baseImage,
- dockerCustomCommands,
- aggregateSubprojects)
- }
- }
- }
-
- val scalacDefaultOptions = Seq("-unchecked", "-deprecation", "-feature", "-Xlint", "-encoding", "utf8")
-
- val scalacLanguageFeatures = Seq(
- "-language:higherKinds",
- "-language:implicitConversions",
- "-language:postfixOps",
- "-language:reflectiveCalls"
- )
-
- override def trigger: PluginTrigger = allRequirements
- override def projectSettings: Seq[Setting[_]] = Seq(
- organization := "xyz.driver",
- crossScalaVersions := List("2.12.4"),
- scalaVersion := crossScalaVersions.value.last,
- scalacOptions := (scalacDefaultOptions ++ scalacLanguageFeatures),
- scalacOptions in (Compile, console) := (scalacDefaultOptions ++ scalacLanguageFeatures),
- scalacOptions in (Compile, consoleQuick) := (scalacDefaultOptions ++ scalacLanguageFeatures),
- scalacOptions in (Compile, consoleProject) := (scalacDefaultOptions ++ scalacLanguageFeatures),
- version := {
- // Sbt release versioning based on git given double -SNAPSHOT suffix
- // if current commit is not tagged AND there are uncommitted changes (e.g., some file is modified),
- // this setting fixes that, by just removing double -SNAPSHOT if it happened somehow
- Option(version.value).map(vv => vv.replaceAll("-SNAPSHOT-SNAPSHOT", "-SNAPSHOT")).getOrElse("0.0.0")
- },
- fork := true
- )
-}