763 lines
No EOL
23 KiB
Bash
Executable file
763 lines
No EOL
23 KiB
Bash
Executable file
#!/bin/bash
|
|
|
|
# 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
|
|
|
|
# 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)"
|
|
|
|
# bootc-specific variables
|
|
SYSTEM_IMAGE="particleos-system:latest"
|
|
LIVE_IMAGE="particleos-live:latest"
|
|
CONTAINER_NAME="particleos-extract"
|
|
|
|
# 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'
|
|
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"
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
# Check if apt-cacher-ng is reachable
|
|
check_proxy_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"
|
|
|
|
local missing_packages=()
|
|
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[@]}" || print_error "Failed to install required packages"
|
|
fi
|
|
|
|
# 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) - 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 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 2>&1; 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"
|
|
|
|
# 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
|
|
|
|
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 the main ParticleOS system OCI image
|
|
create_system_image() {
|
|
print_header "Phase 3: Create ParticleOS System Image"
|
|
|
|
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 for the main system image
|
|
cat > "$BUILD_DIR/Dockerfile.system" << '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.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 \
|
|
&& 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 \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# 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 \
|
|
&& 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
|
|
|
|
# Remove unwanted packages
|
|
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
|
|
|
|
# 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
|
|
|
|
# Copy install script to build context
|
|
if [ -f "$SCRIPT_DIR/install-particleos" ]; then
|
|
cp "$SCRIPT_DIR/install-particleos" "$BUILD_DIR/"
|
|
fi
|
|
|
|
# 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"
|
|
}
|
|
|
|
# Create minimal live ISO with bootc tools
|
|
create_live_iso() {
|
|
print_header "Phase 4: Create Minimal Live ISO with bootc Tools"
|
|
|
|
print_status "Creating minimal live ISO with bootc installation tools..."
|
|
|
|
# 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."
|
|
|
|
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 "Live filesystem extracted"
|
|
}
|
|
|
|
# Create the ISO
|
|
create_iso() {
|
|
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"
|
|
|
|
# Create ISO directory structure
|
|
mkdir -p "$iso_dir"/{casper,boot/grub,EFI/BOOT,isolinux}
|
|
|
|
# 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)
|
|
|
|
if [ -z "$kernel_path" ] || [ -z "$initrd_path" ]; then
|
|
print_error "Kernel or initrd not found in extracted filesystem"
|
|
fi
|
|
|
|
# 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."
|
|
|
|
# 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."
|
|
|
|
# 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
|
|
du -sx --block-size=1 "$extract_dir" | cut -f1 > "$iso_dir/casper/filesystem.size"
|
|
|
|
# Create casper.conf
|
|
cat > "$iso_dir/casper/casper.conf" << 'EOF'
|
|
# 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
|
|
|
|
# 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=30
|
|
set default=0
|
|
|
|
menuentry "ParticleOS Live" {
|
|
linux /casper/vmlinuz boot=casper quiet splash ---
|
|
initrd /casper/initrd
|
|
}
|
|
|
|
menuentry "ParticleOS Live (safe graphics)" {
|
|
linux /casper/vmlinuz boot=casper quiet splash nomodeset ---
|
|
initrd /casper/initrd
|
|
}
|
|
|
|
menuentry "Memory test" {
|
|
linux16 /boot/grub/memtest86+.bin
|
|
}
|
|
|
|
menuentry "Boot from first hard disk" {
|
|
set root=(hd0)
|
|
chainloader +1
|
|
}
|
|
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."
|
|
|
|
# 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}" \
|
|
-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: $OUTPUT_DIR/${PROJECT_NAME}-${BUILD_TIMESTAMP}.iso"
|
|
}
|
|
|
|
# Cleanup function
|
|
cleanup() {
|
|
print_status "Cleaning up..."
|
|
|
|
# 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 build directory
|
|
if [ -d "$BUILD_DIR" ]; then
|
|
sudo rm -rf "$BUILD_DIR"
|
|
fi
|
|
|
|
print_success "Cleanup completed"
|
|
}
|
|
|
|
# Main execution
|
|
main() {
|
|
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: True bootc + Minimal Live ISO"
|
|
echo ""
|
|
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_system_image
|
|
create_live_iso
|
|
create_iso
|
|
|
|
echo ""
|
|
echo -e "${GREEN}================================${NC}"
|
|
echo -e "${GREEN}🎉 Build Completed Successfully!${NC}"
|
|
echo -e "${GREEN}================================${NC}"
|
|
echo ""
|
|
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 -e "${BLUE}📋 Summary of Cacher Usage:${NC}"
|
|
echo " • Container Build: $CACHER_CONTAINER_BUILD_USED"
|
|
echo ""
|
|
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 ""
|
|
}
|
|
|
|
# Run main function
|
|
main "$@" |