#!/usr/bin/env python3 import configparser import pathlib import subprocess import time import click _CONFIG_FILE = pathlib.Path.home() / ".config" / "livecd-rootfs" / "build-livefs.conf" def _read_config() -> dict[str, str]: cp = configparser.ConfigParser() cp.read(_CONFIG_FILE) return dict(cp["defaults"]) if "defaults" in cp else {} @click.command( context_settings={"ignore_unknown_options": True, "allow_extra_args": True} ) @click.option("--suite", required=True, help="Ubuntu suite/series (e.g. noble)") @click.option( "--vm-name", default=None, help="LXD VM name (default: livefs-builder-{suite})", ) @click.option( "--http-proxy", default=None, help="HTTP proxy URL for apt inside the VM (also read from build-livefs.conf)", ) @click.argument("extra_args", nargs=-1, type=click.UNPROCESSED) def main(suite, vm_name, http_proxy, extra_args): livecd_rootfs_root = pathlib.Path(__file__).resolve().parent.parent vm_name = vm_name or f"livefs-builder-{suite}" host_conf = ( pathlib.Path.home() / ".config" / "livecd-rootfs" / "build-livefs.conf" ) if http_proxy is None: http_proxy = _read_config().get("http-proxy") result = subprocess.run(["lxc", "info", vm_name], capture_output=True) if result.returncode != 0: launch_cmd = [ "lxc", "launch", f"ubuntu-daily:{suite}", vm_name, "--vm", "--config", "limits.cpu=4", "--config", "limits.memory=8GiB", "--device", "root,size=100GiB", ] user_data = "#cloud-config\npackage_update: true\n" if http_proxy is not None: user_data += ( "apt:\n" f" http_proxy: {http_proxy}\n" f" https_proxy: {http_proxy}\n" ) launch_cmd += ["--config", f"user.user-data={user_data}"] subprocess.run(launch_cmd, check=True) device_info = subprocess.run( ["lxc", "config", "device", "show", vm_name], capture_output=True, text=True, check=True, ).stdout if "livecd-rootfs" not in device_info: subprocess.run( [ "lxc", "config", "device", "add", vm_name, "livecd-rootfs", "disk", f"source={livecd_rootfs_root}", "path=/srv/livecd-rootfs", ], check=True, ) info = subprocess.run( ["lxc", "info", vm_name], capture_output=True, text=True, check=True ).stdout if "Status: STOPPED" in info: subprocess.run(["lxc", "start", vm_name], check=True) for _ in range(30): result = subprocess.run( ["lxc", "exec", vm_name, "--", "true"], capture_output=True ) if result.returncode == 0: break time.sleep(2) else: raise click.ClickException(f"VM {vm_name!r} did not become ready in time") subprocess.run( ["lxc", "exec", vm_name, "--", "cloud-init", "status", "--wait"], check=True ) subprocess.run( ["lxc", "exec", vm_name, "--", "apt-get", "install", "-y", "livecd-rootfs"], check=True, ) if host_conf.exists(): subprocess.run( [ "lxc", "exec", vm_name, "--", "mkdir", "-p", "/root/.config/livecd-rootfs", ], check=True, ) subprocess.run( [ "lxc", "file", "push", str(host_conf), f"{vm_name}/root/.config/livecd-rootfs/build-livefs.conf", ], check=True, ) subprocess.run( [ "lxc", "exec", vm_name, "--", "/srv/livecd-rootfs/live-build/build-livefs", "--suite", suite, *extra_args, ], check=True, ) if __name__ == "__main__": main()