mateharu mateharu - 16 days ago 8
Scala Question

Produce multiple zip artifacts from one sbt module

I have the following project structure:

my-project/
build.sbt
...
app/
...
config/
dev/
file1.properties
file2.properties
test/
file1.properties
file2.properties
prod/
file1.properties
file2.properties


The module app contains some scala source code and produces a plain jar file.

The problem is with the config module. What I need to do is to create some configuration in build.sbt that will take each folder from config and put its content into a separate zip file.

The result should be as follows:

my-project-config-dev-1.1.zip ~>
file1.properties
file2.properties
my-project-config-uat-1.1.zip ~>
file1.properties
file2.properties
my-project-config-prod-1.1.zip ~>
file1.properties
file2.properties


1.1 is an arbitrary version of the project.

The configuration should work in such way that when I add new environments and new configuration files, more zip files will be produced. In another task all these zip files should be published to Nexus.

Any suggestions?

Answer

I managed to resolve the problem by creating a module config and then a separate sub-module for each environment, so the project structure looks exactly as described in question. It all comes now to proper configuration in build.sbt.

Below is the general idea of what I've done to achieve what I wanted.

lazy val config = (project in file("config")).
  enablePlugins(UniversalPlugin).
  settings(
    name := "my-project",
    version := "1.1",
    publish in Universal := { },       // disable publishing of config module
    publishLocal in Universal := { }
  ).
  aggregate(configDev, configUat, configProd)

lazy val environment = settingKey[String]("Target environment")

lazy val commonSettings = makeDeploymentSettings(Universal, packageBin in Universal, "zip") ++ Seq(  // set package format
  name := "my-project-config",
  version := "1.1",
  environment := baseDirectory.value.getName,                                               // set 'environment' variable based on a configuration folder name
  topLevelDirectory := None,                                                                // set top level directory for each package
  packageName in Universal := s"my-project-config-${environment.value}-${version.value}",   // set package name (example: my-project-config-dev-1.1)
  mappings in Universal ++= contentOf(baseDirectory.value).filterNot { case (_, path) =>    // do not include target folder
    path contains "target"
  }
)

lazy val configDev = (project in file("config/dev")).enablePlugins(UniversalPlugin).settings(commonSettings: _*)
lazy val configUat = (project in file("config/uat")).enablePlugins(UniversalPlugin).settings(commonSettings: _*)
lazy val configProd = (project in file("config/prod")).enablePlugins(UniversalPlugin).settings(commonSettings: _*)

UniversalPlugin is highly configurable, although not all configuration options may be clear at first. I suggest reading its docs and looking at the source code.

To actually package artifacts the following command has be issued:

sbt config/universal:packageBin

Publishing:

sbt config/universal:publish

As can be seen above adding new environments is very easy - only a new folder and one line in build.sbt need to be added.