Rules for creating container image layers from js_binary targets
For example, this js_image_layer target outputs node_modules.tar
and app.tar
with /app
prefix.
load("@aspect_rules_js//js:defs.bzl", "js_image_layer")
js_image_layer(
name = "layers",
binary = "//label/to:js_binary",
root = "/app",
)
js_image_layer(name, binary, compression, generate_empty_layers, owner, platform, root)
Create container image layers from js_binary targets.
By design, js_image_layer doesn't have any preference over which rule assembles the container image.
This means the downstream rule (oci_image
from rules_oci
or container_image
from rules_docker) must
set a proper workdir
and cmd
to for the container work.
A proper cmd
usually looks like /[ js_image_layer 'root' ]
/[ package name of js_image_layer 'binary' target ]/[ name of js_image_layer 'binary' target ]
,
unless you have a custom launcher script that invokes the entry_point of the js_binary
in a different path.
On the other hand, workdir
has to be set to the "runfiles tree root" which would be exactly cmd
but with .runfiles/[ name of the workspace ]
suffix.
If using bzlmod then name of the local workspace is always _main
. If bzlmod is not enabled then the name of the local workspace, if not otherwise specified
in the WORKSPACE
file, is __main__
. If workdir
is not set correctly, some attributes such as chdir
might not work properly.
js_image_layer creates up to 5 layers depending on what files are included in the runfiles of the provided
binary
target.
node
layer contains the Node.js toolchainpackage_store_3p
layer contains all 3p npm deps in thenode_modules/.aspect_rules_js
package storepackage_store_1p
layer contains all 1p npm deps in thenode_modules/.aspect_rules_js
package storenode_modules
layer contains allnode_modules/*
symlinks which point into the package storeapp
layer contains all files that don't fall into any of the above layers
If no files are found in the runfiles of the binary
target for one of the layers above, that
layer is not generated. All generated layer tarballs are provided as DefaultInfo
files.
The rules_js
node_modules/.aspect_rules_js
package store follows the same pattern as the pnpmnode_modules/.pnpm
virtual store. For more information see https://pnpm.io/symlinked-node-modules-structure.
js_image_layer also provides an OutputGroupInfo
with outputs for each of the layers above which
can be used to reference an individual layer with using filegroup
with output_group
. For example,
js_image_layer(
name = "layers",
binary = ":bin",
root = "/app",
)
filegroup(
name = "app_tar",
srcs = [":layers"],
output_group = "app",
)
WARNING: The structure of the generated layers are not subject to semver guarantees and may change without a notice. However, it is guaranteed to work when all generated layers are provided together in the order specified above.
js_image_layer supports transitioning to specific platform
to allow building multi-platform container images.
A partial example using rules_oci with transition to linux/amd64 platform.
load("@aspect_rules_js//js:defs.bzl", "js_binary", "js_image_layer")
load("@rules_oci//oci:defs.bzl", "oci_image")
js_binary(
name = "bin",
entry_point = "main.js",
)
platform(
name = "amd64_linux",
constraint_values = [
"@platforms//os:linux",
"@platforms//cpu:x86_64",
],
)
js_image_layer(
name = "layers",
binary = ":bin",
platform = ":amd64_linux",
root = "/app",
)
oci_image(
name = "image",
cmd = ["/app/bin"],
entrypoint = ["bash"],
tars = [
":layers"
],
workdir = select({
"@aspect_bazel_lib//lib:bzlmod": "/app/bin.runfiles/_main",
"//conditions:default": "/app/bin.runfiles/__main__",
}),
)
A partial example using rules_oci to create multi-platform images.
load("@aspect_rules_js//js:defs.bzl", "js_binary", "js_image_layer")
load("@rules_oci//oci:defs.bzl", "oci_image", "oci_image_index")
js_binary(
name = "bin",
entry_point = "main.js",
)
[
platform(
name = "linux_{}".format(arch),
constraint_values = [
"@platforms//os:linux",
"@platforms//cpu:{}".format(arch if arch != "amd64" else "x86_64"),
],
)
js_image_layer(
name = "{}_layers".format(arch),
binary = ":bin",
platform = ":linux_{arch}",
root = "/app",
)
oci_image(
name = "{}_image".format(arch),
cmd = ["/app/bin"],
entrypoint = ["bash"],
tars = [
":{}_layers".format(arch)
],
workdir = select({
"@aspect_bazel_lib//lib:bzlmod": "/app/bin.runfiles/_main",
"//conditions:default": "/app/bin.runfiles/__main__",
}),
)
for arch in ["amd64", "arm64"]
]
oci_image_index(
name = "image",
images = [
":arm64_image",
":amd64_image"
]
)
An example using legacy rules_docker
See e2e/js_image_docker
for full example.
load("@aspect_rules_js//js:defs.bzl", "js_binary", "js_image_layer")
load("@io_bazel_rules_docker//container:container.bzl", "container_image")
js_binary(
name = "bin",
data = [
"//:node_modules/args-parser",
],
entry_point = "main.js",
)
js_image_layer(
name = "layers",
binary = ":bin",
root = "/app",
visibility = ["//visibility:__pkg__"],
)
filegroup(
name = "node_tar",
srcs = [":layers"],
output_group = "node",
)
container_layer(
name = "node_layer",
tars = [":node_tar"],
)
filegroup(
name = "package_store_3p_tar",
srcs = [":layers"],
output_group = "package_store_3p",
)
container_layer(
name = "package_store_3p_layer",
tars = [":package_store_3p_tar"],
)
filegroup(
name = "package_store_1p_tar",
srcs = [":layers"],
output_group = "package_store_1p",
)
container_layer(
name = "package_store_1p_layer",
tars = [":package_store_1p_tar"],
)
filegroup(
name = "node_modules_tar",
srcs = [":layers"],
output_group = "node_modules",
)
container_layer(
name = "node_modules_layer",
tars = [":node_modules_tar"],
)
filegroup(
name = "app_tar",
srcs = [":layers"],
output_group = "app",
)
container_layer(
name = "app_layer",
tars = [":app_tar"],
)
container_image(
name = "image",
cmd = ["/app/bin"],
entrypoint = ["bash"],
layers = [
":node_layer",
":package_store_3p_layer",
":package_store_1p_layer",
":node_modules_layer",
":app_layer",
],
workdir = select({
"@aspect_bazel_lib//lib:bzlmod": "/app/bin.runfiles/_main",
"//conditions:default": "/app/bin.runfiles/__main__",
}),
)
ATTRIBUTES
Name | Description | Type | Mandatory | Default |
---|---|---|---|---|
name | A unique name for this target. | Name | required | |
binary | Label to an js_binary target | Label | required | |
compression | Compression algorithm. Can be one of gzip , none . |
String | optional | "gzip" |
generate_empty_layers | Generate layers even if they are empty. Helpful when using js_image_layer with rules_docker. See #1714 for more info |
Boolean | optional | False |
owner | Owner of the entries, in GID:UID format. By default 0:0 (root, root) is used. |
String | optional | "0:0" |
platform | Platform to transition. | Label | optional | None |
root | Path where the files from js_binary will reside in. eg: /apps/app1 or /app | String | optional | "" |
js_image_layer_lib.implementation(ctx)
PARAMETERS
Name | Description | Default Value |
---|---|---|
ctx | - |
none |