Loading external settings in build.sbt - scala

I'm trying to add an sbt plugin to a play application.
The plugin requires some configuration since it needs to connect to a database. These are the settings that the plugin requires in the build.sbt file:
jooqOptions := Seq("jdbc.driver" -> "com.mysql.jdbc.Driver",
"jdbc.url" -> "jdbc:mysql://localhost:3306/fnord",
"jdbc.user" -> "fnord",
"jdbc.password" -> "fnord",
"generator.database.name" -> "org.jooq.util.mysql.MySQLDatabase",
"generator.database.inputSchema" -> "fnord",
"generator.target.packageName" -> "com.myproject.jooq")
Since the user and password will depent on the specific machine on which i deploy the app, i would like to load them from somewhere where each user can assign the user and password himself.
How do i do that?

I've solved it using the accepted solution from:
How get application version in play framework and build.sbt
I've added this to my build.sbt:
import com.typesafe.config._
val conf = ConfigFactory.parseFile(new File("conf/application.conf")).resolve()
version := conf.getString("app.version")
and in my application.conf:
app.version="0.2-SNAPSHOT"

One way would be to read them from an environment variable, another to have a config file of some kind in a predefined path that you load and read in your sbt project.
Since the sbt config is scala code you can for example use sys.env to read environment variables.
You can find the scaladoc for sys.env here

Related

Environment variable based runtime configuration with sbt native packager

I'm using the sbt native packager plugin to create a zip file of my application for deployment to elastic beanstalk. I would like to set environment variables in my beanstalk environment and have those be used to configure my application at runtime. I've attempted to reference the env variables in my Procfile like so:
web: ./bin/bridgeservice -Dhttp.port=$PORT
This does not work as $PORT is not interpolated by the start script generated by the packager.
I've also attempted to define the variables in my build.sbt like so:
import scala.util.Properties
javaOptions in Universal ++= Seq(
"-Dhttp.port=" + Properties.envOrElse("PORT", "9004"),
)
This also does not work as the packager expects the PORT env variable at the time of building the distributable zip and hardcodes the default value of 9004 in an application.ini file.
Is it possible to dynamically pass java options based on environment variables at application startup?
The settings in javaOptions in Universal are compiled into conf/application.ini file, but accordingly to sbt-native-packager docs application.ini currently does not support variable substitution:
The file will be installed to ${app_home}/conf/application.ini and
read from there by the startscript. You can use # for comments and new
lines as you like. This file currently doesn’t has any variable
substitution. We recommend using the build.sbt if you need any
information from your build.
So,the env var based runtime settings can be achieved in several ways:
Solution #1. Add extra definitions to generated start script
In build.sbt:
bashScriptExtraDefines += """addJava "-Dhttp.port=${PORT:-9004}""""
Check out Application and runtime configuration documentation for more info.
Solution #2: Set JAVA_OPTS env var in the target server
Just set JAVA_OPTS environment variable on the target server and make it available for the start script. This can be the easiest solution for environments like AWS ElasticBeanstalk where env vars can be set on the app's environment configuration page.
Not sure if this will help, but I had similar issue when building docker images of my multi module project.
I ended with this:
def sysPropOrDefault(propName: String, default: String): String = Option(System.getProperty(propName)).getOrElse(default)
val somePort = sysPropOrDefault("port", "9004")
and in the project definition:
lazy val someProject = project("some-project")
.enablePlugins(JavaServerAppPackaging)
.settings(
javaOptions in Universal ++= Seq(
s"-Dhttp.port=$somePort"
)
)
Adding javaOptions to project settings level was crucial in my case. And s before option is not a typo.
When running command from terminal I called:
sbt clean update -Dport=9005 docker:publishLocal

What are the options to set base packaged directory for a package using sbt-native-packager?

I am trying to build a Debian package using sbt-native-packager as described in this build.sbt.
I set appName using
val appName = "megamgateway"
in project/Build.scala.
All works well. It is just that the contents are stored in /usr/share/megamgateway.
I'd like to have the contents under /usr/share/megam/gateway.
The only way I could find is to use
linuxPackageMapping
as shown here.
Before following along, I'd like to know about other approaches.
You could try to add to your project settings
name := "gateway"
defaultLinuxInstallLocation := "/usr/share/megam/"

How to add some local maven repo url to global sbt, and make them be tried first?

I want to add some fast local maven repository url to sbt, say:
http://maven.example.com/public
I want to add it to "global", so that I don't need to add them to each of sbt project. And also want to be tried first when sbt downloading some jars.
But I can't find useful information to do this, how to do it?
(My sbt version is 0.13.1)
With the help of a friend, finally I found the solution:
create a new file ~/.sbt/repositories
add content like this:
[repositories]
local
my-maven-repo: http://example.org/repo
my-ivy-repo: http://example.org/ivy-repo/, [organization]/[module]/[revision]/[type]s/[artifact](-[classifier]).[ext]
See official doc: http://www.scala-sbt.org/0.13.2/docs/Detailed-Topics/Library-Management.html#override-all-resolvers-for-all-builds
Modify your global configuration file, which is normally located in ~/.sbt/0.13/global.sbt, if it's not there you can create it.
In the file add following line:
externalResolvers := { ("Fast Repo" at "http://maven.example.com/public") +: externalResolvers.value }
You can confirm it's working by executing show externalResolvers in any project to see the list of resolvers. Your newly added resolver should be first.

config directory when using sbt-native-packager

I'd like to ask about reasoning behind the fact, that the sbt-native-packager plugin creates a symlink /etc/ -> /usr/share//conf (instead of really putting files there and somehow specifying in the app where to look for them)?
In particular how does it influence update/uninstall+install process? Are the configs somehow preserved (for example for debian with java_server architecture setting)?
I'd like to ask about reasoning behind the fact, that the sbt-native-packager plugin creates a symlink /etc/ -> /usr/share//conf
Keeping everything in one place. You have your application directory which contains everything and then you just link from the OS-specific folders to the according directories in your application folder.
Are the configs somehow preserved
Yes indeed. You can try it out with a simple play application. Add this to your build.sbt
mappings in Universal <+= (packageBin in Compile, baseDirectory ) map { (_, base) =>
val conf = base / "conf" / "application.conf"
conf -> "conf/application.conf"
}
This will map your application.conf in the conf folder. When you build a debian package with
debian:packageBin
you can see in target/-/DEBIAN/conffiles an entry
/usr/share/<app-name>/conf/application.conf
An apt-get remove your-app won't remove this file, only a purge

simplify settings for deb only build with sbt native packager

I am trying to package my scala main app code as a .deb file. the app will only run on an ubuntu machine, so I do not really care about windows etc.
Currently, I am struggling to find the most easiest way to compile the .deb using the simpliest settings. Let's assume I have a simple object Kernel extends App scala file in my src folder that should be bundled inlcuding the jardependencies.
my current scala based debian settings for the project are:
import com.typesafe.sbt.SbtNativePackager._
import NativePackagerKeys.
val debSettings = mapGenericFilesToLinux ++ linuxSettings ++ debianSettings ++ Seq(
name in Debian := "my-app",
version in Debian := "0.1-version",
mainClass := Some("Kernel"),
packageSummary := "",
target := new java.io.File("target"),
packageDescription := "my app",
packageDescription := "my app desciption",
NativePackagerKeys.normalizedName := "normalizedName",
maintainer := "my name",
sourceDirectory := new java.io.File("./src"),
debianPackageDependencies in Debian ++= Seq("openjdk-7-jre"),
debianPackageRecommends in Debian ++= Seq(),
linuxPackageMappings in Debian ++= Seq() ,
debianMaintainerScripts ++=Seq())
the debian:package-bincall works and a deb is created but no binaries/jars are copied into the deb so i'm obviously missing some configuration. i know that there are still missing linuxPackageMappings etc but i'm wondering if there is an easier configuration for the compilation? using packageArchetype.java_server forces me to include so many not-used variables for windows etc. I want to avoid this.
Any suggestion how to simplify the settings + mappings for a deb-only build?
You can just pull in the settings that are relevant to you from:
https://github.com/sbt/sbt-native-packager/blob/master/src/main/scala/com/typesafe/sbt/packager/archetypes/JavaServerApplication.scala#L24
and
So, this should be something like:
import com.typesafe.sbt.packager.archetypes._
packagerSettings
mapGenericMappingsToLinux
JavaAppPackaging.settings
JavaServerAppPackaging.debianSettings
caveat the correct imports. Let's cover what each of these do:
packagerSettings adds the basic "flow" of the packager tasks, but does not configure any of the files or settings
mapGenericMappingsToLinux is the hook which will take everything configured in mappings in Universal and attempt to make it linux friendly for linux packages.
JavaAppPackaging.settings will take your build definition, and automatically fill out the mappings in Universal configuration with defaults for your application.
JavaServerAppPackaging.debianSettings adds additional settings specifically for debian such that the bundled default application can be started as a server.
One of the goals of the plugin is to allow you the flexibility to use any of these "mappings" and get default behavior or override. It's just not well documented how. I hope this helps!