build-livefs-lxd: helper to run build-livefs inside an LXD VM

Creates (or reuses) a per-suite LXD VM, mounts the livecd-rootfs
checkout into it, waits for the VM to be ready, installs dependencies,
and runs build-livefs inside the VM — keeping the host clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
michael.hudson@canonical.com 2026-02-23 13:56:37 +13:00
parent 0e292ea3f2
commit 6175dfb3ac
No known key found for this signature in database
GPG Key ID: 80E627A0AB757E23

126
live-build/build-livefs-lxd Executable file
View File

@ -0,0 +1,126 @@
#!/usr/bin/env python3
import pathlib
import subprocess
import time
import click
@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.argument("extra_args", nargs=-1, type=click.UNPROCESSED)
def main(suite, vm_name, 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"
)
result = subprocess.run(["lxc", "info", vm_name], capture_output=True)
if result.returncode != 0:
subprocess.run(
[
"lxc", "launch", f"ubuntu-daily:{suite}", vm_name, "--vm",
"--config", "limits.cpu=4",
"--config", "limits.memory=8GiB",
"--device", "root,size=100GiB",
],
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()