Bazel Rules for apko

Build secure, minimal Wolfi-based container images using Bazel

rules_apko is an open source plugin for Bazel that makes it possible to build secure, minimal Wolfi-based container images using the popular Bazel build system. This wraps the apko tool for use under Bazel.

Prerequisites

First, be sure you have Bazel installed, you can follow the Bazel installation guide for more details.

Next, rules_apko requires a one-time setup to configure Bazel to be able to make partial fetches.

Paste the following into your root BUILD file.

load("@rules_apko//apko:defs.bzl", "apko_bazelrc")

apko_bazelrc()

Note: By default, apko_bazelrc will generate .bazelrc to accomodate for fetching from dl-cdn.alpinelinux.org and packages.wolfi.dev. this can be configured by passing the repositories attribute to apko_bazelrc() call.

Then, run the following command.

bazel run @@//:apko_bazelrc && chmod +x .apko/range.sh

Finally, paste this into your preferred `.bazelrc` file,

# Required for rules_apko to make range requests
try-import %workspace%/.apko/.bazelrc

Review additional initial setup documentation updates in the rules_apko repo.

Installation

To install v1.0.0, you can follow one of the options below. For other releases, follow the instructions in the release notes from the release you wish to use: https://github.com/chainguard-dev/rules_apko/releases.

Using Bzlmod with Bazel 6

  1. Enable with common --enable_bzlmod in .bazelrc.
  2. Add to your MODULE.bazel file:
bazel_dep(name = "rules_apko", version = "1.0.0-rc1")

Using WORKSPACE

Paste this snippet into your file:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "rules_apko",
    sha256 = "5c91a2322bec84a0005dd8178495775938b581053812e70d21b030bef3625623",
    strip_prefix = "rules_apko-1.0.0-rc1",
    url = "https://github.com/chainguard-dev/rules_apko/releases/download/v1.0.0-rc1/rules_apko-v1.0.0-rc1.tar.gz",
)

######################
# rules_apko setup #
######################
# Fetches the rules_apko dependencies.
# If you want to have a different version of some dependency,
# you should fetch it *before* calling this.
# Alternatively, you can skip calling this function, so long as you've
# already fetched all the dependencies.
load("@rules_apko//apko:repositories.bzl", "apko_register_toolchains", "rules_apko_dependencies")

rules_apko_dependencies()

apko_register_toolchains(name = "apko")

load("@rules_apko//apko:translate_lock.bzl", "translate_apko_lock")

translate_apko_lock(
    name = "example_lock",
    lock = "@//:apko.lock.json",
)

load("@example_lock//:repositories.bzl", "apko_repositories")

apko_repositories()

Rules

Public API re-exports

apko_image

apko_image(name, architecture, args, config, contents, output, tag)

Build OCI images from APK packages directly without Dockerfile.

This rule creates Alpine images using the apko.yaml configuration file and relies on cache contents generated by translate_lock to be fast.

apko_image(
    name = "example",
    config = "apko.yaml",
    contents = "@example_lock//:contents",
    tag = "example:latest",
)

The label @example_lock//:contents is generated by the translate_lock extension, which consumes an ‘apko.lock.json’ file. For more details, refer to the apko cache documentation.

An example demonstrating usage with rules_oci:

apko_image(
    name = "alpine_base",
    config = "apko.yaml",
    contents = "@alpine_base_lock//:contents",
    tag = "alpine_base:latest",
)

oci_image(
    name = "app",
    base = ":alpine_base"
)

For more examples checkout the examples directory.

Attributes

NameDescriptionTypeMandatoryDefault
nameA unique name for this target.Namerequired
architecturethe CPU architecture which this image should be built to run on. See https://github.com/chainguard-dev/apko/blob/main/docs/apko_file.md#archs-top-level-elementStringoptional""
argsadditional arguments to provide when running the apko build command.List of stringsoptional[]
configLabel to the apko.yaml file.Labelrequired
contentsLabel to the contents repository generated by translate_lock. See apko-cache documentation.Labelrequired
output-Stringoptional“oci”
tagtag to apply to the resulting docker tarball. only applicable when output is dockerStringrequired

apko_bazelrc

apko_bazelrc(name, repositories, kwargs)

Helper macro for generating .bazelrc and range.sh files to allow for partial package fetches.

Review Prerequisites documentation for more information.

Parameters

NameDescriptionDefault Value
namename of the target“apko_bazelrc”
repositorieslist of repositories to generate .bazelrc for[“dl-cdn.alpinelinux.org”, “packages.wolfi.dev”]
kwargspassed to expanding targets. only well known attributes such as tags testonly ought to be present.none

Fetching and Caching Contents

To ensure efficient operation, the apko_image rule must maintain a cache of remote contents that it fetches from repositories. While outside of Bazel, apko manages its own cache, under Bazel, the cache must be maintained by Bazel to ensure correctness and speed. Therefore, Bazel needs to know what needs to be fetched and from where to cache these HTTP requests and provide them to apko as required.

The apko.lock.json file contains all the necessary information about how to perform the HTTP fetches required by apko to build the container image.

Generating the Lock File

Note: Documentation for lockfile generation will be added to the repository docs once the apko resolve command is available.

Using translate_lock

Having just the apko.lock.json file alone is insufficient; all the information needs to be converted into apk_<content_type> repository calls to make them accessible to Bazel. The translate_lock tool accomplishes this by taking the apko.lock.json file and dynamically generating the required Bazel repositories.

translate_lock will create a new bazel repository named after itself. This repository will also have a target named contents, which you can pass to apko_image:

apko_image(
    name = "lock",
    config = "apko.yaml",
    # name of the repository is the same translate_lock!
    contents = "@examples_lock//:contents",
    tag = "lock:latest",
)

Usage with bzlmod

apk = use_extension("//apko:extensions.bzl", "apko")

apk.translate_lock(
    name = "examples_lock",
    lock = "//path/to/lock:apko.lock.json",
)
use_repo(apk, "examples_lock")

Usage with Workspace

load("@rules_apko//apko:translate_lock.bzl", "translate_apko_lock")

translate_apko_lock(
    name = "example_lock",
    lock = "//path/to/lock:apko.lock.json",
)

load("@example_lock//:repositories.bzl", "apko_repositories")

apko_repositories()

Repository rules for translating apko.lock.json

translate_apko_lock

translate_apko_lock(name, lock, repo_mapping, target_name)

Repository rule to generate starlark code from an apko.lock.json file.

Review the section above for more information.

Attributes

NameDescriptionTypeMandatoryDefault
nameA unique name for this repository.Namerequired
locklabel to the apko.lock.json file.Labelrequired
repo_mappingA dictionary from local repository name to global repository name. This allows controls over workspace dependency resolution for dependencies of this repository.<p>For example, an entry "@foo": “@bar” declares that, for any time this repository depends on @foo (such as a dependency on @foo//some:target, it should actually resolve that dependency within globally-declared @bar (@bar//some:target).Dictionary: String -> Stringrequired
target_nameinternal. do not use!Stringoptional""

Last updated: 2023-10-23 16:49