"""ISO generator — squashes rootfs and creates bootable ISO.""" from __future__ import annotations import subprocess import tempfile from pathlib import Path def generate(rootfs: Path, config: dict, output: Path) -> Path: """Generate a bootable ISO from a rootfs.""" iso_cfg = config.get("iso", {}) label = iso_cfg.get("label", "EURI") name = iso_cfg.get("name", "Euri Linux") version = iso_cfg.get("version", "1.0") print(f"\n\033[1;36m==>\033[0m \033[1mGenerating ISO: {name} {version}\033[0m") workdir = Path(tempfile.mkdtemp(prefix="mklive-iso-")) iso_dir = workdir / "iso" iso_dir.mkdir() # copy rootfs into ISO staging _run(["cp", "-a", str(rootfs) + "/.", str(iso_dir)]) # squashfs squash = workdir / "filesystem.squashfs" print(f"\033[1;36m==>\033[0m \033[1mCreating squashfs image...\033[0m") _run(["mksquashfs", str(iso_dir), str(squash), "-comp", "xz", "-b", "1M"]) # create ISO layout live_dir = iso_dir / "boot" / "x86_64" live_dir.mkdir(parents=True, exist_ok=True) _run(["cp", str(squash), str(live_dir / "filesystem.squashfs")]) # EFI boot efi_dir = iso_dir / "EFI" / "BOOT" efi_dir.mkdir(parents=True, exist_ok=True) _run(["cp", "/usr/lib/grub/x86_64-efi/monolithic/grubx64.efi", str(efi_dir / "BOOTX64.EFI")]) # GRUB config grub_cfg = iso_dir / "boot" / "grub" / "grub.cfg" grub_cfg.parent.mkdir(parents=True, exist_ok=True) grub_cfg.write_text(f""" set timeout=10 set default=0 menuentry "{name}" {{ linux /boot/x86_64/vmlinuz boot=live components initrd /boot/x86_64/initrd }} menuentry "{name} (copy to RAM)" {{ linux /boot/x86_64/vmlinuz boot=live components toram initrd /boot/x86_64/initrd }} """) # xorriso to create ISO iso_path = output / f"{name.lower().replace(' ', '-')}-{version}-x86_64.iso" print(f"\033[1;36m==>\033[0m \033[1mCreating ISO: {iso_path.name}\033[0m") _run([ "xorriso", "-as", "mkisofs", "-V", label, "-o", str(iso_path), "-J", "-joliet-long", "-b", "boot/x86_64/filesystem.squashfs", "-no-emul-boot", "-boot-load-size", "4", "-boot-info-table", "-eltorito-alt-boot", "-e", "boot/x86_64/filesystem.squashfs", "-no-emul-boot", str(iso_dir), ]) size_mb = iso_path.stat().st_size / (1024 * 1024) print(f"\n\033[1;32m==>\033[0m \033[1mISO ready: {iso_path} ({size_mb:.1f} MB)\033[0m") return iso_path def _run(cmd: list[str], **kw) -> subprocess.CompletedProcess: print(f" \033[90m$\033[0m \033[1m{' '.join(cmd)}\033[0m") return subprocess.run(cmd, check=True, **kw)