michael.hudson@canonical.com b3fdc4e615
Add isobuild tool to build installer ISOs
This adds a new tool, isobuild, which replaces the ISO-building
functionality previously provided by live-build and cdimage. It is
invoked from auto/build when MAKE_ISO=yes.

The tool supports:
 - Layered desktop images (Ubuntu Desktop, flavors)
 - Non-layered images (Kubuntu, Ubuntu Unity)
 - Images with package pools (most installers)
 - Images without pools (Ubuntu Core Installer)

The isobuild command has several subcommands:
 - init: Initialize the ISO build directory structure
 - setup-apt: Configure APT for package pool generation
 - generate-pool: Create the package pool from a seed
 - generate-sources: Generate cdrom.sources for the installed system
 - add-live-filesystem: Add squashfs and kernel/initrd to the ISO
 - make-bootable: Add GRUB and other boot infrastructure
 - make-iso: Generate the final ISO image

auto/config is updated to:
 - Set MAKE_ISO=yes for relevant image types
 - Set POOL_SEED_NAME for images that need a package pool
 - Invoke gen-iso-ids to compute ISO metadata

auto/build is updated to:
 - Remove old live-build ISO handling code
 - Invoke isobuild at appropriate points in the build

lb_binary_layered is updated to create squashfs files with
cdrom.sources included for use in the ISO.
2026-02-11 09:41:06 +13:00

59 lines
1.6 KiB
Python

import pathlib
import subprocess
key_conf = """\
%no-protection
Key-Type: eddsa
Key-Curve: Ed25519
Key-Usage: sign
Name-Real: Ubuntu ISO One-Time Signing Key
Name-Email: noone@nowhere.invalid
Expire-Date: 0
"""
class EphemeralGPGKey:
def __init__(self, logger, gpghome):
self.logger = logger
self.gpghome = gpghome
def _run_gpg(self, cmd, **kwargs):
return self.logger.run(
["gpg", "--homedir", self.gpghome] + cmd, check=True, **kwargs
)
def create(self):
with self.logger.logged("creating gpg key ...", done_msg="... done"):
self.gpghome.mkdir(mode=0o700)
self._run_gpg(
["--gen-key", "--batch"],
input=key_conf,
text=True,
)
def sign(self, path: pathlib.Path):
with self.logger.logged(f"signing {path}"):
with path.open("rb") as inp:
with pathlib.Path(str(path) + ".gpg").open("wb") as outp:
self._run_gpg(
[
"--no-options",
"--batch",
"--no-tty",
"--armour",
"--digest-algo",
"SHA512",
"--detach-sign",
],
stdin=inp,
stdout=outp,
)
def export_public(self) -> str:
return self._run_gpg(
["--export", "--armor"],
stdout=subprocess.PIPE,
text=True,
).stdout