Skip to content

Commit

Permalink
Enable: bazel run @rules_apko//apko resolve ./apko.yaml (#36)
Browse files Browse the repository at this point in the history
Adds @rules_apko//apko(:apko) target that is executable and runs apko
(resolved by toolchain) in the working directory.

This simplify processes as avoids needs to install (and keep in sync)
separate instance of apko outside of bazel.

Please see the `./examples/resolve.sh` file for the examplar usage.

---------

Signed-off-by: Piotr Tabor <piotr.tabor@snowflake.com>
Signed-off-by: Jason Hall <jason@chainguard.dev>
Co-authored-by: Jason Hall <jason@chainguard.dev>
  • Loading branch information
sfc-gh-ptabor and imjasonh authored Nov 29, 2023
1 parent 53076d1 commit 092cf1f
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 28 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@ using the GitHub-provided source archive like

Apko usage begins with an `apko.yaml` configuration file. The `apko resolve` tool will create a corresponding
`apko.resolved.json` file, and this is where Bazel will read to fetch external content.
Assuming `apko_rules` are already loaded in your `MODULE.bazel` or `WORKSPACE` file one can call:
`bazel run @rules_apko//apko resolve ./apko.yaml` to lock the dependencies and generate `apko.resolved.json` file.

First you import these base layers into Bazel:
Than you import these base layers into Bazel:

- With Bazel 6 and [bzlmod], call `apk.translate_lock` in `MODULE.bazel`
- Otherwise, call `translate_apko_lock` in `WORKSPACE`

Then, call `apko resolve path/to/apko.yaml` to generate `apko.resolved.json` and use the `apko_image` rule to build the image, producing an OCI format output.
Periodically one can call `apko resolve path/to/apko.yaml` or `bazel run @rules_apko//apko resolve path/to/apko.yaml`
to regenerate `apko.resolved.json` and use the `apko_image` rule to build the image, producing an OCI format output.

Finally, we recommend using <https://github.com/bazel-contrib/rules_oci> as the next step in your Bazel build
to add application code from your repo as the next layers of the image.
Expand Down
9 changes: 9 additions & 0 deletions apko/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
load("//apko/private:resolved_toolchain.bzl", "resolved_toolchain")
load("@rules_apko//apko/private:apko_run.bzl", "apko_run")

# For stardoc to reference the files
exports_files([
Expand Down Expand Up @@ -65,3 +66,11 @@ bzl_library(
srcs = ["toolchain.bzl"],
visibility = ["//visibility:public"],
)

# Enables calling apko tool directly by bazel.
# To resolve given `./apko.yaml` file into `./apko.resolved.json`, once can call:
# e.g. (cd ./examples/oci; bazel run @rules_apko//apko resolve ./apko.yaml)
apko_run(
name = "apko",
visibility = ["//visibility:public"],
)
43 changes: 23 additions & 20 deletions apko/private/apk.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -126,31 +126,34 @@ APK_KEYRING_TMPL = """\
# Generated by apk_import. DO NOT EDIT
filegroup(
name = "keyring",
srcs = glob(["**/*.pub"]),
srcs = ["{public_key}"],
visibility = ["//visibility:public"]
)
"""

def _apk_keyring_impl(rctx):
scheme = "https"
url = rctx.attr.url
if url.startswith("http://"):
url = url[len("http://"):]
scheme = "http"
if url.startswith("https://"):
url = url[len("https://"):]

# split at first slash once to get base url and the path
url_split = url.split("/", 1)

path = url_split[1]
repo = util.url_escape("{}://{}/".format(scheme, url_split[0]))
def _cachePathFromURL(url):
"""
Translates URL to a name of local directory that can be used to represent prefetched content of the URL.
Mimicks https://github.com/chainguard-dev/go-apk/blob/7b08e8f3b0fcaa0f0a44757aedf23f6778cd8e4f/pkg/apk/cache.go#L326C6-L326C22
Is interprets URL as following path: {repo}/{arch}/{file} [but also used for keyring files that don't obey {arch} part].
Examples:
https://packages.wolfi.dev/os/wolfi-signing.rsa.pub -> https%3A%2F%2Fpackages.wolfi.dev%2F/os/wolfi-signing.rsa.pub
https://packages.wolfi.dev/os/aarch64/sqlite-libs-3.44.0-r0.apk -> https%3A%2F%2Fpackages.wolfi.dev%2Fos/arch64/sqlite-libs-3.44.0-r0.apk
"""
url_split = url.rsplit("/", 2)
repo = url_split[0]
if len(repo.split("/")) <= 3:
# Seems the Apko adds additional "/" if the URL is short.
repo += "/"
repo_escaped = util.url_escape(repo)
return "{}/{}/{}".format(repo_escaped, url_split[1], url_split[2])

rctx.download(
url = [rctx.attr.url],
output = "{}/{}".format(repo, path),
)
rctx.file("BUILD.bazel", APK_KEYRING_TMPL)
def _apk_keyring_impl(rctx):
public_key = _cachePathFromURL(rctx.attr.url)
rctx.download(url = [rctx.attr.url], output = public_key)
rctx.file("BUILD.bazel", APK_KEYRING_TMPL.format(public_key = public_key))

apk_keyring = repository_rule(
implementation = _apk_keyring_impl,
Expand Down
47 changes: 47 additions & 0 deletions apko/private/apko_run.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"A rule for running apko - convenience layer to stay within consistent versions."

_ATTRS = {
}

_DOC = """
apko_run is used internally to defune @rules_apko//apko target that allows to run an apko tool in the version supplied by Bazel.
Thanks to this, `bazel run @rules_apko//apko {flags}` can be called, without need to download/install apko outside of Bazel.
The workdir of the running command is the directory from which bazel has been called.
"""

LAUNCHER_TEMPLATE = """
#!#!/usr/bin/env sh
set -e
LAUNCHER_DIR="${PWD}"
if test "${BUILD_WORKING_DIRECTORY+x}"; then
cd $BUILD_WORKING_DIRECTORY
fi
echo "Workdir: ${PWD}" >&2
${LAUNCHER_DIR}/{{apko_binary}} "${@:1}"
"""

def _impl(ctx):
output = ctx.actions.declare_file("_{}_run.sh".format(ctx.label.name))
apko_info = ctx.toolchains["@rules_apko//apko:toolchain_type"].apko_info

ctx.actions.write(output = output, content = LAUNCHER_TEMPLATE.replace("{{apko_binary}}", apko_info.binary.path), is_executable = True)

return DefaultInfo(executable = output, runfiles = ctx.runfiles(files = [apko_info.binary]))

apko_run_lib = struct(
attrs = _ATTRS,
documentation = _DOC,
implementation = _impl,
toolchains = ["@rules_apko//apko:toolchain_type"],
)

apko_run = rule(
implementation = apko_run_lib.implementation,
attrs = apko_run_lib.attrs,
toolchains = apko_run_lib.toolchains,
doc = apko_run_lib.documentation,
executable = True,
)
7 changes: 1 addition & 6 deletions examples/resolve.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,4 @@

set -e -x

for d in $(find . -name apko.yaml); do
(
cd $(dirname ${d})
docker run -v "$PWD":/work cgr.dev/chainguard/apko@sha256:d5e219c1ceb2e7d56a5933df54819467e5b3331098ea8bebc996fdd30f974f33 resolve /work/apko.yaml
)
done
find . -name apko.yaml | xargs bazel run @rules_apko//apko resolve

0 comments on commit 092cf1f

Please sign in to comment.