sbt Library Dependencies et al

  • 3rd party library dependency (commonly resolved using maven or ivy)
  • hard wired jar filepath (less commonly used and is found in situations where the dependency is not managed by a maven like repository).
  • sub-project based dependency using dependsOn

Managed Dependencies

Managed dependencies usually provided using libraryDependencies and are typically chained with multiple of them.

libraryDependencies += organization %% moduleName % version

Provided

Provided is a way dependencies are not transitive added during publish step.

libraryDependencies += "org.scalatest" %% "scalatest" % "3.1.0"     % "test" % Provided

exclude()

Its also possible to exclude selective transitive dependencies using exclude() . And these are excluded during publish step.

libraryDependencies += "log4j" % "log4j" % "1.2.15" exclude("javax.jms", "jms")

dependencyOverride

dependencyOverride is a way to override certain transitive dependencies that are resolved to a project.

dependencyOverrides += "com.fasterxml.jackson.core" % "jackson-core" % "3.7.0-M11"

Unmanaged Dependencies

While, unmanaged dependencies are provided using a lib directory as a convention. This can be overridden using

unmanagedBase := baseDirectory.value / "custom_lib"

Docker package

These days microservices are typically published as docker images. And this requires dependency on an sbt plugin

addSbtPlugin("se.marcuslonnberg" % "sbt-docker" % "1.8.2")
docker / dockerfile := {
val jarFile: File = (Compile / packageBin / sbt.Keys.`package`).value
val dependencies = (Compile / dependencyClasspathAsJars).value
val jarTarget = s"/app/${jarFile.getName}"
def addLibPath(files: Seq[File]) = files.map("/app/" + _.getName)
val classpathString = (Seq(jarTarget, "/app/config") ++ addLibPath(dependencies.files)).mkString(":")

val banner =
"""echo "Using JAVA_OPTS: ${JAVA_OPTS}"
|echo "Using args $@"
|""".stripMargin
val javaEnv = "java -Dfile.encoding=UTF-8 $JAVA_OPTS"
val args = """"$@""""

val content =
s"""#!/bin/bash
|$banner
|echo '${name.value} - v${version.value}'
|echo 'Built on ${LocalDateTime.now()} - ${git.gitHeadCommit.value.getOrElse("No head commit")}'
|$javaEnv -cp $classpathString $mainclass $args
|""".stripMargin

val programName = "my-app"

val startScript = file(s"""/tmp/$programName""")
IO.write(startScript, content, append = false)

new sbtdocker.Dockerfile {
from("amazoncorretto:11")
add(dependencies.files, "/app/")
add(startScript, s"""/$programName""")
run("chmod", "+x", s"""/$programName""")
add(jarFile, jarTarget)
run("mkdir", "-p", "/app/config")
run("touch", "/app/config/application.conf")
env("JAVA_OPTS", "-Xms8g")
env("JAVA_HOME", "/usr/lib/jvm/jre")
entryPoint(s"""/$programName""")
}
}
sbt 'project sub-project-1' docker dockerPush

Publishing to Maven

Typically if one wants to publish to maven using artifactory

ThisBuild / resolvers += "Artifactory-release" at "https://test1.jfrog.io/artifactory/libs-release/"
ThisBuild / resolvers += "Artifactory-snapshot" at "https://test1.jfrog.io/artifactory/libs-snapshot/"
ThisBuild / credentials += Credentials(Path.userHome / ".sbt" / "credentials")
sbt 'project sub-project-1' publishLocal publish

Tip

In the case, if one has multiple non-mono repository based dependencies, its easier to verify dependency integrity using publishLocal and by providing a local resolver using

resolvers += Resolver.file("local-ivy", file(Path.userHome.absolutePath + "/.ivy2/local"))(Resolver.ivyStylePatterns)

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store