From 69234507c45f2b92dbc6d64c830a18ead41bda31 Mon Sep 17 00:00:00 2001 From: Alex Pyrgiotis Date: Wed, 26 Feb 2025 18:28:27 +0200 Subject: [PATCH] Build container image using `repro-build` Invoke the `repro-build` script when building a container image, instead of the underlying Docker/Podman commands. The `repro-build` script handles the underlying complexity to call Docker/Podman in a manner that makes the image reproducible. Moreover, mirror some arguments from the `repro-build` script, so that consumers of `build-image.py` can pass them to it. Important: the resulting image will be in .tar format, not .tar.gz, starting from this commit. This means that our tests will be broken for the next few commits. Fixes #1074 --- install/common/build-image.py | 105 ++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 50 deletions(-) diff --git a/install/common/build-image.py b/install/common/build-image.py index 91fe79c..65a763d 100644 --- a/install/common/build-image.py +++ b/install/common/build-image.py @@ -1,5 +1,4 @@ import argparse -import gzip import platform import secrets import subprocess @@ -13,8 +12,6 @@ if platform.system() in ["Darwin", "Windows"]: elif platform.system() == "Linux": CONTAINER_RUNTIME = "podman" -ARCH = platform.machine() - def str2bool(v): if isinstance(v, bool): @@ -50,6 +47,16 @@ def determine_git_tag(): ) +def determine_debian_archive_date(): + """Get the date of the Debian archive from Dockerfile.env.""" + for env in Path("Dockerfile.env").read_text().split("\n"): + if env.startswith("DEBIAN_ARCHIVE_DATE"): + return env.split("=")[1] + raise Exception( + "Could not find 'DEBIAN_ARCHIVE_DATE' build argument in Dockerfile.env" + ) + + def main(): parser = argparse.ArgumentParser() parser.add_argument( @@ -59,16 +66,15 @@ def main(): help=f"The container runtime for building the image (default: {CONTAINER_RUNTIME})", ) parser.add_argument( - "--no-save", - action="store_true", - help="Do not save the container image as a tarball in share/container.tar.gz", + "--platform", + default=None, + help=f"The platform for building the image (default: current platform)", ) parser.add_argument( - "--compress-level", - type=int, - choices=range(0, 10), - default=9, - help="The Gzip compression level, from 0 (lowest) to 9 (highest, default)", + "--output", + "-o", + default=str(Path("share") / "container.tar"), + help="Path to store the container image", ) parser.add_argument( "--use-cache", @@ -83,63 +89,62 @@ def main(): default=None, help="Provide a custom tag for the image (for development only)", ) + parser.add_argument( + "--debian-archive-date", + "-d", + default=determine_debian_archive_date(), + help="Use a specific Debian snapshot archive, by its date (default %(default)s)", + ) + parser.add_argument( + "--dry", + default=False, + action="store_true", + help="Do not run any commands, just print what would happen", + ) args = parser.parse_args() - tarball_path = Path("share") / "container.tar.gz" - image_id_path = Path("share") / "image-id.txt" - - print(f"Building for architecture '{ARCH}'") - - tag = args.tag or determine_git_tag() - image_name_tagged = IMAGE_NAME + ":" + tag + tag = args.tag or f"{args.debian_archive_date}-{determine_git_tag()}" + image_name_tagged = f"{IMAGE_NAME}:{tag}" print(f"Will tag the container image as '{image_name_tagged}'") - with open(image_id_path, "w") as f: - f.write(tag) + image_id_path = Path("share") / "image-id.txt" + if not args.dry: + with open(image_id_path, "w") as f: + f.write(tag) # Build the container image, and tag it with the calculated tag print("Building container image") cache_args = [] if args.use_cache else ["--no-cache"] + platform_args = [] if not args.platform else ["--platform", args.platform] + rootless_args = [] if args.runtime == "docker" else ["--rootless"] + rootless_args = [] + dry_args = [] if not args.dry else ["--dry"] + subprocess.run( [ - args.runtime, + "./dev_scripts/repro-build.py", "build", - BUILD_CONTEXT, + "--runtime", + args.runtime, + "--build-arg", + f"DEBIAN_ARCHIVE_DATE={args.debian_archive_date}", + "--datetime", + args.debian_archive_date, + *dry_args, *cache_args, - "-f", - "Dockerfile", + *platform_args, + *rootless_args, "--tag", image_name_tagged, + "--output", + args.output, + "-f", + "Dockerfile", + BUILD_CONTEXT, ], check=True, ) - if not args.no_save: - print("Saving container image") - cmd = subprocess.Popen( - [ - CONTAINER_RUNTIME, - "save", - image_name_tagged, - ], - stdout=subprocess.PIPE, - ) - - print("Compressing container image") - chunk_size = 4 << 20 - with gzip.open( - tarball_path, - "wb", - compresslevel=args.compress_level, - ) as gzip_f: - while True: - chunk = cmd.stdout.read(chunk_size) - if len(chunk) > 0: - gzip_f.write(chunk) - else: - break - cmd.wait(5) - if __name__ == "__main__": sys.exit(main())