From 360df7a19607ec4178bf7313ff55fb28bf42c604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Wawrzyk?= Date: Wed, 19 Jun 2024 13:35:30 +0200 Subject: [PATCH] docs: document how to use rules_oci with scala (#630) --- docs/scala.md | 204 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 202 insertions(+), 2 deletions(-) diff --git a/docs/scala.md b/docs/scala.md index 19f500a2..f6c32f3d 100644 --- a/docs/scala.md +++ b/docs/scala.md @@ -3,8 +3,208 @@ Users are typically migrating from [scala_image](https://github.com/bazelbuild/rules_docker#scala_image) in rules_docker. -TODO: document how to build an image +You can request the *_deploy.jar output of a scala_binary target, which is a single, self-contained launcher that includes all the dependencies. This can then be added to a container with a base image such as gcr.io/distroless/java17 and then executed directly as `java -jar `. ## Example -An example is needed! Please consider contributing one. :pray: +For this example, we will use `App.scala` like below: + +**App.scala** + +```scala +object App { + def main(args: Array[String]): Unit = { + println("Hello, world!") + } +} +``` + +In this example, I will not use bzlmod and fall back to the `WORKSPACE` file, as `rules_scala` doesn't support bzlmod yet. This file setups +the `rules_scala` according to the documentation so that we can build scala targets. Next, it configures `aspect_bazel_lib` so that we can have access to `tar` rule needed later. Finally, it configures `rules_oci` and pulls the base image with Java 17. + +**WORKSPACE** + +```python +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +http_archive( + name = "bazel_skylib", + sha256 = "66ffd9315665bfaafc96b52278f57c7e2dd09f5ede279ea6d39b2be471e7e3aa", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.4.2/bazel-skylib-1.4.2.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.4.2/bazel-skylib-1.4.2.tar.gz", + ], +) + +load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") + +bazel_skylib_workspace() + +http_archive( + name = "io_bazel_rules_scala", + sha256 = "3b00fa0b243b04565abb17d3839a5f4fa6cc2cac571f6db9f83c1982ba1e19e5", + strip_prefix = "rules_scala-6.5.0", + url = "https://github.com/bazelbuild/rules_scala/releases/download/v6.5.0/rules_scala-v6.5.0.tar.gz", +) + +load("@io_bazel_rules_scala//:scala_config.bzl", "scala_config") + +scala_config(scala_version = "2.13.12") + +load("@io_bazel_rules_scala//scala:scala.bzl", "scala_repositories") + +scala_repositories() + +load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains") + +rules_proto_dependencies() + +rules_proto_toolchains() + +load("@io_bazel_rules_scala//scala:toolchains.bzl", "scala_register_toolchains") + +scala_register_toolchains() + +load("@io_bazel_rules_scala//testing:scalatest.bzl", "scalatest_repositories", "scalatest_toolchain") + +scalatest_repositories() + +scalatest_toolchain() + +http_archive( + name = "aspect_bazel_lib", + sha256 = "6d758a8f646ecee7a3e294fbe4386daafbe0e5966723009c290d493f227c390b", + strip_prefix = "bazel-lib-2.7.7", + url = "https://github.com/aspect-build/bazel-lib/releases/download/v2.7.7/bazel-lib-v2.7.7.tar.gz", +) + +load("@aspect_bazel_lib//lib:repositories.bzl", "aspect_bazel_lib_dependencies", "aspect_bazel_lib_register_toolchains") + +# Required bazel-lib dependencies + +aspect_bazel_lib_dependencies() + +# Register bazel-lib toolchains + +aspect_bazel_lib_register_toolchains() + +http_archive( + name = "rules_oci", + sha256 = "647f4c6fd092dc7a86a7f79892d4b1b7f1de288bdb4829ca38f74fd430fcd2fe", + strip_prefix = "rules_oci-1.7.6", + url = "https://github.com/bazel-contrib/rules_oci/releases/download/v1.7.6/rules_oci-v1.7.6.tar.gz", +) + +load("@rules_oci//oci:dependencies.bzl", "rules_oci_dependencies") + +rules_oci_dependencies() + +load("@rules_oci//oci:repositories.bzl", "LATEST_CRANE_VERSION", "oci_register_toolchains") + +oci_register_toolchains( + name = "oci", + crane_version = LATEST_CRANE_VERSION, +) + +# You can pull your base images using oci_pull like this: +load("@rules_oci//oci:pull.bzl", "oci_pull") + +oci_pull( + name = "distroless_java", + digest = "sha256:161a1d97d592b3f1919801578c3a47c8e932071168a96267698f4b669c24c76d", + image = "gcr.io/distroless/java17", +) +``` + +Now, let's create *BUILD.bazel* step by step. First, create a `scala_binary` target for our app. It is safe to add dependencies, but they were omitted here for simplicity. + +**BUILD.bazel** + +```python +load("@io_bazel_rules_scala//scala:scala.bzl", "scala_binary") + +scala_binary( + name = "app", + srcs = ["App.scala"], + main_class = "App", +) +``` + +After that, we can package that binary into a layer using `tar` + +```python +load("@aspect_bazel_lib//lib:tar.bzl", "tar") + +tar( + name = "layer", + srcs = [":app_deploy.jar"], +) +``` + +Next, construct the image from base image and our new layer, using `oci_image` rule. The entrypoint is set to `java -jar /app_deploy.jar` so that the image can be run directly. + +```python +load("@rules_oci//oci:defs.bzl", "oci_image") + +oci_image( + name = "image", + base = "@distroless_java", + entrypoint = ["java", "-jar", "/app_deploy.jar"], + tars = [":layer"], +) +``` + +Finally, create a tarball from `oci_image` that can be loaded by a runtime such as docker. We specify `repo_tags` so that the image can be loaded by a registry. + +```python +load("@rules_oci//oci:defs.bzl", "oci_tarball") + +oci_tarball( + name = "tarball", + image = ":image", + repo_tags = ["my-repository:latest"], +) +``` + +Test if it works: + +```shell +$ bazel run //:tarball +... +$ docker run --rm my-repository:latest +Hello, world! +``` + +Complete `BUILD.bazel` file + +**BUILD.bazel** + +```python +load("@io_bazel_rules_scala//scala:scala.bzl", "scala_binary") +load("@aspect_bazel_lib//lib:tar.bzl", "tar") +load("@rules_oci//oci:defs.bzl", "oci_image", "oci_tarball") + +scala_binary( + name = "app", + srcs = ["App.scala"], + main_class = "App", +) + +tar( + name = "layer", + srcs = [":app_deploy.jar"], +) + +oci_image( + name = "image", + base = "@distroless_java", + entrypoint = ["java", "-jar", "/app_deploy.jar"], + tars = [":layer"], +) + +oci_tarball( + name = "tarball", + image = ":image", + repo_tags = ["my-repository:latest"], +) +```