Skip to content

Commit 6fff788

Browse files
committed
#28 Add sbt-release
1 parent 066fe5e commit 6fff788

9 files changed

+288
-40
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ RUNNING_PID
2020
.settings
2121
tmp
2222
.classpath
23+
.cache-macros
2324

2425
# IntelliJ IDEA
2526
/.idea
@@ -31,3 +32,4 @@ tmp
3132
__pycache__/
3233
*.py[cod]
3334
*$py.class
35+
/bin/

build.sbt

+69-34
Original file line numberDiff line numberDiff line change
@@ -16,35 +16,12 @@ lazy val main = (project in file("."))
1616
.dependsOn(thehiveBackend, thehiveMetrics, thehiveMisp)
1717
.aggregate(thehiveBackend, thehiveMetrics, thehiveMisp)
1818
.settings(aggregate in Docker := false)
19+
.settings(PublishToBinTray.settings: _*)
1920

2021
// Front-end //
21-
22-
val frontendDev = inputKey[Unit]("Build front-end in dev")
23-
24-
frontendDev := {
25-
val s = streams.value
26-
s.log.info("Preparing front-end for dev (grunt wiredep)")
27-
Process("grunt" :: "wiredep" :: Nil, baseDirectory.value / "ui") ! s.log
28-
}
29-
3022
run := {
3123
(run in Compile).evaluated
32-
frontendDev.evaluated
33-
}
34-
35-
val frontendFiles = taskKey[Seq[(File, String)]]("Front-end files")
36-
37-
frontendFiles := {
38-
val s = streams.value
39-
s.log.info("Preparing front-end for prod ...")
40-
s.log.info("npm install")
41-
Process("npm" :: "install" :: Nil, baseDirectory.value / "ui") ! s.log
42-
s.log.info("bower install")
43-
Process("bower" :: "install" :: Nil, baseDirectory.value / "ui") ! s.log
44-
s.log.info("grunt build")
45-
Process("grunt" :: "build" :: Nil, baseDirectory.value / "ui") ! s.log
46-
val dir = baseDirectory.value / "ui" / "dist"
47-
(dir.***) pair rebase(dir, "ui")
24+
frontendDev.value
4825
}
4926

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

66-
// BINTRAY //
67-
publish := BinTray.publish(
68-
(packageBin in Universal).value,
69-
bintrayEnsureCredentials.value,
70-
bintrayOrganization.value,
71-
bintrayRepository.value,
72-
bintrayPackage.value,
73-
version.value,
74-
sLog.value)
43+
// Release //
44+
import ReleaseTransformations._
45+
46+
import Release._
7547

7648
bintrayOrganization := Some("cert-bdf")
7749

7850
bintrayRepository := "thehive"
7951

52+
publish := {
53+
(publishLocal in Docker).value
54+
PublishToBinTray.publishRelease.value
55+
PublishToBinTray.publishLatest.value
56+
// ()
57+
}
58+
59+
releaseProcess := Seq[ReleaseStep](
60+
checkUncommittedChanges,
61+
checkSnapshotDependencies,
62+
getVersionFromBranch,
63+
runTest,
64+
releaseMerge,
65+
checkoutMaster,
66+
setReleaseVersion,
67+
setReleaseUIVersion,
68+
generateChangelog,
69+
commitChanges,
70+
tagRelease,
71+
publishArtifacts,
72+
checkoutDevelop,
73+
setNextVersion,
74+
setNextUIVersion,
75+
commitChanges,
76+
//commitNextVersion,
77+
pushChanges)
78+
8079
// DOCKER //
8180

8281
dockerBaseImage := "elasticsearch:2.3"
@@ -104,3 +103,39 @@ dockerCommands := (dockerCommands.value.head +:
104103
"rm -rf /var/lib/apt/lists/*") +:
105104
Cmd("EXPOSE", "9000") +:
106105
dockerCommands.value.tail)
106+
107+
// Scalariform //
108+
import scalariform.formatter.preferences._
109+
import com.typesafe.sbt.SbtScalariform
110+
import com.typesafe.sbt.SbtScalariform.ScalariformKeys
111+
112+
SbtScalariform.defaultScalariformSettings
113+
114+
ScalariformKeys.preferences := ScalariformKeys.preferences.value
115+
.setPreference(AlignParameters, false)
116+
// .setPreference(FirstParameterOnNewline, Force)
117+
.setPreference(AlignArguments, true)
118+
// .setPreference(FirstArgumentOnNewline, true)
119+
.setPreference(AlignSingleLineCaseStatements, true)
120+
.setPreference(AlignSingleLineCaseStatements.MaxArrowIndent, 60)
121+
.setPreference(CompactControlReadability, true)
122+
.setPreference(CompactStringConcatenation, false)
123+
.setPreference(DoubleIndentClassDeclaration, true)
124+
// .setPreference(DoubleIndentMethodDeclaration, true)
125+
.setPreference(FormatXml, true)
126+
.setPreference(IndentLocalDefs, false)
127+
.setPreference(IndentPackageBlocks, false)
128+
.setPreference(IndentSpaces, 2)
129+
.setPreference(IndentWithTabs, false)
130+
.setPreference(MultilineScaladocCommentsStartOnFirstLine, false)
131+
// .setPreference(NewlineAtEndOfFile, true)
132+
.setPreference(PlaceScaladocAsterisksBeneathSecondAsterisk, false)
133+
.setPreference(PreserveSpaceBeforeArguments, false)
134+
// .setPreference(PreserveDanglingCloseParenthesis, false)
135+
.setPreference(RewriteArrowSymbols, true)
136+
.setPreference(SpaceBeforeColon, false)
137+
// .setPreference(SpaceBeforeContextColon, false)
138+
.setPreference(SpaceInsideBrackets, false)
139+
.setPreference(SpaceInsideParentheses, false)
140+
.setPreference(SpacesWithinPatternBinders, true)
141+
.setPreference(SpacesAroundMultiImports, true)

project/Bintray.scala

+37-4
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,51 @@ import scala.concurrent.duration.Duration
44
import scala.concurrent.Await
55
import scala.concurrent.ExecutionContext.Implicits.global
66

7-
import sbt.Logger
7+
import sbt._
8+
import sbt.Keys._
89

910
import dispatch.{ Http, FunctionHandler }
1011

1112
import bintry.Client
1213
import bintray.BintrayCredentials
13-
14-
object BinTray {
14+
import bintray.BintrayKeys.{ bintrayEnsureCredentials, bintrayOrganization, bintrayRepository, bintrayPackage }
15+
import com.typesafe.sbt.packager.universal.UniversalPlugin.autoImport.Universal
16+
17+
object PublishToBinTray {
18+
val publishRelease = taskKey[Unit]("Publish binary in bintray")
19+
val publishLatest = taskKey[Unit]("Publish latest binary in bintray")
20+
21+
lazy val settings = Seq(
22+
publishRelease := {
23+
val file = (packageBin in Universal).value
24+
publish(file.getName,
25+
file,
26+
bintrayEnsureCredentials.value,
27+
bintrayOrganization.value,
28+
bintrayRepository.value,
29+
bintrayPackage.value,
30+
version.value,
31+
sLog.value)
32+
},
33+
publishLatest := {
34+
val file = (packageBin in Universal).value
35+
val latestName = file.getName.replace(version.value, "latest")
36+
if (latestName == file.getName)
37+
sLog.value.warn(s"Latest package name can't be built using package name [$latestName], publish aborted")
38+
else
39+
publish(latestName,
40+
file,
41+
bintrayEnsureCredentials.value,
42+
bintrayOrganization.value,
43+
bintrayRepository.value,
44+
bintrayPackage.value,
45+
version.value,
46+
sLog.value)
47+
})
1548

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

18-
def publish(file: File, credential: BintrayCredentials, org: Option[String], repoName: String, packageName: String, version: String, log: Logger) = {
51+
def publish(filename: String, file: File, credential: BintrayCredentials, org: Option[String], repoName: String, packageName: String, version: String, log: Logger) = {
1952
val BintrayCredentials(user, key) = credential
2053
val owner: String = org.getOrElse(user)
2154
val client: Client = Client(user, key, new Http())

project/BuildSettings.scala

-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ object BasicSettings extends AutoPlugin {
77
override def projectSettings = Seq(
88
organization := "org.cert-bdf",
99
licenses += "AGPL-V3" -> url("https://www.gnu.org/licenses/agpl-3.0.html"),
10-
version := "2.9.1-SNAPSHOT",
1110
resolvers += Resolver.bintrayRepo("cert-bdf", "elastic4play"),
1211
scalaVersion := Dependencies.scalaVersion,
1312
scalacOptions ++= Seq(

project/FrontEnd.scala

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import sbt._
2+
import sbt.Keys._
3+
4+
object FrontEnd extends AutoPlugin {
5+
6+
object autoImport {
7+
val frontendDev = taskKey[Unit]("Build front-end in dev")
8+
val frontendFiles = taskKey[Seq[(File, String)]]("Front-end files")
9+
}
10+
11+
import autoImport._
12+
13+
override def trigger = allRequirements
14+
15+
override def projectSettings = Seq[Setting[_]](
16+
frontendDev := {
17+
val s = streams.value
18+
s.log.info("Preparing front-end for dev (grunt wiredep)")
19+
Process("grunt" :: "wiredep" :: Nil, baseDirectory.value / "ui") ! s.log
20+
()
21+
},
22+
23+
frontendFiles := {
24+
val s = streams.value
25+
s.log.info("Preparing front-end for prod ...")
26+
s.log.info("npm install")
27+
Process("npm" :: "install" :: Nil, baseDirectory.value / "ui") ! s.log
28+
s.log.info("bower install")
29+
Process("bower" :: "install" :: Nil, baseDirectory.value / "ui") ! s.log
30+
s.log.info("grunt build")
31+
Process("grunt" :: "build" :: Nil, baseDirectory.value / "ui") ! s.log
32+
val dir = baseDirectory.value / "ui" / "dist"
33+
(dir.***) pair rebase(dir, "ui")
34+
})
35+
}

project/Release.scala

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import sbt._
2+
import sbt.Keys.baseDirectory
3+
import sbt.{ Project, Extracted, State, IO, File, StateOps }
4+
import sbtrelease.ReleasePlugin.autoImport._
5+
import sbtrelease.ReleaseStateTransformations.readVersion
6+
import sbtrelease.{ Vcs, Versions }
7+
import play.api.libs.json._
8+
9+
object Release {
10+
val releaseVersionUIFile = settingKey[File]("The json package file to write the version to")
11+
val changelogFile = settingKey[File]("Changelog file")
12+
13+
lazy val settings = Seq(
14+
releaseVersionUIFile := baseDirectory.value / "ui" / "package.json",
15+
changelogFile := baseDirectory.value / "CHANGELOG.md")
16+
17+
val releaseNumberExtractor = "release/(.*)".r
18+
19+
def getReleaseNumber(branchName: String): String =
20+
branchName match {
21+
case releaseNumberExtractor(version) version
22+
case _ sys.error(s"The current branch ($branchName) is not a release branch (must starts with release/)")
23+
}
24+
25+
def vcs(st: State): Vcs = {
26+
Project.extract(st).get(releaseVcs).getOrElse(sys.error("Aborting release. Working directory is not a repository of a recognized VCS."))
27+
}
28+
29+
def checkUncommittedChanges: ReleaseStep = { st: State =>
30+
vcs(st)
31+
.cmd("fetch") ! st.log
32+
val ret = vcs(st)
33+
.cmd("diff-index", "--quiet", "HEAD", "--") ! st.log
34+
if (ret > 0) sys.error("Aborting release. Your working directory is not clean.")
35+
st
36+
}
37+
38+
lazy val releaseMerge: ReleaseStep = { st: State
39+
val version = st.get(ReleaseKeys.versions).getOrElse(sys.error("Version not set ?!"))._1
40+
val ret = vcs(st).cmd("flow", "release", "finish", version, "-m", s"Release $version") ! st.log
41+
if (ret > 0) sys.error("Release finish failed")
42+
st
43+
}
44+
45+
lazy val checkoutMaster: ReleaseStep = { st: State =>
46+
vcs(st).cmd("checkout", "master") ! st.log
47+
st
48+
}
49+
50+
lazy val checkoutDevelop: ReleaseStep = { st: State =>
51+
vcs(st).cmd("checkout", "develop") ! st.log
52+
st
53+
}
54+
55+
def currentBranch(st: State): String = {
56+
vcs(st)
57+
.cmd("rev-parse", "--abbrev-ref", "HEAD").!!.trim
58+
}
59+
60+
lazy val setReleaseUIVersion: ReleaseStep = setUIVersion(_._1)
61+
lazy val setNextUIVersion: ReleaseStep = setUIVersion(_._2)
62+
63+
def setUIVersion(selectVersion: Versions => String): ReleaseStep = { st: State
64+
val vs = st.get(ReleaseKeys.versions).getOrElse(sys.error("No versions are set! Was this release part executed before inquireVersions?"))
65+
val version = selectVersion(vs)
66+
//val version = st.get(ReleaseKeys.versions).getOrElse(sys.error("Version not set ?!"))._1
67+
val packageFile = new File("ui/package.json")
68+
val pkgJson = Json.parse(IO.read(packageFile))
69+
70+
pkgJson.transform(
71+
(__ \ 'version).json.update(
72+
__.read[JsString].map(_ => JsString(version)))) match {
73+
case JsSuccess(newPkgJson, _) => IO.write(packageFile, Json.prettyPrint(newPkgJson))
74+
case JsError(error) => sys.error(s"Invalid package file format: $error")
75+
}
76+
st
77+
}
78+
79+
lazy val getVersionFromBranch: ReleaseStep = { st: State
80+
val extracted = Project.extract(st)
81+
val useDefs = st.get(ReleaseKeys.useDefaults).getOrElse(false)
82+
83+
val currentV = getReleaseNumber(currentBranch(st))
84+
val releaseFunc = extracted.get(releaseVersion)
85+
val suggestedReleaseV = releaseFunc(currentV)
86+
87+
st.log.info(s"Release version : $currentV")
88+
val nextFunc = extracted.get(releaseNextVersion)
89+
val suggestedNextV = nextFunc(currentV)
90+
//flatten the Option[Option[String]] as the get returns an Option, and the value inside is an Option
91+
val nextV = readVersion(suggestedNextV, "Next version [%s] : ", useDefs, st.get(ReleaseKeys.commandLineNextVersion).flatten)
92+
st.put(ReleaseKeys.versions, (currentV, nextV))
93+
}
94+
95+
lazy val generateChangelog: ReleaseStep = { st: State =>
96+
val changeLogFile = Project.extract(st).get(changelogFile)
97+
st.log.info("Generating changelog in ")
98+
val properties = new java.util.Properties
99+
val credentialsFile = new File("~/.github/credentials")
100+
IO.load(properties, credentialsFile)
101+
val token = Option(properties.getProperty("token")).fold("")(t => s"-t $t")
102+
s"github_changelog_generator $token" ! st.log
103+
st
104+
}
105+
106+
lazy val commitChanges: ReleaseStep = { st: State =>
107+
val base = vcs(st).baseDir
108+
Seq(releaseVersionFile, releaseVersionUIFile, changelogFile)
109+
.foreach { f =>
110+
val file = Project.extract(st).get(f)
111+
val relativePath = IO.relativize(base, file).getOrElse(sys.error(s"Version file [$file] is outside of this VCS repository with base directory [$base]!"))
112+
vcs(st).add(relativePath) !! st.log
113+
}
114+
val status = (vcs(st).status.!!).trim
115+
116+
val newState = if (status.nonEmpty) {
117+
val (state, msg) = Project.extract(st).runTask(releaseCommitMessage, st)
118+
vcs(state).commit(msg) ! st.log
119+
state
120+
} else {
121+
// nothing to commit. this happens if the version.sbt file hasn't changed.
122+
st
123+
}
124+
newState
125+
}
126+
127+
}

project/build.sbt

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
libraryDependencies += "com.typesafe.play" %% "play-json" % "2.4.8"
2+
3+
scalacOptions in ThisBuild ++= Seq(
4+
"-encoding", "UTF-8",
5+
"-deprecation", // warning and location for usages of deprecated APIs
6+
"-feature", // warning and location for usages of features that should be imported explicitly
7+
"-unchecked", // additional warnings where generated code depends on assumptions
8+
"-Xlint", // recommended additional warnings
9+
"-Ywarn-adapted-args", // Warn if an argument list is modified to match the receiver
10+
"-Ywarn-value-discard", // Warn when non-Unit expression results are unused
11+
"-Ywarn-inaccessible",
12+
"-Ywarn-dead-code"
13+
)

0 commit comments

Comments
 (0)