'Sbt task to load system environment variables
I have some yaml file with system environment variables needed for service run.
system:
service: "name"
port: 123
I must have all these variables, loaded to the shell before run service start. But I want to load them by some sbt variable, maybe load-dev-env
.
The main problem that I can't import libraries for yaml parsing (circe-yaml
) into sbt shell execution and all imports like
import io.circe._
import cats._
are failed due to circe is no the subpackage of io or etc...
- I've tried to put libraryDependencies to the
./project/build.sbt
- no success
The last one thing - read all key/values manually but it doesn't look correct
Has someone done smth like this?
Solution 1:[1]
This SBT
build definition works:
Dir structure:
build.sbt
src/main/resources/config.yml
project/build.sbt
project/build.properties
build.sbt:
ThisBuild / scalaVersion := "2.13.8"
ThisBuild / organization := "com.example"
lazy val hello = (project in file("."))
.settings(
name := "Hello",
//libraryDependencies += "io.circe" %% "circe-yaml" % "0.14.1"
)
lazy val loadEnv = TaskKey[Unit]("load-dev-env")
loadEnv := {
import _root_.io.circe._
import _root_.io.circe.yaml.parser
import scala.io.Source
val filename = "src/main/resources/config.yml"
val confStr = Source.fromFile(filename).getLines.mkString("\n")
println(confStr)
val json: Either[ParsingFailure, Json] = parser.parse(confStr)
println(json)
}
project/build.sbt:
libraryDependencies += "io.circe" %% "circe-yaml" % "0.14.1"
project/build.properties:
sbt.version=1.6.1
src/main/resources/config.yml:
---
system:
port: 123
service: name
Example of running it:
sbt:Hello> loadDevEnv
---
system:
port: 123
service: name
Right({
"system" : {
"port" : 123,
"service" : "name"
}
})
It prints string from file followed by parsed json.
The important details are:
- You need to put library dependency in the other build file in
project
dir. - You need to use
_root_
because otherwise imports don't resolve if they are not part of core Scala or SBT plugins. At least I didn't find the way yet.
You can checkout the code here: https://github.com/izmailoff/sbt-import-test.
--- UPDATE ---:
I've added the full example in the repo. That required some changes since you can't update SBT settings from a Task
you need to do it in a Command
. Updated files below:
build.sbt:
ThisBuild / scalaVersion := "2.13.8"
ThisBuild / organization := "com.example"
// This is needed to get new env values from SBT:
fork := true
lazy val hello = (project in file("."))
.settings(
name := "Hello",
//libraryDependencies += "io.circe" %% "circe-yaml" % "0.14.1"
)
commands += Command.command("loadDevEnv") { state =>
val env = Config.getEnvFromConf()
val envStr = Config.prettyPrint(env)
s"set envVars ++= ${envStr}" :: state
}
project/Build.scala:
case class Service(port: Integer, service: String)
case class System(system: Service)
object Config {
def getEnvFromConf(): Map[String, String] = {
val filename = "src/main/resources/config.yml"
val confStr = readConfig(filename)
val conf = parseConfig(confStr)
println(conf)
val env = toEnv(conf)
env
}
def readConfig(filename: String): String = {
import scala.io.Source
Source.fromFile(filename).getLines.mkString("\n") // use better way?
}
def parseConfig(conf: String): System = {
import _root_.io.circe._
import _root_.io.circe.yaml
import _root_.io.circe.yaml._
import _root_.io.circe.yaml.syntax._
import _root_.io.circe.generic.auto._
import cats.syntax.either._
val json: Either[ParsingFailure, Json] = yaml.parser.parse(conf)
json
.leftMap(err => err: Error)
.flatMap(_.as[System])
.valueOr(throw _)
}
def toEnv(conf: System): Map[String, String] = {
Map(s"service.${conf.system.service}" -> conf.system.port.toString)
}
def prettyPrint(x: Map[String, String]): String = {
"Map(" + (x.map{ case (k, v) => s""""${k}"->"${v}""""}).mkString(", ") + ")"
}
}
project.build.sbt:
val circeVersion = "0.14.1"
libraryDependencies ++= Seq(
"io.circe" %% "circe-yaml",
"io.circe" %% "circe-generic",
"io.circe" %% "circe-parser"
).map(_ % circeVersion)
src/main/scala/Main.scala:
object Main extends App {
println("Found the following environment variable:")
println(System.getenv("service.name"))
}
EXAMPLE:
Run SBT from your OS shell:
> sbt
Run the app to check if environment variables are set:
sbt:Hello> run
[info] running (fork) Main
[info] Found the following environment variable:
[info] null
Getting null
. Load env:
sbt:Hello> loadDevEnv
[info] Defining envVars
[info] The new value will be used by Compile / bspBuildTargetRun, Compile / bspScalaMainClassesItem and 6 others.
[info] Run `last` for details.
[info] Reapplying settings...
[info] set current project to Hello (in build file:/home/tvc/repos/sbt_test/)
Run the app to check again:
sbt:Hello> run
[info] running (fork) Main
[info] Found the following environment variable:
[info] 123
Found value 123
.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |