From ffa88a045d08a3ffcfbcbc59eed08686ddfeec0a Mon Sep 17 00:00:00 2001 From: Alex Eagle Date: Mon, 25 Mar 2024 13:47:13 -0700 Subject: [PATCH] feat: publish multi-arch images (#199) --- WORKSPACE | 48 ++++++++++------ cmd/bb_copy/BUILD.bazel | 9 +-- cmd/bb_replicator/BUILD.bazel | 9 +-- cmd/bb_storage/BUILD.bazel | 9 +-- tools/BUILD.bazel | 12 ++++ tools/container.bzl | 55 +++++++++++++++++-- .../workflows_template.libsonnet | 1 + 7 files changed, 103 insertions(+), 40 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index df4f9891..ce21b0c1 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -12,9 +12,21 @@ http_archive( ) http_archive( - name = "io_bazel_rules_docker", - sha256 = "b1e80761a8a8243d03ebca8845e9cc1ba6c82ce7c5179ce2b295cd36f7e394bf", - urls = ["https://github.com/bazelbuild/rules_docker/releases/download/v0.25.0/rules_docker-v0.25.0.tar.gz"], + name = "aspect_bazel_lib", + sha256 = "6c25c59581041ede31e117693047f972cc4700c89acf913658dc89d04c338f8d", + strip_prefix = "bazel-lib-2.5.3", + url = "https://github.com/aspect-build/bazel-lib/releases/download/v2.5.3/bazel-lib-v2.5.3.tar.gz", +) + +load("@aspect_bazel_lib//lib:repositories.bzl", "register_expand_template_toolchains") + +register_expand_template_toolchains() + +http_archive( + name = "rules_oci", + sha256 = "4a276e9566c03491649eef63f27c2816cc222f41ccdebd97d2c5159e84917c3b", + strip_prefix = "rules_oci-1.7.4", + url = "https://github.com/bazel-contrib/rules_oci/releases/download/v1.7.4/rules_oci-v1.7.4.tar.gz", ) http_archive( @@ -36,7 +48,7 @@ http_archive( ], ) -load("@bazel_gazelle//:deps.bzl", "go_repository") +load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository") # Override the version of gomock to one that includes support for # generating mocks for function types. We can't do this through go.mod, @@ -61,21 +73,25 @@ go_rules_dependencies() go_register_toolchains(version = "1.21.5") -load("@io_bazel_rules_docker//repositories:repositories.bzl", container_repositories = "repositories") - -container_repositories() - -load("@io_bazel_rules_docker//repositories:deps.bzl", container_deps = "deps") - -container_deps() - -load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies") - gazelle_dependencies() -load("@io_bazel_rules_docker//go:image.bzl", _go_image_repos = "repositories") +load("@rules_oci//oci:pull.bzl", "oci_pull") +load("@rules_oci//oci:repositories.bzl", "LATEST_CRANE_VERSION", "oci_register_toolchains") -_go_image_repos() +oci_register_toolchains( + name = "oci", + crane_version = LATEST_CRANE_VERSION, +) + +oci_pull( + name = "distroless_static", + digest = "sha256:7e5c6a2a4ae854242874d36171b31d26e0539c98fc6080f942f16b03e82851ab", + image = "gcr.io/distroless/static", + platforms = [ + "linux/amd64", + "linux/arm64/v8", + ], +) http_archive( name = "com_github_bazelbuild_buildtools", diff --git a/cmd/bb_copy/BUILD.bazel b/cmd/bb_copy/BUILD.bazel index 79205db3..a91d0226 100644 --- a/cmd/bb_copy/BUILD.bazel +++ b/cmd/bb_copy/BUILD.bazel @@ -1,6 +1,5 @@ -load("//tools:container.bzl", "container_push_official") -load("@io_bazel_rules_docker//go:image.bzl", "go_image") load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") +load("//tools:container.bzl", "container_push_official", "multiarch_go_image") go_library( name = "bb_copy_lib", @@ -27,11 +26,9 @@ go_binary( visibility = ["//visibility:public"], ) -go_image( +multiarch_go_image( name = "bb_copy_container", - embed = [":bb_copy_lib"], - pure = "on", - visibility = ["//visibility:public"], + binary = ":bb_copy", ) container_push_official( diff --git a/cmd/bb_replicator/BUILD.bazel b/cmd/bb_replicator/BUILD.bazel index b6bf71dc..0149cd3a 100644 --- a/cmd/bb_replicator/BUILD.bazel +++ b/cmd/bb_replicator/BUILD.bazel @@ -1,6 +1,5 @@ -load("//tools:container.bzl", "container_push_official") -load("@io_bazel_rules_docker//go:image.bzl", "go_image") load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") +load("//tools:container.bzl", "container_push_official", "multiarch_go_image") go_library( name = "bb_replicator_lib", @@ -29,11 +28,9 @@ go_binary( visibility = ["//visibility:public"], ) -go_image( +multiarch_go_image( name = "bb_replicator_container", - embed = [":bb_replicator_lib"], - pure = "on", - visibility = ["//visibility:public"], + binary = ":bb_replicator", ) container_push_official( diff --git a/cmd/bb_storage/BUILD.bazel b/cmd/bb_storage/BUILD.bazel index 189057f8..a4a12daf 100644 --- a/cmd/bb_storage/BUILD.bazel +++ b/cmd/bb_storage/BUILD.bazel @@ -1,6 +1,5 @@ -load("//tools:container.bzl", "container_push_official") -load("@io_bazel_rules_docker//go:image.bzl", "go_image") load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") +load("//tools:container.bzl", "container_push_official", "multiarch_go_image") go_library( name = "bb_storage_lib", @@ -37,11 +36,9 @@ go_binary( visibility = ["//visibility:public"], ) -go_image( +multiarch_go_image( name = "bb_storage_container", - embed = [":bb_storage_lib"], - pure = "on", - visibility = ["//visibility:public"], + binary = ":bb_storage", ) container_push_official( diff --git a/tools/BUILD.bazel b/tools/BUILD.bazel index 4f5694e5..f3177c5f 100644 --- a/tools/BUILD.bazel +++ b/tools/BUILD.bazel @@ -1,3 +1,4 @@ +load("@aspect_bazel_lib//lib:expand_template.bzl", "expand_template") load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( @@ -11,3 +12,14 @@ go_library( "@org_golang_x_lint//:lint", ], ) + +# When built with --stamp, creates a non-deterministic output file for pushing images to a remote registry. +# With --nostamp, produces a deterministic output so dependents get cache hits. +expand_template( + name = "stamped_tags", + out = "_stamped.tags.txt", + stamp_substitutions = {"_TAG_": "{BUILD_SCM_TIMESTAMP}-{BUILD_SCM_REVISION}"}, + substitutions = {"_TAG_": "0.0.0"}, + template = ["_TAG_"], + visibility = ["//visibility:public"], +) diff --git a/tools/container.bzl b/tools/container.bzl index f50880b7..9748f747 100644 --- a/tools/container.bzl +++ b/tools/container.bzl @@ -1,11 +1,54 @@ -load("@io_bazel_rules_docker//container:container.bzl", "container_push") +load("@aspect_bazel_lib//lib:transitions.bzl", "platform_transition_filegroup") +load("@rules_oci//oci:defs.bzl", "oci_image", "oci_image_index", "oci_push") +load("@rules_pkg//pkg:tar.bzl", "pkg_tar") + +def multiarch_go_image(name, binary): + """Create a container image with two variants of the given go_binary target. + + Args: + name: resulting oci_image_index target + binary: label of a go_binary target; it may be transitioned to another architecture + """ + images = [] + tar_target = "_{}.tar".format(name) + image_target = "_{}.image".format(name) + + pkg_tar( + name = tar_target, + srcs = [binary], + include_runfiles = True, + package_dir = "app", + ) + + oci_image( + name = image_target, + base = "@distroless_static", + entrypoint = ["/app/{}".format(native.package_relative_label(binary).name)], + tars = [tar_target], + # Don't build un-transitioned images, as the default target architecture might be unsupported + # For example when building on linux-i386. + tags = ["manual"], + ) + + for arch in ["amd64", "arm64"]: + arch_image_target = "{}_{}_image".format(name, arch) + target_platform = "@io_bazel_rules_go//go/toolchain:linux_{}".format(arch) + images.append(arch_image_target) + platform_transition_filegroup( + name = arch_image_target, + srcs = [image_target], + target_platform = target_platform, + ) + + oci_image_index( + name = name, + images = images, + ) def container_push_official(name, image, component): - container_push( + oci_push( name = name, - format = "Docker", image = image, - registry = "ghcr.io", - repository = "buildbarn/" + component, - tag = "{BUILD_SCM_TIMESTAMP}-{BUILD_SCM_REVISION}", + repository = "ghcr.io/buildbarn/" + component, + remote_tags = "@com_github_buildbarn_bb_storage//tools:stamped_tags", ) diff --git a/tools/github_workflows/workflows_template.libsonnet b/tools/github_workflows/workflows_template.libsonnet index ab43c505..e0aff86e 100644 --- a/tools/github_workflows/workflows_template.libsonnet +++ b/tools/github_workflows/workflows_template.libsonnet @@ -41,6 +41,7 @@ buildAndTestCommand: 'build', // Building '//...' is broken for FreeBSD, because rules_docker // doesn't want to initialize properly. + // TODO(who?): now that rules_docker is removed, this could be revisited buildJustBinaries: true, extension: '', },