blob: 0576a2dfc339f85d4ae22b9affd28a86fbab3f49 [file] [log] [blame] [view]
<!-- Generated with Stardoc: http://skydoc.bazel.build -->
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.
```starlark
load("@aspect_rules_js//js:defs.bzl", "js_image_layer")
js_image_layer(
name = "layers",
binary = "//label/to:js_binary",
root = "/app",
)
```
<a id="js_image_layer"></a>
## js_image_layer
<pre>
js_image_layer(<a href="#js_image_layer-name">name</a>, <a href="#js_image_layer-binary">binary</a>, <a href="#js_image_layer-compression">compression</a>, <a href="#js_image_layer-directory_mode">directory_mode</a>, <a href="#js_image_layer-file_mode">file_mode</a>, <a href="#js_image_layer-generate_empty_layers">generate_empty_layers</a>,
<a href="#js_image_layer-layer_groups">layer_groups</a>, <a href="#js_image_layer-owner">owner</a>, <a href="#js_image_layer-platform">platform</a>, <a href="#js_image_layer-preserve_symlinks">preserve_symlinks</a>, <a href="#js_image_layer-root">root</a>)
</pre>
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](https://github.com/bazel-contrib/rules_oci)
or `container_image` from [rules_docker](https://github.com/bazelbuild/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.
1. `node` layer contains the Node.js toolchain
2. `package_store_3p` layer contains all 3p npm deps in the `node_modules/.aspect_rules_js` package store
3. `package_store_1p` layer contains all 1p npm deps in the `node_modules/.aspect_rules_js` package store
4. `node_modules` layer contains all `node_modules/*` symlinks which point into the package store
5. `app` 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 pnpm
> `node_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,
```starlark
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.**
```starlark
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.**
```starlark
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.
```starlark
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__",
}),
)
```
## Performance
For better performance, it is recommended to split the large parts of a `js_binary` to have a separate layer.
The matching order for layer groups is as follows:
1. `layer_groups` are checked in order first
2. If no match is found for `layer_groups`, the `default layer groups` are checked.
3. Any remaining files are placed into the app layer.
The default layer groups are as follows and always created.
```
{
"node": "/js/private/node-patches/|/bin/nodejs/",
"package_store_1p": "\.aspect_rules_js/.*@0\.0\.0/node_modules",
"package_store_3p": "\.aspect_rules_js/.*/node_modules",
"node_modules": "/node_modules/",
"app": "", # empty means just match anything.
}
```
**ATTRIBUTES**
| Name | Description | Type | Mandatory | Default |
| :------------- | :------------- | :------------- | :------------- | :------------- |
| <a id="js_image_layer-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
| <a id="js_image_layer-binary"></a>binary | Label to an js_binary target | <a href="https://bazel.build/concepts/labels">Label</a> | required | |
| <a id="js_image_layer-compression"></a>compression | Compression algorithm. See https://github.com/bazel-contrib/bazel-lib/blob/bdc6ade0ba1ebe88d822bcdf4d4aaa2ce7e2cd37/lib/private/tar.bzl#L29-L39 | String | optional | `"gzip"` |
| <a id="js_image_layer-directory_mode"></a>directory_mode | Mode of the directories, in `octal` format. By default `0755` is used. | String | optional | `"0755"` |
| <a id="js_image_layer-file_mode"></a>file_mode | Mode of the files, in `octal` format. By default `0555` is used. | String | optional | `"0555"` |
| <a id="js_image_layer-generate_empty_layers"></a>generate_empty_layers | DEPRECATED. An empty layer is always generated if the layer group have no matching files. | Boolean | optional | `False` |
| <a id="js_image_layer-layer_groups"></a>layer_groups | Layer groups to create. These are utilized to categorize files into distinct layers, determined by their respective paths. The expected format for each entry is "<key>": "<value>", where <key> MUST be a valid Bazel and JavaScript identifier (alphanumeric characters), and <value> MAY be either an empty string (signifying a universal match) or a valid regular expression. | <a href="https://bazel.build/rules/lib/dict">Dictionary: String -> String</a> | optional | `{}` |
| <a id="js_image_layer-owner"></a>owner | Owner of the entries, in `GID:UID` format. By default `0:0` (root, root) is used. | String | optional | `"0:0"` |
| <a id="js_image_layer-platform"></a>platform | Platform to transition. | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `None` |
| <a id="js_image_layer-preserve_symlinks"></a>preserve_symlinks | Preserve symlinks for entries matching the pattern. By default symlinks within the `node_modules` is preserved. | String | optional | `".*/node_modules/.*"` |
| <a id="js_image_layer-root"></a>root | Path where the files from js_binary will reside in. eg: /apps/app1 or /app | String | optional | `""` |
<a id="js_image_layer_lib.implementation"></a>
## js_image_layer_lib.implementation
<pre>
js_image_layer_lib.implementation(<a href="#js_image_layer_lib.implementation-ctx">ctx</a>)
</pre>
**PARAMETERS**
| Name | Description | Default Value |
| :------------- | :------------- | :------------- |
| <a id="js_image_layer_lib.implementation-ctx"></a>ctx | <p align="center"> - </p> | none |