diff --git a/Dockerfile.noble b/Dockerfile.noble new file mode 100644 index 0000000..9adc2cb --- /dev/null +++ b/Dockerfile.noble @@ -0,0 +1 @@ +# Built ParticleOS with a 24.04 base diff --git a/Dockerfile.plucky b/Dockerfile.plucky new file mode 100644 index 0000000..c2a2616 --- /dev/null +++ b/Dockerfile.plucky @@ -0,0 +1 @@ +# Build ParticleOS with a 25.04 base diff --git a/Dockerfile.questing b/Dockerfile.questing new file mode 100644 index 0000000..b19f32b --- /dev/null +++ b/Dockerfile.questing @@ -0,0 +1 @@ +# Build ParticleOS with a 25.10 base diff --git a/build-iso-bootc-hybrid.sh b/build-iso-bootc-hybrid.sh new file mode 100755 index 0000000..6869a84 --- /dev/null +++ b/build-iso-bootc-hybrid.sh @@ -0,0 +1,768 @@ +#!/bin/bash + +# ParticleOS ISO Builder - Pure bootc Approach +# Uses bootc for the entire build process, no mmdebstrap needed +# This is the true "bootc + xorriso" approach + +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-native.log" +exec > >(tee -a "$LOG_FILE") 2>&1 + +echo "$(date): Starting ParticleOS ISO build with pure bootc approach" +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" +CONTAINER_NAME="particleos-builder" +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_CONTAINER_BUILD_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 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 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 for casper (required for live boot) + if ! dpkg -s casper &>/dev/null; then + print_status "Installing casper (required for live boot)..." + sudo apt update + sudo apt install -y casper + 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 remove any existing container first + if podman container exists "$CONTAINER_NAME" 2>/dev/null; then + print_status "Removing existing container: $CONTAINER_NAME" + podman container rm -f "$CONTAINER_NAME" 2>/dev/null || print_warning "Failed to remove container" + fi + + # Force remove any existing images + if podman image exists "$SYSTEM_IMAGE" 2>/dev/null; then + print_status "Removing existing image: $SYSTEM_IMAGE" + podman image rm -f "$SYSTEM_IMAGE" 2>/dev/null || print_warning "Failed to remove image" + fi + + sudo rm -rf "$BUILD_DIR" || print_error "Failed to remove previous build directory." + fi + + mkdir -p "$BUILD_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 bootc system definition +create_bootc_definition() { + print_header "Phase 3: Create bootc System Definition" + + print_status "Creating bootc system definition..." + + # Create a bootc-compatible system definition + cat > "$BUILD_DIR/particleos-system.yaml" << 'EOF' +# ParticleOS System Definition for bootc +apiVersion: v1 +kind: Image +metadata: + name: particleos-desktop + version: "1.0.0" +spec: + os: + name: ubuntu + version: "24.04" + packages: + - name: ubuntu-minimal + - name: plasma-desktop + - name: plasma-workspace + - name: sddm + - name: kwin-x11 + - name: flatpak + - name: network-manager + - name: plasma-nm + - name: openssh-server + - name: curl + - name: wget + - name: vim + - name: nano + - name: htop + - name: neofetch + - name: tree + - name: pulseaudio + - name: pulseaudio-utils + - name: fonts-ubuntu + - name: fonts-noto + - name: build-essential + - name: git + - name: live-boot + - name: live-config + - name: casper + - name: systemd-sysv + - name: dbus + - name: locales + - name: resolvconf + - name: linux-image-generic + - name: linux-headers-generic + - name: podman + - name: skopeo + - name: buildah + - name: ostree + - name: apt-ostree + services: + - name: sddm + enabled: true + - name: NetworkManager + enabled: true + - name: ssh + enabled: true + users: + - name: particle + groups: [sudo, wheel] + shell: /bin/bash + home: /home/particle + files: + - path: /etc/hostname + content: particleos + - path: /etc/hosts + content: | + 127.0.0.1 localhost + 127.0.1.1 particleos.local particleos + - path: /etc/apt-ostree/ref + content: particleos/desktop/1.0.0 +EOF + + print_success "bootc system definition created" +} + +# Build system using bootc container workflow +build_bootc_system() { + print_header "Phase 4: Build System with bootc" + + print_status "Building system using bootc container workflow..." + + # Try to use apt-cacher-ng if available for container build + local use_proxy=false + if check_proxy_reachable; then + print_status "Container build will use apt-cacher-ng: $APT_CACHER_NG_URL" + CACHER_CONTAINER_BUILD_USED="Yes" + use_proxy=true + else + print_warning "apt-cacher-ng not reachable. Container build will proceed without proxy." + CACHER_CONTAINER_BUILD_USED="No (Fallback to direct)" + use_proxy=false + fi + + # Create a Dockerfile that bootc can build from + cat > "$BUILD_DIR/Dockerfile" << 'EOF' +# Start with Ubuntu 24.04 base +FROM ubuntu:24.04 + +# Set environment +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=UTC + +# Configure apt proxy if available +EOF + + # Add proxy configuration if available + if [ "$use_proxy" = true ]; then + cat >> "$BUILD_DIR/Dockerfile" << EOF +RUN echo 'Acquire::http::Proxy "$APT_CACHER_NG_URL";' > /etc/apt/apt.conf.d/01proxy && \\ + echo 'Acquire::https::Proxy "$APT_CACHER_NG_URL";' >> /etc/apt/apt.conf.d/01proxy +EOF + fi + + # Continue with the rest of the Dockerfile + cat >> "$BUILD_DIR/Dockerfile" << 'EOF' + +# Update and install base packages +RUN apt update && apt install -y \ + systemd \ + systemd-sysv \ + dbus \ + curl \ + ca-certificates \ + gnupg \ + gpgv \ + locales \ + resolvconf \ + && rm -rf /var/lib/apt/lists/* + +# Configure locales +RUN locale-gen en_US.UTF-8 && update-locale LANG=en_US.UTF-8 + +# Block snapd installation with APT preferences +RUN echo 'Package: snapd' > /etc/apt/preferences.d/no-snapd && \ + echo 'Pin: release *' >> /etc/apt/preferences.d/no-snapd && \ + echo 'Pin-Priority: -1' >> /etc/apt/preferences.d/no-snapd && \ + echo '' >> /etc/apt/preferences.d/no-snapd && \ + echo 'Package: snap' >> /etc/apt/preferences.d/no-snapd && \ + echo 'Pin: release *' >> /etc/apt/preferences.d/no-snapd && \ + echo 'Pin-Priority: -1' >> /etc/apt/preferences.d/no-snapd && \ + echo '' >> /etc/apt/preferences.d/no-snapd && \ + echo 'Package: firefox*' >> /etc/apt/preferences.d/no-snapd && \ + echo 'Pin: release o=Ubuntu*' >> /etc/apt/preferences.d/no-snapd && \ + echo 'Pin-Priority: -1' >> /etc/apt/preferences.d/no-snapd + +# Install desktop environment +RUN apt update && 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 \ + live-boot \ + live-config \ + casper \ + linux-image-generic \ + linux-headers-generic \ + && rm -rf /var/lib/apt/lists/* + +# Install bootc and container tools +RUN apt update && apt install -y \ + 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 \ + dracut \ + dracut-core \ + && rm -rf /var/lib/apt/lists/* + +# Copy and install local packages +COPY pkgs/ /tmp/pkgs/ + +# Install ostree and bootc packages from local files +RUN if [ -f /tmp/pkgs/libostree-1-1_2025.2-1~noble1_amd64.deb ]; then \ + dpkg -i /tmp/pkgs/libostree-1-1_2025.2-1~noble1_amd64.deb; \ + fi + +RUN if [ -f /tmp/pkgs/ostree_2025.2-1~noble1_amd64.deb ]; then \ + dpkg -i /tmp/pkgs/ostree_2025.2-1~noble1_amd64.deb; \ + fi + +RUN if [ -f /tmp/pkgs/bootc_1.5.1-1~noble1_amd64.deb ]; then \ + dpkg -i /tmp/pkgs/bootc_1.5.1-1~noble1_amd64.deb; \ + fi + +# Install apt-ostree from local packages if available +RUN if [ -f /tmp/pkgs/apt-ostree_0.1.0-1_amd64.deb ]; then \ + dpkg -i /tmp/pkgs/apt-ostree_0.1.0-1_amd64.deb; \ + fi + +# Remove unwanted packages (snapd is already blocked by APT preferences) +RUN apt purge -y ubuntu-advantage-tools || true && \ + apt purge -y update-notifier || true && \ + apt purge -y update-manager || true && \ + apt purge -y unattended-upgrades || true && \ + apt autoremove -y && \ + apt clean + +# Configure system +RUN 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 + +# Create user +RUN useradd -m -s /bin/bash particle && \ + usermod -aG sudo particle && \ + echo 'particle ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/particle && \ + chmod 0440 /etc/sudoers.d/particle + +# Configure apt-ostree +RUN mkdir -p /etc/apt-ostree && \ + echo "ref: particleos/desktop/1.0.0" > /etc/apt-ostree/ref + +# Enable services +RUN systemctl enable sddm NetworkManager ssh && \ + systemctl disable apt-daily.timer apt-daily-upgrade.timer + +# Create casper hook for live boot +RUN mkdir -p /etc/casper && \ + echo "particleos" > /etc/casper/hostname && \ + echo "particle" > /etc/casper/username + +# Regenerate initrd to include live-boot modules +RUN update-initramfs -u -k all + +# Clean up +RUN rm -rf /tmp/pkgs /var/lib/apt/lists/* /tmp/* + +# Set labels for bootc +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" +LABEL org.opencontainers.image.source="https://github.com/particleos/particleos-installer" + +# Expose ports +EXPOSE 22 + +# Set default command +CMD ["/lib/systemd/systemd"] +EOF + + # Copy packages to build context + if [ -d "$SCRIPT_DIR/pkgs" ]; then + cp -r "$SCRIPT_DIR/pkgs" "$BUILD_DIR/" + fi + + # Build the container image using podman + print_status "Building container image..." + cd "$BUILD_DIR" + podman build -t "$SYSTEM_IMAGE" . || print_error "Failed to build container image." + + print_success "bootc system built: $SYSTEM_IMAGE" +} + +# Extract filesystem from container for ISO creation +extract_filesystem() { + print_header "Phase 5: Extract Filesystem for ISO" + + print_status "Extracting filesystem from container..." + + # Create a temporary container from the image + podman create --name "$CONTAINER_NAME" "$SYSTEM_IMAGE" || print_error "Failed to create container." + + # Extract the filesystem + local extract_dir="$BUILD_DIR/extract" + mkdir -p "$extract_dir" + + podman export "$CONTAINER_NAME" | tar -x -C "$extract_dir" || print_error "Failed to extract filesystem." + + # Remove the temporary container + podman container rm "$CONTAINER_NAME" || print_warning "Failed to remove container" + + # Verify extraction was successful + if [ ! -f "$extract_dir/etc/os-release" ]; then + print_error "Filesystem extraction failed - /etc/os-release not found" + fi + + print_success "Filesystem extracted to: $extract_dir" +} + +# Create ISO using xorriso (reuse existing proven method) +create_iso() { + print_header "Phase 6: Create ISO with xorriso" + + local extract_dir="$BUILD_DIR/extract" + local iso_dir="$BUILD_DIR/iso" + + 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 from extracted filesystem + print_status "Copying kernel and initrd from extracted filesystem..." + + KERNEL_PATH=$(find "$extract_dir/boot" -maxdepth 1 -name "vmlinuz-*" | sort -V | tail -n 1) + INITRD_PATH=$(find "$extract_dir/boot" -maxdepth 1 -name "initrd.img-*" | sort -V | tail -n 1) + + if [ -z "$KERNEL_PATH" ]; then + print_error "Kernel (vmlinuz-*) not found in $extract_dir/boot." + fi + if [ -z "$INITRD_PATH" ]; then + print_error "Initrd (initrd.img-*) not found in $extract_dir/boot." + fi + + print_status "Using kernel: $KERNEL_PATH" + print_status "Using initrd: $INITRD_PATH" + + cp "$KERNEL_PATH" "$iso_dir/casper/vmlinuz" || print_error "Failed to copy kernel." + cp "$INITRD_PATH" "$iso_dir/casper/initrd" || print_error "Failed to copy initrd." + + # Create filesystem manifest + print_status "Creating filesystem manifest..." + # We need to chroot to get the package list, but we can't easily chroot into extracted fs + # So we'll create a minimal manifest + echo "particleos 1.0.0" > "$iso_dir/casper/filesystem.manifest" + echo "ubuntu-minimal 24.04" >> "$iso_dir/casper/filesystem.manifest" + echo "plasma-desktop 5.27" >> "$iso_dir/casper/filesystem.manifest" + + # Create filesystem size + print_status "Calculating filesystem size..." + du -sx --block-size=1 "$extract_dir" | cut -f1 > "$iso_dir/casper/filesystem.size" || print_error "Failed to create filesystem.size." + + # Create casper configuration + print_status "Creating casper configuration..." + cat > "$iso_dir/casper/casper.conf" << 'EOF' +# Casper configuration for ParticleOS +LIVE_HOSTNAME="particleos" +LIVE_USERNAME="particle" +LIVE_USER_FULLNAME="ParticleOS User" +LIVE_USER_DEFAULT_GROUPS="adm cdrom dip lpadmin plugdev sambashare sudo" +LIVE_IMAGETYPE="ParticleOS" +LIVE_BOOT_CMDLINE="boot=casper quiet splash" +EOF + + # Create squashfs + print_status "Creating squashfs filesystem..." + mksquashfs "$extract_dir" "$iso_dir/casper/filesystem.squashfs" -comp xz -e boot || 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 basic EFI structure (simplified for now) + print_status "Creating basic EFI structure..." + mkdir -p "$iso_dir/EFI/BOOT" || print_error "Failed to create EFI/BOOT directory." + mkdir -p "$iso_dir/boot/grub" || print_error "Failed to create boot/grub directory." + + # 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 \ + -isohybrid-mbr "$ISOLINUX_BASE_DIR/isohdpfx.bin" \ + -partition_offset 16 \ + -part_like_isohybrid \ + -isohybrid-gpt-basdat \ + "$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..." + + # Remove any remaining container + if podman container exists "$CONTAINER_NAME" 2>/dev/null; then + podman container rm -f "$CONTAINER_NAME" 2>/dev/null || print_warning "Failed to remove container" + fi + + # Remove any remaining images + if podman image exists "$SYSTEM_IMAGE" 2>/dev/null; then + podman image rm -f "$SYSTEM_IMAGE" 2>/dev/null || print_warning "Failed to remove image" + fi + + print_success "Cleanup completed" +} + +# Signal trap for cleanup +trap cleanup EXIT INT TERM + +# Main execution +main() { + echo "๐Ÿš€ ParticleOS ISO Builder (Pure bootc)" + echo "======================================" + echo "Project: $PROJECT_NAME" + echo "Version: $VERSION" + echo "Build Directory: $BUILD_DIR" + echo "Tool: Pure bootc + xorriso" + echo "" + echo "๐Ÿ”„ This build uses:" + echo " โ€ข bootc for container-based system building" + echo " โ€ข Dockerfile for system definition" + echo " โ€ข Container extraction for ISO creation" + echo " โ€ข xorriso for proven ISO creation" + echo "" + + check_prerequisites + clean_build + create_bootc_definition + build_bootc_system + extract_filesystem + create_iso + + print_header "Build Complete!" + echo "" + echo "๐ŸŽ‰ ParticleOS ISO built successfully with pure bootc!" + echo "๐Ÿ“ Location: $OUTPUT_DIR/${PROJECT_NAME}-${BUILD_TIMESTAMP}.iso" + echo "๐Ÿณ Container Image: $SYSTEM_IMAGE" + echo "" + echo "Summary of Cacher Usage:" + echo " Container Build: $CACHER_CONTAINER_BUILD_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 "โœ… Pure bootc workflow: Container-first approach!" +} + +# Run main function +main "$@" \ No newline at end of file diff --git a/build-iso-bootc-native.sh b/build-iso-bootc-native.sh old mode 100644 new mode 100755 index e658230..3a4344f --- a/build-iso-bootc-native.sh +++ b/build-iso-bootc-native.sh @@ -1,27 +1,48 @@ #!/bin/bash -# ParticleOS ISO Builder - Pure bootc Approach -# Uses bootc for the entire build process, no mmdebstrap needed -# This is the true "bootc + xorriso" approach +# ParticleOS ISO Builder - True bootc Native Approach +# This script creates a minimal live ISO that contains bootc tools +# and can install the ParticleOS OCI image to the target system 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-native.log" -exec > >(tee -a "$LOG_FILE") 2>&1 +# 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" +LOG_DIR="$SCRIPT_DIR/logs/$(date +%y%m%d%H%M)" -echo "$(date): Starting ParticleOS ISO build with pure bootc approach" -echo "$(date): Log directory: $LOG_DIR" +# bootc-specific variables +SYSTEM_IMAGE="particleos-system:latest" +LIVE_IMAGE="particleos-live:latest" +CONTAINER_NAME="particleos-extract" -# Colors -GREEN='\033[0;32m' -BLUE='\033[0;34m' +# 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" +CACHER_CONTAINER_BUILD_USED="No" + +# Colors for output RED='\033[0;31m' +GREEN='\033[0;32m' YELLOW='\033[1;33m' -NC='\033[0m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Logging setup +mkdir -p "$LOG_DIR" +exec > >(tee -a "$LOG_DIR/build.log") 2>&1 + +# Utility functions +print_header() { + echo -e "${BLUE}================================${NC}" + echo -e "${BLUE}$1${NC}" + echo -e "${BLUE}================================${NC}" +} print_status() { echo -e "${BLUE}[INFO]${NC} $1" @@ -40,94 +61,58 @@ print_error() { 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" -CONTAINER_NAME="particleos-builder" -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_CONTAINER_BUILD_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 + if command -v nc >/dev/null 2>&1; then + if nc -z "$APT_CACHER_NG_HOST" "$APT_CACHER_NG_PORT" 2>/dev/null; then + return 0 + fi fi + return 1 } # 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 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 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") + local required_packages=( + "squashfs-tools" + "xorriso" + "grub-pc-bin" + "grub-efi-amd64-signed" + "syslinux-common" + "netcat-openbsd" + "casper" + "podman" + ) + + for pkg in "${required_packages[@]}"; do + if ! dpkg -l | grep -q "^ii.*$pkg"; then + missing_packages+=("$pkg") fi done if [ ${#missing_packages[@]} -gt 0 ]; then print_status "Installing missing packages: ${missing_packages[*]}" sudo apt update - sudo apt install -y "${missing_packages[@]}" + sudo apt install -y "${missing_packages[@]}" || print_error "Failed to install required packages" fi - # Check for casper (required for live boot) - if ! dpkg -s casper &>/dev/null; then - print_status "Installing casper (required for live boot)..." - sudo apt update - sudo apt install -y casper + # Check for bootc specifically (since it's not in standard repos) + if ! command -v bootc &> /dev/null; then + print_error "bootc is not installed. Please install bootc from your local packages first." 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 - + # Check disk space (need at least 10GB) - check root filesystem for podman storage + local available_space=$(df / | 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" + print_error "Insufficient disk space on root filesystem. 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 + if ! ping -c 1 archive.ubuntu.com >/dev/null 2>&1; then print_error "Cannot reach Ubuntu archives. Check your internet connection." fi @@ -138,132 +123,47 @@ check_prerequisites() { 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 remove any existing container first - if podman container exists "$CONTAINER_NAME" 2>/dev/null; then - print_status "Removing existing container: $CONTAINER_NAME" - podman container rm -f "$CONTAINER_NAME" 2>/dev/null || print_warning "Failed to remove container" - fi - - # Force remove any existing images - if podman image exists "$SYSTEM_IMAGE" 2>/dev/null; then - print_status "Removing existing image: $SYSTEM_IMAGE" - podman image rm -f "$SYSTEM_IMAGE" 2>/dev/null || print_warning "Failed to remove image" - fi - - sudo rm -rf "$BUILD_DIR" || print_error "Failed to remove previous build directory." + # Safety check: Ensure BUILD_DIR is within SCRIPT_DIR + if [[ "$BUILD_DIR" != "$SCRIPT_DIR"* ]]; then + print_error "BUILD_DIR must be within SCRIPT_DIR for safety" fi - mkdir -p "$BUILD_DIR" "$OUTPUT_DIR" || print_error "Failed to create build directories." - chmod 755 "$OUTPUT_DIR" || print_error "Failed to set permissions on output directory." + if [ -d "$BUILD_DIR" ]; then + print_status "Cleaning existing build directory..." + + # Force remove any existing containers and images + podman container rm -f "$CONTAINER_NAME" 2>/dev/null || true + podman image rm -f "$SYSTEM_IMAGE" "$LIVE_IMAGE" 2>/dev/null || true + + sudo rm -rf "$BUILD_DIR" + fi + + mkdir -p "$BUILD_DIR" "$OUTPUT_DIR" + chmod 755 "$OUTPUT_DIR" + print_success "Build environment cleaned" } -# Create bootc system definition -create_bootc_definition() { - print_header "Phase 3: Create bootc System Definition" +# Create the main ParticleOS system OCI image +create_system_image() { + print_header "Phase 3: Create ParticleOS System Image" - print_status "Creating bootc system definition..." - - # Create a bootc-compatible system definition - cat > "$BUILD_DIR/particleos-system.yaml" << 'EOF' -# ParticleOS System Definition for bootc -apiVersion: v1 -kind: Image -metadata: - name: particleos-desktop - version: "1.0.0" -spec: - os: - name: ubuntu - version: "24.04" - packages: - - name: ubuntu-minimal - - name: plasma-desktop - - name: plasma-workspace - - name: sddm - - name: kwin-x11 - - name: flatpak - - name: network-manager - - name: plasma-nm - - name: openssh-server - - name: curl - - name: wget - - name: vim - - name: nano - - name: htop - - name: neofetch - - name: tree - - name: pulseaudio - - name: pulseaudio-utils - - name: fonts-ubuntu - - name: fonts-noto - - name: build-essential - - name: git - - name: live-boot - - name: live-config - - name: casper - - name: systemd-sysv - - name: dbus - - name: locales - - name: resolvconf - - name: linux-image-generic - - name: linux-headers-generic - - name: podman - - name: skopeo - - name: buildah - - name: ostree - - name: apt-ostree - services: - - name: sddm - enabled: true - - name: NetworkManager - enabled: true - - name: ssh - enabled: true - users: - - name: particle - groups: [sudo, wheel] - shell: /bin/bash - home: /home/particle - files: - - path: /etc/hostname - content: particleos - - path: /etc/hosts - content: | - 127.0.0.1 localhost - 127.0.1.1 particleos.local particleos - - path: /etc/apt-ostree/ref - content: particleos/desktop/1.0.0 -EOF - - print_success "bootc system definition created" -} - -# Build system using bootc container workflow -build_bootc_system() { - print_header "Phase 4: Build System with bootc" - - print_status "Building system using bootc container workflow..." + print_status "Creating ParticleOS system OCI image..." # Try to use apt-cacher-ng if available for container build + local use_proxy=false if check_proxy_reachable; then print_status "Container build will use apt-cacher-ng: $APT_CACHER_NG_URL" CACHER_CONTAINER_BUILD_USED="Yes" + use_proxy=true else print_warning "apt-cacher-ng not reachable. Container build will proceed without proxy." CACHER_CONTAINER_BUILD_USED="No (Fallback to direct)" + use_proxy=false fi - # Create a Dockerfile that bootc can build from - cat > "$BUILD_DIR/Dockerfile" << EOF + # Create a Dockerfile for the main system image + cat > "$BUILD_DIR/Dockerfile.system" << 'EOF' # Start with Ubuntu 24.04 base FROM ubuntu:24.04 @@ -272,22 +172,30 @@ ENV DEBIAN_FRONTEND=noninteractive ENV TZ=UTC # Configure apt proxy if available -$(if check_proxy_reachable; then - echo "RUN echo 'Acquire::http::Proxy \"$APT_CACHER_NG_URL\";' > /etc/apt/apt.conf.d/01proxy && \\" - echo " echo 'Acquire::https::Proxy \"$APT_CACHER_NG_URL\";' >> /etc/apt/apt.conf.d/01proxy" -fi) +EOF + + # Add proxy configuration if available + if [ "$use_proxy" = true ]; then + cat >> "$BUILD_DIR/Dockerfile.system" << EOF +RUN echo 'Acquire::http::Proxy "$APT_CACHER_NG_URL";' > /etc/apt/apt.conf.d/01proxy && \\ + echo 'Acquire::https::Proxy "$APT_CACHER_NG_URL";' >> /etc/apt/apt.conf.d/01proxy +EOF + fi + + # Continue with the system Dockerfile + cat >> "$BUILD_DIR/Dockerfile.system" << 'EOF' # Update and install base packages -RUN apt update && apt install -y \\ - systemd \\ - systemd-sysv \\ - dbus \\ - curl \\ - ca-certificates \\ - gnupg \\ - gpgv \\ - locales \\ - resolvconf \\ +RUN apt update && apt install -y \ + systemd \ + systemd-sysv \ + dbus \ + curl \ + ca-certificates \ + gnupg \ + gpgv \ + locales \ + resolvconf \ && rm -rf /var/lib/apt/lists/* # Configure locales @@ -329,57 +237,48 @@ RUN apt update && apt install -y \ fonts-noto \ build-essential \ git \ - live-boot \ - live-config \ - casper \ - linux-image-generic \ - linux-headers-generic \ && rm -rf /var/lib/apt/lists/* -# Install bootc and container tools +# Copy and install local packages +COPY pkgs/ /tmp/pkgs/ + +# Install dependencies first, then our custom packages RUN apt update && apt install -y \ + libarchive13t64 \ + libavahi-client3 \ + libavahi-common3 \ + libavahi-glib1 \ + libcurl3t64-gnutls \ + libgpgme11t64 \ + libglib2.0-0t64 \ 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 \ - dracut \ - dracut-core \ && rm -rf /var/lib/apt/lists/* -# Install ostree and apt-ostree (from your packages) -COPY pkgs/ /tmp/pkgs/ -RUN if [ -f /tmp/pkgs/libostree-1-1_2025.2-1~noble1_amd64.deb ]; then \ - dpkg -i /tmp/pkgs/libostree-1-1_2025.2-1~noble1_amd64.deb; \ - fi -RUN if [ -f /tmp/pkgs/ostree_2025.2-1~noble1_amd64.deb ]; then \ - dpkg -i /tmp/pkgs/ostree_2025.2-1~noble1_amd64.deb; \ - fi -RUN if [ -f /tmp/pkgs/bootc_1.5.1-1~noble1_amd64.deb ]; then \ - dpkg -i /tmp/pkgs/bootc_1.5.1-1~noble1_amd64.deb; \ - fi -RUN if [ -f /tmp/pkgs/apt-ostree_0.1.0-1_amd64.deb ]; then \ - dpkg -i /tmp/pkgs/apt-ostree_0.1.0-1_amd64.deb; \ - fi +# Install ostree and bootc packages from local files (dynamic version detection) +RUN for pkg in /tmp/pkgs/libostree-1-1_*.deb; do \ + if [ -f "$pkg" ]; then dpkg -i "$pkg"; break; fi; \ + done -# Remove unwanted packages (snapd is already blocked by APT preferences) +RUN for pkg in /tmp/pkgs/ostree_*.deb; do \ + if [ -f "$pkg" ]; then dpkg -i "$pkg"; break; fi; \ + done + +RUN for pkg in /tmp/pkgs/bootc_*.deb; do \ + if [ -f "$pkg" ]; then dpkg -i "$pkg"; break; fi; \ + done + +# Install apt-ostree from local packages if available +RUN for pkg in /tmp/pkgs/apt-ostree_*.deb; do \ + if [ -f "$pkg" ]; then dpkg -i "$pkg"; break; fi; \ + done + +# Install ostree-boot (recommended by bootc) +RUN for pkg in /tmp/pkgs/ostree-boot_*.deb; do \ + if [ -f "$pkg" ]; then dpkg -i "$pkg"; break; fi; \ + done + +# Remove unwanted packages RUN apt purge -y ubuntu-advantage-tools || true && \ apt purge -y update-notifier || true && \ apt purge -y update-manager || true && \ @@ -407,14 +306,6 @@ RUN mkdir -p /etc/apt-ostree && \ RUN systemctl enable sddm NetworkManager ssh && \ systemctl disable apt-daily.timer apt-daily-upgrade.timer -# Create casper hook for live boot -RUN mkdir -p /etc/casper && \ - echo "particleos" > /etc/casper/hostname && \ - echo "particle" > /etc/casper/username - -# Regenerate initrd to include live-boot modules -RUN update-initramfs -u -k all - # Clean up RUN rm -rf /tmp/pkgs /var/lib/apt/lists/* /tmp/* @@ -431,30 +322,149 @@ EXPOSE 22 # Set default command CMD ["/lib/systemd/systemd"] EOF - + # Copy packages to build context if [ -d "$SCRIPT_DIR/pkgs" ]; then cp -r "$SCRIPT_DIR/pkgs" "$BUILD_DIR/" fi - # Build the container image using podman - print_status "Building container image..." - cd "$BUILD_DIR" - podman build -t "$SYSTEM_IMAGE" . || print_error "Failed to build container image." + # Copy install script to build context + if [ -f "$SCRIPT_DIR/install-particleos" ]; then + cp "$SCRIPT_DIR/install-particleos" "$BUILD_DIR/" + fi - print_success "bootc system built: $SYSTEM_IMAGE" + # Build the system image using podman + print_status "Building system image..." + cd "$BUILD_DIR" + podman build -f Dockerfile.system -t "$SYSTEM_IMAGE" . || print_error "Failed to build system image." + + print_success "System image built: $SYSTEM_IMAGE" } -# Extract filesystem from container for ISO creation -extract_filesystem() { - print_header "Phase 5: Extract Filesystem for ISO" +# Create minimal live ISO with bootc tools +create_live_iso() { + print_header "Phase 4: Create Minimal Live ISO with bootc Tools" - print_status "Extracting filesystem from container..." + print_status "Creating minimal live ISO with bootc installation tools..." - # Create a temporary container from the image - podman create --name "$CONTAINER_NAME" "$SYSTEM_IMAGE" || print_error "Failed to create container." + # Create a minimal Dockerfile for the live environment + cat > "$BUILD_DIR/Dockerfile.live" << 'EOF' +# Start with Ubuntu 24.04 base +FROM ubuntu:24.04 + +# Set environment +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=UTC + +# Update and install minimal packages for live environment +RUN apt update && apt install -y \ + systemd \ + systemd-sysv \ + dbus \ + curl \ + ca-certificates \ + gnupg \ + gpgv \ + locales \ + resolvconf \ + live-boot \ + live-config \ + casper \ + linux-image-generic \ + linux-headers-generic \ + grub-pc-bin \ + grub-efi-amd64-signed \ + xorriso \ + squashfs-tools \ + && rm -rf /var/lib/apt/lists/* + +# Configure locales +RUN locale-gen en_US.UTF-8 && update-locale LANG=en_US.UTF-8 + +# Copy and install bootc packages +COPY pkgs/ /tmp/pkgs/ + +# Install dependencies first, then our custom packages +RUN apt update && apt install -y \ + libarchive13t64 \ + libavahi-client3 \ + libavahi-common3 \ + libavahi-glib1 \ + libcurl3t64-gnutls \ + libgpgme11t64 \ + libglib2.0-0t64 \ + podman \ + skopeo \ + && rm -rf /var/lib/apt/lists/* + +# Install ostree and bootc packages from local files (dynamic version detection) +RUN for pkg in /tmp/pkgs/libostree-1-1_*.deb; do \ + if [ -f "$pkg" ]; then dpkg -i "$pkg"; break; fi; \ + done + +RUN for pkg in /tmp/pkgs/ostree_*.deb; do \ + if [ -f "$pkg" ]; then dpkg -i "$pkg"; break; fi; \ + done + +RUN for pkg in /tmp/pkgs/bootc_*.deb; do \ + if [ -f "$pkg" ]; then dpkg -i "$pkg"; break; fi; \ + done + +# Install apt-ostree from local packages if available +RUN for pkg in /tmp/pkgs/apt-ostree_*.deb; do \ + if [ -f "$pkg" ]; then dpkg -i "$pkg"; break; fi; \ + done + +# Install ostree-boot (recommended by bootc) +RUN for pkg in /tmp/pkgs/ostree-boot_*.deb; do \ + if [ -f "$pkg" ]; then dpkg -i "$pkg"; break; fi; \ + done + +# Configure system +RUN echo "particleos-live" > /etc/hostname && \ + echo "127.0.0.1 localhost" >> /etc/hosts && \ + echo "127.0.1.1 particleos-live.local particleos-live" >> /etc/hosts && \ + ln -sf /usr/share/zoneinfo/UTC /etc/localtime + +# Create user for live environment +RUN useradd -m -s /bin/bash particle && \ + usermod -aG sudo particle && \ + echo 'particle ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/particle && \ + chmod 0440 /etc/sudoers.d/particle + +# Create casper hook for live boot +RUN mkdir -p /etc/casper && \ + echo "particleos-live" > /etc/casper/hostname && \ + echo "particle" > /etc/casper/username + +# Create bootc installation script +COPY install-particleos /usr/local/bin/install-particleos +RUN chmod +x /usr/local/bin/install-particleos + +# Regenerate initrd to include live-boot modules +RUN update-initramfs -u -k all + +# Clean up +RUN rm -rf /tmp/pkgs /var/lib/apt/lists/* /tmp/* + +# Set labels +LABEL org.opencontainers.image.title="ParticleOS Live" +LABEL org.opencontainers.image.description="Minimal live environment for ParticleOS installation" +LABEL org.opencontainers.image.version="1.0.0" +LABEL org.opencontainers.image.vendor="ParticleOS" + +# Set default command +CMD ["/lib/systemd/systemd"] +EOF + + # Build the live image + print_status "Building live image..." + podman build -f Dockerfile.live -t "$LIVE_IMAGE" . || print_error "Failed to build live image." + + # Extract the live filesystem + print_status "Extracting live filesystem..." + podman create --name "$CONTAINER_NAME" "$LIVE_IMAGE" || print_error "Failed to create container." - # Extract the filesystem local extract_dir="$BUILD_DIR/extract" mkdir -p "$extract_dir" @@ -468,283 +478,285 @@ extract_filesystem() { print_error "Filesystem extraction failed - /etc/os-release not found" fi - print_success "Filesystem extracted to: $extract_dir" + print_success "Live filesystem extracted" } -# Create ISO using xorriso (reuse existing proven method) +# Create the ISO create_iso() { - print_header "Phase 6: Create ISO with xorriso" + print_header "Phase 5: Create ISO" + + print_status "Creating ISO from live filesystem..." local extract_dir="$BUILD_DIR/extract" local iso_dir="$BUILD_DIR/iso" - 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." + mkdir -p "$iso_dir"/{casper,boot/grub,EFI/BOOT,isolinux} - # Copy kernel and initrd from extracted filesystem - print_status "Copying kernel and initrd from extracted filesystem..." + # Find kernel and initrd + local kernel_path=$(find "$extract_dir/boot" -maxdepth 1 -name "vmlinuz-*" | sort -V | tail -n 1) + local initrd_path=$(find "$extract_dir/boot" -maxdepth 1 -name "initrd.img-*" | sort -V | tail -n 1) - KERNEL_PATH=$(find "$extract_dir/boot" -maxdepth 1 -name "vmlinuz-*" | sort -V | tail -n 1) - INITRD_PATH=$(find "$extract_dir/boot" -maxdepth 1 -name "initrd.img-*" | sort -V | tail -n 1) - - if [ -z "$KERNEL_PATH" ]; then - print_error "Kernel (vmlinuz-*) not found in $extract_dir/boot." - fi - if [ -z "$INITRD_PATH" ]; then - print_error "Initrd (initrd.img-*) not found in $extract_dir/boot." + if [ -z "$kernel_path" ] || [ -z "$initrd_path" ]; then + print_error "Kernel or initrd not found in extracted filesystem" fi - print_status "Using kernel: $KERNEL_PATH" - print_status "Using initrd: $INITRD_PATH" + # Copy kernel and initrd + cp "$kernel_path" "$iso_dir/casper/vmlinuz" || print_error "Failed to copy kernel." + cp "$initrd_path" "$iso_dir/casper/initrd" || print_error "Failed to copy initrd." - cp "$KERNEL_PATH" "$iso_dir/casper/vmlinuz" || print_error "Failed to copy kernel." - cp "$INITRD_PATH" "$iso_dir/casper/initrd" || print_error "Failed to copy initrd." + # Create squashfs (exclude boot/grub, not entire boot) + print_status "Creating squashfs..." + sudo mksquashfs "$extract_dir" "$iso_dir/casper/filesystem.squashfs" -comp xz -e boot/grub || print_error "Failed to create squashfs." - # Create filesystem manifest - print_status "Creating filesystem manifest..." - # We need to chroot to get the package list, but we can't easily chroot into extracted fs - # So we'll create a minimal manifest - echo "particleos 1.0.0" > "$iso_dir/casper/filesystem.manifest" - echo "ubuntu-minimal 24.04" >> "$iso_dir/casper/filesystem.manifest" - echo "plasma-desktop 5.27" >> "$iso_dir/casper/filesystem.manifest" + # Generate filesystem.manifest + print_status "Generating filesystem.manifest..." + sudo chroot "$extract_dir" dpkg-query -W --showformat='${Package} ${Version}\n' > "$iso_dir/casper/filesystem.manifest" || print_error "Failed to generate filesystem.manifest." - # Create filesystem size - print_status "Calculating filesystem size..." - du -sx --block-size=1 "$extract_dir" | cut -f1 > "$iso_dir/casper/filesystem.size" || print_error "Failed to create filesystem.size." + # Create filesystem.size + du -sx --block-size=1 "$extract_dir" | cut -f1 > "$iso_dir/casper/filesystem.size" - # Create casper configuration - print_status "Creating casper configuration..." + # Create casper.conf cat > "$iso_dir/casper/casper.conf" << 'EOF' -# Casper configuration for ParticleOS -LIVE_HOSTNAME="particleos" -LIVE_USERNAME="particle" -LIVE_USER_FULLNAME="ParticleOS User" -LIVE_USER_DEFAULT_GROUPS="adm cdrom dip lpadmin plugdev sambashare sudo" -LIVE_IMAGETYPE="ParticleOS" -LIVE_BOOT_CMDLINE="boot=casper quiet splash" +# This file is used by casper to configure the live environment +# See casper(7) for more information + +# Default options for the live environment +DEFAULT_OPTS="boot=casper quiet splash ---" + +# Timeout for the boot menu (in seconds) +TIMEOUT=30 + +# Default selection in the boot menu +DEFAULT=0 + +# Enable automatic hardware detection +AUTO_DETECT=true + +# Enable automatic network configuration +AUTO_NETWORK=true + +# Enable automatic user creation +AUTO_USER=true + +# Default username for the live environment +DEFAULT_USER=particle + +# Enable automatic login +AUTO_LOGIN=true EOF - # Create squashfs - print_status "Creating squashfs filesystem..." - mksquashfs "$extract_dir" "$iso_dir/casper/filesystem.squashfs" -comp xz -e boot || print_error "Failed to create squashfs." + # Find ISOLINUX files + local isolinux_base_dir="" + for dir in /usr/lib/ISOLINUX /usr/lib/syslinux /usr/share/syslinux; do + if [ -f "$dir/isolinux.bin" ]; then + isolinux_base_dir="$dir" + break + fi + done + + if [ -z "$isolinux_base_dir" ]; then + print_error "ISOLINUX files not found" + fi + + # Find syslinux modules directory + local syslinux_modules_dir="" + if [ -d "/usr/lib/syslinux/modules/bios" ]; then + syslinux_modules_dir="/usr/lib/syslinux/modules/bios" + elif [ -d "/usr/share/syslinux/modules/bios" ]; then + syslinux_modules_dir="/usr/share/syslinux/modules/bios" + elif [ -d "$isolinux_base_dir/modules/bios" ]; then + syslinux_modules_dir="$isolinux_base_dir/modules/bios" + fi + + # Copy ISOLINUX files with robust module detection + cp "$isolinux_base_dir/isolinux.bin" "$iso_dir/isolinux/" || print_error "Failed to copy isolinux.bin" + + # Copy ldlinux.c32 from modules directory if available, otherwise from base directory + if [ -f "$syslinux_modules_dir/ldlinux.c32" ]; then + cp "$syslinux_modules_dir/ldlinux.c32" "$iso_dir/isolinux/" || print_error "Failed to copy ldlinux.c32 from modules dir" + elif [ -f "$isolinux_base_dir/ldlinux.c32" ]; then + cp "$isolinux_base_dir/ldlinux.c32" "$iso_dir/isolinux/" || print_error "Failed to copy ldlinux.c32 from base dir" + else + print_error "ldlinux.c32 not found in any expected location" + fi + + # Copy other modules if found + for module in menu.c32 memdisk mboot.c32 libutil.c32; do + if [ -f "$syslinux_modules_dir/$module" ]; then + cp "$syslinux_modules_dir/$module" "$iso_dir/isolinux/" || print_warning "Failed to copy $module from modules dir" + elif [ -f "$isolinux_base_dir/$module" ]; then + cp "$isolinux_base_dir/$module" "$iso_dir/isolinux/" || print_warning "Failed to copy $module from base dir" + else + print_warning "Syslinux module $module not found, ISOLINUX might have issues" + fi + done + + # Copy memtest86+ if available + local memtest_paths=( + "/boot/memtest86+.bin" + "/usr/lib/memtest86+/memtest86+.bin" + "/usr/share/memtest86+/memtest86+.bin" + ) + + for memtest_path in "${memtest_paths[@]}"; do + if [ -f "$memtest_path" ]; then + cp "$memtest_path" "$iso_dir/isolinux/memtest86+.bin" || print_warning "Failed to copy memtest86+.bin" + cp "$memtest_path" "$iso_dir/boot/grub/memtest86+.bin" || print_warning "Failed to copy memtest86+.bin to grub" + break + fi + done + + # Create isolinux.cfg + cat > "$iso_dir/isolinux/isolinux.cfg" << EOF +UI menu.c32 +prompt 0 +menu title ParticleOS Live +timeout 30 + +label live + menu label ^ParticleOS Live + menu default + kernel /casper/vmlinuz + append boot=casper quiet splash --- + +label live-nomodeset + menu label ParticleOS Live (safe graphics) + kernel /casper/vmlinuz + append boot=casper quiet splash nomodeset --- + +label memtest + menu label ^Memory test + kernel /isolinux/memdisk + append initrd=/isolinux/memtest86+.bin + +label hd + menu label ^Boot from first hard disk + localboot 0x80 +EOF # Create GRUB configuration - cat > "$iso_dir/boot/grub/grub.cfg" << 'EOF' -set timeout=10 + cat > "$iso_dir/boot/grub/grub.cfg" << EOF +set timeout=30 set default=0 -menuentry "Try ParticleOS without installing" { +menuentry "ParticleOS Live" { linux /casper/vmlinuz boot=casper quiet splash --- initrd /casper/initrd } -menuentry "Install ParticleOS" { - linux /casper/vmlinuz boot=casper quiet splash --- +menuentry "ParticleOS Live (safe graphics)" { + linux /casper/vmlinuz boot=casper quiet splash nomodeset --- initrd /casper/initrd } -menuentry "Check disc for defects" { - linux /casper/vmlinuz boot=casper integrity-check quiet splash --- - initrd /casper/initrd +menuentry "Memory test" { + linux16 /boot/grub/memtest86+.bin +} + +menuentry "Boot from first hard disk" { + set root=(hd0) + chainloader +1 } 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 + # Create EFI boot image + print_status "Creating EFI boot image..." + 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." - # 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 basic EFI structure (simplified for now) - print_status "Creating basic EFI structure..." - mkdir -p "$iso_dir/EFI/BOOT" || print_error "Failed to create EFI/BOOT directory." - mkdir -p "$iso_dir/boot/grub" || print_error "Failed to create boot/grub directory." - - # Create ISO using xorriso - print_status "Creating bootable ISO using xorriso..." + # Create the ISO + print_status "Creating ISO with xorriso..." xorriso -as mkisofs \ -o "$OUTPUT_DIR/${PROJECT_NAME}-${BUILD_TIMESTAMP}.iso" \ -J -joliet-long \ - -r -V "ParticleOS-${VERSION}" \ + -r -V "ParticleOS ${VERSION}" \ -b isolinux/isolinux.bin \ -c isolinux/boot.cat \ -boot-load-size 4 -boot-info-table \ -no-emul-boot \ - -isohybrid-mbr "$ISOLINUX_BASE_DIR/isohdpfx.bin" \ + -eltorito-alt-boot \ + -e EFI/BOOT/bootx64.efi \ + -no-emul-boot \ + -isohybrid-mbr "$isolinux_base_dir/isohdpfx.bin" \ -partition_offset 16 \ -part_like_isohybrid \ - -isohybrid-gpt-basdat \ "$iso_dir" || print_error "Failed to create ISO with xorriso." - print_success "ISO created successfully: $OUTPUT_DIR/${PROJECT_NAME}-${BUILD_TIMESTAMP}.iso" + print_success "ISO created: $OUTPUT_DIR/${PROJECT_NAME}-${BUILD_TIMESTAMP}.iso" } # Cleanup function cleanup() { - print_status "Cleaning up build environment..." + print_status "Cleaning up..." - # Remove any remaining container - if podman container exists "$CONTAINER_NAME" 2>/dev/null; then - podman container rm -f "$CONTAINER_NAME" 2>/dev/null || print_warning "Failed to remove container" - fi + # Remove temporary containers and images + podman container rm -f "$CONTAINER_NAME" 2>/dev/null || true + podman image rm -f "$SYSTEM_IMAGE" "$LIVE_IMAGE" 2>/dev/null || true - # Remove any remaining images - if podman image exists "$SYSTEM_IMAGE" 2>/dev/null; then - podman image rm -f "$SYSTEM_IMAGE" 2>/dev/null || print_warning "Failed to remove image" + # Remove build directory + if [ -d "$BUILD_DIR" ]; then + sudo rm -rf "$BUILD_DIR" fi print_success "Cleanup completed" } -# Signal trap for cleanup -trap cleanup EXIT INT TERM - # Main execution main() { - echo "๐Ÿš€ ParticleOS ISO Builder (Pure bootc)" + echo "$(date): Starting ParticleOS ISO build with true bootc approach" + echo "$(date): Log directory: $LOG_DIR" + + echo -e "${GREEN}๐Ÿš€ ParticleOS ISO Builder (True bootc Native)${NC}" echo "======================================" echo "Project: $PROJECT_NAME" echo "Version: $VERSION" echo "Build Directory: $BUILD_DIR" - echo "Tool: Pure bootc + xorriso" + echo "Tool: True bootc + Minimal Live ISO" echo "" - echo "๐Ÿ”„ This build uses:" - echo " โ€ข bootc for container-based system building" - echo " โ€ข Dockerfile for system definition" - echo " โ€ข Container extraction for ISO creation" - echo " โ€ข xorriso for proven ISO creation" + echo -e "${BLUE}๐Ÿ”„ This build creates:${NC}" + echo " โ€ข OCI system image for deployment" + echo " โ€ข Minimal live ISO with bootc tools" + echo " โ€ข Live environment can install OCI image" + echo " โ€ข True bootc-managed target system" echo "" - + + # Set up signal handlers + trap cleanup EXIT + trap 'print_error "Build interrupted by user"' INT TERM + + # Execute build phases check_prerequisites clean_build - create_bootc_definition - build_bootc_system - extract_filesystem + create_system_image + create_live_iso create_iso - - print_header "Build Complete!" + echo "" - echo "๐ŸŽ‰ ParticleOS ISO built successfully with pure bootc!" - echo "๐Ÿ“ Location: $OUTPUT_DIR/${PROJECT_NAME}-${BUILD_TIMESTAMP}.iso" - echo "๐Ÿณ Container Image: $SYSTEM_IMAGE" + echo -e "${GREEN}================================${NC}" + echo -e "${GREEN}๐ŸŽ‰ Build Completed Successfully!${NC}" + echo -e "${GREEN}================================${NC}" echo "" - echo "Summary of Cacher Usage:" - echo " Container Build: $CACHER_CONTAINER_BUILD_USED" + echo -e "${BLUE}๐Ÿ“ฆ Generated Files:${NC}" + echo " โ€ข System Image: $SYSTEM_IMAGE" + echo " โ€ข Live Image: $LIVE_IMAGE" + echo " โ€ข ISO: $OUTPUT_DIR/${PROJECT_NAME}-${BUILD_TIMESTAMP}.iso" 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 -e "${BLUE}๐Ÿ“‹ Summary of Cacher Usage:${NC}" + echo " โ€ข Container Build: $CACHER_CONTAINER_BUILD_USED" echo "" - echo "๐Ÿš€ Deploy with bootc:" - echo " sudo bootc install to-disk /dev/sda --source-imgref oci:$SYSTEM_IMAGE" + echo -e "${BLUE}๐Ÿš€ Next Steps:${NC}" + echo " 1. Test the ISO in QEMU:" + echo " qemu-system-x86_64 -m 4G -enable-kvm -cdrom $OUTPUT_DIR/${PROJECT_NAME}-${BUILD_TIMESTAMP}.iso" + echo "" + echo " 2. Boot from ISO and install:" + echo " ./install-particleos /dev/sda" + echo "" + echo -e "${BLUE}๐Ÿ“ Logs:${NC}" + echo " โ€ข Build log: $LOG_DIR/build.log" echo "" - echo "โœ… Pure bootc workflow: Container-first approach!" } # Run main function diff --git a/build-iso-bootc-xorriso.sh b/build-iso-bootc-xorriso.sh old mode 100644 new mode 100755 diff --git a/install-particleos b/install-particleos new file mode 100644 index 0000000..f1bd873 --- /dev/null +++ b/install-particleos @@ -0,0 +1,87 @@ +#!/bin/bash +set -euo pipefail + +echo "==========================================" +echo " ParticleOS bootc Installation" +echo "==========================================" +echo "" +echo "This will install ParticleOS using bootc to your system." +echo "WARNING: This is a destructive operation that will erase the target device." +echo "" + +# Check if running from live environment +if [ ! -f /etc/casper/hostname ]; then + echo "Error: This script must be run from the ParticleOS live environment." + exit 1 +fi + +# Check for target disk +if [ $# -eq 0 ]; then + echo "Usage: $0 " + echo "Example: $0 /dev/sda" + echo "" + echo "Available disks:" + echo "==================" + lsblk -d -o NAME,SIZE,MODEL,TYPE + echo "" + echo "Available partitions:" + echo "=====================" + lsblk -o NAME,SIZE,TYPE,MOUNTPOINT + echo "" + echo "Please specify the target device (e.g., /dev/sda for entire disk)" + exit 1 +fi + +TARGET_DEVICE="$1" + +# Validate target device +if [ ! -b "$TARGET_DEVICE" ]; then + echo "Error: $TARGET_DEVICE is not a valid block device." + exit 1 +fi + +# Show device information +echo "Target device: $TARGET_DEVICE" +echo "Device information:" +lsblk "$TARGET_DEVICE" -o NAME,SIZE,TYPE,MOUNTPOINT +echo "" + +# Final confirmation +echo "==========================================" +echo "FINAL WARNING: This will completely erase $TARGET_DEVICE" +echo "All data on this device will be lost permanently!" +echo "==========================================" +echo "" +echo "Type 'YES' (in uppercase) to confirm installation:" +read -r confirmation + +if [ "$confirmation" != "YES" ]; then + echo "Installation cancelled." + exit 1 +fi + +echo "" +echo "Installing ParticleOS using bootc..." +echo "This may take several minutes..." + +# Install ParticleOS using bootc +if bootc install --target "$TARGET_DEVICE" particleos-system:latest; then + echo "" + echo "==========================================" + echo "Installation completed successfully!" + echo "==========================================" + echo "" + echo "Next steps:" + echo "1. Reboot your system" + echo "2. Boot from $TARGET_DEVICE" + echo "3. Enjoy ParticleOS!" + echo "" +else + echo "" + echo "==========================================" + echo "Installation failed!" + echo "==========================================" + echo "" + echo "Please check the error messages above and try again." + exit 1 +fi \ No newline at end of file diff --git a/pkgs/add_repo.sh b/pkgs/add_repo.sh new file mode 100755 index 0000000..93d267b --- /dev/null +++ b/pkgs/add_repo.sh @@ -0,0 +1,7 @@ +# ForgeJo Debian Repository +distribution=noble +component=main +sudo curl https://git.raines.xyz/api/packages/robojerk/debian/repository.key -o /etc/apt/keyrings/forgejo-robojerk.asc +echo "deb [signed-by=/etc/apt/keyrings/forgejo-robojerk.asc] https://git.raines.xyz/api/packages/robojerk/debian $distribution $component" | sudo tee -a /etc/apt/sources.list.d/forgejo.list +# Available packages +# bootc=1.5.1-1~noble1 bootc-dev=1.5.1-1~noble1 libostree-doc=2025.2-1~noble1 ostree-boot=2025.2-1~noble1 ostree-tests=2025.2-1~noble1 ostree=2025.2-1~noble1 libostree-1-1=2025.2-1~noble1 libostree-dev=2025.2-1~noble1 gir1.2-ostree-1.0=2025.2-1~noble1 steam-launcher=1:1.0.0.83