diff --git a/oci/private/tarball.bzl b/oci/private/tarball.bzl index 93041106..1cae840f 100644 --- a/oci/private/tarball.bzl +++ b/oci/private/tarball.bzl @@ -51,13 +51,14 @@ def _tarball_impl(ctx): yq_bin = ctx.toolchains["@aspect_bazel_lib//lib:yq_toolchain_type"].yqinfo.bin bsdtar = ctx.toolchains["@aspect_bazel_lib//lib:tar_toolchain_type"] executable = ctx.actions.declare_file("{}/tarball.sh".format(ctx.label.name)) + mtree = ctx.actions.declare_file("_{}.mtree".format(ctx.label.name)) repo_tags = ctx.file.repo_tags substitutions = { "{{yq}}": yq_bin.path, "{{tar}}": bsdtar.tarinfo.binary.path, "{{image_dir}}": image.path, - "{{tarball_path}}": tarball.path, + "{{mtree_out}}": mtree.path, } if ctx.attr.repo_tags: @@ -72,16 +73,24 @@ def _tarball_impl(ctx): ctx.actions.run( executable = util.maybe_wrap_launcher_for_windows(ctx, executable), - inputs = depset( - direct = [image, repo_tags, executable], - transitive = [bsdtar.default.files], - ), - outputs = [tarball], + inputs = [image, repo_tags, executable], + outputs = [mtree], tools = [yq_bin], mnemonic = "OCITarball", progress_message = "OCI Tarball %{label}", ) + # TODO(2.0): this oci_tarball rule should just produce an mtree manifest instead, + # and then the tar rule can be composed in the oci_tarball macro in defs.bzl. + # To make it a non-breaking change, call the tar action ourselves instead. + ctx.actions.run( + executable = bsdtar.tarinfo.binary, + inputs = depset(direct = [mtree], transitive = [bsdtar.default.files]), + outputs = [tarball], + arguments = ["--create", "--file", tarball.path, "@" + mtree.path], + mnemonic = "Tar", + ) + exe = ctx.actions.declare_file(ctx.label.name + ".sh") ctx.actions.expand_template( diff --git a/oci/private/tarball.sh.tpl b/oci/private/tarball.sh.tpl index ca42f691..71d4b5a6 100644 --- a/oci/private/tarball.sh.tpl +++ b/oci/private/tarball.sh.tpl @@ -6,7 +6,7 @@ readonly YQ="{{yq}}" readonly TAR="{{tar}}" readonly IMAGE_DIR="{{image_dir}}" readonly BLOBS_DIR="${STAGING_DIR}/blobs" -readonly TARBALL_PATH="{{tarball_path}}" +readonly OUT="{{mtree_out}}" readonly TAGS_FILE="{{tags}}" MANIFEST_DIGEST=$(${YQ} eval '.manifests[0].digest | sub(":"; "/")' "${IMAGE_DIR}/index.json" | tr -d '"') @@ -35,4 +35,8 @@ layers="${LAYERS}" \ --null-input '.[0] = {"Config": env(config), "RepoTags": "${repo_tags}" | envsubst | split("%") | map(select(. != "")) , "Layers": env(layers) | map( "blobs/" + . + ".tar.gz") }' \ --output-format json > "${STAGING_DIR}/manifest.json" -${TAR} -C "${STAGING_DIR}" -cf "${TARBALL_PATH}" manifest.json blobs +# Write output in mtree format +# https://man.freebsd.org/cgi/man.cgi?mtree(8) +# so that tar produces a deterministic output. +echo "manifest.json uid=0 gid=0 mode=0755 type=file content=${STAGING_DIR}/manifest.json">>${OUT} +echo "blobs uid=0 gid=0 mode=0755 type=dir">>${OUT}