diff --git a/MODULE.bazel b/MODULE.bazel index 5b906517..f3b4e42a 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -6,7 +6,7 @@ module( compatibility_level = 1, ) -bazel_dep(name = "aspect_bazel_lib", version = "2.7.2") +bazel_dep(name = "aspect_bazel_lib", version = "2.9.3") bazel_dep(name = "bazel_features", version = "1.10.0") bazel_dep(name = "bazel_skylib", version = "1.5.0") bazel_dep(name = "platforms", version = "0.0.8") diff --git a/examples/dockerfile/BUILD.bazel b/examples/dockerfile/BUILD.bazel index 887476d4..1b4194dc 100644 --- a/examples/dockerfile/BUILD.bazel +++ b/examples/dockerfile/BUILD.bazel @@ -1,3 +1,4 @@ +load("@aspect_bazel_lib//lib:tar.bzl", "mtree_mutate", "mtree_spec", "tar") load("@aspect_bazel_lib//lib:run_binary.bzl", "run_binary") load("@bazel_skylib//rules:native_binary.bzl", "native_binary") load("@configure_buildx//:defs.bzl", "BUILDER_NAME", "TARGET_COMPATIBLE_WITH") @@ -87,3 +88,95 @@ assert_oci_image_command( || || """, ) + +# This tar can be arranged in arbitrary ways. +# For example, one could grab pkg_files from elsewhere in the workspace. +# Anything in the tar can be used by a Dockerfile ADD or COPY. +mtree_spec( + name = "buildx_context_tree", + srcs = [ + "context.Dockerfile", + "requirements.txt", + ] + glob([ + "src/*", + ]), +) + +mtree_mutate( + name = "buildx_context_tree_prefixed", + mtree = ":buildx_context_tree", + strip_prefix = package_name(), + package_dir = "/app", +) + +tar( + name = "buildx_context", + mtree = ":buildx_context_tree_prefixed", +) + +# In order to use a local tar as a context, we must pipe the tar into the BuildX binary +# This is a bash operation, so we must wrap the BuildX tool in an sh_binary that does this for us +sh_binary( + name = "buildx_wrapper", + srcs = [ + "buildx_wrapper.sh", + ], +) + +# This is similar to :base except it uses a tar file as a Docker context +run_binary( + name = "base_context", + tool = ":buildx_wrapper", + out_dirs = [ "base_context" ], + srcs = [ + ":buildx_context", + ":buildx", + ], + env = { + "BUILDX": "$(location :buildx)", + "BUILDER_NAME": BUILDER_NAME, + "OUTPUT_DIR": "$@", + "BUILDX_CONTEXT": "$(location :buildx_context)", + "DOCKER_FILE": "/app/context.Dockerfile", + "PLATFORM": "linux/amd64", + }, + execution_requirements = { + "local": "1", + "requires-network": "1", + }, + target_compatible_with = TARGET_COMPATIBLE_WITH, +) + +oci_image( + name = "image_context", + base = ":base_context", +) + +assert_oci_config( + name = "assert_metadata_context", + cmd_eq = ["/app/src/say.py"], + entrypoint_eq = None, + image = ":image_context", +) + +assert_oci_image_command( + name = "assert_cow_says_moo_context", + args = [ + "python", + "/app/src/say.py", + ], + exit_code_eq = 0, + image = ":image_context", + output_eq = """\ + ____ +| moo! | + ==== + \\ + \\ + ^__^ + (oo)\\_______ + (__)\\ )\\/\\ + ||----w | + || || +""", +) diff --git a/examples/dockerfile/Dockerfile b/examples/dockerfile/Dockerfile index a4a7c862..85aea135 100644 --- a/examples/dockerfile/Dockerfile +++ b/examples/dockerfile/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.11.9-bullseye +FROM python:3.11.9-bullseye@sha256:64da8e5fd98057b05db01b49289b774e9fa3b81e87b4883079f6c31fb141b252 ARG DEBIAN_FRONTEND=noninteractive diff --git a/examples/dockerfile/README.md b/examples/dockerfile/README.md index c2bb39a0..22432280 100644 --- a/examples/dockerfile/README.md +++ b/examples/dockerfile/README.md @@ -8,10 +8,14 @@ STOP before committing this atrocity. Here's some good reasons why you should no - Building the same Dockerfile one month apart will yield different results. - `FROM python:3.11.9-bullseye` is non-producible. +So you have chosen to walk this path... one thing that can be tricky is to get Bazel artifacts into the build context of Docker. Docker does not like symlinks or backtracking out of the build context. However, we can provide a complete context using [a tar file and piping it into BuildX](https://docs.docker.com/build/concepts/context/#local-tarballs). Since we have full control over tar layout in Bazel, we now have full control over the build context! + +This technique offers an easy migration path from `container_run_and_commit` in `rules_docker`, but use it sparingly. + # Resources -https://reproducible-builds.org/ -https://github.com/bazel-contrib/rules_oci/issues/35#issuecomment-1285954483 -https://github.com/bazel-contrib/rules_oci/blob/main/docs/compare_dockerfile.md -https://github.com/moby/moby/issues/43124 -https://medium.com/nttlabs/bit-for-bit-reproducible-builds-with-dockerfile-7cc2b9faed9f +- https://reproducible-builds.org/ +- https://github.com/bazel-contrib/rules_oci/issues/35#issuecomment-1285954483 +- https://github.com/bazel-contrib/rules_oci/blob/main/docs/compare_dockerfile.md +- https://github.com/moby/moby/issues/43124 +- https://medium.com/nttlabs/bit-for-bit-reproducible-builds-with-dockerfile-7cc2b9faed9f diff --git a/examples/dockerfile/buildx_wrapper.sh b/examples/dockerfile/buildx_wrapper.sh new file mode 100755 index 00000000..e35c5bed --- /dev/null +++ b/examples/dockerfile/buildx_wrapper.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -o errexit -o nounset -o pipefail + +$BUILDX \ + build - \ + --no-cache \ + --builder $BUILDER_NAME \ + --file $DOCKER_FILE \ + --platform $PLATFORM \ + --output=type=oci,tar=false,dest=$OUTPUT_DIR \ + < $BUILDX_CONTEXT diff --git a/examples/dockerfile/context.Dockerfile b/examples/dockerfile/context.Dockerfile new file mode 100644 index 00000000..9b9ae1d5 --- /dev/null +++ b/examples/dockerfile/context.Dockerfile @@ -0,0 +1,15 @@ +FROM python:3.11.9-bullseye@sha256:64da8e5fd98057b05db01b49289b774e9fa3b81e87b4883079f6c31fb141b252 + +ARG DEBIAN_FRONTEND=noninteractive + +ENV LC_ALL=C.UTF-8 \ + LANG=C.UTF-8 \ + LANGUAGE=C.UTF-8 + +ADD /app /app + +WORKDIR /app + +RUN pip install --root-user-action=ignore -r requirements.txt + +CMD ["/app/src/say.py"] \ No newline at end of file diff --git a/examples/dockerfile/requirements.txt b/examples/dockerfile/requirements.txt new file mode 100644 index 00000000..9c405168 --- /dev/null +++ b/examples/dockerfile/requirements.txt @@ -0,0 +1 @@ +cowsay==6.1.0 \ No newline at end of file