As you probably know the Exalate team stores the common part of all our applications in the separate project named Exalate-components. We build the sub-projects there and use them as dependencies in all our products.
Before we used the maven in several Exalate-component modules and other modules were built by sbt. It was very convenient. For releasing the new version of Exalate-components we had to change the version in all .pom and .sbt files and after this published separately maven and sbt modules: firstly we executed the atlas-mvn clean install in our Exalate-components directory, after this sbt clean publishLocal in Exalate-persistence and Exalate-replication.
Our Exalate-component was like Frankenstein. We wanted to change this and made interaction with exacomp more comfortable. We want to use one build tool for all our Exalate-component and in this blog I want to describe you the way how we made this.
First step
At this step, we will replace all our pom.xml by build.sbt. In each build.sbt file we will add the specific properties for this module such as name and libraryDependencies:
exalate-domain/build.sbt
name := "exalate-domain" libraryDependencies ++= Seq( "com.atlassian.annotations" % "atlassian-annotations" % "0.12", "org.apache.commons" % "commons-lang3" % "3.2.1" )
Currently we have all our sub project as sbt submodules of the Exalate-component project
Second step
At this step we will define the common settings for all our modules in
exalate-components/build.sbt
lazy val commonSettings = Seq( organization := "com.exalate", scalaVersion := "2.11.6", version := "0.5.0-SNAPSHOT", publishTo <<= version { version: String => val artifactoryIdalkoCom = "https://artifactory.test.com/artifactory/" val url = if (version.contains("-SNAPSHOT")) { artifactoryIdalkoCom + "plugins-snapshot-local" } else { artifactoryIdalkoCom + "plugins-release-local" } Some("Artifactory Realm" at url) }, credentials += Credentials("Artifactory Realm", "artifactory.test.com", "*****", "*****"), resolvers ++= Seq( "Artifactory All" at "https://artifactory.test.com/artifactory/all", "Local Maven Repository" at "file://" + Path.userHome.absolutePath + "/.m2/repository" ) )
We defined the sequence of the commonSettings here. We will add this setting to all our sub-projects, but about this I will tell later.
So, here will be the one place, where we will have to change the version of our exacomp.
Also, in this settings we will describe all settings in the publishTo task that we will need for exacomp publishing to the remote repository. This is the one of the SBT features. We can easily paste the Scala code into task body. We are able to override predefined task and create our own task. Here we said that all our project jars will be published to the Idalko artifactory.
Third step
Tt this step we need to describe the property for scala and java projects
This is the settings for our scala projects. For scala project we use the local ivy repository and and perform publishing via publishLocal:
lazy val scalaProjectSettings = Seq( publishM2 := { }, javacOptions ++= Seq("-source", "1.8", "-target", "1.8") )
Because of aggregation (we will build and publish our projects all together) we need to override the publishM2 task to prevent publishing into maven repository.
Also here we will define the java compilation option for all scala projects.
This is common settings for all java projects. As in case with scala we don’t want to publish them to ivy repository and to prevent this we override the publishLocal task. We will publish the java projects with the help of publishM2:
lazy val javaProjectSettings = Seq( publishLocal := { }, publishMavenStyle:=true, crossPaths := false, libraryDependencies ++= Seq( "com.google.code.findbugs" % "annotations" % "3.0.0" ), javacOptions ++= Seq("-source", "1.6") )
We have additional properties here and I want to provide their description from the official documentation:
Last step
The last thing we need to do is to describe our modules into the Exalate-components/build.sbt and mark the dependencies between them.
lazy val `exalate-components` = (project in file(".")) .settings(publish := { }, publishLocal:= { }, publishM2 := { }) .aggregate(`exalate-utils`, `exalate-domain`, `exalate-domain-utils`, `exalate-license`, `exalate-processors`, `exalate-persistence`, `exalate-replication`) lazy val `exalate-utils` = (project in file("exalate-utils")) .settings(commonSettings: _*) .settings(javaProjectSettings: _*) lazy val `exalate-domain` = (project in file("exalate-domain")) .settings(commonSettings: _*) .settings(javaProjectSettings: _*) .dependsOn(`exalate-utils`) lazy val `exalate-domain-utils` = (project in file("exalate-domain-utils")) .settings(commonSettings: _*) .settings(javaProjectSettings: _*) .dependsOn(`exalate-domain`) lazy val `exalate-license` = (project in file("exalate-license")) .settings(commonSettings: _*) .settings(javaProjectSettings: _*) .dependsOn(`exalate-domain`) lazy val `exalate-processors` = (project in file("exalate-processors")) .settings(commonSettings: _*) .settings(javaProjectSettings: _*) .dependsOn(`exalate-domain`) .dependsOn(`exalate-utils`) lazy val `exalate-persistence` = (project in file("exalate-persistence")) .settings(commonSettings: _*) .settings(scalaProjectSettings: _*) .dependsOn(`exalate-domain`) lazy val `exalate-replication` = (project in file("exalate-replication")) .enablePlugins(PlayScala) .disablePlugins(PlayLayoutPlugin) .settings(commonSettings: _*) .settings(scalaProjectSettings: _*) .dependsOn(`exalate-processors`) .dependsOn(`exalate-domain-utils`) .dependsOn(`exalate-persistence`)
The Exalate-components – is our parent project and we don’t want to publish it. It is just a container for build.sbt file with the rules for all our sub-modules, so we will override all the publish tasks for it. The aggregate function means that running a task on the aggregate project will also run it on the aggregated projects.
Below we see the description of all our sub-projects. For each of them we apply the common settings and the settings based on the project type. Also we use dependsOn function to organize the order of build and inject the dependency into the module. For example in exalate-domain project we don’t need to set the dependency on exalate-utils version 0.5.0-SNAPSHOT in the exalate-domain/build.sbt. This dependency will be added automatically.
And the last thing. In exalate-replication module we enable the Play plugin. We do this for resolving the play dependency inside it.
Conclusion
Now after refactoring and switching to the SBT we can easily control our exalate-components. For releasing a new version we just need to change the version in one main build.sbt file and run:
sbt publishM2
for releasing all java project
sbt publishM2 publishLocal
for releasing all project
Also we can run all the tests in exalate components before build using
sbt test publishM2 publishLocal
The complete Exalate-components/build.sbt:
import sbt.Keys._ lazy val commonSettings = Seq( organization := "com.exalate", scalaVersion := "2.11.6", version := "0.5.0-SNAPSHOT", publishTo <<= version { version: String => val artifactoryIdalkoCom = "https://artifactory.test.com/artifactory/" val url = if (version.contains("-SNAPSHOT")) { artifactoryIdalkoCom + "plugins-snapshot-local" } else { artifactoryIdalkoCom + "plugins-release-local" } Some("Artifactory Realm" at url) }, credentials += Credentials("Artifactory Realm", "artifactory.test.com", "*****", "*****"), resolvers ++= Seq( "Artifactory All" at "https://artifactory.test.com/artifactory/all", "Local Maven Repository" at "file://" + Path.userHome.absolutePath + "/.m2/repository" ) ) lazy val scalaProjectSettings = Seq( publishM2 := { }, javacOptions ++= Seq("-source", "1.8", "-target", "1.8") ) lazy val javaProjectSettings = Seq( publishLocal := { }, publishMavenStyle:=true, crossPaths := false, libraryDependencies ++= Seq( "com.google.code.findbugs" % "annotations" % "3.0.0" ), javacOptions ++= Seq("-source", "1.7") ) lazy val `exalate-components` = (project in file(".")) .settings(publish := { }, publishLocal:= { }, publishM2 := { }) .aggregate(`exalate-utils`, `exalate-domain`, `exalate-domain-utils`, `exalate-license`, `exalate-processors`, `exalate-persistence`, `exalate-replication`) lazy val `exalate-utils` = (project in file("exalate-utils")) .settings(commonSettings: _*) .settings(javaProjectSettings: _*) lazy val `exalate-domain` = (project in file("exalate-domain")) .settings(commonSettings: _*) .settings(javaProjectSettings: _*) .dependsOn(`exalate-utils`) lazy val `exalate-domain-utils` = (project in file("exalate-domain-utils")) .settings(commonSettings: _*) .settings(javaProjectSettings: _*) .dependsOn(`exalate-domain`) lazy val `exalate-license` = (project in file("exalate-license")) .settings(commonSettings: _*) .settings(javaProjectSettings: _*) .dependsOn(`exalate-domain`) lazy val `exalate-processors` = (project in file("exalate-processors")) .settings(commonSettings: _*) .settings(javaProjectSettings: _*) .dependsOn(`exalate-domain`) .dependsOn(`exalate-utils`) lazy val `exalate-persistence` = (project in file("exalate-persistence")) .settings(commonSettings: _*) .settings(scalaProjectSettings: _*) .dependsOn(`exalate-domain`) lazy val `exalate-replication` = (project in file("exalate-replication")) .enablePlugins(PlayScala) .disablePlugins(PlayLayoutPlugin) .settings(commonSettings: _*) .settings(scalaProjectSettings: _*) .dependsOn(`exalate-processors`) .dependsOn(`exalate-domain-utils`) .dependsOn(`exalate-persistence`