diff --git a/.bazelrc b/.bazelrc index 66aa14b..be64676 100644 --- a/.bazelrc +++ b/.bazelrc @@ -21,6 +21,10 @@ build --java_runtime_version=remotejdk_11 # https://github.com/GoogleContainerTools/rules_distroless/actions/runs/7118944984/job/19382981899?pr=9#step:8:51 common:linux --sandbox_tmpfs_path=/tmp + +# Allow external dependencies to be retried. debian snapshot is unreliable and needs retries. +common --experimental_repository_downloader_retries=10 + # Load any settings specific to the current user. # .bazelrc.user should appear in .gitignore so that settings are not shared with team members # This needs to be last statement in this diff --git a/MODULE.bazel b/MODULE.bazel index e68bc08..689ae23 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -7,11 +7,19 @@ module( ) bazel_dep(name = "bazel_skylib", version = "1.5.0") -bazel_dep(name = "aspect_bazel_lib", version = "2.5.3") +bazel_dep(name = "aspect_bazel_lib", version = "2.6.0") +bazel_dep(name = "container_structure_test", version = "1.16.0") +bazel_dep(name = "rules_oci", version = "1.7.4") bazel_lib_toolchains = use_extension("@aspect_bazel_lib//lib:extensions.bzl", "toolchains") -bazel_lib_toolchains.tar() use_repo(bazel_lib_toolchains, "bsd_tar_toolchains") +use_repo(bazel_lib_toolchains, "yq_darwin_amd64") +use_repo(bazel_lib_toolchains, "yq_darwin_arm64") +use_repo(bazel_lib_toolchains, "yq_linux_amd64") +use_repo(bazel_lib_toolchains, "yq_linux_arm64") +use_repo(bazel_lib_toolchains, "yq_linux_ppc64le") +use_repo(bazel_lib_toolchains, "yq_linux_s390x") +use_repo(bazel_lib_toolchains, "yq_windows_amd64") bazel_dep(name = "gazelle", version = "0.34.0", dev_dependency = True, repo_name = "bazel_gazelle") bazel_dep(name = "bazel_skylib_gazelle_plugin", version = "1.5.0", dev_dependency = True) diff --git a/WORKSPACE.bazel b/WORKSPACE.bazel index 14b7be1..2acd9a8 100644 --- a/WORKSPACE.bazel +++ b/WORKSPACE.bazel @@ -4,7 +4,7 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "example-bullseye-ca-certificates", - build_file_content = 'exports_files(["data.tar.xz"])', + build_file_content = 'exports_files(["data.tar.xz", "control.tar.xz"])', sha256 = "b2d488ad4d8d8adb3ba319fc9cb2cf9909fc42cb82ad239a26c570a2e749c389", urls = ["https://snapshot.debian.org/archive/debian/20231106T210201Z/pool/main/c/ca-certificates/ca-certificates_20210119_all.deb"], ) @@ -22,3 +22,16 @@ http_archive( sha256 = "38c44247c5b3e864d6db2877edd9c9a0555fc4e23ae271b73d7f527802616df5", urls = ["https://snapshot.debian.org/archive/debian-security/20231106T230332Z/pool/updates/main/g/glibc/libc-bin_2.36-9+deb12u3_armhf.deb"], ) + +load("//apt:index.bzl", "deb_index") + +# bazel run @bullseye//:lock +deb_index( + name = "bullseye", + lock = "//examples/apt:bullseye.lock.json", + manifest = "//examples/apt:bullseye.yaml", +) + +load("@bullseye//:packages.bzl", "bullseye_packages") + +bullseye_packages() diff --git a/apt/BUILD.bazel b/apt/BUILD.bazel new file mode 100644 index 0000000..07a67c2 --- /dev/null +++ b/apt/BUILD.bazel @@ -0,0 +1,25 @@ +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") + +exports_files([ + "index.bzl", +]) + +bzl_library( + name = "defs", + srcs = ["defs.bzl"], + visibility = ["//visibility:public"], + deps = [ + "//apt/private:dpkg_status", + "//apt/private:dpkg_statusd", + ], +) + +bzl_library( + name = "index", + srcs = ["index.bzl"], + visibility = ["//visibility:public"], + deps = [ + "//apt/private:index", + "//apt/private:resolve", + ], +) diff --git a/apt/defs.bzl b/apt/defs.bzl new file mode 100644 index 0000000..df59c3d --- /dev/null +++ b/apt/defs.bzl @@ -0,0 +1,7 @@ +"EXPERIMENTAL! Public API" + +load("//apt/private:dpkg_status.bzl", _dpkg_status = "dpkg_status") +load("//apt/private:dpkg_statusd.bzl", _dpkg_statusd = "dpkg_statusd") + +dpkg_status = _dpkg_status +dpkg_statusd = _dpkg_statusd diff --git a/apt/index.bzl b/apt/index.bzl new file mode 100644 index 0000000..a589d17 --- /dev/null +++ b/apt/index.bzl @@ -0,0 +1,79 @@ +"apt-get" + +load("//apt/private:index.bzl", _deb_package_index = "deb_package_index") +load("//apt/private:resolve.bzl", _deb_resolve = "deb_resolve") + +def deb_index( + name, + manifest, + lock = None, + package_template = None, + resolve_transitive = True): + """A convience repository macro around package_index and resolve repository rules. + + WORKSPACE example; + + ```starlark + load("@rules_distroless//apt:index.bzl", "deb_index") + + deb_index( + name = "bullseye", + # For the initial setup, the lockfile attribute can be omitted and generated by running + # bazel run @bullseye//:lock + # This will generate the lock.json file next to the manifest file by replacing `.yaml` with `.lock.json` + lock = "//examples/apt:bullseye.lock.json", + manifest = "//examples/apt:bullseye.yaml", + ) + + load("@bullseye//:packages.bzl", "bullseye_packages") + bullseye_packages() + ``` + + BZLMOD example; + ```starlark + # TODO: support BZLMOD + ``` + + This macro will expand to two repositories; `` and `_resolve`. + + A typical workflow for `deb_index` involves generation of a lockfile `deb_resolve` + and consumption of lockfile by `deb_package_index` for generating a DAG. + + The lockfile generation can be `on-demand` by omitting the lock attribute, however, + this comes with the cost of doing a new package resolution on repository cache misses. + + While we strongly encourage users to check in the generated lockfile, it's not always + possible to check in the generated lockfile as by default Debian repositories are rolling, + therefore a lockfile generated today might not work work tomorrow as the upstream + repository might publish new version of a package. + + That said, users can still use a `debian archive snapshot` repository and check-in the + generated lockfiles. This is possible because by design `debian snapshot` repositories + are immutable point-in-time snapshot of the upstream repositories, which means packages + never get deleted or updated in a specific snapshot. + + An example of this could be found [here](/examples/apt). + + Args: + name: name of the repository + manifest: label to a `manifest.yaml` + lock: label to a `lock.json` + package_template: (EXPERIMENTAL!) a template string for generated BUILD files. + Available template replacement keys are: `{target_name}`, `{deps}`, `{urls}`, `{name}`, `{arch}`, `{sha256}`, `{repo_name}` + resolve_transitive: whether dependencies of dependencies should be resolved and added to the lockfile. + """ + _deb_resolve( + name = name + "_resolution", + manifest = manifest, + resolve_transitive = resolve_transitive, + ) + + if not lock: + # buildifier: disable=print + print("\nNo lockfile was given, please run `bazel run @%s//:lock` to create the lockfile." % name) + + _deb_package_index( + name = name, + lock = lock if lock else "@" + name + "_resolution//:lock.json", + package_template = package_template, + ) diff --git a/apt/private/BUILD.bazel b/apt/private/BUILD.bazel new file mode 100644 index 0000000..61f9cbd --- /dev/null +++ b/apt/private/BUILD.bazel @@ -0,0 +1,73 @@ +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") + +exports_files([ + "dpkg_statusd.sh", + "dpkg_status.sh", +]) + +bzl_library( + name = "dpkg_status", + srcs = ["dpkg_status.bzl"], + visibility = ["//apt:__subpackages__"], + deps = ["//distroless/private:tar"], +) + +bzl_library( + name = "dpkg_statusd", + srcs = ["dpkg_statusd.bzl"], + visibility = ["//apt:__subpackages__"], + deps = ["//distroless/private:tar"], +) + +bzl_library( + name = "index", + srcs = ["index.bzl"], + visibility = ["//apt:__subpackages__"], + deps = [":lockfile"], +) + +bzl_library( + name = "lockfile", + srcs = ["lockfile.bzl"], + visibility = ["//apt:__subpackages__"], + deps = [":util"], +) + +bzl_library( + name = "package_index", + srcs = ["package_index.bzl"], + visibility = ["//apt:__subpackages__"], + deps = [":util"], +) + +bzl_library( + name = "package_resolution", + srcs = ["package_resolution.bzl"], + visibility = ["//apt:__subpackages__"], + deps = [":version"], +) + +bzl_library( + name = "resolve", + srcs = ["resolve.bzl"], + visibility = ["//apt:__subpackages__"], + deps = [ + ":lockfile", + ":package_index", + ":package_resolution", + "@aspect_bazel_lib//lib:repo_utils", + ], +) + +bzl_library( + name = "version", + srcs = ["version.bzl"], + visibility = ["//apt:__subpackages__"], + deps = ["@aspect_bazel_lib//lib:strings"], +) + +bzl_library( + name = "util", + srcs = ["util.bzl"], + visibility = ["//apt:__subpackages__"], +) diff --git a/apt/private/dpkg_status.bzl b/apt/private/dpkg_status.bzl new file mode 100644 index 0000000..25fa0d2 --- /dev/null +++ b/apt/private/dpkg_status.bzl @@ -0,0 +1,46 @@ +"dpkg_status" + +# buildifier: disable=bzl-visibility +load("//distroless/private:tar.bzl", "tar_lib") + +_DOC = """TODO: docs""" + +def _dpkg_status_impl(ctx): + bsdtar = ctx.toolchains[tar_lib.TOOLCHAIN_TYPE] + + output = ctx.actions.declare_file(ctx.attr.name + ".tar") + + args = ctx.actions.args() + args.add(bsdtar.tarinfo.binary) + args.add(output) + args.add_all(ctx.files.controls) + + ctx.actions.run( + executable = ctx.executable._dpkg_status_sh, + inputs = ctx.files.controls, + outputs = [output], + tools = bsdtar.default.files, + arguments = [args], + ) + + return [ + DefaultInfo(files = depset([output])), + ] + +dpkg_status = rule( + doc = _DOC, + attrs = { + "_dpkg_status_sh": attr.label( + allow_single_file = True, + executable = True, + cfg = "exec", + default = ":dpkg_status.sh", + ), + "controls": attr.label_list( + allow_files = [".tar.xz", ".tar.gz", ".tar"], + mandatory = True, + ), + }, + implementation = _dpkg_status_impl, + toolchains = [tar_lib.TOOLCHAIN_TYPE], +) diff --git a/apt/private/dpkg_status.sh b/apt/private/dpkg_status.sh new file mode 100755 index 0000000..866545d --- /dev/null +++ b/apt/private/dpkg_status.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -o pipefail -o errexit -o nounset + +readonly bsdtar="$1" +readonly out="$2" +shift 2 + +tmp_out=$(mktemp) + +while (( $# > 0 )); do + control="$($bsdtar -xf "$1" --to-stdout ./control)" + echo "$control" | head -n 1 >> $tmp_out + echo "Status: install ok installed" >> $tmp_out + echo "$control" | tail -n +2 >> $tmp_out + echo "" >> $tmp_out + shift +done + +mtree_out=$(mktemp) +echo "#mtree +./var/lib/dpkg/status type=file uid=0 gid=0 mode=0644 contents=$tmp_out +" > $mtree_out + +"$bsdtar" $@ -cf "$out" "@$mtree_out" + +rm $tmp_out $mtree_out \ No newline at end of file diff --git a/apt/private/dpkg_statusd.bzl b/apt/private/dpkg_statusd.bzl new file mode 100644 index 0000000..c8862b0 --- /dev/null +++ b/apt/private/dpkg_statusd.bzl @@ -0,0 +1,54 @@ +"dpkg_statusd" + +# buildifier: disable=bzl-visibility +load("//distroless/private:tar.bzl", "tar_lib") + +_DOC = """TODO: docs""" + +def _dpkg_statusd_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(bsdtar.tarinfo.binary) + args.add(output) + args.add(ctx.file.control) + args.add(ctx.attr.package_name) + tar_lib.common.add_compression_args(ctx.attr.compression, args) + + ctx.actions.run( + executable = ctx.executable._dpkg_statusd_sh, + inputs = [ctx.file.control], + outputs = [output], + tools = bsdtar.default.files, + arguments = [args], + ) + + return [ + DefaultInfo(files = depset([output])), + ] + +dpkg_statusd = rule( + doc = _DOC, + attrs = { + "_dpkg_statusd_sh": attr.label( + allow_single_file = True, + executable = True, + cfg = "exec", + default = ":dpkg_statusd.sh", + ), + "package_name": attr.string(mandatory = True), + "control": attr.label( + allow_single_file = [".tar.xz", ".tar.gz", ".tar"], + mandatory = True, + ), + "compression": attr.string( + doc = "Compress the archive file with a supported algorithm.", + values = tar_lib.common.accepted_compression_types, + ), + }, + implementation = _dpkg_statusd_impl, + toolchains = [tar_lib.TOOLCHAIN_TYPE], +) diff --git a/apt/private/dpkg_statusd.sh b/apt/private/dpkg_statusd.sh new file mode 100755 index 0000000..e2e1fb5 --- /dev/null +++ b/apt/private/dpkg_statusd.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +set -o pipefail -o errexit -o nounset + +readonly bsdtar="$1" +readonly out="$2" +readonly control_path="$3" +readonly package_name="$4" +shift 4 + +include=(--include "^./control$" --include "^./md5sums$") + +tmp=$(mktemp -d) +"$bsdtar" -xf "$control_path" "${include[@]}" -C "$tmp" + +"$bsdtar" -cf - $@ --format=mtree "${include[@]}" --options '!gname,!uname,!sha1,!nlink,!time' "@$control_path" | \ +awk -v pkg="$package_name" '{ + if ($1=="#mtree") { + print $1; next + }; + sub(/^\.?\//, "", $1); + $1 = "./var/lib/dpkg/status.d/" pkg "/" $1 " contents=./" $1; + print $0 +}' | "$bsdtar" $@ -cf "$out" -C "$tmp/" @- diff --git a/apt/private/index.bzl b/apt/private/index.bzl new file mode 100644 index 0000000..631e8ba --- /dev/null +++ b/apt/private/index.bzl @@ -0,0 +1,115 @@ +"apt-get" + +load(":lockfile.bzl", "lockfile") + +_DEB_IMPORT_TMPL = '''\ + http_archive( + name = "{name}", + urls = {urls}, + sha256 = "{sha256}", + build_file_content = """\ +filegroup( + name = "data", + visibility = ["//visibility:public"], + srcs = ["data.tar.xz"] +) + +filegroup( + name = "control", + visibility = ["//visibility:public"], + srcs = ["control.tar.xz"] +) +""" + ) +''' + +_PACKAGE_TMPL = '''\ + +load("@rules_distroless//distroless:defs.bzl", "flatten") + +# TODO: https://github.com/bazel-contrib/rules_oci/pull/523 +flatten( + name = "data", + tars = [{src}], + compress = "gzip" +) + +alias( + name = "control", + actual = "@{repo_name}//:control", + visibility = ["//visibility:public"], +) + +filegroup( + name = "{target_name}", + visibility = ["//visibility:public"], + srcs = [ + {deps} + ] + [":data"] +) +''' + +def _deb_package_index_impl(rctx): + package_defs = [ + '"""Generated by rules_distroless. DO NOT EDIT."""', + """load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")""", + "", + "", + "# buildifier: disable=function-docstring", + "def {}_packages():".format(rctx.attr.name), + ] + + lockf = lockfile.from_json(rctx, rctx.attr.lock) + + if len(lockf.packages()) < 1: + package_defs.append(" pass") + + for (package) in lockf.packages(): + name = lockfile.make_package_key( + package["name"], + package["version"], + package["arch"], + ) + package_defs.append( + _DEB_IMPORT_TMPL.format( + name = "%s_%s" % (rctx.attr.name, package["key"]), + package_name = package["name"], + urls = [package["url"]], + sha256 = package["sha256"], + ), + ) + + rctx.file( + "%s/%s/BUILD.bazel" % (package["name"], package["arch"]), + rctx.attr.package_template.format( + target_name = package["arch"], + src = '"@%s_%s//:data"' % (rctx.attr.name, name), + deps = ",\n ".join([ + '"//%s/%s"' % (dep["name"], package["arch"]) + for dep in package["dependencies"] + ]), + urls = [package["url"]], + name = package["name"], + arch = package["arch"], + sha256 = package["sha256"], + repo_name = "%s_%s" % (rctx.attr.name, name), + ), + ) + + rctx.file("packages.bzl", "\n".join(package_defs)) + rctx.file("BUILD.bazel", """ +alias( + name = "lock", + actual = "@{}_resolution//:lock", + tags = ["manual"], +) +exports_files(glob(['packages.bzl'])) +""".format(rctx.attr.name)) + +deb_package_index = repository_rule( + implementation = _deb_package_index_impl, + attrs = { + "lock": attr.label(), + "package_template": attr.string(default = _PACKAGE_TMPL), + }, +) diff --git a/apt/private/lockfile.bzl b/apt/private/lockfile.bzl new file mode 100644 index 0000000..fc76ab0 --- /dev/null +++ b/apt/private/lockfile.bzl @@ -0,0 +1,81 @@ +"lock" + +load(":util.bzl", "util") + +def _make_package_key(name, version, arch): + return "%s_%s_%s" % ( + util.sanitize(name), + util.sanitize(version), + arch, + ) + +def _package_key(package, arch): + return _make_package_key(package["Package"], package["Version"], arch) + +def _add_package(lock, package, arch): + k = _package_key(package, arch) + if k in lock.fast_package_lookup: + return + lock.packages.append({ + "key": k, + "name": package["Package"], + "version": package["Version"], + "url": "%s/%s" % (package["Root"], package["Filename"]), + "sha256": package["SHA256"], + "arch": arch, + "dependencies": [], + }) + lock.fast_package_lookup[k] = len(lock.packages) - 1 + +def _add_package_dependency(lock, package, dependency, arch): + k = _package_key(package, arch) + if k not in lock.fast_package_lookup: + fail("Broken state: %s is not in the lockfile." % package["Package"]) + i = lock.fast_package_lookup[k] + lock.packages[i]["dependencies"].append(dict( + key = _package_key(dependency, arch), + name = dependency["Package"], + version = dependency["Version"], + )) + +def _has_package(lock, name, version, arch): + key = "%s_%s_%s" % (util.sanitize(name), util.sanitize(version), arch) + return key in lock.fast_package_lookup + +def _create(rctx, lock): + return struct( + has_package = lambda *args, **kwargs: _has_package(lock, *args, **kwargs), + add_package = lambda *args, **kwargs: _add_package(lock, *args, **kwargs), + add_package_dependency = lambda *args, **kwargs: _add_package_dependency(lock, *args, **kwargs), + packages = lambda: lock.packages, + write = lambda out: rctx.file(out, json.encode_indent(struct(version = lock.version, packages = lock.packages))), + ) + +def _empty(rctx): + lock = struct( + version = 1, + packages = list(), + fast_package_lookup = dict(), + ) + return _create(rctx, lock) + +def _from_json(rctx, path): + lock = json.decode(rctx.read(path)) + if lock["version"] != 1: + fail("invalid lockfile version") + + lock = struct( + version = lock["version"], + packages = lock["packages"], + fast_package_lookup = dict(), + ) + for (i, package) in enumerate(lock.packages): + lock.packages[i] = package + lock.fast_package_lookup[package["key"]] = i + return _create(rctx, lock) + +lockfile = struct( + empty = _empty, + from_json = _from_json, + make_package_key = _make_package_key, +) diff --git a/apt/private/package_index.bzl b/apt/private/package_index.bzl new file mode 100644 index 0000000..620a021 --- /dev/null +++ b/apt/private/package_index.bzl @@ -0,0 +1,77 @@ +"package index" + +load(":util.bzl", "util") + +def _fetch_package_index(rctx, url, dist, comp, arch, integrity): + target_triple = "{dist}/{comp}/{arch}".format(dist = dist, comp = comp, arch = arch) + output = "{}/Packages.xz".format(target_triple) + r = rctx.download( + url = "{}/dists/{}/{}/binary-{}/Packages.xz".format(url, dist, comp, arch), + output = output, + integrity = integrity, + ) + rctx.execute([ + "xz", + "--decompress", + output, + ]) + return ("{}/Packages".format(target_triple), r.integrity) + +def _parse_package_index(state, contents, arch, root): + last_key = "" + pkg = {} + for group in contents.split("\n\n"): + for line in group.split("\n"): + if line.strip() == "": + continue + if line[0] == " ": + pkg[last_key] += "\n" + line + continue + + (key, value) = line.split(": ", 1) + if not last_key and len(pkg) == 0 and key != "Package": + fail("do not expect this. fix it.") + + last_key = key + pkg[key] = value + + if len(pkg.keys()) != 0: + pkg["Root"] = root + util.set_dict(state.packages, value = pkg, keys = (arch, pkg["Package"], pkg["Version"])) + last_key = "" + pkg = {} + +def _package_versions(state, name, arch): + if name not in state.packages[arch]: + return [] + return state.packages[arch][name].keys() + +def _package(state, name, version, arch): + if name not in state.packages[arch]: + return None + if version not in state.packages[arch][name]: + return None + return state.packages[arch][name][version] + +def _create(rctx, sources, archs): + state = struct( + packages = dict(), + ) + + for arch in archs: + for (url, dist, comp) in sources: + rctx.report_progress("Fetching package index: {}/{}".format(dist, arch)) + (output, _) = _fetch_package_index(rctx, url, dist, comp, arch, "") + + # TODO: this is expensive to perform. + rctx.report_progress("Parsing package index: {}/{}".format(dist, arch)) + _parse_package_index(state, rctx.read(output), arch, url) + + return struct( + package_versions = lambda **kwargs: _package_versions(state, **kwargs), + package = lambda **kwargs: _package(state, **kwargs), + ) + +package_index = struct( + new = _create, +) diff --git a/apt/private/package_resolution.bzl b/apt/private/package_resolution.bzl new file mode 100644 index 0000000..09ab165 --- /dev/null +++ b/apt/private/package_resolution.bzl @@ -0,0 +1,152 @@ +"package resolution" + +load(":version.bzl", "version") + +def _parse_dep(raw): + raw = raw.strip() # remove leading & trailing whitespace + name = None + version = None + archs = None + + sqb_start_i = raw.find("[") + if sqb_start_i != -1: + sqb_end_i = raw.find("]") + if sqb_end_i == -1: + fail('invalid version string %s expected a closing brackets "]"' % raw) + archs = raw[sqb_start_i + 1:sqb_end_i].strip().split(" ") + raw = raw[:sqb_start_i] + raw[sqb_end_i + 1:] + + paren_start_i = raw.find("(") + if paren_start_i != -1: + paren_end_i = raw.find(")") + if paren_end_i == -1: + fail('invalid version string %s expected a closing paren ")"' % raw) + name = raw[:paren_start_i].strip() + version_and_const = raw[paren_start_i + 1:paren_end_i].strip() + raw = raw[:paren_start_i] + raw[paren_end_i + 1:] + + vconst_i = version_and_const.find(" ") + if vconst_i == -1: + fail('invalid version string %s expected a version constraint ">=", "=", ">=", "<<", ">>"' % version_and_const) + + version = (version_and_const[:vconst_i], version_and_const[vconst_i + 1:]) + + name = raw.strip() + return {"name": name, "version": version, "arch": archs} + +def _parse_depends(depends_raw): + depends = [] + for dep in depends_raw.split(","): + if dep.find("|") != -1: + depends.append([ + _parse_dep(adep) + for adep in dep.split("|") + ]) + else: + depends.append(_parse_dep(dep)) + + return depends + +def _version_relop(va, vb, op): + if op == "<<": + return version.lt(va, vb) + elif op == ">>": + return version.gt(va, vb) + elif op == "<=": + return version.lte(va, vb) + elif op == ">=": + return version.gte(va, vb) + elif op == "=": + return version.eq(va, vb) + fail("unknown op %s" % op) + +def _resolve_package(state, name, version, arch): + versions = state.index.package_versions(name = name, arch = arch) + package = None + if version: + for av in versions: + if _version_relop(av, version[1], version[0]): + package = state.index.package(name = name, version = av, arch = arch) + break + elif len(versions) > 0: + # TODO: what do we do when there is no version constraint? + package = state.index.package(name = name, version = versions[0], arch = arch) + return package + +def _resolve_all(state, name, version, arch, in_lock, include_transitive): + root_package = None + already_recursed = {} + unmet_dependencies = [] + dependencies = [] + iteration_max = 2147483646 + + stack = [(name, version)] + + for i in range(0, iteration_max + 1): + if not len(stack): + break + if i == iteration_max: + fail("resolve_dependencies exhausted the iteration") + (name, version) = stack.pop() + + package = _resolve_package(state, name, version, arch) + + if not package: + key = "%s~~%s" % (name, version[1] if version else "") + unmet_dependencies.append((name, version)) + continue + + if i == 0: + # Set the root package + root_package = package + + # PERF: If the lockfile has this package already, it means we did the transitive closure + # resolution already. + if in_lock(package["Package"], package["Version"]): + continue + + key = "%s~~%s" % (package["Package"], package["Version"]) + + # If we encountered package before in the transitive closure, skip it + if key in already_recursed: + continue + + if i != 0: + # Add it to the dependencies + already_recursed[key] = True + dependencies.append(package) + + deps = [] + + # Extend the lookup with all the items in the dependency closure + if "Pre-Depends" in package and include_transitive: + deps.extend(_parse_depends(package["Pre-Depends"])) + + # Extend the lookup with all the items in the dependency closure + if "Depends" in package and include_transitive: + deps.extend(_parse_depends(package["Depends"])) + + for dep in deps: + if type(dep) == "list": + # buildifier: disable=print + print("Warning: optional dependencies are not supported yet. https://github.com/GoogleContainerTools/rules_distroless/issues/27") + + # TODO: optional dependencies + continue + + # TODO: arch + stack.append((dep["name"], dep["version"])) + + return (root_package, dependencies, unmet_dependencies) + +def _create_resolution(index): + state = struct(index = index) + return struct( + resolve_all = lambda **kwargs: _resolve_all(state, **kwargs), + resolve_package = lambda **kwargs: _resolve_package(state, **kwargs), + ) + +package_resolution = struct( + new = _create_resolution, + parse_depends = _parse_depends, +) diff --git a/apt/private/resolve.bzl b/apt/private/resolve.bzl new file mode 100644 index 0000000..ea443d2 --- /dev/null +++ b/apt/private/resolve.bzl @@ -0,0 +1,141 @@ +"repository rule for resolving and generating lockfile" + +load("@aspect_bazel_lib//lib:repo_utils.bzl", "repo_utils") +load(":lockfile.bzl", "lockfile") +load(":package_index.bzl", "package_index") +load(":package_resolution.bzl", "package_resolution") + +_COPY_SH = """\ +#!/usr/bin/env bash +set -o pipefail -o errexit -o nounset + +lock=$(realpath $1) + +cd $BUILD_WORKING_DIRECTORY + +echo '' +echo 'Writing lockfile to {workspace_relative_path}' +cp $lock {workspace_relative_path} + +if [[ "${{2:-}}" == "--autofix" ]]; then + echo '' + buildozer 'set lock "{label}"' WORKSPACE.bazel:{name} +else + echo '' + echo 'Run the following command to add the lockfile or pass --autofix flag to do it automatically.' + echo '' + echo ' buildozer 'set lock "{label}"' WORKSPACE.bazel:{name}' + echo '' +fi +""" + +def _parse_manifest(rctx): + is_windows = repo_utils.is_windows(rctx) + host_yq = Label("@{}_{}//:yq{}".format(rctx.attr.yq_toolchain_prefix, repo_utils.platform(rctx), ".exe" if is_windows else "")) + yq_args = [ + str(rctx.path(host_yq)), + str(rctx.path(rctx.attr.manifest)), + "-o=json", + ] + result = rctx.execute(yq_args) + if result.return_code: + fail("failed to parse manifest yq. '{}' exited with {}: \nSTDOUT:\n{}\nSTDERR:\n{}".format(" ".join(yq_args), result.return_code, result.stdout, result.stderr)) + + return json.decode(result.stdout if result.stdout != "null" else "{}") + +def _deb_resolve_impl(rctx): + manifest = _parse_manifest(rctx) + + if manifest["version"] != 1: + fail("Unsupported manifest version, {}. Please use `version: 1` manifest.".format(manifest["version"])) + + if type(manifest["sources"]) != "list": + fail("`sources` should be an array") + + if type(manifest["archs"]) != "list": + fail("`archs` should be an array") + + if type(manifest["packages"]) != "list": + fail("`packages` should be an array") + + sources = [] + + dist = None + for src in manifest["sources"]: + (distr, _, comp) = src["channel"].partition(" ") + if not dist: + dist = distr + sources.append(( + src["url"], + distr, + comp, + )) + + pkgindex = package_index.new(rctx, sources = sources, archs = manifest["archs"]) + pkgresolution = package_resolution.new(index = pkgindex) + lockf = lockfile.empty(rctx) + + for arch in manifest["archs"]: + for dep_constraint in manifest["packages"]: + constraint = package_resolution.parse_depends(dep_constraint).pop() + + rctx.report_progress("Resolving %s" % dep_constraint) + (package, dependencies, unmet_dependencies) = pkgresolution.resolve_all( + name = constraint["name"], + version = constraint["version"], + arch = arch, + include_transitive = rctx.attr.resolve_transitive, + in_lock = lambda name, version: lockf.has_package(name, version, arch), + ) + + if not package: + fail("Unable to locate package `%s`" % dep_constraint) + + if len(unmet_dependencies): + # buildifier: disable=print + print("the following packages have unmet dependencies: %s" % ",".join([up[0] for up in unmet_dependencies])) + + lockf.add_package(package, arch) + + for dep in dependencies: + lockf.add_package(dep, arch) + lockf.add_package_dependency(package, dep, arch) + + lockf.write("lock.json") + + locklabel = rctx.attr.manifest.relative(rctx.attr.manifest.name.replace(".yaml", ".lock.json")) + + rctx.file( + "copy.sh", + _COPY_SH.format( + name = rctx.name.removesuffix("_resolution"), + label = locklabel, + workspace_relative_path = "%s/%s" % (locklabel.package, locklabel.name), + ), + executable = True, + ) + + rctx.file("BUILD.bazel", """\ +filegroup( + name = "lockfile", + srcs = ["lock.json"], + tags = ["manual"], +) +sh_binary( + name = "lock", + srcs = ["copy.sh"], + data = ["lock.json"], + tags = ["manual"], + args = ["$(location :lock.json)"], + visibility = ["//visibility:public"] +) +""") + +deb_resolve = repository_rule( + implementation = _deb_resolve_impl, + attrs = { + "manifest": attr.label(), + "resolve_transitive": attr.bool(default = True), + "yq_toolchain_prefix": attr.string(default = "yq"), + }, +) diff --git a/apt/private/util.bzl b/apt/private/util.bzl new file mode 100644 index 0000000..7c3745d --- /dev/null +++ b/apt/private/util.bzl @@ -0,0 +1,19 @@ +"utilities" + +def _set_dict(struct, value = None, keys = []): + klen = len(keys) + for i in range(klen - 1): + k = keys[i] + if not k in struct: + struct[k] = {} + struct = struct[k] + + struct[keys[-1]] = value + +def _sanitize(str): + return str.replace("+", "-p-").replace(":", "-") + +util = struct( + sanitize = _sanitize, + set_dict = _set_dict, +) diff --git a/apt/private/version.bzl b/apt/private/version.bzl new file mode 100644 index 0000000..aaee1e3 --- /dev/null +++ b/apt/private/version.bzl @@ -0,0 +1,141 @@ +"parse debian version strings" + +load("@aspect_bazel_lib//lib:strings.bzl", "ord") + +# https://www.debian.org/doc/debian-policy/ch-controlfields.html#version +# https://github.com/Debian/apt/blob/main/apt-pkg/deb/debversion.cc +def _parse_version(rv): + epoch_idx = rv.find(":") + epoch = None + if epoch_idx != -1: + epoch = rv[:epoch_idx] + rv = rv[epoch_idx + 1:] + + revision_idx = rv.rfind("-") + revision = None + if revision_idx != -1: + revision = rv[revision_idx + 1:] + rv = rv[:revision_idx] + + upstream = rv + + return (epoch, upstream, revision) + +def _cmp(a, b): + if a < b: + return -1 + elif a > b: + return 1 + return 0 + +def _getdigits(st): + return "".join([c for c in st.elems() if c.isdigit()]) + +def _order(char): + if len(char) > 1: + fail("expected a single char") + if char == "~": + return -1 + elif char.isdigit(): + return int(char) + 1 + elif char.isalpha(): + return ord(char) + else: + return ord(char) + 256 + +def _version_cmp_string(va, vb): + la = [_order(x) for x in va.elems()] + lb = [_order(x) for x in vb.elems()] + + for i in range(max(len(la), len(lb))): + a = 0 + b = 0 + if i < len(la): + a = la[i] + if i < len(lb): + b = lb[i] + res = _cmp(a, b) + if res != 0: + return res + return 0 + +# Iterate over the whole string and split it into groups of +# numeric and non numeric portions. +# a67bhgs89 -> 'a', '67', 'bhgs', '89'. +# 2.7.2-linux-1 -> '2', '.', '7', '.' ,'-linux-','1' +def _split_alpha_and_digit(v): + v = v.elems() + parts = [] + current_part = "" + for (i, c) in enumerate(v): + # skip the first iteration as we just began grouping + if i == 0: + current_part = c + continue + p = v[i - 1] + if c.isdigit() != p.isdigit(): + parts.append(current_part) + current_part = "" + current_part += c + + # push leftover if theres any + if current_part: + parts.append(current_part) + return parts + +# https://github.com/Debian/apt/blob/2845127968cda30be8423e1d3a24dae0e797bcc8/apt-pkg/deb/debversion.cc#L52 +def _version_cmp_part(va, vb): + la = _split_alpha_and_digit(va) + lb = _split_alpha_and_digit(vb) + + # compare alpha and digit parts of two strings + for i in range(max(len(la), len(lb))): + a = "0" + b = "0" + if i < len(la): + a = la[i] + if i < len(lb): + b = lb[i] + a_digits = _getdigits(a) + b_digits = _getdigits(b) + + # compare if both parts are digits + if a_digits and b_digits: + a = int(a_digits) + b = int(b_digits) + res = _cmp(a, b) + if res != 0: + return res + + # do string comparison between the parts + else: + res = _version_cmp_string(a, b) + if res != 0: + return res + return 0 + +def _compare_version(va, vb): + vap = _parse_version(va) + vbp = _parse_version(vb) + + # compare epoch + res = _cmp(int(vap[0] or "0"), int(vbp[0] or "0")) + if res != 0: + return res + + # compare upstream version + res = _version_cmp_part(vap[1], vbp[1]) + if res != 0: + return res + + # compare debian revision + return _version_cmp_part(vap[2] or "0", vbp[2] or "0") + +version = struct( + parse = _parse_version, + gt = lambda va, vb: _compare_version(va, vb) == 1, + gte = lambda va, vb: _compare_version(va, vb) >= 0, + lt = lambda va, vb: _compare_version(va, vb) == -1, + lte = lambda va, vb: _compare_version(va, vb) <= 0, + eq = lambda va, vb: _compare_version(va, vb) == 0, +) diff --git a/apt/tests/BUILD.bazel b/apt/tests/BUILD.bazel new file mode 100644 index 0000000..b4afaef --- /dev/null +++ b/apt/tests/BUILD.bazel @@ -0,0 +1,8 @@ +load(":package_resolution_test.bzl", "version_depends_test") +load(":version_test.bzl", "version_compare_test", "version_parse_test") + +version_compare_test(name = "version_compare_test") + +version_parse_test(name = "version_parse_test") + +version_depends_test(name = "version_depends") diff --git a/apt/tests/package_resolution_test.bzl b/apt/tests/package_resolution_test.bzl new file mode 100644 index 0000000..a078075 --- /dev/null +++ b/apt/tests/package_resolution_test.bzl @@ -0,0 +1,55 @@ +"unit tests for version parsing" + +load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest") +load("//apt/private:package_resolution.bzl", "package_resolution") + +def _parse_depends_test(ctx): + env = unittest.begin(ctx) + asserts.equals( + env, + [ + {"name": "libc6", "version": (">=", "2.2.1"), "arch": None}, + [{"name": "default-mta", "version": None, "arch": None}, {"name": "mail-transport-agent", "version": None, "arch": None}], + ], + package_resolution.parse_depends("libc6 (>= 2.2.1), default-mta | mail-transport-agent"), + ) + + asserts.equals( + env, + [ + {"name": "libluajit5.1-dev", "version": None, "arch": ["i386", "amd64", "kfreebsd-i386", "armel", "armhf", "powerpc", "mips"]}, + {"name": "liblua5.1-dev", "version": None, "arch": ["hurd-i386", "ia64", "kfreebsd-amd64", "s390x", "sparc"]}, + ], + package_resolution.parse_depends("libluajit5.1-dev [i386 amd64 kfreebsd-i386 armel armhf powerpc mips], liblua5.1-dev [hurd-i386 ia64 kfreebsd-amd64 s390x sparc]"), + ) + + asserts.equals( + env, + [ + [ + {"name": "emacs", "version": None, "arch": None}, + {"name": "emacsen", "version": None, "arch": None}, + ], + {"name": "make", "version": None, "arch": None}, + {"name": "debianutils", "version": (">=", "1.7"), "arch": None}, + ], + package_resolution.parse_depends("emacs | emacsen, make, debianutils (>= 1.7)"), + ) + + asserts.equals( + env, + [ + {"name": "libcap-dev", "version": None, "arch": ["!kfreebsd-i386", "!kfreebsd-amd64", "!hurd-i386"]}, + {"name": "autoconf", "version": None, "arch": None}, + {"name": "debhelper", "version": (">>", "5.0.0"), "arch": None}, + {"name": "file", "version": None, "arch": None}, + {"name": "libc6", "version": (">=", "2.7-1"), "arch": None}, + {"name": "libpaper1", "version": None, "arch": None}, + {"name": "psutils", "version": None, "arch": None}, + ], + package_resolution.parse_depends("libcap-dev [!kfreebsd-i386 !kfreebsd-amd64 !hurd-i386], autoconf, debhelper (>> 5.0.0), file, libc6 (>= 2.7-1), libpaper1, psutils"), + ) + + return unittest.end(env) + +version_depends_test = unittest.make(_parse_depends_test) diff --git a/apt/tests/version_test.bzl b/apt/tests/version_test.bzl new file mode 100644 index 0000000..fdcbdbc --- /dev/null +++ b/apt/tests/version_test.bzl @@ -0,0 +1,55 @@ +"unit tests for version parsing" + +load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest") +load("//apt/private:version.bzl", "version") + +def _parse_version_test(ctx): + env = unittest.begin(ctx) + asserts.equals(env, version.parse("1:1.4.1-1"), ("1", "1.4.1", "1")) + asserts.equals(env, version.parse("7.1.ds-1"), (None, "7.1.ds", "1")) + asserts.equals(env, version.parse("10.11.1.3-2"), (None, "10.11.1.3", "2")) + asserts.equals(env, version.parse("4.0.1.3.dfsg.1-2"), (None, "4.0.1.3.dfsg.1", "2")) + asserts.equals(env, version.parse("0.4.23debian1"), (None, "0.4.23debian1", None)) + asserts.equals(env, version.parse("1.2.10+cvs20060429-1"), (None, "1.2.10+cvs20060429", "1")) + asserts.equals(env, version.parse("0.2.0-1+b1"), (None, "0.2.0", "1+b1")) + asserts.equals(env, version.parse("4.3.90.1svn-r21976-1"), (None, "4.3.90.1svn-r21976", "1")) + asserts.equals(env, version.parse("1.5+E-14"), (None, "1.5+E", "14")) + asserts.equals(env, version.parse("20060611-0.0"), (None, "20060611", "0.0")) + asserts.equals(env, version.parse("0.52.2-5.1"), (None, "0.52.2", "5.1")) + asserts.equals(env, version.parse("7.0-035+1"), (None, "7.0", "035+1")) + asserts.equals(env, version.parse("1.1.0+cvs20060620-1+2.6.15-8"), (None, "1.1.0+cvs20060620-1+2.6.15", "8")) + asserts.equals(env, version.parse("1.1.0+cvs20060620-1+1.0"), (None, "1.1.0+cvs20060620", "1+1.0")) + asserts.equals(env, version.parse("4.2.0a+stable-2sarge1"), (None, "4.2.0a+stable", "2sarge1")) + asserts.equals(env, version.parse("1.8RC4b"), (None, "1.8RC4b", None)) + asserts.equals(env, version.parse("0.9~rc1-1"), (None, "0.9~rc1", "1")) + asserts.equals(env, version.parse("2:1.0.4+svn26-1ubuntu1"), ("2", "1.0.4+svn26", "1ubuntu1")) + asserts.equals(env, version.parse("2:1.0.4~rc2-1"), ("2", "1.0.4~rc2", "1")) + return unittest.end(env) + +version_parse_test = unittest.make(_parse_version_test) + +def _version_compare_test(ctx): + env = unittest.begin(ctx) + asserts.true(env, version.lt("0", "a")) + asserts.true(env, version.lt("1.0", "1.1")) + asserts.true(env, version.lt("1.2", "1.11")) + asserts.true(env, version.lt("1.0-0.1", "1.1")) + asserts.true(env, version.lt("1.0-0.1", "1.0-1")) + asserts.true(env, version.eq("1.0", "1.0")) + asserts.true(env, version.eq("1.0-0.1", "1.0-0.1")) + asserts.true(env, version.eq("1:1.0-0.1", "1:1.0-0.1")) + asserts.true(env, version.eq("1:1.0", "1:1.0")) + asserts.true(env, version.lt("1.0-0.1", "1.0-1")) + asserts.true(env, version.gt("1.0final-5sarge1", "1.0final-5")) + asserts.true(env, version.gt("1.0final-5", "1.0a7-2")) + asserts.true(env, version.lt("0.9.2-5", "0.9.2+cvs.1.0.dev.2004.07.28-1.5")) + asserts.true(env, version.lt("1:500", "1:5000")) + asserts.true(env, version.gt("100:500", "11:5000")) + asserts.true(env, version.gt("1.0.4-2", "1.0pre7-2")) + asserts.true(env, version.lt("1.5~rc1", "1.5")) + asserts.true(env, version.lt("1.5~rc1", "1.5+b1")) + asserts.true(env, version.lt("1.5~rc1", "1.5~rc2")) + asserts.true(env, version.gt("1.5~rc1", "1.5~dev0")) + return unittest.end(env) + +version_compare_test = unittest.make(_version_compare_test) diff --git a/distroless/dependencies.bzl b/distroless/dependencies.bzl index c82c2c0..039523f 100644 --- a/distroless/dependencies.bzl +++ b/distroless/dependencies.bzl @@ -30,7 +30,7 @@ def distroless_dependencies(): http_archive( 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", + sha256 = "3e0a430ada9b8f0f845767a267cf584bc94b8ec642d6093f31dca3938b18f6a1", + strip_prefix = "bazel-lib-2.6.0", + url = "https://github.com/aspect-build/bazel-lib/releases/download/v2.6.0/bazel-lib-v2.6.0.tar.gz", ) diff --git a/distroless/private/BUILD.bazel b/distroless/private/BUILD.bazel index 9314eba..75c3b8e 100644 --- a/distroless/private/BUILD.bazel +++ b/distroless/private/BUILD.bazel @@ -94,7 +94,10 @@ bzl_library( bzl_library( name = "tar", srcs = ["tar.bzl"], - visibility = ["//distroless:__subpackages__"], + visibility = [ + "//apt:__subpackages__", + "//distroless:__subpackages__", + ], deps = [ "@aspect_bazel_lib//lib:tar", "@bazel_skylib//lib:sets", diff --git a/distroless/private/cacerts.bzl b/distroless/private/cacerts.bzl index ed50466..4e67f18 100644 --- a/distroless/private/cacerts.bzl +++ b/distroless/private/cacerts.bzl @@ -17,7 +17,6 @@ http_archive = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "ht http_archive( name = "ca-certificates", type = ".deb", - canonical_id = "test", sha256 = "b2d488ad4d8d8adb3ba319fc9cb2cf9909fc42cb82ad239a26c570a2e749c389", urls = ["https://snapshot.debian.org/archive/debian/20231106T210201Z/pool/main/c/ca-certificates/ca-certificates_20210119_all.deb"], build_file_content = "exports_files(["data.tar.xz"])" diff --git a/distroless/private/cacerts.sh b/distroless/private/cacerts.sh index 278ebee..b174eaf 100755 --- a/distroless/private/cacerts.sh +++ b/distroless/private/cacerts.sh @@ -7,9 +7,9 @@ readonly cacerts_out="$3" readonly copyright_out="$4" readonly tmp="$(mktemp -d)" -"$bsdtar" -xf "$package_path" -C "$tmp" --strip-components 3 ./usr/share/ca-certificates ./usr/share/doc/ca-certificates/copyright +"$bsdtar" -xf "$package_path" -C "$tmp" ./usr/share/ca-certificates ./usr/share/doc/ca-certificates/copyright -mv "$tmp/doc/ca-certificates/copyright" "$copyright_out" +mv "$tmp/usr/share/doc/ca-certificates/copyright" "$copyright_out" function add_cert () { local dir="$1" @@ -22,5 +22,5 @@ function add_cert () { done } -add_cert "$tmp/ca-certificates" +add_cert "$tmp/usr/share/ca-certificates" rm -rf "$tmp" diff --git a/distroless/private/flatten.bzl b/distroless/private/flatten.bzl index 22b36fe..2c6346a 100644 --- a/distroless/private/flatten.bzl +++ b/distroless/private/flatten.bzl @@ -7,12 +7,12 @@ _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" + ext = tar_lib.common.compression_to_extension[ctx.attr.compress] if ctx.attr.compress 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) + tar_lib.common.add_compression_args(ctx.attr.compress, args) args.add("--file", output) args.add_all(ctx.files.tars, format_each = "@%s") @@ -37,7 +37,7 @@ flatten = rule( allow_empty = False, doc = "List of tars to flatten", ), - "compression": attr.string( + "compress": attr.string( doc = "Compress the archive file with a supported algorithm.", values = tar_lib.common.accepted_compression_types, ), diff --git a/docs/BUILD.bazel b/docs/BUILD.bazel index c15da0b..bc7ee0d 100644 --- a/docs/BUILD.bazel +++ b/docs/BUILD.bazel @@ -7,4 +7,9 @@ stardoc_with_diff_test( bzl_library_target = "//distroless:defs", ) +stardoc_with_diff_test( + name = "apt", + bzl_library_target = "//apt:index", +) + update_docs(name = "update") diff --git a/docs/apt.md b/docs/apt.md new file mode 100644 index 0000000..e459699 --- /dev/null +++ b/docs/apt.md @@ -0,0 +1,70 @@ + + +apt-get + + + +## deb_index + +
+deb_index(name, manifest, lock, package_template, resolve_transitive)
+
+ +A convience repository macro around package_index and resolve repository rules. + +WORKSPACE example; + +```starlark +load("@rules_distroless//apt:index.bzl", "deb_index") + +deb_index( + name = "bullseye", + # For the initial setup, the lockfile attribute can be omitted and generated by running + # bazel run @bullseye//:lock + # This will generate the lock.json file next to the manifest file by replacing `.yaml` with `.lock.json` + lock = "//examples/apt:bullseye.lock.json", + manifest = "//examples/apt:bullseye.yaml", +) + +load("@bullseye//:packages.bzl", "bullseye_packages") +bullseye_packages() +``` + +BZLMOD example; +```starlark +# TODO: support BZLMOD +``` + +This macro will expand to two repositories; `<name>` and `<name>_resolve`. + +A typical workflow for `deb_index` involves generation of a lockfile `deb_resolve` +and consumption of lockfile by `deb_package_index` for generating a DAG. + +The lockfile generation can be `on-demand` by omitting the lock attribute, however, +this comes with the cost of doing a new package resolution on repository cache misses. + +While we strongly encourage users to check in the generated lockfile, it's not always +possible to check in the generated lockfile as by default Debian repositories are rolling, +therefore a lockfile generated today might not work work tomorrow as the upstream +repository might publish new version of a package. + +That said, users can still use a `debian archive snapshot` repository and check-in the +generated lockfiles. This is possible because by design `debian snapshot` repositories +are immutable point-in-time snapshot of the upstream repositories, which means packages +never get deleted or updated in a specific snapshot. + +An example of this could be found [here](/examples/apt). + + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| name | name of the repository | none | +| manifest | label to a manifest.yaml | none | +| lock | label to a lock.json | None | +| package_template | (EXPERIMENTAL!) a template string for generated BUILD files. Available template replacement keys are: {target_name}, {deps}, {urls}, {name}, {arch}, {sha256}, {repo_name} | None | +| resolve_transitive | whether dependencies of dependencies should be resolved and added to the lockfile. | True | + + diff --git a/docs/rules.md b/docs/rules.md index 396f09e..3149219 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -25,7 +25,6 @@ http_archive = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "ht http_archive( name = "ca-certificates", type = ".deb", - canonical_id = "test", sha256 = "b2d488ad4d8d8adb3ba319fc9cb2cf9909fc42cb82ad239a26c570a2e749c389", urls = ["https://snapshot.debian.org/archive/debian/20231106T210201Z/pool/main/c/ca-certificates/ca-certificates_20210119_all.deb"], build_file_content = "exports_files(["data.tar.xz"])" @@ -57,7 +56,7 @@ cacerts( ## flatten
-flatten(name, compression, tars)
+flatten(name, compress, tars)
 
Flatten multiple archives into single archive. @@ -68,7 +67,7 @@ Flatten multiple archives into single archive. | 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 | "" | +| compress | Compress the archive file with a supported algorithm. | String | optional | "" | | tars | List of tars to flatten | List of labels | required | | diff --git a/examples/apt/BUILD.bazel b/examples/apt/BUILD.bazel new file mode 100644 index 0000000..a375cdf --- /dev/null +++ b/examples/apt/BUILD.bazel @@ -0,0 +1,114 @@ +load("@aspect_bazel_lib//lib:tar.bzl", "tar") +load("@container_structure_test//:defs.bzl", "container_structure_test") +load("@rules_distroless//apt:defs.bzl", "dpkg_status") +load("@rules_distroless//distroless:defs.bzl", "group", "passwd") +load("@rules_oci//oci:defs.bzl", "oci_image", "oci_tarball") + +passwd( + name = "passwd", + entries = [ + { + "uid": 0, + "gid": 0, + "home": "/root", + "shell": "/bin/bash", + "username": "r00t", + }, + { + "uid": 100, + "gid": 65534, + "home": "/home/_apt", + "shell": "/usr/sbin/nologin", + "username": "_apt", + }, + ], +) + +group( + name = "group", + entries = [ + { + "name": "root", + "gid": 0, + }, + { + "name": "_apt", + "gid": 65534, + }, + ], +) + +tar( + name = "sh", + mtree = [ + # needed as dpkg assumes sh is installed in a typical debian installation. + "./bin/sh type=link link=/bin/bash", + ], +) + +PACKAGES = [ + "@bullseye//ncurses-base", + "@bullseye//libncurses6", + "@bullseye//tzdata", + "@bullseye//bash", + "@bullseye//coreutils", + "@bullseye//dpkg", + "@bullseye//apt", + "@bullseye//perl", +] + +# Creates /var/lib/dpkg/status with installed package information. +dpkg_status( + name = "dpkg_status", + controls = select({ + "@platforms//cpu:arm64": [ + "%s/arm64:control" % package + for package in PACKAGES + ], + "@platforms//cpu:x86_64": [ + "%s/amd64:control" % package + for package in PACKAGES + ], + }), +) + +oci_image( + name = "apt", + architecture = select({ + "@platforms//cpu:arm64": "arm64", + "@platforms//cpu:x86_64": "amd64", + }), + os = "linux", + tars = [ + ":sh", + ":passwd", + ":group", + ":dpkg_status", + ] + select({ + "@platforms//cpu:arm64": [ + "%s/arm64" % package + for package in PACKAGES + ], + "@platforms//cpu:x86_64": [ + "%s/amd64" % package + for package in PACKAGES + ], + }), +) + +oci_tarball( + name = "tarball", + image = ":apt", + repo_tags = [ + "distroless/test:latest", + ], +) + +container_structure_test( + name = "test", + configs = select({ + "@platforms//cpu:arm64": ["test_linux_arm64.yaml"], + "@platforms//cpu:x86_64": ["test_linux_amd64.yaml"], + }), + image = ":apt", +) diff --git a/examples/apt/bullseye.lock.json b/examples/apt/bullseye.lock.json new file mode 100755 index 0000000..973af2f --- /dev/null +++ b/examples/apt/bullseye.lock.json @@ -0,0 +1,1897 @@ +{ + "packages": [ + { + "arch": "amd64", + "dependencies": [], + "key": "ncurses-base_6.2-p-20201114-2-p-deb11u2_amd64", + "name": "ncurses-base", + "sha256": "a55a5f94299448279da6a6c2031a9816dc768cd300668ff82ecfc6480bbfc83d", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/n/ncurses/ncurses-base_6.2+20201114-2+deb11u2_all.deb", + "version": "6.2+20201114-2+deb11u2" + }, + { + "arch": "amd64", + "dependencies": [ + { + "key": "libc6_2.31-13-p-deb11u8_amd64", + "name": "libc6", + "version": "2.31-13+deb11u8" + }, + { + "key": "libcrypt1_1-4.4.18-4_amd64", + "name": "libcrypt1", + "version": "1:4.4.18-4" + }, + { + "key": "libgcc-s1_10.2.1-6_amd64", + "name": "libgcc-s1", + "version": "10.2.1-6" + }, + { + "key": "gcc-10-base_10.2.1-6_amd64", + "name": "gcc-10-base", + "version": "10.2.1-6" + }, + { + "key": "libtinfo6_6.2-p-20201114-2-p-deb11u2_amd64", + "name": "libtinfo6", + "version": "6.2+20201114-2+deb11u2" + } + ], + "key": "libncurses6_6.2-p-20201114-2-p-deb11u2_amd64", + "name": "libncurses6", + "sha256": "5b75c540d26d0525f231d39e5cf27ea7919d57305ba7101ea430c975369095eb", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/n/ncurses/libncurses6_6.2+20201114-2+deb11u2_amd64.deb", + "version": "6.2+20201114-2+deb11u2" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libc6_2.31-13-p-deb11u8_amd64", + "name": "libc6", + "sha256": "d55d9c9769336f9b8516c20bd8364ce90746fb860ae3dda242f421e711af3d1a", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/g/glibc/libc6_2.31-13+deb11u8_amd64.deb", + "version": "2.31-13+deb11u8" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libcrypt1_1-4.4.18-4_amd64", + "name": "libcrypt1", + "sha256": "f617952df0c57b4ee039448e3941bccd3f97bfff71e9b0f87ca6dae15cb3f5ef", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libx/libxcrypt/libcrypt1_4.4.18-4_amd64.deb", + "version": "1:4.4.18-4" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libgcc-s1_10.2.1-6_amd64", + "name": "libgcc-s1", + "sha256": "e478f2709d8474165bb664de42e16950c391f30eaa55bc9b3573281d83a29daf", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/g/gcc-10/libgcc-s1_10.2.1-6_amd64.deb", + "version": "10.2.1-6" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "gcc-10-base_10.2.1-6_amd64", + "name": "gcc-10-base", + "sha256": "be65535e94f95fbf04b104e8ab36790476f063374430f7dfc6c516cbe2d2cd1e", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/g/gcc-10/gcc-10-base_10.2.1-6_amd64.deb", + "version": "10.2.1-6" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libtinfo6_6.2-p-20201114-2-p-deb11u2_amd64", + "name": "libtinfo6", + "sha256": "96ed58b8fd656521e08549c763cd18da6cff1b7801a3a22f29678701a95d7e7b", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/n/ncurses/libtinfo6_6.2+20201114-2+deb11u2_amd64.deb", + "version": "6.2+20201114-2+deb11u2" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "tzdata_2024a-0-p-deb11u1_amd64", + "name": "tzdata", + "sha256": "13befffb7ee127f569af92d736e30c86c199bbd58f9c3cca0d071ed63e04d003", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/t/tzdata/tzdata_2024a-0+deb11u1_all.deb", + "version": "2024a-0+deb11u1" + }, + { + "arch": "amd64", + "dependencies": [ + { + "key": "debianutils_4.11.2_amd64", + "name": "debianutils", + "version": "4.11.2" + }, + { + "key": "base-files_11.1-p-deb11u9_amd64", + "name": "base-files", + "version": "11.1+deb11u9" + } + ], + "key": "bash_5.1-2-p-deb11u1_amd64", + "name": "bash", + "sha256": "f702ef058e762d7208a9c83f6f6bbf02645533bfd615c54e8cdcce842cd57377", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/b/bash/bash_5.1-2+deb11u1_amd64.deb", + "version": "5.1-2+deb11u1" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "debianutils_4.11.2_amd64", + "name": "debianutils", + "sha256": "83d21669c5957e3eaee20096a7d8c596bd07f57f1e95dc74f192b3fb7bb2e6a9", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/d/debianutils/debianutils_4.11.2_amd64.deb", + "version": "4.11.2" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "base-files_11.1-p-deb11u9_amd64", + "name": "base-files", + "sha256": "1ff08cf6e1b97af1e37cda830f3658f9af43a906abb80a21951c81aea02ce230", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/b/base-files/base-files_11.1+deb11u9_amd64.deb", + "version": "11.1+deb11u9" + }, + { + "arch": "amd64", + "dependencies": [ + { + "key": "libselinux1_3.1-3_amd64", + "name": "libselinux1", + "version": "3.1-3" + }, + { + "key": "libpcre2-8-0_10.36-2-p-deb11u1_amd64", + "name": "libpcre2-8-0", + "version": "10.36-2+deb11u1" + }, + { + "key": "libgmp10_2-6.2.1-p-dfsg-1-p-deb11u1_amd64", + "name": "libgmp10", + "version": "2:6.2.1+dfsg-1+deb11u1" + }, + { + "key": "libattr1_1-2.4.48-6_amd64", + "name": "libattr1", + "version": "1:2.4.48-6" + }, + { + "key": "libacl1_2.2.53-10_amd64", + "name": "libacl1", + "version": "2.2.53-10" + } + ], + "key": "coreutils_8.32-4-p-b1_amd64", + "name": "coreutils", + "sha256": "3558a412ab51eee4b60641327cb145bb91415f127769823b68f9335585b308d4", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/c/coreutils/coreutils_8.32-4+b1_amd64.deb", + "version": "8.32-4+b1" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libselinux1_3.1-3_amd64", + "name": "libselinux1", + "sha256": "339f5ede10500c16dd7192d73169c31c4b27ab12130347275f23044ec8c7d897", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libs/libselinux/libselinux1_3.1-3_amd64.deb", + "version": "3.1-3" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libpcre2-8-0_10.36-2-p-deb11u1_amd64", + "name": "libpcre2-8-0", + "sha256": "ee192c8d22624eb9d0a2ae95056bad7fb371e5abc17e23e16b1de3ddb17a1064", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/p/pcre2/libpcre2-8-0_10.36-2+deb11u1_amd64.deb", + "version": "10.36-2+deb11u1" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libgmp10_2-6.2.1-p-dfsg-1-p-deb11u1_amd64", + "name": "libgmp10", + "sha256": "fc117ccb084a98d25021f7e01e4dfedd414fa2118fdd1e27d2d801d7248aebbc", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/g/gmp/libgmp10_6.2.1+dfsg-1+deb11u1_amd64.deb", + "version": "2:6.2.1+dfsg-1+deb11u1" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libattr1_1-2.4.48-6_amd64", + "name": "libattr1", + "sha256": "af3c3562eb2802481a2b9558df1b389f3c6d9b1bf3b4219e000e05131372ebaf", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/a/attr/libattr1_2.4.48-6_amd64.deb", + "version": "1:2.4.48-6" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libacl1_2.2.53-10_amd64", + "name": "libacl1", + "sha256": "aa18d721be8aea50fbdb32cd9a319cb18a3f111ea6ad17399aa4ba9324c8e26a", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/a/acl/libacl1_2.2.53-10_amd64.deb", + "version": "2.2.53-10" + }, + { + "arch": "amd64", + "dependencies": [ + { + "key": "tar_1.34-p-dfsg-1-p-deb11u1_amd64", + "name": "tar", + "version": "1.34+dfsg-1+deb11u1" + }, + { + "key": "zlib1g_1-1.2.11.dfsg-2-p-deb11u2_amd64", + "name": "zlib1g", + "version": "1:1.2.11.dfsg-2+deb11u2" + }, + { + "key": "liblzma5_5.2.5-2.1~deb11u1_amd64", + "name": "liblzma5", + "version": "5.2.5-2.1~deb11u1" + }, + { + "key": "libbz2-1.0_1.0.8-4_amd64", + "name": "libbz2-1.0", + "version": "1.0.8-4" + } + ], + "key": "dpkg_1.20.13_amd64", + "name": "dpkg", + "sha256": "eb2b7ba3a3c4e905a380045a2d1cd219d2d45755aba5966d6c804b79400beb05", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/d/dpkg/dpkg_1.20.13_amd64.deb", + "version": "1.20.13" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "tar_1.34-p-dfsg-1-p-deb11u1_amd64", + "name": "tar", + "sha256": "41c9c31f67a76b3532036f09ceac1f40a9224f1680395d120a8b24eae60dd54a", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/t/tar/tar_1.34+dfsg-1+deb11u1_amd64.deb", + "version": "1.34+dfsg-1+deb11u1" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "zlib1g_1-1.2.11.dfsg-2-p-deb11u2_amd64", + "name": "zlib1g", + "sha256": "03d2ab2174af76df6f517b854b77460fbdafc3dac0dca979317da67538159a3e", + "url": "https://snapshot-cloudflare.debian.org/archive/debian-security/20240210T223313Z/pool/updates/main/z/zlib/zlib1g_1.2.11.dfsg-2+deb11u2_amd64.deb", + "version": "1:1.2.11.dfsg-2+deb11u2" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "liblzma5_5.2.5-2.1~deb11u1_amd64", + "name": "liblzma5", + "sha256": "1c79a02415ca5ee7234ac60502fb33ee94fa70b02d1c329a6a14178f8329c435", + "url": "https://snapshot-cloudflare.debian.org/archive/debian-security/20240210T223313Z/pool/updates/main/x/xz-utils/liblzma5_5.2.5-2.1~deb11u1_amd64.deb", + "version": "5.2.5-2.1~deb11u1" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libbz2-1.0_1.0.8-4_amd64", + "name": "libbz2-1.0", + "sha256": "16e27c3ebd97981e70db3733f899963362748f178a62644df69d1f247e741379", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/b/bzip2/libbz2-1.0_1.0.8-4_amd64.deb", + "version": "1.0.8-4" + }, + { + "arch": "amd64", + "dependencies": [ + { + "key": "libsystemd0_247.3-7-p-deb11u4_amd64", + "name": "libsystemd0", + "version": "247.3-7+deb11u4" + }, + { + "key": "libzstd1_1.4.8-p-dfsg-2.1_amd64", + "name": "libzstd1", + "version": "1.4.8+dfsg-2.1" + }, + { + "key": "liblz4-1_1.9.3-2_amd64", + "name": "liblz4-1", + "version": "1.9.3-2" + }, + { + "key": "libgcrypt20_1.8.7-6_amd64", + "name": "libgcrypt20", + "version": "1.8.7-6" + }, + { + "key": "libgpg-error0_1.38-2_amd64", + "name": "libgpg-error0", + "version": "1.38-2" + }, + { + "key": "libstdc-p--p-6_10.2.1-6_amd64", + "name": "libstdc++6", + "version": "10.2.1-6" + }, + { + "key": "libseccomp2_2.5.1-1-p-deb11u1_amd64", + "name": "libseccomp2", + "version": "2.5.1-1+deb11u1" + }, + { + "key": "libgnutls30_3.7.1-5-p-deb11u4_amd64", + "name": "libgnutls30", + "version": "3.7.1-5+deb11u4" + }, + { + "key": "libunistring2_0.9.10-4_amd64", + "name": "libunistring2", + "version": "0.9.10-4" + }, + { + "key": "libtasn1-6_4.16.0-2-p-deb11u1_amd64", + "name": "libtasn1-6", + "version": "4.16.0-2+deb11u1" + }, + { + "key": "libp11-kit0_0.23.22-1_amd64", + "name": "libp11-kit0", + "version": "0.23.22-1" + }, + { + "key": "libffi7_3.3-6_amd64", + "name": "libffi7", + "version": "3.3-6" + }, + { + "key": "libnettle8_3.7.3-1_amd64", + "name": "libnettle8", + "version": "3.7.3-1" + }, + { + "key": "libidn2-0_2.3.0-5_amd64", + "name": "libidn2-0", + "version": "2.3.0-5" + }, + { + "key": "libhogweed6_3.7.3-1_amd64", + "name": "libhogweed6", + "version": "3.7.3-1" + }, + { + "key": "debian-archive-keyring_2021.1.1-p-deb11u1_amd64", + "name": "debian-archive-keyring", + "version": "2021.1.1+deb11u1" + }, + { + "key": "libapt-pkg6.0_2.2.4_amd64", + "name": "libapt-pkg6.0", + "version": "2.2.4" + }, + { + "key": "libxxhash0_0.8.0-2_amd64", + "name": "libxxhash0", + "version": "0.8.0-2" + }, + { + "key": "libudev1_247.3-7-p-deb11u4_amd64", + "name": "libudev1", + "version": "247.3-7+deb11u4" + }, + { + "key": "adduser_3.118-p-deb11u1_amd64", + "name": "adduser", + "version": "3.118+deb11u1" + }, + { + "key": "passwd_1-4.8.1-1_amd64", + "name": "passwd", + "version": "1:4.8.1-1" + }, + { + "key": "libpam-modules_1.4.0-9-p-deb11u1_amd64", + "name": "libpam-modules", + "version": "1.4.0-9+deb11u1" + }, + { + "key": "libpam-modules-bin_1.4.0-9-p-deb11u1_amd64", + "name": "libpam-modules-bin", + "version": "1.4.0-9+deb11u1" + }, + { + "key": "libpam0g_1.4.0-9-p-deb11u1_amd64", + "name": "libpam0g", + "version": "1.4.0-9+deb11u1" + }, + { + "key": "libaudit1_1-3.0-2_amd64", + "name": "libaudit1", + "version": "1:3.0-2" + }, + { + "key": "libcap-ng0_0.7.9-2.2-p-b1_amd64", + "name": "libcap-ng0", + "version": "0.7.9-2.2+b1" + }, + { + "key": "libaudit-common_1-3.0-2_amd64", + "name": "libaudit-common", + "version": "1:3.0-2" + }, + { + "key": "libtirpc3_1.3.1-1-p-deb11u1_amd64", + "name": "libtirpc3", + "version": "1.3.1-1+deb11u1" + }, + { + "key": "libtirpc-common_1.3.1-1-p-deb11u1_amd64", + "name": "libtirpc-common", + "version": "1.3.1-1+deb11u1" + }, + { + "key": "libgssapi-krb5-2_1.18.3-6-p-deb11u4_amd64", + "name": "libgssapi-krb5-2", + "version": "1.18.3-6+deb11u4" + }, + { + "key": "libkrb5support0_1.18.3-6-p-deb11u4_amd64", + "name": "libkrb5support0", + "version": "1.18.3-6+deb11u4" + }, + { + "key": "libkrb5-3_1.18.3-6-p-deb11u4_amd64", + "name": "libkrb5-3", + "version": "1.18.3-6+deb11u4" + }, + { + "key": "libssl1.1_1.1.1w-0-p-deb11u1_amd64", + "name": "libssl1.1", + "version": "1.1.1w-0+deb11u1" + }, + { + "key": "libkeyutils1_1.6.1-2_amd64", + "name": "libkeyutils1", + "version": "1.6.1-2" + }, + { + "key": "libk5crypto3_1.18.3-6-p-deb11u4_amd64", + "name": "libk5crypto3", + "version": "1.18.3-6+deb11u4" + }, + { + "key": "libcom-err2_1.46.2-2_amd64", + "name": "libcom-err2", + "version": "1.46.2-2" + }, + { + "key": "libnsl2_1.3.0-2_amd64", + "name": "libnsl2", + "version": "1.3.0-2" + }, + { + "key": "libdb5.3_5.3.28-p-dfsg1-0.8_amd64", + "name": "libdb5.3", + "version": "5.3.28+dfsg1-0.8" + }, + { + "key": "libsemanage1_3.1-1-p-b2_amd64", + "name": "libsemanage1", + "version": "3.1-1+b2" + }, + { + "key": "libsepol1_3.1-1_amd64", + "name": "libsepol1", + "version": "3.1-1" + }, + { + "key": "libsemanage-common_3.1-1_amd64", + "name": "libsemanage-common", + "version": "3.1-1" + } + ], + "key": "apt_2.2.4_amd64", + "name": "apt", + "sha256": "75f07c4965ff0813f26623a1164e162538f5e94defba6961347527ed71bc4f3d", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/a/apt/apt_2.2.4_amd64.deb", + "version": "2.2.4" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libsystemd0_247.3-7-p-deb11u4_amd64", + "name": "libsystemd0", + "sha256": "e6f3e65e388196a399c1a36564c38ad987337350358732056227db1b6e708878", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/s/systemd/libsystemd0_247.3-7+deb11u4_amd64.deb", + "version": "247.3-7+deb11u4" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libzstd1_1.4.8-p-dfsg-2.1_amd64", + "name": "libzstd1", + "sha256": "5dcadfbb743bfa1c1c773bff91c018f835e8e8c821d423d3836f3ab84773507b", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libz/libzstd/libzstd1_1.4.8+dfsg-2.1_amd64.deb", + "version": "1.4.8+dfsg-2.1" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "liblz4-1_1.9.3-2_amd64", + "name": "liblz4-1", + "sha256": "79ac6e9ca19c483f2e8effcc3401d723dd9dbb3a4ae324714de802adb21a8117", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/l/lz4/liblz4-1_1.9.3-2_amd64.deb", + "version": "1.9.3-2" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libgcrypt20_1.8.7-6_amd64", + "name": "libgcrypt20", + "sha256": "7a2e0eef8e0c37f03f3a5fcf7102a2e3dc70ba987f696ab71949f9abf36f35ef", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libg/libgcrypt20/libgcrypt20_1.8.7-6_amd64.deb", + "version": "1.8.7-6" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libgpg-error0_1.38-2_amd64", + "name": "libgpg-error0", + "sha256": "16a507fb20cc58b5a524a0dc254a9cb1df02e1ce758a2d8abde0bc4a3c9b7c26", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libg/libgpg-error/libgpg-error0_1.38-2_amd64.deb", + "version": "1.38-2" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libstdc-p--p-6_10.2.1-6_amd64", + "name": "libstdc++6", + "sha256": "5c155c58935870bf3b4bfe769116841c0d286a74f59eccfd5645693ac23f06b1", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/g/gcc-10/libstdc++6_10.2.1-6_amd64.deb", + "version": "10.2.1-6" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libseccomp2_2.5.1-1-p-deb11u1_amd64", + "name": "libseccomp2", + "sha256": "2617fc8b99dca0fa8ed466ee0f5fe087aa4e8413b88ca45d717290f4a0551e36", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libs/libseccomp/libseccomp2_2.5.1-1+deb11u1_amd64.deb", + "version": "2.5.1-1+deb11u1" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libgnutls30_3.7.1-5-p-deb11u4_amd64", + "name": "libgnutls30", + "sha256": "b2fa128881a16c2196caddb551d3577baa296a7bc5d38109a978e8e69fdb5c94", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/g/gnutls28/libgnutls30_3.7.1-5+deb11u4_amd64.deb", + "version": "3.7.1-5+deb11u4" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libunistring2_0.9.10-4_amd64", + "name": "libunistring2", + "sha256": "654433ad02d3a8b05c1683c6c29a224500bf343039c34dcec4e5e9515345e3d4", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libu/libunistring/libunistring2_0.9.10-4_amd64.deb", + "version": "0.9.10-4" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libtasn1-6_4.16.0-2-p-deb11u1_amd64", + "name": "libtasn1-6", + "sha256": "6ebb579337cdc9d6201237a66578425a7a221db622524354e27c0c1bcb6dd7ca", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libt/libtasn1-6/libtasn1-6_4.16.0-2+deb11u1_amd64.deb", + "version": "4.16.0-2+deb11u1" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libp11-kit0_0.23.22-1_amd64", + "name": "libp11-kit0", + "sha256": "bfef5f31ee1c730e56e16bb62cc5ff8372185106c75bf1ed1756c96703019457", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/p/p11-kit/libp11-kit0_0.23.22-1_amd64.deb", + "version": "0.23.22-1" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libffi7_3.3-6_amd64", + "name": "libffi7", + "sha256": "30ca89bfddae5fa6e0a2a044f22b6e50cd17c4bc6bc850c579819aeab7101f0f", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libf/libffi/libffi7_3.3-6_amd64.deb", + "version": "3.3-6" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libnettle8_3.7.3-1_amd64", + "name": "libnettle8", + "sha256": "e4f8ec31ed14518b241eb7b423ad5ed3f4a4e8ac50aae72c9fd475c569582764", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/n/nettle/libnettle8_3.7.3-1_amd64.deb", + "version": "3.7.3-1" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libidn2-0_2.3.0-5_amd64", + "name": "libidn2-0", + "sha256": "cb80cd769171537bafbb4a16c12ec427065795946b3415781bc9792e92d60b59", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libi/libidn2/libidn2-0_2.3.0-5_amd64.deb", + "version": "2.3.0-5" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libhogweed6_3.7.3-1_amd64", + "name": "libhogweed6", + "sha256": "6aab2e892cdb2dfba45707601bc6c3b19aa228f70ae5841017f14c3b0ca3d22f", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/n/nettle/libhogweed6_3.7.3-1_amd64.deb", + "version": "3.7.3-1" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "debian-archive-keyring_2021.1.1-p-deb11u1_amd64", + "name": "debian-archive-keyring", + "sha256": "28ca7749ab7978f3c571732c3aa1c56e3ad1d5db3c915293763d4f6cb8fcce89", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/d/debian-archive-keyring/debian-archive-keyring_2021.1.1+deb11u1_all.deb", + "version": "2021.1.1+deb11u1" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libapt-pkg6.0_2.2.4_amd64", + "name": "libapt-pkg6.0", + "sha256": "4ae47bedf773ad1342e5aae8fa6275f864cfc87a45f4472775f5a9cdd60abbbf", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/a/apt/libapt-pkg6.0_2.2.4_amd64.deb", + "version": "2.2.4" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libxxhash0_0.8.0-2_amd64", + "name": "libxxhash0", + "sha256": "3fb82550a71d27d05672472508548576dfb34486847bc860d3066cda5aaf186f", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/x/xxhash/libxxhash0_0.8.0-2_amd64.deb", + "version": "0.8.0-2" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libudev1_247.3-7-p-deb11u4_amd64", + "name": "libudev1", + "sha256": "9274ca1aa37fcdf5895dad1de0895162351099ef8dff8a62f2f4c9eb181a8fce", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/s/systemd/libudev1_247.3-7+deb11u4_amd64.deb", + "version": "247.3-7+deb11u4" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "adduser_3.118-p-deb11u1_amd64", + "name": "adduser", + "sha256": "1478a610fd50e190882ff41e16c57b628a508bcf5b5ac5313affb49d20818e0a", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/a/adduser/adduser_3.118+deb11u1_all.deb", + "version": "3.118+deb11u1" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "passwd_1-4.8.1-1_amd64", + "name": "passwd", + "sha256": "542593f26502e87b4276fa778e6e3ae52e66b973979986fff77803d9fcb2c2e8", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/s/shadow/passwd_4.8.1-1_amd64.deb", + "version": "1:4.8.1-1" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libpam-modules_1.4.0-9-p-deb11u1_amd64", + "name": "libpam-modules", + "sha256": "ca1e121700bf4b3eb33e30e0774d3e63e1adae9d4b6a940ea3501225db3cc287", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/p/pam/libpam-modules_1.4.0-9+deb11u1_amd64.deb", + "version": "1.4.0-9+deb11u1" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libpam-modules-bin_1.4.0-9-p-deb11u1_amd64", + "name": "libpam-modules-bin", + "sha256": "abbbd181329c236676222d3e912df13f8d1d90a117559edd997d90006369e5c8", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/p/pam/libpam-modules-bin_1.4.0-9+deb11u1_amd64.deb", + "version": "1.4.0-9+deb11u1" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libpam0g_1.4.0-9-p-deb11u1_amd64", + "name": "libpam0g", + "sha256": "496771218fb585bb716fdae6ef8824dbfb5d544b4fa2f3cd4d0e4d7158ae2220", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/p/pam/libpam0g_1.4.0-9+deb11u1_amd64.deb", + "version": "1.4.0-9+deb11u1" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libaudit1_1-3.0-2_amd64", + "name": "libaudit1", + "sha256": "e3aa1383e387dc077a1176f7f3cbfdbc084bcc270a8938f598d5cb119773b268", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/a/audit/libaudit1_3.0-2_amd64.deb", + "version": "1:3.0-2" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libcap-ng0_0.7.9-2.2-p-b1_amd64", + "name": "libcap-ng0", + "sha256": "d34e29769b8ef23e9b9920814afb7905b8ee749db0814e6a8d937ccc4f309830", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libc/libcap-ng/libcap-ng0_0.7.9-2.2+b1_amd64.deb", + "version": "0.7.9-2.2+b1" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libaudit-common_1-3.0-2_amd64", + "name": "libaudit-common", + "sha256": "0d52f4826a57aea13cea1a85bfae354024c7b2f7b95e39cd1ce225e4db27d0f6", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/a/audit/libaudit-common_3.0-2_all.deb", + "version": "1:3.0-2" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libtirpc3_1.3.1-1-p-deb11u1_amd64", + "name": "libtirpc3", + "sha256": "86b216d59b6efcd07d56d14b8f4281d5c47f24e9c962f46bbaf02fce762c5e6a", + "url": "https://snapshot-cloudflare.debian.org/archive/debian-security/20240210T223313Z/pool/updates/main/libt/libtirpc/libtirpc3_1.3.1-1+deb11u1_amd64.deb", + "version": "1.3.1-1+deb11u1" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libtirpc-common_1.3.1-1-p-deb11u1_amd64", + "name": "libtirpc-common", + "sha256": "b2f10cb79e7d7a2f9b30bcdf036127df55cd4a34688547bc2886fa38f4969f77", + "url": "https://snapshot-cloudflare.debian.org/archive/debian-security/20240210T223313Z/pool/updates/main/libt/libtirpc/libtirpc-common_1.3.1-1+deb11u1_all.deb", + "version": "1.3.1-1+deb11u1" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libgssapi-krb5-2_1.18.3-6-p-deb11u4_amd64", + "name": "libgssapi-krb5-2", + "sha256": "037cc4bb34a6cd0d7a6e83bdcae6d68e0d0f9218eb7dedafc8099c8c0be491a2", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/k/krb5/libgssapi-krb5-2_1.18.3-6+deb11u4_amd64.deb", + "version": "1.18.3-6+deb11u4" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libkrb5support0_1.18.3-6-p-deb11u4_amd64", + "name": "libkrb5support0", + "sha256": "da8d022e3dd7f4a72ea32e328b3ac382dbe6bdb91606c5738fe17a29f8ea8080", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/k/krb5/libkrb5support0_1.18.3-6+deb11u4_amd64.deb", + "version": "1.18.3-6+deb11u4" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libkrb5-3_1.18.3-6-p-deb11u4_amd64", + "name": "libkrb5-3", + "sha256": "b785fa324cf27e6bf7f97fc0279470e6ce8a8cc54f8ccc6c9b24c8111ba5c952", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/k/krb5/libkrb5-3_1.18.3-6+deb11u4_amd64.deb", + "version": "1.18.3-6+deb11u4" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libssl1.1_1.1.1w-0-p-deb11u1_amd64", + "name": "libssl1.1", + "sha256": "aadf8b4b197335645b230c2839b4517aa444fd2e8f434e5438c48a18857988f7", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/o/openssl/libssl1.1_1.1.1w-0+deb11u1_amd64.deb", + "version": "1.1.1w-0+deb11u1" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libkeyutils1_1.6.1-2_amd64", + "name": "libkeyutils1", + "sha256": "f01060b434d8cad3c58d5811d2082389f11b3db8152657d6c22c1d298953f2a5", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/k/keyutils/libkeyutils1_1.6.1-2_amd64.deb", + "version": "1.6.1-2" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libk5crypto3_1.18.3-6-p-deb11u4_amd64", + "name": "libk5crypto3", + "sha256": "f635062bcbfe2eef5a83fcba7d1a8ae343fc7c779cae88b11cae90fd6845a744", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/k/krb5/libk5crypto3_1.18.3-6+deb11u4_amd64.deb", + "version": "1.18.3-6+deb11u4" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libcom-err2_1.46.2-2_amd64", + "name": "libcom-err2", + "sha256": "d478f132871f4ab8352d39becf936d0ad74db905398bf98465d8fe3da6fb1126", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/e/e2fsprogs/libcom-err2_1.46.2-2_amd64.deb", + "version": "1.46.2-2" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libnsl2_1.3.0-2_amd64", + "name": "libnsl2", + "sha256": "c0d83437fdb016cb289436f49f28a36be44b3e8f1f2498c7e3a095f709c0d6f8", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libn/libnsl/libnsl2_1.3.0-2_amd64.deb", + "version": "1.3.0-2" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libdb5.3_5.3.28-p-dfsg1-0.8_amd64", + "name": "libdb5.3", + "sha256": "00b9e63e287f45300d4a4f59b6b88e25918443c932ae3e5845d5761ae193c530", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/d/db5.3/libdb5.3_5.3.28+dfsg1-0.8_amd64.deb", + "version": "5.3.28+dfsg1-0.8" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libsemanage1_3.1-1-p-b2_amd64", + "name": "libsemanage1", + "sha256": "d8f2835b22df58ba45d52eb3aab224190f193576caf05e3f80deb2e4f927fad6", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libs/libsemanage/libsemanage1_3.1-1+b2_amd64.deb", + "version": "3.1-1+b2" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libsepol1_3.1-1_amd64", + "name": "libsepol1", + "sha256": "b6057dc6806a6dfaef74b09d84d1f18716d7a6d2f1da30520cef555210c6af62", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libs/libsepol/libsepol1_3.1-1_amd64.deb", + "version": "3.1-1" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libsemanage-common_3.1-1_amd64", + "name": "libsemanage-common", + "sha256": "d319a026ecd02e2f605c52350949279f3c331a19380f8b6888ce5b9ef0d31349", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libs/libsemanage/libsemanage-common_3.1-1_all.deb", + "version": "3.1-1" + }, + { + "arch": "amd64", + "dependencies": [ + { + "key": "libperl5.32_5.32.1-4-p-deb11u3_amd64", + "name": "libperl5.32", + "version": "5.32.1-4+deb11u3" + }, + { + "key": "perl-modules-5.32_5.32.1-4-p-deb11u3_amd64", + "name": "perl-modules-5.32", + "version": "5.32.1-4+deb11u3" + }, + { + "key": "perl-base_5.32.1-4-p-deb11u3_amd64", + "name": "perl-base", + "version": "5.32.1-4+deb11u3" + }, + { + "key": "libgdbm6_1.19-2_amd64", + "name": "libgdbm6", + "version": "1.19-2" + }, + { + "key": "libgdbm-compat4_1.19-2_amd64", + "name": "libgdbm-compat4", + "version": "1.19-2" + } + ], + "key": "perl_5.32.1-4-p-deb11u3_amd64", + "name": "perl", + "sha256": "d5f710c7db9fcd6d9d6f119cd0dea64a4f765867447dd97b24ab44be1de7c60f", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/p/perl/perl_5.32.1-4+deb11u3_amd64.deb", + "version": "5.32.1-4+deb11u3" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libperl5.32_5.32.1-4-p-deb11u3_amd64", + "name": "libperl5.32", + "sha256": "078487a45916167e3e4ee2e584c50306c84368dd06dae276604861ca0426c34e", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/p/perl/libperl5.32_5.32.1-4+deb11u3_amd64.deb", + "version": "5.32.1-4+deb11u3" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "perl-modules-5.32_5.32.1-4-p-deb11u3_amd64", + "name": "perl-modules-5.32", + "sha256": "9a5cb99d0f33cb11c7f535aaebfb569c6b6f97a75d748a9a52ea3afed5bd3960", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/p/perl/perl-modules-5.32_5.32.1-4+deb11u3_all.deb", + "version": "5.32.1-4+deb11u3" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "perl-base_5.32.1-4-p-deb11u3_amd64", + "name": "perl-base", + "sha256": "94c6299552866aadc58acb8ec5111a74b17bcb453f6e2f45ea5f7c4f42580d13", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/p/perl/perl-base_5.32.1-4+deb11u3_amd64.deb", + "version": "5.32.1-4+deb11u3" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libgdbm6_1.19-2_amd64", + "name": "libgdbm6", + "sha256": "e54cfe4d8b8f209bb7df31a404ce040f7c2f9b1045114a927a7e1061cdf90727", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/g/gdbm/libgdbm6_1.19-2_amd64.deb", + "version": "1.19-2" + }, + { + "arch": "amd64", + "dependencies": [], + "key": "libgdbm-compat4_1.19-2_amd64", + "name": "libgdbm-compat4", + "sha256": "e62caed68b0ffaa03b5fa539d6fdc08c4151f66236d5878949bead0b71b7bb09", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/g/gdbm/libgdbm-compat4_1.19-2_amd64.deb", + "version": "1.19-2" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "ncurses-base_6.2-p-20201114-2-p-deb11u2_arm64", + "name": "ncurses-base", + "sha256": "a55a5f94299448279da6a6c2031a9816dc768cd300668ff82ecfc6480bbfc83d", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/n/ncurses/ncurses-base_6.2+20201114-2+deb11u2_all.deb", + "version": "6.2+20201114-2+deb11u2" + }, + { + "arch": "arm64", + "dependencies": [ + { + "key": "libc6_2.31-13-p-deb11u8_arm64", + "name": "libc6", + "version": "2.31-13+deb11u8" + }, + { + "key": "libcrypt1_1-4.4.18-4_arm64", + "name": "libcrypt1", + "version": "1:4.4.18-4" + }, + { + "key": "libgcc-s1_10.2.1-6_arm64", + "name": "libgcc-s1", + "version": "10.2.1-6" + }, + { + "key": "gcc-10-base_10.2.1-6_arm64", + "name": "gcc-10-base", + "version": "10.2.1-6" + }, + { + "key": "libtinfo6_6.2-p-20201114-2-p-deb11u2_arm64", + "name": "libtinfo6", + "version": "6.2+20201114-2+deb11u2" + } + ], + "key": "libncurses6_6.2-p-20201114-2-p-deb11u2_arm64", + "name": "libncurses6", + "sha256": "039b71b8839538a92988003e13c29e7cf1149cdc6a77d3de882f1d386a5f3a5c", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/n/ncurses/libncurses6_6.2+20201114-2+deb11u2_arm64.deb", + "version": "6.2+20201114-2+deb11u2" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libc6_2.31-13-p-deb11u8_arm64", + "name": "libc6", + "sha256": "6eb629090615ebda5dcac2365a7358c035add00b89c2724c2e9e13ccd5bd9f7c", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/g/glibc/libc6_2.31-13+deb11u8_arm64.deb", + "version": "2.31-13+deb11u8" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libcrypt1_1-4.4.18-4_arm64", + "name": "libcrypt1", + "sha256": "22b586b29e840dabebf0bf227d233376628b87954915d064bc142ae85d1b7979", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libx/libxcrypt/libcrypt1_4.4.18-4_arm64.deb", + "version": "1:4.4.18-4" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libgcc-s1_10.2.1-6_arm64", + "name": "libgcc-s1", + "sha256": "e2fcdb378d3c1ad1bcb64d4fb6b37aab44011152beca12a4944f435a2582df1f", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/g/gcc-10/libgcc-s1_10.2.1-6_arm64.deb", + "version": "10.2.1-6" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "gcc-10-base_10.2.1-6_arm64", + "name": "gcc-10-base", + "sha256": "7d782bece7b4a36bed045a7e17d17244cb8f7e4732466091b01412ebf215defb", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/g/gcc-10/gcc-10-base_10.2.1-6_arm64.deb", + "version": "10.2.1-6" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libtinfo6_6.2-p-20201114-2-p-deb11u2_arm64", + "name": "libtinfo6", + "sha256": "58027c991756930a2abb2f87a829393d3fdbfb76f4eca9795ef38ea2b0510e27", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/n/ncurses/libtinfo6_6.2+20201114-2+deb11u2_arm64.deb", + "version": "6.2+20201114-2+deb11u2" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "tzdata_2024a-0-p-deb11u1_arm64", + "name": "tzdata", + "sha256": "13befffb7ee127f569af92d736e30c86c199bbd58f9c3cca0d071ed63e04d003", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/t/tzdata/tzdata_2024a-0+deb11u1_all.deb", + "version": "2024a-0+deb11u1" + }, + { + "arch": "arm64", + "dependencies": [ + { + "key": "debianutils_4.11.2_arm64", + "name": "debianutils", + "version": "4.11.2" + }, + { + "key": "base-files_11.1-p-deb11u9_arm64", + "name": "base-files", + "version": "11.1+deb11u9" + } + ], + "key": "bash_5.1-2-p-deb11u1_arm64", + "name": "bash", + "sha256": "d7c7af5d86f43a885069408a89788f67f248e8124c682bb73936f33874e0611b", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/b/bash/bash_5.1-2+deb11u1_arm64.deb", + "version": "5.1-2+deb11u1" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "debianutils_4.11.2_arm64", + "name": "debianutils", + "sha256": "6543b2b1a61b4b7b4b55b4bd25162309d7d23d14d3303649aee84ad314c30e02", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/d/debianutils/debianutils_4.11.2_arm64.deb", + "version": "4.11.2" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "base-files_11.1-p-deb11u9_arm64", + "name": "base-files", + "sha256": "c40dc4d5c6b82f5cfe75efa1a12bd09b9d5b9b8446ea045a991896a1ead8b02c", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/b/base-files/base-files_11.1+deb11u9_arm64.deb", + "version": "11.1+deb11u9" + }, + { + "arch": "arm64", + "dependencies": [ + { + "key": "libselinux1_3.1-3_arm64", + "name": "libselinux1", + "version": "3.1-3" + }, + { + "key": "libpcre2-8-0_10.36-2-p-deb11u1_arm64", + "name": "libpcre2-8-0", + "version": "10.36-2+deb11u1" + }, + { + "key": "libgmp10_2-6.2.1-p-dfsg-1-p-deb11u1_arm64", + "name": "libgmp10", + "version": "2:6.2.1+dfsg-1+deb11u1" + }, + { + "key": "libattr1_1-2.4.48-6_arm64", + "name": "libattr1", + "version": "1:2.4.48-6" + }, + { + "key": "libacl1_2.2.53-10_arm64", + "name": "libacl1", + "version": "2.2.53-10" + } + ], + "key": "coreutils_8.32-4_arm64", + "name": "coreutils", + "sha256": "6210c84d6ff84b867dc430f661f22f536e1704c27bdb79de38e26f75b853d9c0", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/c/coreutils/coreutils_8.32-4_arm64.deb", + "version": "8.32-4" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libselinux1_3.1-3_arm64", + "name": "libselinux1", + "sha256": "da98279a47dabaa46a83514142f5c691c6a71fa7e582661a3a3db6887ad3e9d1", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libs/libselinux/libselinux1_3.1-3_arm64.deb", + "version": "3.1-3" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libpcre2-8-0_10.36-2-p-deb11u1_arm64", + "name": "libpcre2-8-0", + "sha256": "27a4362a4793cb67a8ae571bd8c3f7e8654dc2e56d99088391b87af1793cca9c", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/p/pcre2/libpcre2-8-0_10.36-2+deb11u1_arm64.deb", + "version": "10.36-2+deb11u1" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libgmp10_2-6.2.1-p-dfsg-1-p-deb11u1_arm64", + "name": "libgmp10", + "sha256": "d52619b6ff8829aa5424dfe3189dd05f22118211e69273e9576030584ffcce80", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/g/gmp/libgmp10_6.2.1+dfsg-1+deb11u1_arm64.deb", + "version": "2:6.2.1+dfsg-1+deb11u1" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libattr1_1-2.4.48-6_arm64", + "name": "libattr1", + "sha256": "cb9b59be719a6fdbaabaa60e22aa6158b2de7a68c88ccd7c3fb7f41a25fb43d0", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/a/attr/libattr1_2.4.48-6_arm64.deb", + "version": "1:2.4.48-6" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libacl1_2.2.53-10_arm64", + "name": "libacl1", + "sha256": "f164c48192cb47746101de6c59afa3f97777c8fc821e5a30bb890df1f4cb4cfd", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/a/acl/libacl1_2.2.53-10_arm64.deb", + "version": "2.2.53-10" + }, + { + "arch": "arm64", + "dependencies": [ + { + "key": "tar_1.34-p-dfsg-1-p-deb11u1_arm64", + "name": "tar", + "version": "1.34+dfsg-1+deb11u1" + }, + { + "key": "zlib1g_1-1.2.11.dfsg-2-p-deb11u2_arm64", + "name": "zlib1g", + "version": "1:1.2.11.dfsg-2+deb11u2" + }, + { + "key": "liblzma5_5.2.5-2.1~deb11u1_arm64", + "name": "liblzma5", + "version": "5.2.5-2.1~deb11u1" + }, + { + "key": "libbz2-1.0_1.0.8-4_arm64", + "name": "libbz2-1.0", + "version": "1.0.8-4" + } + ], + "key": "dpkg_1.20.13_arm64", + "name": "dpkg", + "sha256": "87b0bce7361d94cc15caf27709fa8a70de44f9dd742cf0d69d25796a03d24853", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/d/dpkg/dpkg_1.20.13_arm64.deb", + "version": "1.20.13" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "tar_1.34-p-dfsg-1-p-deb11u1_arm64", + "name": "tar", + "sha256": "0f94aac4e6d25e07ed23b7fc3ed06e69074c95276d82caae7fc7b207fd714e39", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/t/tar/tar_1.34+dfsg-1+deb11u1_arm64.deb", + "version": "1.34+dfsg-1+deb11u1" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "zlib1g_1-1.2.11.dfsg-2-p-deb11u2_arm64", + "name": "zlib1g", + "sha256": "e3963985d1a020d67ffd4180e6f9c4b5c600b515f0c9d8fda513d7a0e48e63a1", + "url": "https://snapshot-cloudflare.debian.org/archive/debian-security/20240210T223313Z/pool/updates/main/z/zlib/zlib1g_1.2.11.dfsg-2+deb11u2_arm64.deb", + "version": "1:1.2.11.dfsg-2+deb11u2" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "liblzma5_5.2.5-2.1~deb11u1_arm64", + "name": "liblzma5", + "sha256": "d865bba41952c707b3fa3ae8cab4d4bd337ee92991d2aead66c925bf7cc48846", + "url": "https://snapshot-cloudflare.debian.org/archive/debian-security/20240210T223313Z/pool/updates/main/x/xz-utils/liblzma5_5.2.5-2.1~deb11u1_arm64.deb", + "version": "5.2.5-2.1~deb11u1" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libbz2-1.0_1.0.8-4_arm64", + "name": "libbz2-1.0", + "sha256": "da340e8470e96445c56966f74e48a9a91dee0fa5c89876e88a4575cc17d17a97", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/b/bzip2/libbz2-1.0_1.0.8-4_arm64.deb", + "version": "1.0.8-4" + }, + { + "arch": "arm64", + "dependencies": [ + { + "key": "libsystemd0_247.3-7-p-deb11u4_arm64", + "name": "libsystemd0", + "version": "247.3-7+deb11u4" + }, + { + "key": "libzstd1_1.4.8-p-dfsg-2.1_arm64", + "name": "libzstd1", + "version": "1.4.8+dfsg-2.1" + }, + { + "key": "liblz4-1_1.9.3-2_arm64", + "name": "liblz4-1", + "version": "1.9.3-2" + }, + { + "key": "libgcrypt20_1.8.7-6_arm64", + "name": "libgcrypt20", + "version": "1.8.7-6" + }, + { + "key": "libgpg-error0_1.38-2_arm64", + "name": "libgpg-error0", + "version": "1.38-2" + }, + { + "key": "libstdc-p--p-6_10.2.1-6_arm64", + "name": "libstdc++6", + "version": "10.2.1-6" + }, + { + "key": "libseccomp2_2.5.1-1-p-deb11u1_arm64", + "name": "libseccomp2", + "version": "2.5.1-1+deb11u1" + }, + { + "key": "libgnutls30_3.7.1-5-p-deb11u4_arm64", + "name": "libgnutls30", + "version": "3.7.1-5+deb11u4" + }, + { + "key": "libunistring2_0.9.10-4_arm64", + "name": "libunistring2", + "version": "0.9.10-4" + }, + { + "key": "libtasn1-6_4.16.0-2-p-deb11u1_arm64", + "name": "libtasn1-6", + "version": "4.16.0-2+deb11u1" + }, + { + "key": "libp11-kit0_0.23.22-1_arm64", + "name": "libp11-kit0", + "version": "0.23.22-1" + }, + { + "key": "libffi7_3.3-6_arm64", + "name": "libffi7", + "version": "3.3-6" + }, + { + "key": "libnettle8_3.7.3-1_arm64", + "name": "libnettle8", + "version": "3.7.3-1" + }, + { + "key": "libidn2-0_2.3.0-5_arm64", + "name": "libidn2-0", + "version": "2.3.0-5" + }, + { + "key": "libhogweed6_3.7.3-1_arm64", + "name": "libhogweed6", + "version": "3.7.3-1" + }, + { + "key": "debian-archive-keyring_2021.1.1-p-deb11u1_arm64", + "name": "debian-archive-keyring", + "version": "2021.1.1+deb11u1" + }, + { + "key": "libapt-pkg6.0_2.2.4_arm64", + "name": "libapt-pkg6.0", + "version": "2.2.4" + }, + { + "key": "libxxhash0_0.8.0-2_arm64", + "name": "libxxhash0", + "version": "0.8.0-2" + }, + { + "key": "libudev1_247.3-7-p-deb11u4_arm64", + "name": "libudev1", + "version": "247.3-7+deb11u4" + }, + { + "key": "adduser_3.118-p-deb11u1_arm64", + "name": "adduser", + "version": "3.118+deb11u1" + }, + { + "key": "passwd_1-4.8.1-1_arm64", + "name": "passwd", + "version": "1:4.8.1-1" + }, + { + "key": "libpam-modules_1.4.0-9-p-deb11u1_arm64", + "name": "libpam-modules", + "version": "1.4.0-9+deb11u1" + }, + { + "key": "libpam-modules-bin_1.4.0-9-p-deb11u1_arm64", + "name": "libpam-modules-bin", + "version": "1.4.0-9+deb11u1" + }, + { + "key": "libpam0g_1.4.0-9-p-deb11u1_arm64", + "name": "libpam0g", + "version": "1.4.0-9+deb11u1" + }, + { + "key": "libaudit1_1-3.0-2_arm64", + "name": "libaudit1", + "version": "1:3.0-2" + }, + { + "key": "libcap-ng0_0.7.9-2.2-p-b1_arm64", + "name": "libcap-ng0", + "version": "0.7.9-2.2+b1" + }, + { + "key": "libaudit-common_1-3.0-2_arm64", + "name": "libaudit-common", + "version": "1:3.0-2" + }, + { + "key": "libtirpc3_1.3.1-1-p-deb11u1_arm64", + "name": "libtirpc3", + "version": "1.3.1-1+deb11u1" + }, + { + "key": "libtirpc-common_1.3.1-1-p-deb11u1_arm64", + "name": "libtirpc-common", + "version": "1.3.1-1+deb11u1" + }, + { + "key": "libgssapi-krb5-2_1.18.3-6-p-deb11u4_arm64", + "name": "libgssapi-krb5-2", + "version": "1.18.3-6+deb11u4" + }, + { + "key": "libkrb5support0_1.18.3-6-p-deb11u4_arm64", + "name": "libkrb5support0", + "version": "1.18.3-6+deb11u4" + }, + { + "key": "libkrb5-3_1.18.3-6-p-deb11u4_arm64", + "name": "libkrb5-3", + "version": "1.18.3-6+deb11u4" + }, + { + "key": "libssl1.1_1.1.1w-0-p-deb11u1_arm64", + "name": "libssl1.1", + "version": "1.1.1w-0+deb11u1" + }, + { + "key": "libkeyutils1_1.6.1-2_arm64", + "name": "libkeyutils1", + "version": "1.6.1-2" + }, + { + "key": "libk5crypto3_1.18.3-6-p-deb11u4_arm64", + "name": "libk5crypto3", + "version": "1.18.3-6+deb11u4" + }, + { + "key": "libcom-err2_1.46.2-2_arm64", + "name": "libcom-err2", + "version": "1.46.2-2" + }, + { + "key": "libnsl2_1.3.0-2_arm64", + "name": "libnsl2", + "version": "1.3.0-2" + }, + { + "key": "libdb5.3_5.3.28-p-dfsg1-0.8_arm64", + "name": "libdb5.3", + "version": "5.3.28+dfsg1-0.8" + }, + { + "key": "libsemanage1_3.1-1-p-b2_arm64", + "name": "libsemanage1", + "version": "3.1-1+b2" + }, + { + "key": "libsepol1_3.1-1_arm64", + "name": "libsepol1", + "version": "3.1-1" + }, + { + "key": "libsemanage-common_3.1-1_arm64", + "name": "libsemanage-common", + "version": "3.1-1" + } + ], + "key": "apt_2.2.4_arm64", + "name": "apt", + "sha256": "39cbe42f3e64c6359b445d6fed7385273881e507b8be1d3b653ec9fb7d4c917c", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/a/apt/apt_2.2.4_arm64.deb", + "version": "2.2.4" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libsystemd0_247.3-7-p-deb11u4_arm64", + "name": "libsystemd0", + "sha256": "32e8c12301a9ada555adea9a4c2f15df788411dadd164baca5c31690fe06e381", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/s/systemd/libsystemd0_247.3-7+deb11u4_arm64.deb", + "version": "247.3-7+deb11u4" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libzstd1_1.4.8-p-dfsg-2.1_arm64", + "name": "libzstd1", + "sha256": "dd01659c6c122f983a3369a04ede63539f666585d52a03f8aa2c27b307e547e0", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libz/libzstd/libzstd1_1.4.8+dfsg-2.1_arm64.deb", + "version": "1.4.8+dfsg-2.1" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "liblz4-1_1.9.3-2_arm64", + "name": "liblz4-1", + "sha256": "83f0ee547cd42854e1b2a2e4c1a5705e28259ee5fa6560119f918f961a5dada2", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/l/lz4/liblz4-1_1.9.3-2_arm64.deb", + "version": "1.9.3-2" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libgcrypt20_1.8.7-6_arm64", + "name": "libgcrypt20", + "sha256": "61ec779149f20923b30adad7bdf4732957e88a5b6a26d94b2210dfe79409959b", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libg/libgcrypt20/libgcrypt20_1.8.7-6_arm64.deb", + "version": "1.8.7-6" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libgpg-error0_1.38-2_arm64", + "name": "libgpg-error0", + "sha256": "d1116f4281d6db35279799a21051e0d0e2600d110d7ee2b95b3cca6bec28067c", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libg/libgpg-error/libgpg-error0_1.38-2_arm64.deb", + "version": "1.38-2" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libstdc-p--p-6_10.2.1-6_arm64", + "name": "libstdc++6", + "sha256": "7869aa540cc46e9f3d4267d5bde2af0e5b429a820c1d6f1a4cfccfe788c31890", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/g/gcc-10/libstdc++6_10.2.1-6_arm64.deb", + "version": "10.2.1-6" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libseccomp2_2.5.1-1-p-deb11u1_arm64", + "name": "libseccomp2", + "sha256": "5b8983c2e330790dbe04ae990f166d7939a3e14b75556a8489309ae704fbeb50", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libs/libseccomp/libseccomp2_2.5.1-1+deb11u1_arm64.deb", + "version": "2.5.1-1+deb11u1" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libgnutls30_3.7.1-5-p-deb11u4_arm64", + "name": "libgnutls30", + "sha256": "7153ec6ee985eebba710dcb6e425bb881c91ee5987a4517518f3f44a9bb5fc1a", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/g/gnutls28/libgnutls30_3.7.1-5+deb11u4_arm64.deb", + "version": "3.7.1-5+deb11u4" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libunistring2_0.9.10-4_arm64", + "name": "libunistring2", + "sha256": "53ff395ea4d8cf17c52155a452a0dc15af0ee2fa5cb3b0085b9c7335de8d5f7f", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libu/libunistring/libunistring2_0.9.10-4_arm64.deb", + "version": "0.9.10-4" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libtasn1-6_4.16.0-2-p-deb11u1_arm64", + "name": "libtasn1-6", + "sha256": "f469147bbd3969055c51fc661c9aa0d56d48eccd070d233f1424b0d8b3f29295", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libt/libtasn1-6/libtasn1-6_4.16.0-2+deb11u1_arm64.deb", + "version": "4.16.0-2+deb11u1" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libp11-kit0_0.23.22-1_arm64", + "name": "libp11-kit0", + "sha256": "ac6e8eda3277708069bc6f03aff06dc319855d64ede9fca219938e52f92ee09c", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/p/p11-kit/libp11-kit0_0.23.22-1_arm64.deb", + "version": "0.23.22-1" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libffi7_3.3-6_arm64", + "name": "libffi7", + "sha256": "eb748e33ae4ed46f5a4c14b7a2a09792569f2029ede319d0979c373829ba1532", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libf/libffi/libffi7_3.3-6_arm64.deb", + "version": "3.3-6" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libnettle8_3.7.3-1_arm64", + "name": "libnettle8", + "sha256": "5061c931f95dc7277d95fc58bce7c17b1a95c6aa9a9aac781784f3b3dc909047", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/n/nettle/libnettle8_3.7.3-1_arm64.deb", + "version": "3.7.3-1" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libidn2-0_2.3.0-5_arm64", + "name": "libidn2-0", + "sha256": "0d2e6d39bf65f16861f284be567c1a6c5d4dc6b54dcfdf9dba631546ff4e6796", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libi/libidn2/libidn2-0_2.3.0-5_arm64.deb", + "version": "2.3.0-5" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libhogweed6_3.7.3-1_arm64", + "name": "libhogweed6", + "sha256": "3e9eea5e474dd98a7de9e4c1ecfbfd6f6efb1d40bf51d6473de9713cf41d2191", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/n/nettle/libhogweed6_3.7.3-1_arm64.deb", + "version": "3.7.3-1" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "debian-archive-keyring_2021.1.1-p-deb11u1_arm64", + "name": "debian-archive-keyring", + "sha256": "28ca7749ab7978f3c571732c3aa1c56e3ad1d5db3c915293763d4f6cb8fcce89", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/d/debian-archive-keyring/debian-archive-keyring_2021.1.1+deb11u1_all.deb", + "version": "2021.1.1+deb11u1" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libapt-pkg6.0_2.2.4_arm64", + "name": "libapt-pkg6.0", + "sha256": "7cb6015ea5c185ef93706989fb730377406878c72f6943b6ecdd956697f1abe6", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/a/apt/libapt-pkg6.0_2.2.4_arm64.deb", + "version": "2.2.4" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libxxhash0_0.8.0-2_arm64", + "name": "libxxhash0", + "sha256": "a31effcbd7a248b64dd480330557f41ea796a010b2c2e7ac91ed10f94e605065", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/x/xxhash/libxxhash0_0.8.0-2_arm64.deb", + "version": "0.8.0-2" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libudev1_247.3-7-p-deb11u4_arm64", + "name": "libudev1", + "sha256": "d53ca63927b51ad6f9a85ee1e4ce74d20ef45651179fd70f3c8d72607071e393", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/s/systemd/libudev1_247.3-7+deb11u4_arm64.deb", + "version": "247.3-7+deb11u4" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "adduser_3.118-p-deb11u1_arm64", + "name": "adduser", + "sha256": "1478a610fd50e190882ff41e16c57b628a508bcf5b5ac5313affb49d20818e0a", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/a/adduser/adduser_3.118+deb11u1_all.deb", + "version": "3.118+deb11u1" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "passwd_1-4.8.1-1_arm64", + "name": "passwd", + "sha256": "5a675c9d23f176ea195678a949e144b23c7a8b268b03e0df8919a2cfc198e585", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/s/shadow/passwd_4.8.1-1_arm64.deb", + "version": "1:4.8.1-1" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libpam-modules_1.4.0-9-p-deb11u1_arm64", + "name": "libpam-modules", + "sha256": "7f46ae216fdc6c69b0120d430936f40f3c5f37249296042324aeb584d5566a3c", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/p/pam/libpam-modules_1.4.0-9+deb11u1_arm64.deb", + "version": "1.4.0-9+deb11u1" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libpam-modules-bin_1.4.0-9-p-deb11u1_arm64", + "name": "libpam-modules-bin", + "sha256": "bc20fa16c91a239de350ffcc019fbae5ce7c47c21235b332ff9d67638804866e", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/p/pam/libpam-modules-bin_1.4.0-9+deb11u1_arm64.deb", + "version": "1.4.0-9+deb11u1" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libpam0g_1.4.0-9-p-deb11u1_arm64", + "name": "libpam0g", + "sha256": "4905e523ce38e80b79f13f0227fca519f6833eb116dd9c58cbbecb39c0e01e3d", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/p/pam/libpam0g_1.4.0-9+deb11u1_arm64.deb", + "version": "1.4.0-9+deb11u1" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libaudit1_1-3.0-2_arm64", + "name": "libaudit1", + "sha256": "c93da146715dcd0c71759629c04afb01a41c879d91b2f5330adc74365db03763", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/a/audit/libaudit1_3.0-2_arm64.deb", + "version": "1:3.0-2" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libcap-ng0_0.7.9-2.2-p-b1_arm64", + "name": "libcap-ng0", + "sha256": "b7b14e0b7747872f04691efe6c126de5ed0bf1dc200f51b93039cc2f4a65a96a", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libc/libcap-ng/libcap-ng0_0.7.9-2.2+b1_arm64.deb", + "version": "0.7.9-2.2+b1" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libaudit-common_1-3.0-2_arm64", + "name": "libaudit-common", + "sha256": "0d52f4826a57aea13cea1a85bfae354024c7b2f7b95e39cd1ce225e4db27d0f6", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/a/audit/libaudit-common_3.0-2_all.deb", + "version": "1:3.0-2" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libtirpc3_1.3.1-1-p-deb11u1_arm64", + "name": "libtirpc3", + "sha256": "ccff0927f55b97fe9ea13057fab8bff9920bf4628eb2d5d48b9656f2fb74d2e1", + "url": "https://snapshot-cloudflare.debian.org/archive/debian-security/20240210T223313Z/pool/updates/main/libt/libtirpc/libtirpc3_1.3.1-1+deb11u1_arm64.deb", + "version": "1.3.1-1+deb11u1" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libtirpc-common_1.3.1-1-p-deb11u1_arm64", + "name": "libtirpc-common", + "sha256": "b2f10cb79e7d7a2f9b30bcdf036127df55cd4a34688547bc2886fa38f4969f77", + "url": "https://snapshot-cloudflare.debian.org/archive/debian-security/20240210T223313Z/pool/updates/main/libt/libtirpc/libtirpc-common_1.3.1-1+deb11u1_all.deb", + "version": "1.3.1-1+deb11u1" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libgssapi-krb5-2_1.18.3-6-p-deb11u4_arm64", + "name": "libgssapi-krb5-2", + "sha256": "5572a462c7f78f9610bd4f1dd9f8e4f8243fa9dc2d1deb5b1cf7cec1f1df83dc", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/k/krb5/libgssapi-krb5-2_1.18.3-6+deb11u4_arm64.deb", + "version": "1.18.3-6+deb11u4" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libkrb5support0_1.18.3-6-p-deb11u4_arm64", + "name": "libkrb5support0", + "sha256": "d44585771e26c9b8d115aad33736fcc3e03cf98238ea7c7985554f166441aa07", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/k/krb5/libkrb5support0_1.18.3-6+deb11u4_arm64.deb", + "version": "1.18.3-6+deb11u4" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libkrb5-3_1.18.3-6-p-deb11u4_arm64", + "name": "libkrb5-3", + "sha256": "3dcdadb1db461d14b6051a19c6a94ae9f61c3d2b1d35fd9d63326cd8f4ae49e5", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/k/krb5/libkrb5-3_1.18.3-6+deb11u4_arm64.deb", + "version": "1.18.3-6+deb11u4" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libssl1.1_1.1.1w-0-p-deb11u1_arm64", + "name": "libssl1.1", + "sha256": "fe7a7d313c87e46e62e614a07137e4a476a79fc9e5aab7b23e8235211280fee3", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/o/openssl/libssl1.1_1.1.1w-0+deb11u1_arm64.deb", + "version": "1.1.1w-0+deb11u1" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libkeyutils1_1.6.1-2_arm64", + "name": "libkeyutils1", + "sha256": "7101c2380ab47a3627a6fa076a149ab71078263064f936fccbd43efbaed4a2da", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/k/keyutils/libkeyutils1_1.6.1-2_arm64.deb", + "version": "1.6.1-2" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libk5crypto3_1.18.3-6-p-deb11u4_arm64", + "name": "libk5crypto3", + "sha256": "d8f31a8bd83fe2593e83a930fc2713e1213f25311a629836dfcde5bd23a85e83", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/k/krb5/libk5crypto3_1.18.3-6+deb11u4_arm64.deb", + "version": "1.18.3-6+deb11u4" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libcom-err2_1.46.2-2_arm64", + "name": "libcom-err2", + "sha256": "fc95d415c35f5b687871f660a5bf66963fd117daa490110499119411e2d6145e", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/e/e2fsprogs/libcom-err2_1.46.2-2_arm64.deb", + "version": "1.46.2-2" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libnsl2_1.3.0-2_arm64", + "name": "libnsl2", + "sha256": "8f9ba58b219779b43c4ccc78c79b0a23f721fc96323c202abb31e02f942104b3", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libn/libnsl/libnsl2_1.3.0-2_arm64.deb", + "version": "1.3.0-2" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libdb5.3_5.3.28-p-dfsg1-0.8_arm64", + "name": "libdb5.3", + "sha256": "cf9aa3eae9cfc4c84f93e32f3d11e2707146e4d9707712909e3c61530b50353e", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/d/db5.3/libdb5.3_5.3.28+dfsg1-0.8_arm64.deb", + "version": "5.3.28+dfsg1-0.8" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libsemanage1_3.1-1-p-b2_arm64", + "name": "libsemanage1", + "sha256": "342a804007338314211981fac0bc083c3c66c6040bca0e47342c6d9ff44f103e", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libs/libsemanage/libsemanage1_3.1-1+b2_arm64.deb", + "version": "3.1-1+b2" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libsepol1_3.1-1_arm64", + "name": "libsepol1", + "sha256": "354d36c3084c14f242baba3a06372a3c034cec7a0cb38e626fc03cc4751b2cd3", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libs/libsepol/libsepol1_3.1-1_arm64.deb", + "version": "3.1-1" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libsemanage-common_3.1-1_arm64", + "name": "libsemanage-common", + "sha256": "d319a026ecd02e2f605c52350949279f3c331a19380f8b6888ce5b9ef0d31349", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/libs/libsemanage/libsemanage-common_3.1-1_all.deb", + "version": "3.1-1" + }, + { + "arch": "arm64", + "dependencies": [ + { + "key": "libperl5.32_5.32.1-4-p-deb11u3_arm64", + "name": "libperl5.32", + "version": "5.32.1-4+deb11u3" + }, + { + "key": "perl-modules-5.32_5.32.1-4-p-deb11u3_arm64", + "name": "perl-modules-5.32", + "version": "5.32.1-4+deb11u3" + }, + { + "key": "perl-base_5.32.1-4-p-deb11u3_arm64", + "name": "perl-base", + "version": "5.32.1-4+deb11u3" + }, + { + "key": "libgdbm6_1.19-2_arm64", + "name": "libgdbm6", + "version": "1.19-2" + }, + { + "key": "libgdbm-compat4_1.19-2_arm64", + "name": "libgdbm-compat4", + "version": "1.19-2" + } + ], + "key": "perl_5.32.1-4-p-deb11u3_arm64", + "name": "perl", + "sha256": "6ed36a59241bbeec132eebec770567a4d23884f71dc922ac6770862cac1f3d9a", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/p/perl/perl_5.32.1-4+deb11u3_arm64.deb", + "version": "5.32.1-4+deb11u3" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libperl5.32_5.32.1-4-p-deb11u3_arm64", + "name": "libperl5.32", + "sha256": "9a5524101015f14773246336cb615c0e58fff2e7420a79f511262df9a7ff1c91", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/p/perl/libperl5.32_5.32.1-4+deb11u3_arm64.deb", + "version": "5.32.1-4+deb11u3" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "perl-modules-5.32_5.32.1-4-p-deb11u3_arm64", + "name": "perl-modules-5.32", + "sha256": "9a5cb99d0f33cb11c7f535aaebfb569c6b6f97a75d748a9a52ea3afed5bd3960", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/p/perl/perl-modules-5.32_5.32.1-4+deb11u3_all.deb", + "version": "5.32.1-4+deb11u3" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "perl-base_5.32.1-4-p-deb11u3_arm64", + "name": "perl-base", + "sha256": "53e09d9594692c462f33d4e9394bff60f95fe74b70402772dc7396a5829b76e5", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/p/perl/perl-base_5.32.1-4+deb11u3_arm64.deb", + "version": "5.32.1-4+deb11u3" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libgdbm6_1.19-2_arm64", + "name": "libgdbm6", + "sha256": "97a88c2698bd836d04e51ad70c76826850857869b51e90b5343621ba30bbf525", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/g/gdbm/libgdbm6_1.19-2_arm64.deb", + "version": "1.19-2" + }, + { + "arch": "arm64", + "dependencies": [], + "key": "libgdbm-compat4_1.19-2_arm64", + "name": "libgdbm-compat4", + "sha256": "0853cc0b0f92784b7fbd193d737c63b1d95f932e2b95dc1bb10c273e01a0f754", + "url": "https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z/pool/main/g/gdbm/libgdbm-compat4_1.19-2_arm64.deb", + "version": "1.19-2" + } + ], + "version": 1 +} diff --git a/examples/apt/bullseye.yaml b/examples/apt/bullseye.yaml new file mode 100644 index 0000000..b67e516 --- /dev/null +++ b/examples/apt/bullseye.yaml @@ -0,0 +1,33 @@ +# Packages for examples/apt. +# +# Anytime this file is changed, the lockfile needs to be regenerated. +# +# To generate the bullseye.lock.json run the following command +# +# bazel run @bullseye//:lock +# +# See debian_package_index at WORKSPACE.bazel +version: 1 + +sources: + - channel: bullseye main + url: https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z + - channel: bullseye-security main + url: https://snapshot-cloudflare.debian.org/archive/debian-security/20240210T223313Z + - channel: bullseye-updates main + url: https://snapshot-cloudflare.debian.org/archive/debian/20240210T223313Z + +archs: + - "amd64" + - "arm64" + +packages: + - "ncurses-base" + - "libncurses6" + - "tzdata" + - "bash" + - "coreutils" # for commands like `ls` + # for apt list --installed + - "dpkg" + - "apt" + - "perl" diff --git a/examples/apt/test_linux_amd64.yaml b/examples/apt/test_linux_amd64.yaml new file mode 100644 index 0000000..f1bebdf --- /dev/null +++ b/examples/apt/test_linux_amd64.yaml @@ -0,0 +1,23 @@ +schemaVersion: "2.0.0" + +commandTests: + - name: "echo hello" + command: "/bin/bash" + args: ["-c", "echo hello world!"] + expectedOutput: ["hello world!"] + - name: "apt list --installed" + command: "apt" + args: ["list", "--installed"] + expectedOutput: + - Listing\.\.\. + - apt/now 2\.2\.4 amd64 \[installed,local\] + - bash/now 5\.1-2\+deb11u1 amd64 \[installed,local\] + - coreutils/now 8\.32-4\+b1 amd64 \[installed,local\] + - dpkg/now 1\.20\.13 amd64 \[installed,local\] + - libncurses6/now 6\.2\+20201114-2\+deb11u2 amd64 \[installed,local\] + - ncurses-base/now 6\.2\+20201114-2\+deb11u2 all \[installed,local\] + - perl/now 5\.32\.1-4\+deb11u3 amd64 \[installed,local\] + - tzdata/now 2024a-0\+deb11u1 all \[installed,local\] + - name: "whoami" + command: "whoami" + expectedOutput: [r00t] diff --git a/examples/apt/test_linux_arm64.yaml b/examples/apt/test_linux_arm64.yaml new file mode 100644 index 0000000..73a4fc3 --- /dev/null +++ b/examples/apt/test_linux_arm64.yaml @@ -0,0 +1,23 @@ +schemaVersion: "2.0.0" + +commandTests: + - name: "echo hello" + command: "/bin/bash" + args: ["-c", "echo hello world!"] + expectedOutput: ["hello world!"] + - name: "apt list --installed" + command: "apt" + args: ["list", "--installed"] + expectedOutput: + - Listing\.\.\. + - apt/now 2\.2\.4 arm64 \[installed,local\] + - bash/now 5\.1-2\+deb11u1 arm64 \[installed,local\] + - coreutils/now 8\.32-4 arm64 \[installed,local\] + - dpkg/now 1\.20\.13 arm64 \[installed,local\] + - libncurses6/now 6\.2\+20201114-2\+deb11u2 arm64 \[installed,local\] + - ncurses-base/now 6\.2\+20201114-2\+deb11u2 all \[installed,local\] + - perl/now 5\.32\.1-4\+deb11u3 arm64 \[installed,local\] + - tzdata/now 2024a-0\+deb11u1 all \[installed,local\] + - name: "whoami" + command: "whoami" + expectedOutput: [r00t] diff --git a/examples/statusd/BUILD.bazel b/examples/statusd/BUILD.bazel new file mode 100644 index 0000000..c389d5b --- /dev/null +++ b/examples/statusd/BUILD.bazel @@ -0,0 +1,19 @@ +# buildifier: disable=bzl-visibility +load("//apt:defs.bzl", "dpkg_statusd") +load("//distroless/tests:asserts.bzl", "assert_tar_listing") + +dpkg_statusd( + name = "statusd", + package_name = "ca-certificates", + control = "@example-bullseye-ca-certificates//:control.tar.xz", +) + +assert_tar_listing( + name = "test_statusd", + actual = "statusd", + expected = """\ +#mtree +./var/lib/dpkg/status.d/ca-certificates/control time=1611051064.0 mode=644 gid=0 uid=0 type=file size=793 sha1digest=214a8bc9b7e7ce33fbd2e08b0b01aa5e2b717495 +./var/lib/dpkg/status.d/ca-certificates/md5sums time=1611051064.0 mode=644 gid=0 uid=0 type=file size=14644 sha1digest=80ae4d6a7dbfeefd6e16ccab344671c6f7426422 +""", +)