diff --git a/MODULE.bazel b/MODULE.bazel index 711569b..98c11e9 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -7,11 +7,10 @@ module( ) bazel_dep(name = "bazel_skylib", version = "1.5.0") -bazel_dep(name = "aspect_bazel_lib", version = "2.0.0-rc1") +bazel_dep(name = "aspect_bazel_lib", version = "2.0.3") bazel_lib_toolchains = use_extension("@aspect_bazel_lib//lib:extensions.bzl", "toolchains") - -# bazel_lib_toolchains.tar() +bazel_lib_toolchains.tar() use_repo(bazel_lib_toolchains, "bsd_tar_toolchains") bazel_dep(name = "gazelle", version = "0.34.0", dev_dependency = True, repo_name = "bazel_gazelle") diff --git a/distroless/BUILD.bazel b/distroless/BUILD.bazel index 2f7d3bd..3815366 100644 --- a/distroless/BUILD.bazel +++ b/distroless/BUILD.bazel @@ -19,6 +19,7 @@ bzl_library( visibility = ["//visibility:public"], deps = [ "//distroless/private:cacerts", + "//distroless/private:flatten", "//distroless/private:group", "//distroless/private:home", "//distroless/private:java_keystore", diff --git a/distroless/defs.bzl b/distroless/defs.bzl index f05cfbb..ed90566 100644 --- a/distroless/defs.bzl +++ b/distroless/defs.bzl @@ -1,6 +1,7 @@ "Public API re-exports" load("//distroless/private:cacerts.bzl", _cacerts = "cacerts") +load("//distroless/private:flatten.bzl", _flatten = "flatten") load("//distroless/private:group.bzl", _group = "group") load("//distroless/private:home.bzl", _home = "home") load("//distroless/private:java_keystore.bzl", _java_keystore = "java_keystore") @@ -15,3 +16,4 @@ group = _group passwd = _passwd java_keystore = _java_keystore home = _home +flatten = _flatten diff --git a/distroless/private/BUILD.bazel b/distroless/private/BUILD.bazel index cb02b65..0d0c991 100644 --- a/distroless/private/BUILD.bazel +++ b/distroless/private/BUILD.bazel @@ -81,8 +81,16 @@ bzl_library( ], ) +bzl_library( + name = "flatten", + srcs = ["flatten.bzl"], + visibility = ["//distroless:__subpackages__"], + deps = [":tar"], +) + bzl_library( name = "tar", srcs = ["tar.bzl"], visibility = ["//distroless:__subpackages__"], + deps = ["@aspect_bazel_lib//lib:tar"], ) diff --git a/distroless/private/flatten.bzl b/distroless/private/flatten.bzl new file mode 100644 index 0000000..22b36fe --- /dev/null +++ b/distroless/private/flatten.bzl @@ -0,0 +1,47 @@ +"flatten" + +load(":tar.bzl", "tar_lib") + +_DOC = """Flatten multiple archives into single archive.""" + +def _flatten_impl(ctx): + bsdtar = ctx.toolchains[tar_lib.TOOLCHAIN_TYPE] + + ext = tar_lib.common.compression_to_extension[ctx.attr.compression] if ctx.attr.compression else ".tar" + output = ctx.actions.declare_file(ctx.attr.name + ext) + + args = ctx.actions.args() + args.add("--create") + tar_lib.common.add_compression_args(ctx.attr.compression, args) + args.add("--file", output) + args.add_all(ctx.files.tars, format_each = "@%s") + + ctx.actions.run( + executable = bsdtar.tarinfo.binary, + inputs = ctx.files.tars, + outputs = [output], + tools = bsdtar.default.files, + arguments = [args], + ) + + return [ + DefaultInfo(files = depset([output])), + ] + +flatten = rule( + doc = _DOC, + attrs = { + "tars": attr.label_list( + allow_files = tar_lib.common.accepted_tar_extensions, + mandatory = True, + allow_empty = False, + doc = "List of tars to flatten", + ), + "compression": attr.string( + doc = "Compress the archive file with a supported algorithm.", + values = tar_lib.common.accepted_compression_types, + ), + }, + implementation = _flatten_impl, + toolchains = [tar_lib.TOOLCHAIN_TYPE], +) diff --git a/distroless/private/home.bzl b/distroless/private/home.bzl index ab8f645..97d0ec8 100644 --- a/distroless/private/home.bzl +++ b/distroless/private/home.bzl @@ -16,7 +16,7 @@ def home(name, dirs, **kwargs): for home in dirs: mtree.extend( - tar_lib.add_directory_with_parents(home["home"], uid = str(home["uid"]), gid = str(home["gid"])), + tar_lib.mtree.add_directory_with_parents(home["home"], uid = str(home["uid"]), gid = str(home["gid"])), ) tar( diff --git a/distroless/private/tar.bzl b/distroless/private/tar.bzl index 408023d..931fc60 100644 --- a/distroless/private/tar.bzl +++ b/distroless/private/tar.bzl @@ -1,6 +1,6 @@ "mtree helpers" -BSDTAR_TOOLCHAIN = "@aspect_bazel_lib//lib:tar_toolchain_type" +load("@aspect_bazel_lib//lib:tar.bzl", tar = "tar_lib") DEFAULT_GID = "0" DEFAULT_UID = "0" @@ -50,7 +50,7 @@ def _add_directory_with_parents(path, **kwargs): return lines def _build_tar(ctx, mtree, output, inputs = [], compression = "gzip", mnemonic = "Tar"): - bsdtar = ctx.toolchains[BSDTAR_TOOLCHAIN] + bsdtar = ctx.toolchains[tar.toolchain_type] inputs = inputs[:] inputs.append(mtree) @@ -88,9 +88,12 @@ def _create_mtree(ctx): ) tar_lib = struct( + TOOLCHAIN_TYPE = tar.toolchain_type, create_mtree = _create_mtree, - line = _mtree_line, - add_directory_with_parents = _add_directory_with_parents, - add_file_with_parents = _add_file_with_parents, - TOOLCHAIN_TYPE = BSDTAR_TOOLCHAIN, + mtree = struct( + line = _mtree_line, + add_directory_with_parents = _add_directory_with_parents, + add_file_with_parents = _add_file_with_parents, + ), + common = tar.common, ) diff --git a/docs/rules.md b/docs/rules.md index 6ae8ba4..47c833d 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -50,6 +50,26 @@ cacerts( | package | - | Label | required | | + + +## flatten + +
+flatten(name, compression, tars)
+
+ +Flatten multiple archives into single archive. + +**ATTRIBUTES** + + +| Name | Description | Type | Mandatory | Default | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | +| compression | Compress the archive file with a supported algorithm. | String | optional | "" | +| tars | List of tars to flatten | List of labels | required | | + + ## java_keystore diff --git a/examples/flatten/BUILD.bazel b/examples/flatten/BUILD.bazel new file mode 100644 index 0000000..c8b05b4 --- /dev/null +++ b/examples/flatten/BUILD.bazel @@ -0,0 +1,66 @@ +load("@aspect_bazel_lib//lib:tar.bzl", "tar") +load("//distroless:defs.bzl", "flatten", "home", "passwd") +load("//distroless/tests:asserts.bzl", "assert_tar_listing") + +passwd( + name = "passwd", + passwds = [ + { + "gecos": ["root"], + "gid": 0, + "home": "root", + "shell": "usr/bin/bash", + "uid": 0, + "username": "root", + }, + ], +) + +home( + name = "home", + dirs = [ + { + "home": "root", + "uid": 0, + "gid": 0, + }, + { + "home": "home/nonroot", + "uid": 666, + "gid": 666, + }, + ], +) + +tar( + name = "source", + srcs = glob(["dir/**/*"]), + compress = "xz", +) + +flatten( + name = "flatten", + tars = [ + ":passwd", + ":home", + ":source", + ], +) + +assert_tar_listing( + name = "test_flatten", + actual = "flatten", + expected = """\ +#mtree +./etc/passwd nlink=0 time=0.0 mode=700 gid=0 uid=0 type=file size=33 cksum=3891093834 sha1digest=94f013494b98f8ed618ce2e670d405f818ec3915 +./examples time=1672560000.0 mode=755 gid=0 uid=0 type=dir +./examples/flatten time=1672560000.0 mode=755 gid=0 uid=0 type=dir +./examples/flatten/dir time=1672560000.0 mode=755 gid=0 uid=0 type=dir +./examples/flatten/dir/changelog nlink=0 time=1672560000.0 mode=755 gid=0 uid=0 type=file size=0 cksum=4294967295 sha1digest=da39a3ee5e6b4b0d3255bfef95601890afd80709 +./examples/flatten/dir/sub time=1672560000.0 mode=755 gid=0 uid=0 type=dir +./examples/flatten/dir/sub/content.txt nlink=0 time=1672560000.0 mode=755 gid=0 uid=0 type=file size=0 cksum=4294967295 sha1digest=da39a3ee5e6b4b0d3255bfef95601890afd80709 +./home time=0.0 mode=755 gid=0 uid=0 type=dir +./home/nonroot time=0.0 mode=755 gid=666 uid=666 type=dir +./root time=0.0 mode=755 gid=0 uid=0 type=dir +""", +) diff --git a/examples/flatten/dir/changelog b/examples/flatten/dir/changelog new file mode 100644 index 0000000..e69de29 diff --git a/examples/flatten/dir/sub/content.txt b/examples/flatten/dir/sub/content.txt new file mode 100644 index 0000000..e69de29