I'm working on a Play library that's designed to plug into both the build process and the application code. I want the library to be centrally configured from the application's application.conf, and both the build-time and run-time parts need to access that configuration. At run-time, this is no problem; I just define a reference.conf with any defaults and use ConfigFactory.load, as usual.
However, I'm not sure what's best for build-time. I'm using the following in an SBT task:
import com.typesafe.config.ConfigFactory
val baseDir = baseDirectory.value
val config = ConfigFactory.parseFile(baseDir / "conf/application.conf")
, because ConfigFactory.load doesn't seem to work at build-time. But this strikes me as poor form, because it bypasses the normal logic of load. Is there a preferred way of doing this?
Related
So the problem is really simple and I hope solution will be as well.
So basically I have two configuration files application.conf and dev.conf. I'm passing my config files from command line like that sbt -Dconfig.file=dev.conf.
The problem is when I use ConfigFactory.load from main object(the one which extends App) it loads config I passed via command line(in this case dev.conf), but when I load the config from different object it loads default application.conf.
Can I load somehow config passed from arguments from any object?
When you run your application with the runMain SBT task, then by default SBT won't create a separate JVM for your code. This has several consequences around the application lifecycle, and of course with regard to system properties as well.
In general, your approach should work, as long as your build configuration does not enable forking. However, I think the better approach would be to actually rely on forking and specify the system property explicitly. This is guaranteed to work. To do this, you need to set the fork setting in the run task to true, and then add a JVM command line option:
Compile / run / fork := true,
Compile / run / javaOptions += "-Dconfig.file=dev.conf",
Don't forget to restart SBT after that. You won't need to pass the config.file property to SBT with this approach; rather, it is controlled by the javaOptions setting, as in the example above.
Using scala playframework 2.5,
I build the app into a jar using sbt plugin PlayScala,
And then build and pushes a docker image out of it using sbt plugin DockerPlugin
Residing in the source code repository conf/development.conf (same where application.conf is).
The last line in application.conf says include development which means that in case development.conf exists, the entries inside of it will override some of the entries in application.conf in such way that provides all default values necessary for making the application runnable locally right out of the box after the source was cloned from source control with zero extra configuration. This technique allows every new developer to slip right in a working application without wasting time on configuration.
The only missing piece to make that architectural design complete is finding a way to exclude development.conf from the final runtime of the app - otherwise this overrides leak into production runtime and obviously the application fails to run.
That can be achieved in various different ways.
One way could be to some how inject logic into the build task (provided as part of the sbt pluging PlayScala I assume) to exclude the file from the jar artifact.
Other way could be injecting logic into the docker image creation process. this logic could manually delete development.conf from the existing jar prior to executing it (assuming that's possible)
If you ever implemented one of the ideas offered,
or maybe some different architectural approach that gives the same "works out of the box" feature, please be kind enough to share :)
I usually have the inverse logic:
I use the application.conf file (that Play uses by default) with all the things needed to run locally. I then have a production.conf file that starts by including the application.conf, and then overrides the necessary stuff.
for deploying to production (or staging) I specify the production/staging.conf file to be used
This is how I solved it eventually.
conf/application.conf is production ready configuration, it contains placeholders for environment variables whom values will be injected in runtime by k8s given the service's deployment.yaml file.
right next to it, conf/development.conf - its first line is include application.conf and the rest of it are overrides which will make the application run out of the box right after git clone by a simple sbt run
What makes the above work, is the addition of the following to build.sbt :
PlayKeys.devSettings := Seq(
"config.resource" -> "development.conf"
)
Works like a charm :)
This can be done via the mappings config key of sbt-native-packager:
mappings in Universal ~= (_.filterNot(_._1.name == "development.conf"))
See here.
I am trying to load different config file per task in sbt, for example I want to be able to run a task like:
sbt devRun
and loads src/main/resources/application.dev.conf while running:
sbt run
will load src/main/resources/application.conf.
As I understand it, we can load different application.conf when running test by putting it on src/test/resource/application.conf, but using different config file for different task (or scope) will need some code on SBT.
I've been trying to google around and usually the suggestion is to use it on run task like:
$ sbt run -Dconfig.resources="application.dev.conf"
But as far as I understand, the above will only load different config on runtime. I have multiple projects with lots of config file. I want to load them dynamically, based on scope / task. What's the best way to do this?
Thanks before.
You can(must) use the javaOptions setting. It represents Java options when sbt runs a forked JVM, e.g. it runs tests in a different JVM process(by default).
javaOptions in Test += "-Dconfig.resource=" + System.getProperty("config.resource", "application.test.conf")
Instead of Test plug in whatever config you have. If you don't explicitly pass a config.resource parameter it will use application.test.conf.
This is a follow up to the question I asked (ans answered) earlier: Scala SBT - sbt-native-packager, how to specify custom stage directory
When I alter stagingDirectory in Universal property (e.g. stage-v1) and stage the project - it works as expected. I can see my project in target/universal/stage-v1.
Then I specify different staging directory (stage-v2) and stage it. What happens first is that it deletes contents of stage-v1 folder, and then stages it in stage-v2.
How can I preserve different stage builds in different staging directories?
To put this into perspective I have a build time dependency. It's a backend for a computation library which can be CPU or GPU. I have different use cases for them so I want to keep 2 builds handy at all times.
Thanks,
Anton
The recommended way to build the same application, but with different configurations is to use sbt submodules. This would look something like this:
lazy val application = project
lazy val cpuApplication = project.settings(...).dependsOn(application)
lazy val gpuApplication = project.settings(...).dependsOn(application)
The advantage of this is, that you can configure every thing very easily. You can also call everything very explicit, e.g. gpuApplication:stage stages the gpu application. The downside is that your build.sbt grows.
I'm using playframework 2.1-RC2. First of all I've seen all the similar questions, so I followed the common instruction of separating application.conf file per environment. So I have application.test.conf and I run tests this way:
play -Dconfig.file=./conf/application.test.conf "test"
I tried different combinations, like
play -Dconfig.file=./conf/application.test.conf ~test
or
play -Dconfig.file=conf/application.test.conf ~test
Still no luck, it just does not get picked, default one (application.conf) is instead.
From the other side, if I do
play -Dconfig.file=./conf/application.dev.conf "run"
then application picks the right config.
So how can I specify the test configuration file?
I found the most robust way to specifiy this in a cross-platform compatible manner is to include it directly in the Build.scala:
val main = play.Project(appName, appVersion, appDependencies).settings(
javaOptions in Test += "-Dconfig.file=conf/test.conf",
...
)
Bonus: configure once and forget ;-)
Another approach is to override method on GlobalSettings / Global named onLoadConfig and that enables you to have control where your app will look for your configuration.
So in one of our application I have this setup below for my conf/ folder.
conf/application.conf --> configurations common for all environment
conf/dev/application.conf --> configurations for development environment
conf/test/application.conf --> configurations for testing environment
conf/prod/application.conf --> configurations for production environment
With that, you are able to implement inheritance like setup for configuration, you have common and 3 others for specific environment mode.
The code inside your onLoadConfig method should just load main configuration and set correct fallback configuration specific to your environment then return the configuration instance like below:
**return new Configuration(baseConfig.withFallback(envConfig));**
Try check this blog post for complete snippet of the code.
I hope this helps.