Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document how to use rules_oci with scala #630

Merged
merged 2 commits into from
Jun 19, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 203 additions & 1 deletion docs/scala.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,210 @@
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 <your jar>`.

## Example

An example is needed! Please consider contributing one. :pray:
lukaszwawrzyk marked this conversation as resolved.
Show resolved Hide resolved

For 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"],
)
```