diff --git a/build-iso-bootc-xorriso.sh b/build-iso-bootc-xorriso.sh new file mode 100644 index 0000000..36f9d83 --- /dev/null +++ b/build-iso-bootc-xorriso.sh @@ -0,0 +1,736 @@ +#!/bin/bash + +# ParticleOS ISO Builder with bootc + xorriso +# Combines modern container-based system management with proven ISO creation +# This approach uses bootc for system definition and xorriso for ISO creation + +set -euo pipefail + +# Enable logging with timestamped directory +BUILD_TIMESTAMP=$(date +"%y%m%d%H%M") +LOG_DIR="logs/${BUILD_TIMESTAMP}" +mkdir -p "$LOG_DIR" +LOG_FILE="$LOG_DIR/build-iso-bootc-xorriso.log" +exec > >(tee -a "$LOG_FILE") 2>&1 + +echo "$(date): Starting ParticleOS ISO build with bootc + xorriso" +echo "$(date): Log directory: $LOG_DIR" + +# Colors +GREEN='\033[0;32m' +BLUE='\033[0;34m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +NC='\033[0m' + +print_status() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +print_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +print_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +print_error() { + echo -e "${RED}[ERROR]${NC} $1" + exit 1 +} + +print_header() { + echo "" + echo -e "${BLUE}================================${NC}" + echo -e "${BLUE}$1${NC}" + echo -e "${BLUE}================================${NC}" +} + +# Configuration +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_NAME="particleos" +VERSION="1.0.0" +BUILD_TIMESTAMP=$(date +"%y%m%d-%H%M") +BUILD_DIR="$SCRIPT_DIR/build" +OUTPUT_DIR="$SCRIPT_DIR/output" +CHROOT_DIR="$BUILD_DIR/chroot" +ISO_DIR="$BUILD_DIR/iso" +SYSTEM_IMAGE="particleos-system.oci" + +# apt-cacher-ng configuration +APT_CACHER_NG_HOST="192.168.1.79" +APT_CACHER_NG_PORT="3142" +APT_CACHER_NG_URL="http://${APT_CACHER_NG_HOST}:${APT_CACHER_NG_PORT}" + +# Variables to track cacher usage +CACHER_MMDEBSTRAP_USED="No (Fallback to direct)" +CACHER_CHROOT_APT_USED="No (Fallback to direct)" + +# Safety check: Ensure BUILD_DIR is within SCRIPT_DIR +if [[ ! "$BUILD_DIR" =~ ^"$SCRIPT_DIR" ]]; then + print_error "BUILD_DIR ($BUILD_DIR) is not within SCRIPT_DIR ($SCRIPT_DIR). Aborting for safety." +fi + +# Check if apt-cacher-ng is reachable +check_proxy_reachable() { + print_status "Checking if apt-cacher-ng at $APT_CACHER_NG_HOST:$APT_CACHER_NG_PORT is reachable..." + # Use netcat to check if the port is open + if nc -zv "$APT_CACHER_NG_HOST" "$APT_CACHER_NG_PORT" &>/dev/null; then + print_success "apt-cacher-ng at $APT_CACHER_NG_HOST:$APT_CACHER_NG_PORT is reachable." + return 0 # Reachable + else + print_warning "apt-cacher-ng at $APT_CACHER_NG_HOST:$APT_CACHER_NG_PORT is NOT reachable." + return 1 # Not reachable + fi +} + +# Check prerequisites +check_prerequisites() { + print_header "Phase 1: Check Prerequisites" + + # Check for bootc + if ! command -v bootc &> /dev/null; then + print_error "bootc is not installed. Please install bootc first." + fi + + # Check for ostree + if ! command -v ostree &> /dev/null; then + print_error "ostree is not installed. Please install ostree first." + fi + + # Check for podman + if ! command -v podman &> /dev/null; then + print_error "podman is not installed. Please install podman first." + fi + + # Check for build tools + local missing_packages=() + for package in mmdebstrap squashfs-tools xorriso grub-pc-bin grub-efi-amd64-bin isolinux syslinux-common netcat-openbsd; do + if ! dpkg -s "$package" &>/dev/null; then + missing_packages+=("$package") + fi + done + + if [ ${#missing_packages[@]} -gt 0 ]; then + print_status "Installing missing packages: ${missing_packages[*]}" + sudo apt update + sudo apt install -y "${missing_packages[@]}" + fi + + # Check disk space (need at least 10GB free) + local available_space=$(df "$SCRIPT_DIR" | awk 'NR==2 {print $4}') + local required_space=$((10 * 1024 * 1024)) # 10GB in KB + + if [ "$available_space" -lt "$required_space" ]; then + print_error "Insufficient disk space. Need at least 10GB free, have $(($available_space / 1024 / 1024))GB" + fi + + # Check network connectivity + if ! ping -c 1 archive.ubuntu.com &>/dev/null; then + print_error "Cannot reach Ubuntu archives. Check your internet connection." + fi + + print_success "All prerequisites satisfied" +} + +# Clean build environment +clean_build() { + print_header "Phase 2: Clean Build Environment" + + # Clean build directories + if [ -d "$BUILD_DIR" ]; then + print_status "Removing previous build directory: $BUILD_DIR..." + if [[ "$BUILD_DIR" == "/" ]] || [[ "$BUILD_DIR" == "/home" ]] || [[ "$BUILD_DIR" == "/opt" ]] || \ + [[ "$BUILD_DIR" == "/usr" ]] || [[ "$BUILD_DIR" == "/var" ]] || [[ "$BUILD_DIR" == "/etc" ]]; then + print_error "BUILD_DIR ($BUILD_DIR) is a critical system directory. Aborting for safety." + fi + + # Force unmount any remaining mounts first + sudo umount -f "$CHROOT_DIR/dev" 2>/dev/null || true + sudo umount -f "$CHROOT_DIR/run" 2>/dev/null || true + sudo umount -f "$CHROOT_DIR/proc" 2>/dev/null || true + sudo umount -f "$CHROOT_DIR/sys" 2>/dev/null || true + + sudo rm -rf "$BUILD_DIR" || print_error "Failed to remove previous build directory." + fi + + mkdir -p "$BUILD_DIR" "$CHROOT_DIR" "$ISO_DIR" "$OUTPUT_DIR" || print_error "Failed to create build directories." + chmod 755 "$OUTPUT_DIR" || print_error "Failed to set permissions on output directory." + print_success "Build environment cleaned" +} + +# Create base system using mmdebstrap (similar to current approach) +create_base_system() { + print_header "Phase 3: Create Base System" + + # Try to use apt-cacher-ng if available for mmdebstrap + if check_proxy_reachable; then + print_status "Attempting mmdebstrap with apt-cacher-ng: $APT_CACHER_NG_URL" + export http_proxy="$APT_CACHER_NG_URL/" + export https_proxy="$APT_CACHER_NG_URL/" + CACHER_MMDEBSTRAP_USED="Yes" + else + print_warning "apt-cacher-ng not reachable. mmdebstrap will proceed without proxy." + # Ensure proxy variables are unset if they were set previously + unset http_proxy + unset https_proxy + CACHER_MMDEBSTRAP_USED="No (Fallback to direct)" + fi + + print_status "Creating base Ubuntu system using mmdebstrap..." + + # Create base system with mmdebstrap + mmdebstrap \ + --architectures=amd64 \ + --variant=apt \ + --include=systemd,systemd-sysv,dbus,curl,ca-certificates,gnupg,gpgv,locales,resolvconf \ + --mode=unshare \ + noble \ + "$CHROOT_DIR" \ + http://archive.ubuntu.com/ubuntu/ || print_error "mmdebstrap failed." + + # Unset the proxy variables after mmdebstrap completes to avoid affecting subsequent commands outside the chroot + unset http_proxy + unset https_proxy + + print_success "Base system created" +} + +# Configure base system with bootc integration +configure_base_system() { + print_header "Phase 4: Configure Base System" + + print_status "Configuring base system with bootc integration..." + + # Mount necessary filesystems + sudo mount --bind /dev "$CHROOT_DIR/dev" + sudo mount --bind /run "$CHROOT_DIR/run" + sudo mount -t proc none "$CHROOT_DIR/proc" + sudo mount -t sysfs none "$CHROOT_DIR/sys" + + # Configure package sources + sudo chroot "$CHROOT_DIR" bash -c ' +echo "deb http://archive.ubuntu.com/ubuntu noble main restricted universe multiverse" > /etc/apt/sources.list && \ +echo "deb http://archive.ubuntu.com/ubuntu noble-updates main restricted universe multiverse" >> /etc/apt/sources.list && \ +echo "deb http://security.ubuntu.com/ubuntu noble-security main restricted universe multiverse" >> /etc/apt/sources.list +' || print_error "Failed to configure APT sources." + + # Configure APT proxy for chroot, only if cacher is reachable + if check_proxy_reachable; then + print_status "Configuring chroot APT to use apt-cacher-ng proxy: $APT_CACHER_NG_URL" + sudo chroot "$CHROOT_DIR" bash -c "cat > \"/etc/apt/apt.conf.d/01proxy\" << EOF +Acquire::http::Proxy \"$APT_CACHER_NG_URL\"; +Acquire::https::Proxy \"$APT_CACHER_NG_URL\"; +EOF" || print_error "Failed to configure APT proxy in chroot." + CACHER_CHROOT_APT_USED="Yes" + else + print_warning "apt-cacher-ng not reachable. Chroot APT will connect directly." + CACHER_CHROOT_APT_USED="No (Fallback to direct)" + # Ensure no proxy config file exists in case of previous failed runs + sudo chroot "$CHROOT_DIR" rm -f /etc/apt/apt.conf.d/01proxy || true + fi + + # Block snapd installation with APT preferences + print_status "Configuring APT preferences to block snapd..." + sudo chroot "$CHROOT_DIR" bash -c 'cat > "/etc/apt/preferences.d/no-snapd" << "EOF" +Package: snapd +Pin: release * +Pin-Priority: -1 + +Package: snap +Pin: release * +Pin-Priority: -1 + +Package: firefox* +Pin: release o=Ubuntu* +Pin-Priority: -1 +EOF' || print_error "Failed to configure APT preferences." + + # Install gpgv first for secure package verification + print_status "Installing gpgv for secure package verification..." + sudo chroot "$CHROOT_DIR" bash -c 'DEBIAN_FRONTEND=noninteractive apt update --allow-unauthenticated' || print_error "Failed to update APT lists." + sudo chroot "$CHROOT_DIR" bash -c 'DEBIAN_FRONTEND=noninteractive apt install -y --allow-unauthenticated gpgv' || print_error "Failed to install gpgv." + + # Update package lists securely + sudo chroot "$CHROOT_DIR" apt update || print_error "Failed to update package lists." + + # Install bootc and ostree packages + print_status "Installing bootc and ostree packages..." + sudo chroot "$CHROOT_DIR" bash -c 'DEBIAN_FRONTEND=noninteractive apt install -y \ + initramfs-tools \ + podman \ + skopeo \ + buildah \ + conmon \ + crun \ + netavark \ + aardvark-dns \ + slirp4netns \ + fuse-overlayfs \ + uidmap \ + libsubid4 \ + libslirp0 \ + passt \ + thin-provisioning-tools \ + lvm2 \ + mdadm \ + dmeventd \ + libaio1t64 \ + libdevmapper-event1.02.1 \ + liblvm2cmd2.03 \ + kpartx \ + pigz \ + live-boot \ + live-config \ + casper \ + grub-efi-amd64-signed' || print_error "Failed to install bootc dependencies and live components." + + # Install desktop environment + print_status "Installing desktop environment..." + sudo chroot "$CHROOT_DIR" bash -c 'DEBIAN_FRONTEND=noninteractive apt install -y \ + plasma-desktop \ + plasma-workspace \ + sddm \ + kwin-x11 \ + flatpak \ + network-manager \ + plasma-nm \ + openssh-server \ + curl \ + wget \ + vim \ + nano \ + htop \ + neofetch \ + tree \ + pulseaudio \ + pulseaudio-utils \ + fonts-ubuntu \ + fonts-noto \ + build-essential \ + git \ + systemd-sysv \ + dbus \ + locales \ + resolvconf \ + linux-image-generic \ + linux-headers-generic' || print_error "Failed to install desktop packages." + + # Install downloaded packages if available (install in correct order) + if [ -d "$SCRIPT_DIR/pkgs" ] && [ "$(ls -A "$SCRIPT_DIR/pkgs" 2>/dev/null)" ]; then + print_status "Installing downloaded packages in correct order..." + cp "$SCRIPT_DIR/pkgs"/*.deb "$CHROOT_DIR/tmp/" || print_error "Failed to copy packages." + + # Install packages in dependency order + # 1. First install libostree-1-1 (dependency) + if [ -f "$CHROOT_DIR/tmp/libostree-1-1_2025.2-1~noble1_amd64.deb" ]; then + print_status "Installing libostree-1-1..." + sudo chroot "$CHROOT_DIR" apt install -y /tmp/libostree-1-1_2025.2-1~noble1_amd64.deb || print_error "Failed to install libostree-1-1" + fi + + # 2. Then install ostree + if [ -f "$CHROOT_DIR/tmp/ostree_2025.2-1~noble1_amd64.deb" ]; then + print_status "Installing ostree..." + sudo chroot "$CHROOT_DIR" apt install -y /tmp/ostree_2025.2-1~noble1_amd64.deb || print_error "Failed to install ostree" + fi + + # 3. Then install bootc + if [ -f "$CHROOT_DIR/tmp/bootc_1.5.1-1~noble1_amd64.deb" ]; then + print_status "Installing bootc..." + sudo chroot "$CHROOT_DIR" apt install -y /tmp/bootc_1.5.1-1~noble1_amd64.deb || print_error "Failed to install bootc" + fi + + # 4. Finally install apt-ostree + if [ -f "$CHROOT_DIR/tmp/apt-ostree_0.1.0-1_amd64.deb" ]; then + print_status "Installing apt-ostree..." + sudo chroot "$CHROOT_DIR" apt install -y /tmp/apt-ostree_0.1.0-1_amd64.deb || print_error "Failed to install apt-ostree" + fi + + # 5. Install other packages + for pkg in "$CHROOT_DIR/tmp"/*.deb; do + if [ -f "$pkg" ]; then + pkg_name=$(basename "$pkg") + if [[ "$pkg_name" != *"libostree-1-1"* ]] && [[ "$pkg_name" != *"ostree"* ]] && [[ "$pkg_name" != *"bootc"* ]] && [[ "$pkg_name" != *"apt-ostree"* ]]; then + print_status "Installing $pkg_name..." + sudo chroot "$CHROOT_DIR" apt install -y "/tmp/$pkg_name" || print_warning "Failed to install $pkg_name, continuing anyway" + fi + fi + done + + sudo chroot "$CHROOT_DIR" rm -f /tmp/*.deb || print_warning "Failed to clean up package files" + else + # Fallback: Install apt-ostree from system repositories + print_status "No downloaded packages found, installing apt-ostree from system repositories..." + sudo chroot "$CHROOT_DIR" apt install -y apt-ostree || print_warning "apt-ostree installation failed, continuing anyway" + fi + + # Crucial: Regenerate initramfs after installing live-boot and kernel + print_status "Updating initramfs for all installed kernels..." + sudo chroot "$CHROOT_DIR" update-initramfs -u -k all || print_error "Failed to update initramfs." + + # Remove unwanted packages (snapd is already blocked by APT preferences) + print_status "Removing unwanted packages..." + sudo chroot "$CHROOT_DIR" bash -c 'DEBIAN_FRONTEND=noninteractive apt purge -y ubuntu-advantage-tools update-notifier update-manager unattended-upgrades' || print_error "Failed to purge unwanted packages." + sudo chroot "$CHROOT_DIR" apt autoremove -y || print_error "Failed to autoremove packages." + + # Configure system settings + sudo chroot "$CHROOT_DIR" bash -c ' +echo "particleos" > /etc/hostname && \ +echo "127.0.0.1 localhost" >> /etc/hosts && \ +echo "127.0.1.1 particleos.local particleos" >> /etc/hosts && \ +ln -sf /usr/share/zoneinfo/UTC /etc/localtime && \ +locale-gen en_US.UTF-8 && \ +update-locale LANG=en_US.UTF-8 +' || print_error "Failed to configure system settings." + + # Create user + print_status "Creating particle user..." + sudo chroot "$CHROOT_DIR" useradd -m -s /bin/bash particle || print_error "Failed to create particle user." + sudo chroot "$CHROOT_DIR" usermod -aG sudo particle || print_error "Failed to add particle to sudo group." + sudo chroot "$CHROOT_DIR" bash -c "echo 'particle ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/particle" || print_error "Failed to create sudoers file." + sudo chroot "$CHROOT_DIR" chmod 0440 /etc/sudoers.d/particle || print_error "Failed to set sudoers permissions." + + # Enable services + print_status "Enabling essential services..." + sudo chroot "$CHROOT_DIR" systemctl enable sddm NetworkManager ssh || print_error "Failed to enable services." + sudo chroot "$CHROOT_DIR" systemctl disable apt-daily.timer apt-daily-upgrade.timer || print_error "Failed to disable apt timers." + + # Configure apt-ostree + print_status "Configuring apt-ostree..." + sudo chroot "$CHROOT_DIR" mkdir -p /etc/apt-ostree || print_error "Failed to create apt-ostree config directory." + sudo chroot "$CHROOT_DIR" bash -c 'echo "ref: particleos/desktop/1.0.0" > /etc/apt-ostree/ref' || print_error "Failed to configure apt-ostree ref." + + # Clean up + print_status "Cleaning up..." + sudo chroot "$CHROOT_DIR" apt clean || print_error "Failed to clean apt cache." + sudo chroot "$CHROOT_DIR" rm -rf /var/lib/apt/lists/* /tmp/* || print_error "Failed to remove apt lists." + + # Unmount filesystems + sudo umount "$CHROOT_DIR/dev" + sudo umount "$CHROOT_DIR/run" + sudo umount "$CHROOT_DIR/proc" + sudo umount "$CHROOT_DIR/sys" + + print_success "Base system configured with bootc integration" +} + +# Create bootc-compatible system image +create_bootc_image() { + print_header "Phase 5: Create bootc System Image" + + print_status "Creating bootc-compatible system image..." + + # Create a temporary directory for the image + local image_dir="$BUILD_DIR/image" + mkdir -p "$image_dir" + + # Copy the chroot filesystem to the image directory + print_status "Copying filesystem to image directory..." + sudo cp -a "$CHROOT_DIR"/* "$image_dir/" || print_error "Failed to copy filesystem." + + # Create ostree repository structure + print_status "Creating ostree repository structure..." + mkdir -p "$image_dir/ostree/repo" + + # Initialize ostree repository + ostree init --repo="$image_dir/ostree/repo" --mode=archive || print_error "Failed to initialize ostree repository." + + # Create ostree commit from the filesystem + print_status "Creating ostree commit..." + ostree commit --repo="$image_dir/ostree/repo" \ + --branch=particleos/desktop/1.0.0 \ + --subject="ParticleOS Desktop 1.0.0" \ + --body="ParticleOS Desktop system with apt-ostree" \ + --tree=dir="$image_dir" || print_error "Failed to create ostree commit." + + # Create bootc-compatible container image + print_status "Creating bootc-compatible container image..." + + # Create Dockerfile for the container image + cat > "$BUILD_DIR/Dockerfile" << 'EOF' +FROM scratch +COPY . / +LABEL org.opencontainers.image.title="ParticleOS Desktop" +LABEL org.opencontainers.image.description="Atomic Ubuntu Desktop with apt-ostree" +LABEL org.opencontainers.image.version="1.0.0" +LABEL org.opencontainers.image.vendor="ParticleOS" +EOF + + # Build container image + cd "$BUILD_DIR" + podman build -t "$SYSTEM_IMAGE" . || print_error "Failed to build container image." + + print_success "bootc system image created: $SYSTEM_IMAGE" +} + +# Create ISO using xorriso (reuse existing proven method) +create_iso() { + print_header "Phase 6: Create ISO with xorriso" + + print_status "Creating ISO structure..." + + # Create ISO directory structure + mkdir -p "$ISO_DIR"/{casper,boot/grub,EFI/BOOT,isolinux} || print_error "Failed to create ISO directory structure." + + # Copy kernel and initrd + print_status "Copying kernel and initramfs..." + # Get the latest kernel and initrd + KERNEL_VERSION=$(basename $(ls -1 "$CHROOT_DIR/boot/vmlinuz-"* | sort -V | tail -n 1) | sed 's/vmlinuz-//') + INITRD_VERSION=$(basename $(ls -1 "$CHROOT_DIR/boot/initrd.img-"* | sort -V | tail -n 1) | sed 's/initrd.img-//') + + if [ -z "$KERNEL_VERSION" ]; then + print_error "Kernel (vmlinuz-*) not found in $CHROOT_DIR/boot." + fi + if [ -z "$INITRD_VERSION" ]; then + print_error "Initrd (initrd.img-*) not found in $CHROOT_DIR/boot." + fi + + cp "$CHROOT_DIR/boot/vmlinuz-$KERNEL_VERSION" "$ISO_DIR/casper/vmlinuz" || print_error "Failed to copy kernel." + cp "$CHROOT_DIR/boot/initrd.img-$INITRD_VERSION" "$ISO_DIR/casper/initrd" || print_error "Failed to copy initrd." + + # Create filesystem manifest + print_status "Creating filesystem manifest..." + sudo chroot "$CHROOT_DIR" dpkg-query -W --showformat='${Package} ${Version}\n' > "$ISO_DIR/casper/filesystem.manifest" || print_error "Failed to create filesystem manifest." + + # Create filesystem size + print_status "Calculating filesystem size..." + sudo du -sx --block-size=1 "$CHROOT_DIR" | cut -f1 > "$ISO_DIR/casper/filesystem.size" || print_error "Failed to create filesystem.size." + + # Create squashfs + print_status "Creating squashfs filesystem..." + # Exclude /boot/grub from squashfs to prevent conflicts with the ISO's bootloader + sudo mksquashfs "$CHROOT_DIR" "$ISO_DIR/casper/filesystem.squashfs" -comp xz -e boot/grub || print_error "Failed to create squashfs." + + # Create GRUB configuration + cat > "$ISO_DIR/boot/grub/grub.cfg" << 'EOF' +set timeout=10 +set default=0 + +menuentry "Try ParticleOS without installing" { + linux /casper/vmlinuz boot=casper quiet splash --- + initrd /casper/initrd +} + +menuentry "Install ParticleOS" { + linux /casper/vmlinuz boot=casper quiet splash --- + initrd /casper/initrd +} + +menuentry "Check disc for defects" { + linux /casper/vmlinuz boot=casper integrity-check quiet splash --- + initrd /casper/initrd +} +EOF + + # Create ISOLINUX configuration + cat > "$ISO_DIR/isolinux/isolinux.cfg" << 'EOF' +DEFAULT live +TIMEOUT 300 +PROMPT 1 + +LABEL live + MENU LABEL Try ParticleOS without installing + KERNEL /casper/vmlinuz + APPEND boot=casper initrd=/casper/initrd quiet splash --- + +LABEL live-install + MENU LABEL Install ParticleOS + KERNEL /casper/vmlinuz + APPEND boot=casper initrd=/casper/initrd quiet splash --- + +LABEL check + MENU LABEL Check disc for defects + KERNEL /casper/vmlinuz + APPEND boot=casper integrity-check initrd=/casper/initrd quiet splash --- +EOF + + # Copy ISOLINUX boot files - REVISED PATH DISCOVERY + print_status "Copying ISOLINUX boot files..." + ISOLINUX_BASE_DIR="" + if [ -d "/usr/lib/syslinux" ]; then + ISOLINUX_BASE_DIR="/usr/lib/syslinux" + elif [ -d "/usr/lib/ISOLINUX" ]; then + ISOLINUX_BASE_DIR="/usr/lib/ISOLINUX" + elif [ -d "/usr/share/syslinux" ]; then + ISOLINUX_BASE_DIR="/usr/share/syslinux" + fi + + if [ -z "$ISOLINUX_BASE_DIR" ]; then + print_error "Syslinux base directory not found. Please ensure 'syslinux-utils' or 'isolinux' is installed." + fi + + ISOLINUX_BIN_SRC="" + LDLINUX_C32_SRC="" + MEMDISK_C32_SRC="" + MBOOT_C32_SRC="" + LIBUTIL_C32_SRC="" + + # Search for isolinux.bin + if [ -f "$ISOLINUX_BASE_DIR/isolinux.bin" ]; then + ISOLINUX_BIN_SRC="$ISOLINUX_BASE_DIR/isolinux.bin" + elif [ -f "$ISOLINUX_BASE_DIR/bios/isolinux.bin" ]; then + ISOLINUX_BIN_SRC="$ISOLINUX_BASE_DIR/bios/isolinux.bin" + fi + + # Search for ldlinux.c32 and other common .c32 modules + if [ -f "$ISOLINUX_BASE_DIR/ldlinux.c32" ]; then + LDLINUX_C32_SRC="$ISOLINUX_BASE_DIR/ldlinux.c32" + elif [ -f "$ISOLINUX_BASE_DIR/modules/bios/ldlinux.c32" ]; then + LDLINUX_C32_SRC="$ISOLINUX_BASE_DIR/modules/bios/ldlinux.c32" + elif [ -f "$ISOLINUX_BASE_DIR/isolinux/ldlinux.c32" ]; then + LDLINUX_C32_SRC="$ISOLINUX_BASE_DIR/isolinux/ldlinux.c32" + fi + + # Search for other critical .c32 files + if [ -f "$ISOLINUX_BASE_DIR/memdisk/memdisk.c32" ]; then + MEMDISK_C32_SRC="$ISOLINUX_BASE_DIR/memdisk/memdisk.c32" + elif [ -f "$ISOLINUX_BASE_DIR/modules/bios/memdisk.c32" ]; then + MEMDISK_C32_SRC="$ISOLINUX_BASE_DIR/modules/bios/memdisk.c32" + fi + if [ -f "$ISOLINUX_BASE_DIR/mboot.c32" ]; then + MBOOT_C32_SRC="$ISOLINUX_BASE_DIR/mboot.c32" + elif [ -f "$ISOLINUX_BASE_DIR/modules/bios/mboot.c32" ]; then + MBOOT_C32_SRC="$ISOLINUX_BASE_DIR/modules/bios/mboot.c32" + fi + if [ -f "$ISOLINUX_BASE_DIR/libutil.c32" ]; then + LIBUTIL_C32_SRC="$ISOLINUX_BASE_DIR/libutil.c32" + elif [ -f "$ISOLINUX_BASE_DIR/modules/bios/libutil.c32" ]; then + LIBUTIL_C32_SRC="$ISOLINUX_BASE_DIR/modules/bios/libutil.c32" + fi + + SYSLINUX_MODULES_PATH="" + if [ -d "/usr/lib/syslinux/modules/bios" ]; then + SYSLINUX_MODULES_PATH="/usr/lib/syslinux/modules/bios" + elif [ -d "/usr/share/syslinux/modules/bios" ]; then + SYSLINUX_MODULES_PATH="/usr/share/syslinux/modules/bios" + fi + + if [ -z "$ISOLINUX_BIN_SRC" ]; then + print_error "isolinux.bin not found within detected syslinux paths. Please check your syslinux installation." + fi + if [ -z "$LDLINUX_C32_SRC" ]; then + print_error "ldlinux.c32 not found within detected syslinux paths. Ensure 'syslinux-common' is installed and up-to-date." + fi + + cp "$ISOLINUX_BIN_SRC" "$ISO_DIR/isolinux/" || print_error "Failed to copy isolinux.bin." + cp "$LDLINUX_C32_SRC" "$ISO_DIR/isolinux/" || print_error "Failed to copy ldlinux.c32." + + # Copy other essential .c32 modules that ldlinux.c32 might depend on + if [ -n "$MEMDISK_C32_SRC" ]; then + cp "$MEMDISK_C32_SRC" "$ISO_DIR/isolinux/" || print_warning "Failed to copy memdisk.c32, continuing anyway." + fi + if [ -n "$MBOOT_C32_SRC" ]; then + cp "$MBOOT_C32_SRC" "$ISO_DIR/isolinux/" || print_warning "Failed to copy mboot.c32, continuing anyway." + fi + if [ -n "$LIBUTIL_C32_SRC" ]; then + cp "$LIBUTIL_C32_SRC" "$ISO_DIR/isolinux/" || print_warning "Failed to copy libutil.c32, continuing anyway." + fi + if [ -f "$ISOLINUX_BASE_DIR/menu.c32" ]; then + cp "$ISOLINUX_BASE_DIR/menu.c32" "$ISO_DIR/isolinux/" || print_warning "Failed to copy menu.c32, continuing anyway." + elif [ -f "$SYSLINUX_MODULES_PATH/menu.c32" ]; then + cp "$SYSLINUX_MODULES_PATH/menu.c32" "$ISO_DIR/isolinux/" || print_warning "Failed to copy menu.c32, continuing anyway." + fi + + # Create EFI boot image + print_status "Creating EFI boot image..." + mkdir -p "$ISO_DIR/EFI/BOOT" || print_error "Failed to create EFI/BOOT directory." + + grub-mkimage \ + -o "$ISO_DIR/EFI/BOOT/bootx64.efi" \ + -p "/boot/grub" \ + -O x86_64-efi \ + boot linux normal configfile part_gpt part_msdos fat \ + squash4 test configfile search loadenv \ + efi_gop efi_uga all_video gfxterm_menu \ + chain iso9660 || print_error "Failed to create EFI boot image." + + # Create ISO using xorriso + print_status "Creating bootable ISO using xorriso..." + xorriso -as mkisofs \ + -o "$OUTPUT_DIR/${PROJECT_NAME}-${BUILD_TIMESTAMP}.iso" \ + -J -joliet-long \ + -r -V "ParticleOS ${VERSION}" \ + -b isolinux/isolinux.bin \ + -c isolinux/boot.cat \ + -boot-load-size 4 -boot-info-table \ + -no-emul-boot -eltorito-alt-boot \ + -e EFI/BOOT/bootx64.efi -no-emul-boot \ + -isohybrid-mbr "$ISOLINUX_BASE_DIR/isohdpfx.bin" \ + -partition_offset 16 \ + -part_like_isohybrid \ + "$ISO_DIR" || print_error "Failed to create ISO with xorriso." + + print_success "ISO created successfully: $OUTPUT_DIR/${PROJECT_NAME}-${BUILD_TIMESTAMP}.iso" +} + +# Cleanup function +cleanup() { + print_status "Cleaning up build environment..." + + # Unmount any remaining mounts + if mountpoint -q "$CHROOT_DIR/dev" 2>/dev/null; then + sudo umount "$CHROOT_DIR/dev" 2>/dev/null || true + fi + if mountpoint -q "$CHROOT_DIR/run" 2>/dev/null; then + sudo umount "$CHROOT_DIR/run" 2>/dev/null || true + fi + if mountpoint -q "$CHROOT_DIR/proc" 2>/dev/null; then + sudo umount "$CHROOT_DIR/proc" 2>/dev/null || true + fi + if mountpoint -q "$CHROOT_DIR/sys" 2>/dev/null; then + sudo umount "$CHROOT_DIR/sys" 2>/dev/null || true + fi + + print_success "Cleanup completed" +} + +# Signal trap for cleanup +trap cleanup EXIT INT TERM + +# Main execution +main() { + echo "๐Ÿš€ ParticleOS ISO Builder (bootc + xorriso)" + echo "===========================================" + echo "Project: $PROJECT_NAME" + echo "Version: $VERSION" + echo "Build Directory: $BUILD_DIR" + echo "Tool: bootc + xorriso (Hybrid Approach)" + echo "" + echo "๐Ÿ”„ This build combines:" + echo " โ€ข bootc for modern container-based system management" + echo " โ€ข xorriso for proven ISO creation" + echo " โ€ข ostree for atomic updates" + echo " โ€ข apt-ostree for Debian/Ubuntu package management" + echo "" + + check_prerequisites + clean_build + create_base_system + configure_base_system + create_bootc_image + create_iso + + print_header "Build Complete!" + echo "" + echo "๐ŸŽ‰ ParticleOS ISO built successfully with bootc + xorriso!" + echo "๐Ÿ“ Location: $OUTPUT_DIR/${PROJECT_NAME}-${BUILD_TIMESTAMP}.iso" + echo "๐Ÿณ Container Image: $SYSTEM_IMAGE" + echo "" + echo "Summary of Cacher Usage:" + echo " mmdebstrap: $CACHER_MMDEBSTRAP_USED" + echo " Chroot APT: $CACHER_CHROOT_APT_USED" + echo "" + echo "๐Ÿงช Test the ISO:" + echo " qemu-system-x86_64 -m 4G -enable-kvm \\" + echo " -cdrom $OUTPUT_DIR/${PROJECT_NAME}-${BUILD_TIMESTAMP}.iso \\" + echo " -boot d" + echo "" + echo "๐Ÿš€ Deploy with bootc:" + echo " sudo bootc install to-disk /dev/sda --source-imgref oci:$SYSTEM_IMAGE" + echo "" + echo "โœ… Best of both worlds: Modern container workflow + proven ISO creation!" +} + +# Run main function +main "$@" \ No newline at end of file