Skip to content

Commit

Permalink
#28 Add sbt-release
Browse files Browse the repository at this point in the history
  • Loading branch information
To-om committed Nov 28, 2016
1 parent 066fe5e commit 6fff788
Show file tree
Hide file tree
Showing 9 changed files with 288 additions and 40 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ RUNNING_PID
.settings
tmp
.classpath
.cache-macros

# IntelliJ IDEA
/.idea
Expand All @@ -31,3 +32,4 @@ tmp
__pycache__/
*.py[cod]
*$py.class
/bin/
103 changes: 69 additions & 34 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -16,35 +16,12 @@ lazy val main = (project in file("."))
.dependsOn(thehiveBackend, thehiveMetrics, thehiveMisp)
.aggregate(thehiveBackend, thehiveMetrics, thehiveMisp)
.settings(aggregate in Docker := false)
.settings(PublishToBinTray.settings: _*)

// Front-end //

val frontendDev = inputKey[Unit]("Build front-end in dev")

frontendDev := {
val s = streams.value
s.log.info("Preparing front-end for dev (grunt wiredep)")
Process("grunt" :: "wiredep" :: Nil, baseDirectory.value / "ui") ! s.log
}

run := {
(run in Compile).evaluated
frontendDev.evaluated
}

val frontendFiles = taskKey[Seq[(File, String)]]("Front-end files")

frontendFiles := {
val s = streams.value
s.log.info("Preparing front-end for prod ...")
s.log.info("npm install")
Process("npm" :: "install" :: Nil, baseDirectory.value / "ui") ! s.log
s.log.info("bower install")
Process("bower" :: "install" :: Nil, baseDirectory.value / "ui") ! s.log
s.log.info("grunt build")
Process("grunt" :: "build" :: Nil, baseDirectory.value / "ui") ! s.log
val dir = baseDirectory.value / "ui" / "dist"
(dir.***) pair rebase(dir, "ui")
frontendDev.value
}

mappings in packageBin in Assets ++= frontendFiles.value
Expand All @@ -63,20 +40,42 @@ mappings in Universal ++= {
(dir.***) pair relativeTo(dir.getParentFile)
}

// BINTRAY //
publish := BinTray.publish(
(packageBin in Universal).value,
bintrayEnsureCredentials.value,
bintrayOrganization.value,
bintrayRepository.value,
bintrayPackage.value,
version.value,
sLog.value)
// Release //
import ReleaseTransformations._

import Release._

bintrayOrganization := Some("cert-bdf")

bintrayRepository := "thehive"

publish := {
(publishLocal in Docker).value
PublishToBinTray.publishRelease.value
PublishToBinTray.publishLatest.value
// ()
}

releaseProcess := Seq[ReleaseStep](
checkUncommittedChanges,
checkSnapshotDependencies,
getVersionFromBranch,
runTest,
releaseMerge,
checkoutMaster,
setReleaseVersion,
setReleaseUIVersion,
generateChangelog,
commitChanges,
tagRelease,
publishArtifacts,
checkoutDevelop,
setNextVersion,
setNextUIVersion,
commitChanges,
//commitNextVersion,
pushChanges)

// DOCKER //

dockerBaseImage := "elasticsearch:2.3"
Expand Down Expand Up @@ -104,3 +103,39 @@ dockerCommands := (dockerCommands.value.head +:
"rm -rf /var/lib/apt/lists/*") +:
Cmd("EXPOSE", "9000") +:
dockerCommands.value.tail)

// Scalariform //
import scalariform.formatter.preferences._
import com.typesafe.sbt.SbtScalariform
import com.typesafe.sbt.SbtScalariform.ScalariformKeys

SbtScalariform.defaultScalariformSettings

ScalariformKeys.preferences := ScalariformKeys.preferences.value
.setPreference(AlignParameters, false)
// .setPreference(FirstParameterOnNewline, Force)
.setPreference(AlignArguments, true)
// .setPreference(FirstArgumentOnNewline, true)
.setPreference(AlignSingleLineCaseStatements, true)
.setPreference(AlignSingleLineCaseStatements.MaxArrowIndent, 60)
.setPreference(CompactControlReadability, true)
.setPreference(CompactStringConcatenation, false)
.setPreference(DoubleIndentClassDeclaration, true)
// .setPreference(DoubleIndentMethodDeclaration, true)
.setPreference(FormatXml, true)
.setPreference(IndentLocalDefs, false)
.setPreference(IndentPackageBlocks, false)
.setPreference(IndentSpaces, 2)
.setPreference(IndentWithTabs, false)
.setPreference(MultilineScaladocCommentsStartOnFirstLine, false)
// .setPreference(NewlineAtEndOfFile, true)
.setPreference(PlaceScaladocAsterisksBeneathSecondAsterisk, false)
.setPreference(PreserveSpaceBeforeArguments, false)
// .setPreference(PreserveDanglingCloseParenthesis, false)
.setPreference(RewriteArrowSymbols, true)
.setPreference(SpaceBeforeColon, false)
// .setPreference(SpaceBeforeContextColon, false)
.setPreference(SpaceInsideBrackets, false)
.setPreference(SpaceInsideParentheses, false)
.setPreference(SpacesWithinPatternBinders, true)
.setPreference(SpacesAroundMultiImports, true)
41 changes: 37 additions & 4 deletions project/Bintray.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,51 @@ import scala.concurrent.duration.Duration
import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global

import sbt.Logger
import sbt._
import sbt.Keys._

import dispatch.{ Http, FunctionHandler }

import bintry.Client
import bintray.BintrayCredentials

object BinTray {
import bintray.BintrayKeys.{ bintrayEnsureCredentials, bintrayOrganization, bintrayRepository, bintrayPackage }
import com.typesafe.sbt.packager.universal.UniversalPlugin.autoImport.Universal

object PublishToBinTray {
val publishRelease = taskKey[Unit]("Publish binary in bintray")
val publishLatest = taskKey[Unit]("Publish latest binary in bintray")

lazy val settings = Seq(
publishRelease := {
val file = (packageBin in Universal).value
publish(file.getName,
file,
bintrayEnsureCredentials.value,
bintrayOrganization.value,
bintrayRepository.value,
bintrayPackage.value,
version.value,
sLog.value)
},
publishLatest := {
val file = (packageBin in Universal).value
val latestName = file.getName.replace(version.value, "latest")
if (latestName == file.getName)
sLog.value.warn(s"Latest package name can't be built using package name [$latestName], publish aborted")
else
publish(latestName,
file,
bintrayEnsureCredentials.value,
bintrayOrganization.value,
bintrayRepository.value,
bintrayPackage.value,
version.value,
sLog.value)
})

private def asStatusAndBody = new FunctionHandler({ r => (r.getStatusCode, r.getResponseBody) })

def publish(file: File, credential: BintrayCredentials, org: Option[String], repoName: String, packageName: String, version: String, log: Logger) = {
def publish(filename: String, file: File, credential: BintrayCredentials, org: Option[String], repoName: String, packageName: String, version: String, log: Logger) = {
val BintrayCredentials(user, key) = credential
val owner: String = org.getOrElse(user)
val client: Client = Client(user, key, new Http())
Expand Down
1 change: 0 additions & 1 deletion project/BuildSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ object BasicSettings extends AutoPlugin {
override def projectSettings = Seq(
organization := "org.cert-bdf",
licenses += "AGPL-V3" -> url("https://www.gnu.org/licenses/agpl-3.0.html"),
version := "2.9.1-SNAPSHOT",
resolvers += Resolver.bintrayRepo("cert-bdf", "elastic4play"),
scalaVersion := Dependencies.scalaVersion,
scalacOptions ++= Seq(
Expand Down
35 changes: 35 additions & 0 deletions project/FrontEnd.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import sbt._
import sbt.Keys._

object FrontEnd extends AutoPlugin {

object autoImport {
val frontendDev = taskKey[Unit]("Build front-end in dev")
val frontendFiles = taskKey[Seq[(File, String)]]("Front-end files")
}

import autoImport._

override def trigger = allRequirements

override def projectSettings = Seq[Setting[_]](
frontendDev := {
val s = streams.value
s.log.info("Preparing front-end for dev (grunt wiredep)")
Process("grunt" :: "wiredep" :: Nil, baseDirectory.value / "ui") ! s.log
()
},

frontendFiles := {
val s = streams.value
s.log.info("Preparing front-end for prod ...")
s.log.info("npm install")
Process("npm" :: "install" :: Nil, baseDirectory.value / "ui") ! s.log
s.log.info("bower install")
Process("bower" :: "install" :: Nil, baseDirectory.value / "ui") ! s.log
s.log.info("grunt build")
Process("grunt" :: "build" :: Nil, baseDirectory.value / "ui") ! s.log
val dir = baseDirectory.value / "ui" / "dist"
(dir.***) pair rebase(dir, "ui")
})
}
127 changes: 127 additions & 0 deletions project/Release.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import sbt._
import sbt.Keys.baseDirectory
import sbt.{ Project, Extracted, State, IO, File, StateOps }
import sbtrelease.ReleasePlugin.autoImport._
import sbtrelease.ReleaseStateTransformations.readVersion
import sbtrelease.{ Vcs, Versions }
import play.api.libs.json._

object Release {
val releaseVersionUIFile = settingKey[File]("The json package file to write the version to")
val changelogFile = settingKey[File]("Changelog file")

lazy val settings = Seq(
releaseVersionUIFile := baseDirectory.value / "ui" / "package.json",
changelogFile := baseDirectory.value / "CHANGELOG.md")

val releaseNumberExtractor = "release/(.*)".r

def getReleaseNumber(branchName: String): String =
branchName match {
case releaseNumberExtractor(version) version
case _ sys.error(s"The current branch ($branchName) is not a release branch (must starts with release/)")
}

def vcs(st: State): Vcs = {
Project.extract(st).get(releaseVcs).getOrElse(sys.error("Aborting release. Working directory is not a repository of a recognized VCS."))
}

def checkUncommittedChanges: ReleaseStep = { st: State =>
vcs(st)
.cmd("fetch") ! st.log
val ret = vcs(st)
.cmd("diff-index", "--quiet", "HEAD", "--") ! st.log
if (ret > 0) sys.error("Aborting release. Your working directory is not clean.")
st
}

lazy val releaseMerge: ReleaseStep = { st: State
val version = st.get(ReleaseKeys.versions).getOrElse(sys.error("Version not set ?!"))._1
val ret = vcs(st).cmd("flow", "release", "finish", version, "-m", s"Release $version") ! st.log
if (ret > 0) sys.error("Release finish failed")
st
}

lazy val checkoutMaster: ReleaseStep = { st: State =>
vcs(st).cmd("checkout", "master") ! st.log
st
}

lazy val checkoutDevelop: ReleaseStep = { st: State =>
vcs(st).cmd("checkout", "develop") ! st.log
st
}

def currentBranch(st: State): String = {
vcs(st)
.cmd("rev-parse", "--abbrev-ref", "HEAD").!!.trim
}

lazy val setReleaseUIVersion: ReleaseStep = setUIVersion(_._1)
lazy val setNextUIVersion: ReleaseStep = setUIVersion(_._2)

def setUIVersion(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 version = selectVersion(vs)
//val version = st.get(ReleaseKeys.versions).getOrElse(sys.error("Version not set ?!"))._1
val packageFile = new File("ui/package.json")
val pkgJson = Json.parse(IO.read(packageFile))

pkgJson.transform(
(__ \ 'version).json.update(
__.read[JsString].map(_ => JsString(version)))) match {
case JsSuccess(newPkgJson, _) => IO.write(packageFile, Json.prettyPrint(newPkgJson))
case JsError(error) => sys.error(s"Invalid package file format: $error")
}
st
}

lazy val getVersionFromBranch: ReleaseStep = { st: State
val extracted = Project.extract(st)
val useDefs = st.get(ReleaseKeys.useDefaults).getOrElse(false)

val currentV = getReleaseNumber(currentBranch(st))
val releaseFunc = extracted.get(releaseVersion)
val suggestedReleaseV = releaseFunc(currentV)

st.log.info(s"Release version : $currentV")
val nextFunc = extracted.get(releaseNextVersion)
val suggestedNextV = nextFunc(currentV)
//flatten the Option[Option[String]] as the get returns an Option, and the value inside is an Option
val nextV = readVersion(suggestedNextV, "Next version [%s] : ", useDefs, st.get(ReleaseKeys.commandLineNextVersion).flatten)
st.put(ReleaseKeys.versions, (currentV, nextV))
}

lazy val generateChangelog: ReleaseStep = { st: State =>
val changeLogFile = Project.extract(st).get(changelogFile)
st.log.info("Generating changelog in ")
val properties = new java.util.Properties
val credentialsFile = new File("~/.github/credentials")
IO.load(properties, credentialsFile)
val token = Option(properties.getProperty("token")).fold("")(t => s"-t $t")
s"github_changelog_generator $token" ! st.log
st
}

lazy val commitChanges: ReleaseStep = { st: State =>
val base = vcs(st).baseDir
Seq(releaseVersionFile, releaseVersionUIFile, changelogFile)
.foreach { f =>
val file = Project.extract(st).get(f)
val relativePath = IO.relativize(base, file).getOrElse(sys.error(s"Version file [$file] is outside of this VCS repository with base directory [$base]!"))
vcs(st).add(relativePath) !! st.log
}
val status = (vcs(st).status.!!).trim

val newState = if (status.nonEmpty) {
val (state, msg) = Project.extract(st).runTask(releaseCommitMessage, st)
vcs(state).commit(msg) ! st.log
state
} else {
// nothing to commit. this happens if the version.sbt file hasn't changed.
st
}
newState
}

}
13 changes: 13 additions & 0 deletions project/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
libraryDependencies += "com.typesafe.play" %% "play-json" % "2.4.8"

scalacOptions in ThisBuild ++= Seq(
"-encoding", "UTF-8",
"-deprecation", // warning and location for usages of deprecated APIs
"-feature", // warning and location for usages of features that should be imported explicitly
"-unchecked", // additional warnings where generated code depends on assumptions
"-Xlint", // recommended additional warnings
"-Ywarn-adapted-args", // Warn if an argument list is modified to match the receiver
"-Ywarn-value-discard", // Warn when non-Unit expression results are unused
"-Ywarn-inaccessible",
"-Ywarn-dead-code"
)
Loading

0 comments on commit 6fff788

Please sign in to comment.