From 64f477e36c33afe8dfd87a839ba263e9973b0669 Mon Sep 17 00:00:00 2001 From: Nathan Fischer Date: Wed, 17 Apr 2019 17:58:05 -0700 Subject: Contrib module for building docker images --- build.sc | 4 ++ contrib/docker/src/DockerModule.scala | 73 +++++++++++++++++++++++++++++++++++ docs/pages/9 - Contrib Modules.md | 44 +++++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 contrib/docker/src/DockerModule.scala diff --git a/build.sc b/build.sc index bc693f1c..4d9133d0 100755 --- a/build.sc +++ b/build.sc @@ -306,6 +306,10 @@ object contrib extends MillModule { def moduleDeps = Seq(scalalib) def ivyDeps = Agg(ivy"org.flywaydb:flyway-core:5.2.4") } + + object docker extends MillModule { + def moduleDeps = Seq(scalalib) + } } diff --git a/contrib/docker/src/DockerModule.scala b/contrib/docker/src/DockerModule.scala new file mode 100644 index 00000000..bbf4d926 --- /dev/null +++ b/contrib/docker/src/DockerModule.scala @@ -0,0 +1,73 @@ +import mill.T +import mill.scalalib.JavaModule +import os.Shellable.IterableShellable + +import scala.collection.immutable._ + +trait DockerModule { outer: JavaModule => + + trait DockerConfig extends mill.Module { + /** + * Tags that should be applied to the built image + * In the standard registry/repository:tag format + */ + def tags: T[Seq[String]] = T(List(outer.artifactName())) + def labels: T[Map[String, String]] = Map.empty[String, String] + def baseImage: T[String] = "gcr.io/distroless/java:latest" + def pullBaseImage: T[Boolean] = T(baseImage().endsWith(":latest")) + private def baseImageCacheBuster: T[(Boolean, Double)] = T.input { + val pull = pullBaseImage() + if(pull) (pull, Math.random()) else (pull, 0d) + } + + def dockerfile: T[String] = T { + val jarName = assembly().path.last + val labelRhs = labels() + .map { case (k, v) => + val lineBrokenValue = v + .replace("\r\n", "\\\r\n") + .replace("\n", "\\\n") + .replace("\r", "\\\r") + s""""$k"="$lineBrokenValue"""" + } + .mkString(" ") + + val labelLine = if(labels().isEmpty) "" else s"LABEL $labelRhs" + + s""" + |FROM ${baseImage()} + |$labelLine + |COPY $jarName /$jarName + |ENTRYPOINT ["java", "-jar", "/$jarName"] + """.stripMargin + } + + final def build = T { + val dest = T.ctx().dest + + val asmPath = outer.assembly().path + os.copy(asmPath, dest / asmPath.last) + + os.write(dest / "Dockerfile", dockerfile()) + + val log = T.ctx().log + + val tagArgs = tags().flatMap(t => List("-t", t)) + + val (pull, _) = baseImageCacheBuster() + val pullLatestBase = IterableShellable(if(pull) Some("--pull") else None) + + val result = os + .proc("docker", "build", tagArgs, pullLatestBase, dest) + .call(stdout = os.Inherit, stderr = os.Inherit) + + log.info(s"Docker build completed ${if(result.exitCode == 0) "successfully" else "unsuccessfully"} with ${result.exitCode}") + tags() + } + + final def push() = T.command { + val tags = build() + tags.foreach(t => os.proc("docker", "push", t).call(stdout = os.Inherit, stderr = os.Inherit)) + } + } +} \ No newline at end of file diff --git a/docs/pages/9 - Contrib Modules.md b/docs/pages/9 - Contrib Modules.md index 1b9b55fa..9adddcc5 100644 --- a/docs/pages/9 - Contrib Modules.md +++ b/docs/pages/9 - Contrib Modules.md @@ -38,6 +38,50 @@ object project extends BuildInfo { * `def buildInfoPackageName: Option[String]`, default: `None` The package name of the object. +### Docker + +Automatically build docker images from your mill project. + +Requires the docker CLI to be installed. + +In the simplest configuration just extend `DockerModule` and declare a `DockerConfig` object. + +```scala +import mill._, scalalib._ + +import ivy`com.lihaoyi::mill-contrib-docker:VERSION` +import contrib.docker.DockerModule + +object foo extends JavaModule with DockerModule { + object docker extends DockerConfig +} +``` + +Then + +``` +$ mill foo.docker.build +$ docker run foo +``` + +#### Configuration + +Configure the image by overriding tasks in the `DockerConfig` object + +```scala +object docker extends DockerConfig { + // Override tags to set the output image name + def tags = List("aws_account_id.dkr.ecr.region.amazonaws.com/hello-repository") + + def baseImage = "openjdk:11" + + // Configure whether the docker build should check the remote registry for a new version of the base image before building. + // By default this is true if the base image is using a latest tag + def pullBaseImage = true +} +``` + +Run mill in interactive mode to see the docker client output, like `mill -i foo.docker.build`. ### Flyway -- cgit v1.2.3