build/http_archive_deb: introduce
This adds a new repo rule which is essentially an extended version of
http_archive specifically for extracting Debian packages into repos.
http_archive can extract these, but only the outer AR layer which
leaves you with a data.tar.xyz and control.tar.xyz which is not what you
want most of the time.
This rule does that first step, but then figures out the respective file
extensions and extracts both of the tarballs, with the control tarball
going into a "debian" subdirectory. This allows grabbing files from the
archive directly.
Change-Id: I6a6a6964e6202526ee9093488f9c10e709f19149
Reviewed-on: https://review.monogon.dev/c/monogon/+/4649
Reviewed-by: Jan Schär <jan@monogon.tech>
Tested-by: Jenkins CI
diff --git a/build/http_archive_deb/BUILD.bazel b/build/http_archive_deb/BUILD.bazel
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/build/http_archive_deb/BUILD.bazel
diff --git a/build/http_archive_deb/defs.bzl b/build/http_archive_deb/defs.bzl
new file mode 100644
index 0000000..0f1b75a
--- /dev/null
+++ b/build/http_archive_deb/defs.bzl
@@ -0,0 +1,128 @@
+load(
+ "@bazel_tools//tools/build_defs/repo:cache.bzl",
+ "CANONICAL_ID_DOC",
+ "DEFAULT_CANONICAL_ID_ENV",
+ "get_default_canonical_id",
+)
+load(
+ "@bazel_tools//tools/build_defs/repo:utils.bzl",
+ "get_auth",
+ "update_attrs",
+ "workspace_and_buildfile",
+)
+
+_http_archive_deb_attrs = {
+ "url": attr.string(doc = "A URL to a deb file. See http_archive for more info."),
+ "urls": attr.string_list(doc = "A list of URLs to a deb file. See http_archive for more info."),
+ "integrity": attr.string(
+ doc = """Expected checksum in Subresource Integrity format of the file downloaded.
+
+This must match the checksum of the file downloaded. _It is a security risk
+to omit the checksum as remote files can change._ At best omitting this
+field will make your build non-hermetic. It is optional to make development
+easier but this attribute should be set before shipping.""",
+ ),
+ "netrc": attr.string(
+ doc = "Location of the .netrc file to use for authentication",
+ ),
+ "auth_patterns": attr.string_dict(
+ doc = "See http_archive",
+ ),
+ "canonical_id": attr.string(
+ doc = CANONICAL_ID_DOC,
+ ),
+ "build_file": attr.label(
+ allow_single_file = True,
+ doc =
+ "The file to use as the BUILD file for this repository." +
+ "This attribute is an absolute label (use '@//' for the main " +
+ "repo). The file does not need to be named BUILD, but can " +
+ "be (something like BUILD.new-repo-name may work well for " +
+ "distinguishing it from the repository's actual BUILD files. " +
+ "Either build_file or build_file_content can be specified, but " +
+ "not both.",
+ ),
+ "build_file_content": attr.string(
+ doc =
+ "The content for the BUILD file for this repository. " +
+ "Either build_file or build_file_content can be specified, but " +
+ "not both.",
+ ),
+ "workspace_file": attr.label(
+ doc = "No-op attribute; do not use.",
+ ),
+ "workspace_file_content": attr.string(
+ doc = "No-op attribute; do not use.",
+ ),
+}
+
+def _get_source_urls(ctx):
+ """Returns source urls provided via the url, urls attributes.
+
+ Also checks that at least one url is provided."""
+ if not ctx.attr.url and not ctx.attr.urls:
+ fail("At least one of url and urls must be provided")
+
+ source_urls = []
+ if ctx.attr.urls:
+ source_urls = ctx.attr.urls
+ if ctx.attr.url:
+ source_urls = [ctx.attr.url] + source_urls
+ return source_urls
+
+def _http_archive_deb_impl(ctx):
+ """Implementation of the http_archive_deb rule."""
+ if ctx.attr.build_file and ctx.attr.build_file_content:
+ fail("Only one of build_file and build_file_content can be provided.")
+
+ source_urls = _get_source_urls(ctx)
+ download_info = ctx.download_and_extract(
+ source_urls,
+ output = "debian-package",
+ type = ".deb",
+ canonical_id = ctx.attr.canonical_id or get_default_canonical_id(ctx, source_urls),
+ auth = get_auth(ctx, source_urls),
+ integrity = ctx.attr.integrity,
+ )
+ files = ctx.path("debian-package").readdir()
+
+ data_archive = None
+ control_archive = None
+ has_marker = False
+ for f in files:
+ if f.basename.startswith("data.tar."):
+ data_archive = f.basename
+ elif f.basename.startswith("control.tar."):
+ control_archive = f.basename
+ elif f.basename == "debian-binary":
+ has_marker = True
+
+ if not has_marker:
+ fail("deb package does not contain a debian-binary marker file, check the file.")
+ if not data_archive:
+ fail("Failed to find data.tar.* archive in .deb contents.")
+ if not control_archive:
+ fail("Failed to find control.tar.* archive in .deb contents.")
+
+ ctx.extract(
+ archive = "debian-package/" + data_archive,
+ )
+ ctx.extract(
+ archive = "debian-package/" + control_archive,
+ output = "debian",
+ )
+ ctx.delete("debian-package")
+ workspace_and_buildfile(ctx)
+
+ if ctx.attr.integrity:
+ return ctx.repo_metadata(reproducible = True)
+
+ return ctx.repo_metadata(attrs_for_reproducibility = update_attrs(ctx.attr, _http_archive_deb_attrs.keys(), {"integrity": download_info.integrity}))
+
+http_archive_deb = repository_rule(
+ implementation = _http_archive_deb_impl,
+ attrs = _http_archive_deb_attrs,
+ environ = [DEFAULT_CANONICAL_ID_ENV],
+ doc = """http_archive for Debian packages. Extracts all contents into a
+ repository, control files are extracted into a `debian` subfolder.""",
+)