diff --git a/MODULE.bazel b/MODULE.bazel index 5bf44f0..23dd29d 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -70,6 +70,11 @@ apt.install( manifest = "//examples/debian_snapshot:bullseye_nolock.yaml", nolock = True, ) +apt.install( + name = "bullseye_rproject", + lock = "//examples/debian_flat_repo:bullseye_rproject.lock.json", + manifest = "//examples/debian_flat_repo:bullseye_rproject.yaml", +) apt.install( name = "apt_security", manifest = "//examples/debian_snapshot_security:security.yaml", @@ -85,4 +90,4 @@ apt.install( lock = "//examples/ubuntu_snapshot:noble.lock.json", manifest = "//examples/ubuntu_snapshot:noble.yaml", ) -use_repo(apt, "apt_security", "apt_security_resolve", "bullseye", "bullseye_nolock", "noble", "shared_dependencies") +use_repo(apt, "apt_security", "apt_security_resolve", "bullseye", "bullseye_nolock", "bullseye_rproject", "noble", "shared_dependencies") diff --git a/apt/private/manifest.bzl b/apt/private/manifest.bzl index 668270e..8138185 100644 --- a/apt/private/manifest.bzl +++ b/apt/private/manifest.bzl @@ -41,8 +41,13 @@ def _source(src): index = "Packages" - index_path = "dists/{dist}/{comp}/binary-{arch}".format(**src) - output = "{dist}/{comp}/{arch}/{index}".format(index = index, **src) + if "directory" in src: # flat repo: + src["directory"] = src["directory"].rstrip("/") + index_path = src["directory"] + output = "{directory}/{arch}/{index}".format(index = index, **src) + else: # canonical + index_path = "dists/{dist}/{comp}/binary-{arch}".format(**src) + output = "{dist}/{comp}/{arch}/{index}".format(index = index, **src) return struct( arch = src["arch"], @@ -101,15 +106,43 @@ def _from_dict(manifest, manifest_label): for arch in manifest["archs"]: for src in manifest["sources"]: - dist, components = src["channel"].split(" ", 1) + src["arch"] = arch - for comp in components.split(" "): - src["dist"] = dist - src["comp"] = comp - src["arch"] = arch + channel_chunks = src["channel"].split(" ") + + # support both canonical and flat repos, see: + # canonical: https://wiki.debian.org/DebianRepository/Format#Overview + # flat repo: https://wiki.debian.org/DebianRepository/Format#Flat_Repository_Format + if len(channel_chunks) > 1: # canonical + dist, components = channel_chunks[0], channel_chunks[1:] + + if dist.endswith("/"): + msg = "Debian dist ends in '/' but this is not a flat repo: {}" + failures.append(msg.format(dist)) + + for comp in components: + src["dist"] = dist + src["comp"] = comp + + sources.append(_source(src)) + else: # flat + directory = channel_chunks[0] + + if not directory.endswith("/"): + msg = "Debian flat repo directory must end in '/': {}" + failures.append(msg.format(directory)) + + src["directory"] = directory sources.append(_source(src)) + if failures: + for failure in failures: + msg = "{}: {}".format(manifest_label, failure) + print(msg) + + fail("{}: Invalid manifest".format(manifest_label)) + manifest["sources"] = sources return struct(**manifest) diff --git a/examples/debian_flat_repo/BUILD.bazel b/examples/debian_flat_repo/BUILD.bazel new file mode 100644 index 0000000..d491029 --- /dev/null +++ b/examples/debian_flat_repo/BUILD.bazel @@ -0,0 +1,48 @@ +load("@container_structure_test//:defs.bzl", "container_structure_test") +load("@rules_distroless//apt:defs.bzl", "dpkg_status") +load("@rules_oci//oci:defs.bzl", "oci_image", "oci_load") + +PACKAGES = [ + "@bullseye//dpkg", + "@bullseye//apt", + "@bullseye_rproject//r-mathlib", +] + +# Creates /var/lib/dpkg/status with installed package information. +dpkg_status( + name = "dpkg_status", + controls = [ + "%s/amd64:control" % package + for package in PACKAGES + ], +) + +oci_image( + name = "apt", + architecture = "amd64", + os = "linux", + tars = [ + ":dpkg_status", + ] + [ + "%s/amd64" % package + for package in PACKAGES + ], +) + +oci_load( + name = "tarball", + image = ":apt", + repo_tags = [ + "distroless/test:latest", + ], +) + +container_structure_test( + name = "test", + configs = ["test_linux_amd64.yaml"], + image = ":apt", + target_compatible_with = [ + "@platforms//cpu:x86_64", + "@platforms//os:linux", + ], +) diff --git a/examples/debian_flat_repo/bullseye_rproject.lock.json b/examples/debian_flat_repo/bullseye_rproject.lock.json new file mode 100644 index 0000000..f0325d3 --- /dev/null +++ b/examples/debian_flat_repo/bullseye_rproject.lock.json @@ -0,0 +1,15 @@ +{ + "packages": { + "r-mathlib": { + "amd64": { + "arch": "amd64", + "dependencies": [], + "name": "r-mathlib", + "sha256": "880c95a459cc2d38f4612487c6cb6e2e0d6020670f92dd951e58ba7f561425bc", + "url": "https://cloud.r-project.org/bin/linux/debian/bullseye-cran40/r-mathlib_4.4.2-1~bullseyecran.0_amd64.deb", + "version": "4.4.2-1~bullseyecran.0" + } + } + }, + "version": 2 +} \ No newline at end of file diff --git a/examples/debian_flat_repo/bullseye_rproject.yaml b/examples/debian_flat_repo/bullseye_rproject.yaml new file mode 100644 index 0000000..6c1cd90 --- /dev/null +++ b/examples/debian_flat_repo/bullseye_rproject.yaml @@ -0,0 +1,20 @@ +# Packages for examples/debian_flat_repo. +# +# Anytime this file is changed, the lockfile needs to be regenerated. +# +# To generate the bullseye_rproject.lock.json run the following command +# +# bazel run @bullseye_rproject//:lock +# +# See debian_package_index at WORKSPACE.bazel +version: 1 + +sources: + - channel: bullseye-cran40/ + url: https://cloud.r-project.org/bin/linux/debian + +archs: + - amd64 + +packages: + - r-mathlib diff --git a/examples/debian_flat_repo/test_linux_amd64.yaml b/examples/debian_flat_repo/test_linux_amd64.yaml new file mode 100644 index 0000000..027c4bc --- /dev/null +++ b/examples/debian_flat_repo/test_linux_amd64.yaml @@ -0,0 +1,9 @@ +schemaVersion: "2.0.0" + +commandTests: + - name: "apt list --installed" + command: "apt" + args: ["list", "--installed"] + expectedOutput: + - Listing\.\.\. + - r-mathlib/now 4.4.2-1~bullseyecran.0 amd64 \[installed,local\]