#!/usr/bin/env bash # ───────────────────────────────────────────────────────────────────────────── # Arcline OS — live ISO builder (with graphical installer) # # scripts/build-iso.sh # # Produces a hybrid (BIOS+UEFI) live ISO that boots straight into the Arcline # graphical installer: # 1. ensure the CLEAN rootfs exists (what the installer deploys) # 2. build-live.sh → build/rootfs/-live (live-boot + X + installer) # 3. stage kernel + initramfs + squashfs in isofiles/live, and the clean # rootfs archive in isofiles/install (the installer's install source) # 4. write the grub boot config (live-boot: boot=live) # 5. grub-mkrescue → build/artifacts/arcline---.iso # # Requires root for the rootfs stage; the ISO assembly itself runs unprivileged. # ───────────────────────────────────────────────────────────────────────────── set -euo pipefail source "$(dirname "${BASH_SOURCE[0]}")/common.sh" EDITION="${1:?usage: build-iso.sh }" validate_edition "$EDITION" CLEAN="$ROOTFS_DIR/$EDITION" LIVE_ROOT="$ROOTFS_DIR/$EDITION-live" INSTALL_ARC="$ARTIFACT_DIR/arcline-$EDITION-$VERSION-$ARCH.tar.xz" ISOFILES="$IMAGE_DIR/$EDITION/isofiles" ARTIFACT="$ARTIFACT_DIR/arcline-$EDITION-$VERSION-$ARCH.iso" # ── 1. rootfs(es) ─────────────────────────────────────────────────────────── # The ISO is a live session (built on top of the clean rootfs) and carries the # CLEAN rootfs archive as the install source for the graphical installer. if [[ ! -d "$CLEAN" ]]; then log "clean rootfs missing — building it first" "$ROOT/scripts/build-rootfs.sh" "$EDITION" fi if [[ ! -f "$INSTALL_ARC" ]]; then log "clean rootfs archive missing — building it first" "$ROOT/scripts/build-rootfs.sh" "$EDITION" fi "$ROOT/scripts/build-live.sh" "$EDITION" # ── 2. stage files ────────────────────────────────────────────────────────── log "staging ISO files for edition '$EDITION'" rm -rf "$ISOFILES" mkdir -p "$ISOFILES/live" "$ISOFILES/boot/grub" "$ISOFILES/install" KERNEL="$(find "$LIVE_ROOT/boot" -maxdepth 1 -name 'vmlinuz-*' | sort -V | tail -1)" INITRD="$(find "$LIVE_ROOT/boot" -maxdepth 1 -name 'initrd.img-*' | sort -V | tail -1)" [[ -n "$KERNEL" && -n "$INITRD" ]] || die "kernel or initramfs not found in live rootfs" cp -L "$KERNEL" "$ISOFILES/live/vmlinuz" cp -L "$INITRD" "$ISOFILES/live/initrd.img" # the clean (installed-system) rootfs is what the installer deploys cp "$INSTALL_ARC" "$ISOFILES/install/arcline-$EDITION.tar.xz" # ── 2b. secure boot (optional: ARCLINE_SIGN=1 + a MOK keypair) ────────────── if [[ "${ARCLINE_SIGN:-0}" == "1" ]]; then log "secure boot: signing boot chain and shipping MOK in the live image" [[ -f "$BUILD_DIR/keys/MOK.der" ]] || die "ARCLINE_SIGN=1 but no MOK keypair — run: scripts/secureboot/gen-keys.sh" mkdir -p "$LIVE_ROOT/etc/arcline" cp "$BUILD_DIR/keys/MOK.der" "$LIVE_ROOT/etc/arcline/MOK.der" "$ROOT/scripts/secureboot/sign-image.sh" "$LIVE_ROOT" --keydir "$BUILD_DIR/keys" "$ROOT/scripts/secureboot/sign-image.sh" "$ISOFILES" --keydir "$BUILD_DIR/keys" fi log "compressing live rootfs → squashfs (this takes a while)" # The squashfs is the live session root. Keep it complete — offline man pages # and docs are a product promise (see the landing page), so nothing is excluded. mksquashfs "$LIVE_ROOT" "$ISOFILES/live/arcline.squashfs" -noappend -comp zstd -Xcompression-level 15 2>/dev/null || \ mksquashfs "$LIVE_ROOT" "$ISOFILES/live/arcline.squashfs" -noappend -comp xz # ── 3. grub boot config ───────────────────────────────────────────────────── log "writing grub config" KCMD="$(tr '\n' ' ' < "$(edition_dir "$EDITION")/kernel.cmdline" | sed 's/ */ /g')" cat > "$ISOFILES/boot/grub/grub.cfg" </dev/null || die "grub-mkrescue not found (run scripts/check-host-deps.sh --install)" mkdir -p "$ARTIFACT_DIR" grub-mkrescue -o "$ARTIFACT" "$ISOFILES" -- \ -volume-label "ARCLINE_${EDITION^^}" 2>/dev/null || \ grub-mkrescue -o "$ARTIFACT" "$ISOFILES" # ── 5. verify hybrid boot entries ─────────────────────────────────────────── # grub-mkrescue builds the ISO bootloader from the HOST's GRUB modules. If the # host lacks the x86_64-efi modules or mtools, the ISO has no UEFI boot entry # and UEFI firmware shows "make sure there is a bootable uefi x64 image". # Check the finished ISO instead of trusting the host. if command -v xorriso >/dev/null 2>&1; then log "verifying ISO boot entries" ET="$(xorriso -indev "$ARTIFACT" -report_el_torito cmd 2>/dev/null || true)" HAS_EFI=0 echo "$ET" | grep -qiE 'boot_image efi|efi\.img|BOOTX64\.EFI' && HAS_EFI=1 if [[ $HAS_EFI -eq 0 ]]; then # fallback: some xorriso versions name it differently — look in the fs xorriso -indev "$ARTIFACT" -find / -name 'efi.img' 2>/dev/null | grep -qi 'efi\.img' && HAS_EFI=1 fi HAS_BIOS=0 echo "$ET" | grep -qiE 'boot_image isolinux|boot_image grub' && HAS_BIOS=1 [[ $HAS_EFI -eq 1 ]] && log " ✓ EFI boot entry present" || warn " ISO has NO EFI boot entry (UEFI will refuse it)" [[ $HAS_BIOS -eq 1 ]] && log " ✓ BIOS boot entry present" || warn " ISO has NO BIOS boot entry (legacy BIOS will refuse it)" if [[ "$BOOT" == "efi" && $HAS_EFI -eq 0 ]]; then die "BOOT=efi but the ISO has no EFI boot entry — install grub-efi-amd64-bin + mtools on the build host (scripts/check-host-deps.sh --install), then rebuild" fi fi log "ISO artifact: $ARTIFACT" sha256sum "$ARTIFACT" | tee "$ARTIFACT.sha256"