diff --git a/build-iso-bootc-native.sh b/build-iso-bootc-native.sh index edd685f..e658230 100644 --- a/build-iso-bootc-native.sh +++ b/build-iso-bootc-native.sh @@ -57,6 +57,32 @@ 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" @@ -73,7 +99,7 @@ check_prerequisites() { # Check for build tools local missing_packages=() - for package in squashfs-tools xorriso grub-pc-bin grub-efi-amd64-bin isolinux; do + 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 @@ -92,6 +118,19 @@ check_prerequisites() { 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" } @@ -102,16 +141,28 @@ clean_build() { # Clean build directories if [ -d "$BUILD_DIR" ]; then print_status "Removing previous build directory: $BUILD_DIR..." - rm -rf "$BUILD_DIR" || print_error "Failed to remove previous build directory." - fi - - # Remove any existing container - if podman container exists "$CONTAINER_NAME" 2>/dev/null; then - print_status "Removing existing container: $CONTAINER_NAME" - podman container rm "$CONTAINER_NAME" || print_warning "Failed to remove container" + 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" } @@ -202,8 +253,17 @@ build_bootc_system() { print_status "Building system using bootc container workflow..." + # Try to use apt-cacher-ng if available for container build + if check_proxy_reachable; then + print_status "Container build will use apt-cacher-ng: $APT_CACHER_NG_URL" + CACHER_CONTAINER_BUILD_USED="Yes" + else + print_warning "apt-cacher-ng not reachable. Container build will proceed without proxy." + CACHER_CONTAINER_BUILD_USED="No (Fallback to direct)" + fi + # Create a Dockerfile that bootc can build from - cat > "$BUILD_DIR/Dockerfile" << 'EOF' + cat > "$BUILD_DIR/Dockerfile" << EOF # Start with Ubuntu 24.04 base FROM ubuntu:24.04 @@ -211,17 +271,23 @@ FROM ubuntu:24.04 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) + # 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 @@ -397,6 +463,11 @@ 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" } @@ -502,26 +573,91 @@ LABEL check APPEND boot=casper integrity-check initrd=/casper/initrd quiet splash --- EOF - # Copy ISOLINUX boot files + # Copy ISOLINUX boot files - REVISED PATH DISCOVERY print_status "Copying ISOLINUX boot files..." - ISOLINUX_BIN_PATH="" - if [ -f "/usr/lib/syslinux/isolinux.bin" ]; then - ISOLINUX_BIN_PATH="/usr/lib/syslinux" - elif [ -f "/usr/lib/ISOLINUX/isolinux.bin" ]; then - ISOLINUX_BIN_PATH="/usr/lib/ISOLINUX" + 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 - if [ -z "$ISOLINUX_BIN_PATH" ]; then - print_error "isolinux.bin not found. Please install isolinux." + 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 - - cp "$ISOLINUX_BIN_PATH/isolinux.bin" "$iso_dir/isolinux/" || print_error "Failed to copy isolinux.bin." - - # Copy ldlinux.c32 from the correct location - if [ -f "/usr/lib/syslinux/modules/bios/ldlinux.c32" ]; then - cp "/usr/lib/syslinux/modules/bios/ldlinux.c32" "$iso_dir/isolinux/" || print_error "Failed to copy ldlinux.c32." - else - print_error "ldlinux.c32 not found in /usr/lib/syslinux/modules/bios/." + + 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) @@ -539,7 +675,7 @@ EOF -c isolinux/boot.cat \ -boot-load-size 4 -boot-info-table \ -no-emul-boot \ - -isohybrid-mbr "$ISOLINUX_BIN_PATH/isohdpfx.bin" \ + -isohybrid-mbr "$ISOLINUX_BASE_DIR/isohdpfx.bin" \ -partition_offset 16 \ -part_like_isohybrid \ -isohybrid-gpt-basdat \ @@ -554,7 +690,12 @@ cleanup() { # Remove any remaining container if podman container exists "$CONTAINER_NAME" 2>/dev/null; then - podman container rm "$CONTAINER_NAME" 2>/dev/null || true + 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" @@ -592,6 +733,9 @@ main() { 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 \\" diff --git a/todo.md b/todo.md index 6500753..258d799 100644 --- a/todo.md +++ b/todo.md @@ -1,1045 +1,24 @@ # ParticleOS Installer - TODO List -Remove completed tasks to keep the file tidy. - +## Reminders We are building an installer for an atomic desktop based on Ubuntu - +Remove completed tasks to keep the file tidy. bootc is REQUIRED -Move to using calmares? -see file calmares_plan.md +## Current Task -```bash -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 -sudo apt update -sudo apt install bootc=1.5.1-1~noble1 ostree=2025.2-1~noble1 -wget https://git.raines.xyz/robojerk/apt-ostree/raw/branch/main/apt-ostree_0.1.0-1_amd64.deb -``` -Logs from build-iso-podman.shs should be stored in logs/{YYMMDDHHMM} -The time shold reflect when the build was initiated and all the logs from the start time go in there + _ Discuss what we're actually doing. The goal at the moment should be proving that we can and know how to install an an atomic image to a vm, then to a bare metal system. + - Once we can prove we CAN install our then finally build an iso of our own. Eith LiveCD ( should we just use the image we made? ) or use calmares linux install. - # Forgejo has a oci image repo. See documentation here - # https://forgejo.org/docs/latest/user/packages/container/ - # - # Existing images - # https://git.raines.xyz/robojerk/-/packages/container/aurora-bootable/v1.0 - # docker pull git.raines.xyz/robojerk/aurora-bootable:v1.0 - # Digest: sha256:c3a52d917bf4ac32a5fdcd6de2c1165fbd3ba234fef82a1d35b8b56e2bb1103d - # - # https://git.raines.xyz/robojerk/-/packages/container/aurora-system/v1.0 - # docker pull git.raines.xyz/robojerk/aurora-system:v1.0 - # Digest: sha256:be75ad8f24e0b25d1d5f1d9fdd2b0bf2a5ed02a2e8646647a8e25ca83c5e6828 +## Uncompleted + - Discuess targeting Plucky Penguin or event Questing Quokka since it's only 2 months from release + - Discuss update build to use latest KDE Plasma 6 with default to Wayland + - Discuss blocking AppArmor and switch to SELinux in the image creation. ** See selinux_ubuntu.md ** + - Once we can build a iso, create ci/cd to, include artifacts or release. + - Discuss how to get flatpak installed and install certain flatpak application + - Discuss things missing but ** can ** be migrated from Aurora, Bazzite, and Bluefin -Discuss adding apt-cacher-ng support +## Completed -Even if the host system and the Podman container itself have perfect internet access, the newly created `mmdebstrap` chroot environment often lacks a proper `/etc/resolv.conf` or equivalent, leading to DNS lookup failures when `apt` (or `curl`, `wget`, etc.) tries to reach external repositories. -The `gpgv` error often surfaces because `apt` tries to fetch repository Release files (which contain GPG signatures) but can't resolve the hostname. - -**Here's the most common and effective fix for DNS in a chroot:** - -Copy the host's `/etc/resolv.conf` into the chroot's `/etc/resolv.conf` before any network operations inside the chroot. This leverages the working DNS configuration of the container/host. - ------ - -### Proposed Fix for `build-in-container.sh` - -We need to add a line to copy `/etc/resolv.conf` inside the container's `build-in-container.sh` script, right after the chroot is set up but *before* the first `apt update` or `curl` command that attempts to reach the internet. - -**Placement:** This should go right after the bind mounts are established, as `/etc/resolv.conf` is a fundamental networking configuration. - -```bash -# ... (previous code up to mount --bind /dev, /run, /proc, /sys) ... - -print_status "Configuring APT sources..." -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." - -# FIX: Copy host's resolv.conf to chroot for DNS resolution -print_status "Copying host's /etc/resolv.conf to chroot for DNS resolution..." -cp /etc/resolv.conf "$CHROOT_DIR/etc/resolv.conf" || print_error "Failed to copy /etc/resolv.conf to chroot." - -# CRITICAL FIX: Explicitly install gpgv now to ensure APT can verify signatures -# This resolves the "gpgv, gpgv2 or gpgv1 required for verification" error -print_status "Installing gpgv inside the chroot for APT signature verification..." -chroot "$CHROOT_DIR" DEBIAN_FRONTEND=noninteractive apt update || print_error "Failed to update APT lists for gpgv installation." -chroot "$CHROOT_DIR" DEBIAN_FRONTEND=noninteractive apt install -y gpgv || print_error "Failed to explicitly install gpgv. This is required for secure package verification." -print_success "gpgv successfully installed in chroot." - -# ... (rest of the script) ... -``` - ------ - -**Rationale for the change:** - - * **DNS Resolution:** The `E: gpgv, gpgv2 or gpgv1 required for verification, but neither seems installed` error can be a red herring. Often, `gpgv` *is* present (or gets installed in a later `apt update`), but `apt` fails *before* it even gets to the GPG verification step because it can't resolve the repository's hostname. - * **Early Placement:** Copying `resolv.conf` early ensures that any subsequent `apt update`, `curl`, or `wget` commands executed within the chroot (e.g., to fetch GPG keys or `apt-ostree.deb`) have working DNS. - * **Security:** This is a standard and safe practice for chroot environments, as it just provides a temporary DNS configuration. - -By adding this `cp /etc/resolv.conf "$CHROOT_DIR/etc/resolv.conf"` line, you should resolve the underlying DNS issue that's likely causing the `gpgv` error to manifest. - -NO snapd -remove firefox, it is a snap -Discuss removing AppArmor and use SELinux? - -## ๐Ÿš€ **High Priority** - -### Core Functionality -- [x] **Base System**: Ubuntu Noble (24.04) base -- [x] **Desktop Environment**: KDE Plasma with ParticleOS branding -- [x] **Package Management**: apt-ostree integration -- [x] **Snap Blocking**: Prevent snapd installation and usage -- [x] **ISO Building**: Create bootable live ISO -- [x] **Safety Measures**: Podman containerization for build process - -### Build System -- [x] **Containerized Build**: Use Podman for isolated builds -- [x] **Error Handling**: Comprehensive error checking and cleanup -- [x] **Safety Checks**: Validate environment before building -- [x] **Cleanup**: Proper cleanup of containers and mounts - -## ๐Ÿ”ง **Medium Priority** - -### System Configuration -- [ ] **User Setup**: Create default user with proper permissions -- [ ] **Network Configuration**: Configure networking for live system -- [ ] **Services**: Enable/disable appropriate system services -- [ ] **Locale**: Set up proper locale and timezone -- [ ] **Hostname**: Set ParticleOS hostname - -### Package Management -- [ ] **apt-ostree Repository**: Set up proper repository configuration -- [ ] **Package Selection**: Curate list of included packages -- [ ] **Dependencies**: Ensure all dependencies are properly resolved -- [ ] **Updates**: Configure update mechanism for installed system - -### ISO Features -- [ ] **Boot Options**: Multiple boot options (try, install, check) -- [ ] **Installation**: bootc-based installation process -- [ ] **Persistence**: Optional persistent storage for live system -- [ ] **Encryption**: Support for encrypted installations - -## ๐ŸŽจ **Low Priority** - -### Branding and Customization -- [ ] **Boot Splash**: Custom boot splash screen -- [ ] **Wallpaper**: ParticleOS branded wallpaper -- [ ] **Icons**: Custom application icons -- [ ] **Theme**: Consistent visual theme throughout -- [ ] **Documentation**: User and developer documentation - -### Advanced Features -- [ ] **Atomic Updates**: Implement atomic update system -- [ ] **Rollback**: System rollback functionality -- [ ] **Backup**: Automated backup system -- [ ] **Monitoring**: System health monitoring -- [ ] **Logging**: Comprehensive logging system - -## ๐Ÿงช **Testing and Quality** - -### Testing -- [ ] **ISO Testing**: Test ISO in various environments -- [ ] **Installation Testing**: Test installation process -- [ ] **Update Testing**: Test update mechanisms -- [ ] **Performance Testing**: Benchmark system performance -- [ ] **Compatibility Testing**: Test with various hardware - -### Documentation -- [ ] **README**: Comprehensive project documentation -- [ ] **Build Guide**: Step-by-step build instructions -- [ ] **User Guide**: End-user documentation -- [ ] **Developer Guide**: Developer documentation -- [ ] **Troubleshooting**: Common issues and solutions - -## ๐Ÿ”’ **Security** - -### Security Features -- [ ] **Secure Boot**: UEFI Secure Boot support -- [ ] **Disk Encryption**: Full disk encryption support -- [ ] **Firewall**: Default firewall configuration -- [ ] **Updates**: Security update mechanism -- [ ] **Audit**: Security audit and compliance - -## ๐Ÿ“ฆ **Distribution** - -### Release Management -- [ ] **Versioning**: Semantic versioning system -- [ ] **Release Notes**: Automated release notes generation -- [ ] **Checksums**: Provide checksums for downloads -- [ ] **Signing**: GPG signing of releases -- [ ] **Mirrors**: Set up download mirrors - -### CI/CD -- [ ] **Automated Builds**: GitHub Actions or similar -- [ ] **Testing Pipeline**: Automated testing -- [ ] **Release Pipeline**: Automated releases -- [ ] **Quality Gates**: Quality checks before release - -## ๐ŸŽฏ **Future Enhancements** - -### Advanced Features -- [ ] **Container Support**: Native container runtime -- [ ] **Cloud Integration**: Cloud deployment options -- [ ] **IoT Support**: Internet of Things capabilities -- [ ] **Edge Computing**: Edge computing features -- [ ] **AI/ML**: Machine learning toolchain - -### Ecosystem -- [ ] **Software Center**: Application store -- [ ] **Developer Tools**: Development environment -- [ ] **Gaming**: Gaming support and optimization -- [ ] **Multimedia**: Enhanced multimedia support -- [ ] **Accessibility**: Accessibility features - ---- - -## ๐Ÿ“ **Notes** - -- **Priority**: High priority items should be completed before medium priority -- **Dependencies**: Some items depend on others being completed first -- **Testing**: All features should be thoroughly tested before release -- **Documentation**: Documentation should be updated as features are added -- **Security**: Security should be considered for all features - - - -more hardening and requiring bootc ideas for build-iso-podman.sh -```bash -This is a **very well-structured and robust script**\! You've incorporated excellent error handling, logging, safety checks, and addressed the GPG key issues and the desired Firefox and `ostree`/`bootc` installations. - -Here are a few minor observations and potential improvements, mostly related to edge cases or best practices: - ------ - -### General Script Improvements (Host-Side) - - * **Output ISO Name Consistency**: In your `create_container_build_script` (the content that goes into `build-in-container.sh`), the line that creates the ISO is: - - ```bash - -o "$OUTPUT_DIR/${PROJECT_NAME}-${VERSION}.iso" \ - ``` - - This generates `particleos-1.0.0.iso`. However, in your `main` function on the host side, the final success message and the QEMU command show: - - ```bash - echo "๐Ÿ“ Location: $OUTPUT_DIR/particleos-$VERSION.iso" - echo " ย  ย qemu-system-x86_64 -m 4G -enable-kvm \\" - echo " ย  ย  ย -cdrom $OUTPUT_DIR/particleos-$VERSION.iso \\" - ``` - - This implies `particleos-1.0.iso`. It's a small difference but could lead to confusion if the file name changes. I'd recommend sticking to `particleos-$VERSION.iso` in `main` for consistency or change the `xorriso` output to include the `.0.0` part of the version if you prefer. I'll provide the fix assuming you want `particleos-1.0.0.iso` consistently. - - **Proposed Change:** - In `main()`, change: - - ```bash - echo "๐Ÿ“ Location: $OUTPUT_DIR/particleos-$VERSION.iso" - # ... - echo " ย  ย  ย -cdrom $OUTPUT_DIR/particleos-$VERSION.iso \\" - ``` - - to: - - ```bash - echo "๐Ÿ“ Location: $OUTPUT_DIR/${PROJECT_NAME}-${VERSION}.iso" - # ... - echo " ย  ย  ย -cdrom $OUTPUT_DIR/${PROJECT_NAME}-${VERSION}.iso \\" - ``` - - * **`rm -rf "$BUILD_DIR"` Safety in `clean_build()`**: You have a very good safety check for `BUILD_DIR` not being a critical system directory. This is excellent\! - - * **`PODMAN_CMD` in Cleanup**: The `cleanup` function, when called on `EXIT`, `INT`, `TERM`, relies on `PODMAN_CMD` being set. It's likely already set by `check_podman()`, but as a tiny safeguard, you could ensure `PODMAN_CMD` is actually defined if cleanup is triggered very early (e.g., if `podman` isn't found at all). This is a minor nitpick, as `check_podman` is the first real action. - - * **Logging Initialization Order**: The `exec > >(tee -a "$LOG_FILE") 2>&1` line should ideally be *after* the `LOG_DIR` creation and any initial `echo` statements related to logging itself, to ensure those initial messages are also captured in the log file. It currently is, which is good. - ------ - -### Container Script (`build-in-container.sh`) Improvements - - * **`gpgv` in `mmdebstrap --include`**: You've added `gpgv` to the `--include` list for `mmdebstrap`, which is great for ensuring GPG verification capabilities within the chroot. - - * **Specific `ostree`/`bootc` Versioning**: The logic to try specific versions (`ostree=2025.2-1~noble1`, `bootc=1.5.1-1~noble1`) and then fall back to the latest is excellent\! This makes the build more resilient to version changes while prioritizing a known working version. The `2>/dev/null` for `apt install` is good to suppress the "No such version" output on the first attempt, but make sure the `print_error` still gets the actual APT error if it truly fails. You've handled this with the subsequent `if` conditions. - - * **Firefox Installation Timing**: You've correctly moved the general `firefox` installation to *after* the APT preferences are set for the Mozilla PPA. This ensures the correct Firefox package is pulled. - - * **EFI Boot Image Creation**: - - * You changed `grub-mkimage -o "$ISO_DIR/boot/grub/efi.img"` to `grub-mkimage -o "$ISO_DIR/EFI/BOOT/bootx64.efi"`. This is generally correct for directly creating the EFI executable. - * However, `xorriso`'s `-e` option (`-e boot/grub/efi.img`) expects an **El Torito boot image** which is typically a special `.img` file (often a FAT filesystem containing `bootx64.efi` inside). Directly pointing it to `bootx64.efi` might not work as intended for the El Torito entry. - * Your solution of creating a **dummy `efi.img`** (`dd if=/dev/zero of="$ISO_DIR/boot/grub/efi.img" bs=1M count=1 2>/dev/null`) alongside the `bootx64.efi` is a common workaround for `xorriso` to satisfy its `-e` option while still providing the direct `bootx64.efi` for UEFI boot. This will likely work for creating the ISO, but the `efi.img` El Torito entry might not be truly bootable for older firmware that *only* uses that method. However, modern UEFI systems typically find `EFI/BOOT/bootx64.efi` directly. - * **Recommendation**: This is a tricky part. The best practice for `xorriso` and UEFI is often to build a small FAT filesystem image containing `EFI/BOOT/bootx64.efi` and then point `-e` to that image. Your current method of creating `bootx64.efi` directly in `EFI/BOOT` is good for UEFI, and the dummy `efi.img` handles `xorriso`'s syntax. This setup will likely work on most modern UEFI systems. If you encounter issues, this would be the first place to investigate. - ------ - -### Final Code Review (incorporating suggestions) - -Here's the integrated script with the minor changes discussed: - -```bash -#!/bin/bash - -# ParticleOS ISO Builder with Podman - HARDENED ULTRA SAFE VERSION -# Builds a bootable ISO using Podman containers for complete isolation -# This approach cannot break your host system -# Includes all hardening features from the analysis - -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" || { echo "ERROR: Failed to create log directory $LOG_DIR"; exit 1; } -LOG_FILE="$LOG_DIR/build-iso-podman.log" -exec > >(tee -a "$LOG_FILE") 2>&1 - -echo "$(date): Starting ParticleOS ISO build with logging enabled" -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 immediately on error. If this is an error from the container, - # the container's error message should have already propagated. - 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_DIR="$SCRIPT_DIR/build" -OUTPUT_DIR="$SCRIPT_DIR/output" -CONTAINER_NAME="particleos-builder" -IMAGE_NAME="particleos-builder:latest" - -# 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." - exit 1 -fi - -# Determine Podman command (sudo or not) -PODMAN_CMD="" -check_podman() { - print_header "Phase 1: Check Podman Availability" - - if ! command -v podman &> /dev/null; then - print_error "Podman is not installed. Please install Podman first: sudo apt update && sudo apt install -y podman" - fi - - # Try running podman without sudo first - if podman info &> /dev/null; then - PODMAN_CMD="podman" - print_success "Podman available (rootless or with user access)." - else - # If rootless fails, try with sudo - if sudo podman info &> /dev/null; then - PODMAN_CMD="sudo podman" - print_warning "Podman requires sudo. This is normal for some configurations. Using '$PODMAN_CMD'." - else - print_error "Podman cannot be run even with sudo. Check your Podman installation and user permissions." - fi - fi -} - -# Check prerequisites -check_prerequisites() { - print_header "Phase 2: Check Prerequisites" - - # Check disk space (need at least 15GB free for container + ISO) - local available_space=$(df "$SCRIPT_DIR" | awk 'NR==2 {print $4}') - local required_space=$((15 * 1024 * 1024)) # 15GB in KB - - if [ "$available_space" -lt "$required_space" ]; then - print_error "Insufficient disk space. Need at least 15GB 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 - - # Using --silent --head for curl to check HTTP status code more reliably - if ! curl --silent --head "https://git.raines.xyz/robojerk/apt-ostree/raw/branch/main/apt-ostree_0.1.0-1_amd64.deb" | grep "HTTP/[12] [23].." > /dev/null; then - print_error "Cannot reach apt-ostree repository. Check your internet connection." - fi - - # Check Forgejo repository accessibility (CRITICAL for bootc and ostree) - print_status "Checking Forgejo repository accessibility..." - # Attempt to fetch the GPG key directly to confirm reachability and PGP format - if ! curl -fsSL "https://git.raines.xyz/api/packages/robojerk/debian/gpg" | gpg --dearmor -o /dev/null 2>/dev/null; then - print_error "CRITICAL: Cannot access or process GPG key from Forgejo repository (https://git.raines.xyz/api/packages/robojerk/debian/gpg). bootc and ostree packages are required!" - print_error "Check if https://git.raines.xyz is accessible and returns a valid GPG key." - fi - - # Check if Forgejo repository lists ostree/bootc packages (less critical failure, but good warning) - if ! curl --silent "https://git.raines.xyz/api/packages/robojerk/debian/dists/noble/main/binary-amd64/Packages.gz" | gzip -d | grep -qE "(Package: ostree|Package: bootc)"; then - print_warning "Forgejo repository might not contain expected 'ostree' or 'bootc' packages in noble/main/binary-amd64. This *might* cause the build to fail later if they're not in Ubuntu's default repos or exact versions aren't found. Proceeding anyway..." - fi - - print_success "All prerequisites satisfied" -} - -# Clean build environment -clean_build() { - print_header "Phase 3: Clean Build Environment" - - # Remove any existing container - if $PODMAN_CMD container exists "$CONTAINER_NAME" 2>/dev/null; then - print_status "Removing existing container: $CONTAINER_NAME" - $PODMAN_CMD container rm -f "$CONTAINER_NAME" 2>/dev/null || true - fi - - # Remove any existing image - if $PODMAN_CMD image exists "$IMAGE_NAME" 2>/dev/null; then - print_status "Removing existing image: $IMAGE_NAME" - $PODMAN_CMD image rm -f "$IMAGE_NAME" 2>/dev/null || true - fi - - # Clean build directories - if [ -d "$BUILD_DIR" ]; then - print_status "Removing previous build directory: $BUILD_DIR..." - # Add host-side safety check for rm -rf - 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 - 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." - print_success "Build environment cleaned" -} - -# Create Dockerfile for the build environment -create_dockerfile() { - print_header "Phase 4: Create Build Environment" - - cat > "$BUILD_DIR/Dockerfile" << 'EOF' -FROM ubuntu:noble - -# Set environment variables -ENV DEBIAN_FRONTEND=noninteractive -ENV TZ=UTC - -# Update and install build tools -# Added software-properties-common for add-apt-repository -# Added live-build for more complete live ISO tools if needed, though casper/live-boot should be sufficient -RUN apt update && apt install -y \ - mmdebstrap \ - squashfs-tools \ - xorriso \ - grub-pc-bin \ - grub-efi-amd64-bin \ - isolinux \ - curl \ - wget \ - ca-certificates \ - gnupg \ - software-properties-common \ - live-build \ - && rm -rf /var/lib/apt/lists/* - -# Set up working directory -WORKDIR /build - -# Copy build script -COPY build-in-container.sh /build/ -RUN chmod +x /build/build-in-container.sh - -# Default command -CMD ["/build/build-in-container.sh"] -EOF - - print_success "Dockerfile created" -} - -# Create the build script that runs inside the container -create_container_build_script() { - cat > "$BUILD_DIR/build-in-container.sh" << 'EOF' -#!/bin/bash - -set -euo pipefail - -# 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}[CONTAINER]${NC} $1" -} - -print_success() { - echo -e "${GREEN}[CONTAINER SUCCESS]${NC} $1" -} - -print_error() { - echo -e "${RED}[CONTAINER ERROR]${NC} $1" - exit 1 # Exit immediately on error inside the container -} - -print_header() { - echo "" - echo -e "${BLUE}================================${NC}" - echo -e "${BLUE}$1${NC}" - echo -e "${BLUE}================================${NC}" -} - -# Configuration -PROJECT_NAME="particleos" -VERSION="1.0.0" -CHROOT_DIR="/tmp/chroot" -ISO_DIR="/tmp/iso" -OUTPUT_DIR="/output" # Mapped from host's OUTPUT_DIR - -# Track mounted filesystems for cleanup inside container's chroot -MOUNTED_FILESYSTEMS_IN_CHROOT=() - -# Cleanup function to unmount filesystems inside the container's chroot -cleanup_chroot_mounts() { - print_status "Cleaning up mounted filesystems in chroot..." - # Iterate in reverse for safer unmounting - for (( i=${#MOUNTED_FILESYSTEMS_IN_CHROOT[@]}-1; i>=0; i-- )); do - mount_point="${MOUNTED_FILESYSTEMS_IN_CHROOT[i]}" - if mountpoint -q "$mount_point" 2>/dev/null; then - print_status "Unmounting $mount_point" - umount "$mount_point" 2>/dev/null || print_error "Failed to unmount $mount_point" - fi - done - MOUNTED_FILESYSTEMS_IN_CHROOT=() -} - -# Signal trap for internal script to ensure cleanup on exit/error -trap cleanup_chroot_mounts EXIT INT TERM - -print_header "Starting Container Build Process" - -# Create directories -mkdir -p "$CHROOT_DIR" "$ISO_DIR" "$OUTPUT_DIR" || print_error "Failed to create necessary directories in container." - -# Ensure chroot directory is clean -rm -rf "$CHROOT_DIR"/* || print_error "Failed to clean chroot directory." - -print_header "Phase 1: Create Base System" - -# Create base system with mmdebstrap -print_status "Creating base Ubuntu system..." -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." - -print_success "Base system created" - -print_header "Phase 2: Configure Base System" - -# Mount necessary filesystems -# Using 'mount' directly inside the container, it needs root or privileged container mode -mount --bind /dev "$CHROOT_DIR/dev" || print_error "Failed to bind mount /dev." -MOUNTED_FILESYSTEMS_IN_CHROOT+=("$CHROOT_DIR/dev") - -mount --bind /run "$CHROOT_DIR/run" || print_error "Failed to bind mount /run." -MOUNTED_FILESYSTEMS_IN_CHROOT+=("$CHROOT_DIR/run") - -mount -t proc none "$CHROOT_DIR/proc" || print_error "Failed to mount /proc." -MOUNTED_FILESYSTEMS_IN_CHROOT+=("$CHROOT_DIR/proc") - -mount -t sysfs none "$CHROOT_DIR/sys" || print_point+=("$CHROOT_DIR/sys") - -# Configure package sources -# Corrected chroot bash -c syntax -print_status "Configuring APT sources..." -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." - -# Add Mozilla PPA for Firefox DEB package (no snap) - with proper GPG key management -print_status "Adding Mozilla PPA for Firefox DEB package..." -# Manually fetch and add the PPA key for robustness -chroot "$CHROOT_DIR" bash -c ' -mkdir -p /etc/apt/keyrings && \ -curl -fsSL https://ppa.launchpadcontent.net/mozillateam/ppa/ubuntu/dists/noble/Release.gpg | gpg --dearmor -o /etc/apt/keyrings/mozillateam-ppa.gpg && \ -echo "deb [signed-by=/etc/apt/keyrings/mozillateam-ppa.gpg] https://ppa.launchpadcontent.net/mozillateam/ppa/ubuntu noble main" > /etc/apt/sources.list.d/mozillateam-ppa.list -' || print_error "Failed to add Mozilla PPA key and repository." - -# Add Forgejo repository for apt-ostree and other packages - with proper GPG key management -print_status "Setting up Forgejo repository for apt-ostree and other packages..." -chroot "$CHROOT_DIR" bash -c ' -mkdir -p /etc/apt/keyrings && \ -curl -fsSL https://git.raines.xyz/api/packages/robojerk/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/forgejo-robojerk.gpg && \ -echo "deb [signed-by=/etc/apt/keyrings/forgejo-robojerk.gpg] https://git.raines.xyz/api/packages/robojerk/debian noble main" > /etc/apt/sources.list.d/forgejo.list -' || print_error "Failed to add Forgejo repository key and repository." - -# Update package lists (after adding repositories) -print_status "Updating package lists..." -chroot "$CHROOT_DIR" apt update || print_error "Failed to update package lists." - -# Install desktop and additional packages (enhanced with ostree and bootc) -print_status "Installing desktop environment and essential packages..." -chroot "$CHROOT_DIR" DEBIAN_FRONTEND=noninteractive apt install -y \ - kubuntu-desktop \ - plasma-desktop \ - plasma-workspace \ - kde-plasma-desktop \ - sddm \ - 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 \ - systemd-sysv \ - dbus \ - locales \ - resolvconf || print_error "Failed to install desktop and essential packages." - -# Install ostree and bootc - CRITICAL COMPONENTS -print_status "Installing ostree and bootc (CRITICAL COMPONENTS)..." -print_status "These packages are REQUIRED for ParticleOS to function!" - -# Try specific versions first, then latest, then fail if neither works -print_status "Attempting to install ostree=2025.2-1~noble1..." -if chroot "$CHROOT_DIR" apt install -y ostree=2025.2-1~noble1 2>/dev/null; then - print_success "Installed ostree=2025.2-1~noble1" -else - print_warning "Specific ostree version not available, trying latest version..." - if chroot "$CHROOT_DIR" apt install -y ostree 2>/dev/null; then - print_success "Installed latest ostree version" - else - print_error "CRITICAL: Failed to install ostree. This is required for ParticleOS!" - print_error "Check if the Forgejo repository is accessible and contains ostree packages." - exit 1 - fi -fi - -print_status "Attempting to install bootc=1.5.1-1~noble1..." -if chroot "$CHROOT_DIR" apt install -y bootc=1.5.1-1~noble1 2>/dev/null; then - print_success "Installed bootc=1.5.1-1~noble1" -else - print_warning "Specific bootc version not available, trying latest version..." - if chroot "$CHROOT_DIR" apt install -y bootc 2>/dev/null; then - print_success "Installed latest bootc version" - else - print_error "CRITICAL: Failed to install bootc. This is required for ParticleOS!" - print_error "Check if the Forgejo repository is accessible and contains bootc packages." - exit 1 - fi -fi - -# Verify installations -print_status "Verifying ostree and bootc installations..." -if ! chroot "$CHROOT_DIR" command -v ostree >/dev/null 2>&1; then - print_error "CRITICAL: ostree command not found after installation!" - exit 1 -fi - -if ! chroot "$CHROOT_DIR" command -v bootc >/dev/null 2>&1; then - print_error "CRITICAL: bootc command not found after installation!" - exit 1 -fi - -print_success "ostree and bootc successfully installed and verified!" - -# Download and install apt-ostree from custom repository -print_status "Installing apt-ostree from custom repository directly..." -chroot "$CHROOT_DIR" timeout 60 wget -O /tmp/apt-ostree.deb 'https://git.raines.xyz/robojerk/apt-ostree/raw/branch/main/apt-ostree_0.1.0-1_amd64.deb' || print_error "Failed to download apt-ostree." -chroot "$CHROOT_DIR" dpkg -i /tmp/apt-ostree.deb || chroot "$CHROOT_DIR" apt install -f -y || print_error "Failed to install apt-ostree or fix dependencies." -chroot "$CHROOT_DIR" rm -f /tmp/apt-ostree.deb || print_error "Failed to clean up apt-ostree deb." - -# Enhanced snap removal and blocking -print_status "Removing snapd and setting APT preferences to block snaps..." -chroot "$CHROOT_DIR" DEBIAN_FRONTEND=noninteractive apt purge -y snapd ubuntu-advantage-tools update-notifier update-manager unattended-upgrades || print_error "Failed to purge snapd and related packages." -chroot "$CHROOT_DIR" apt autoremove -y || print_error "Failed to autoremove orphaned packages." -chroot "$CHROOT_DIR" apt-mark hold snapd || print_error "Failed to hold snapd." - -# Create an APT preference file to block snapd and main repo Firefox, prioritize PPA Firefox -chroot "$CHROOT_DIR" bash -c ' -echo "Package: snapd" > /etc/apt/preferences.d/nosnap.pref && \ -echo "Pin: release *" >> /etc/apt/preferences.d/nosnap.pref && \ -echo "Pin-Priority: -1" >> /etc/apt/preferences.d/nosnap.pref && \ -echo "" >> /etc/apt/preferences.d/nosnap.pref && \ -echo "Package: firefox" >> /etc/apt/preferences.d/nosnap.pref && \ -echo "Pin: release o=Ubuntu" >> /etc/apt/preferences.d/nosnap.pref && \ -echo "Pin-Priority: -1" >> /etc/apt/preferences.d/nosnap.pref && \ -echo "" >> /etc/apt/preferences.d/nosnap.pref && \ -echo "Package: firefox" >> /etc/apt/preferences.d/nosnap.pref && \ -echo "Pin: release o=LP-PPA-mozillateam" >> /etc/apt/preferences.d/nosnap.pref && \ -echo "Pin-Priority: 1000" >> /etc/apt/preferences.d/nosnap.pref -' || print_error "Failed to create APT preference file." - -# Now install Firefox after setting preferences -print_status "Installing Firefox from Mozilla PPA..." -chroot "$CHROOT_DIR" apt install -y firefox || print_error "Failed to install Firefox from PPA" - -# Enhanced system configuration -print_status "Configuring system settings..." -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..." -chroot "$CHROOT_DIR" useradd -m -s /bin/bash particle || print_error "Failed to create particle user." -chroot "$CHROOT_DIR" echo 'particle:particle' | chpasswd || print_error "Failed to set particle password." -chroot "$CHROOT_DIR" usermod -aG sudo particle || print_error "Failed to add particle to sudo group." -chroot "$CHROOT_DIR" bash -c "echo 'particle ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/particle" || print_error "Failed to create sudoers file for particle." -chroot "$CHROOT_DIR" chmod 0440 /etc/sudoers.d/particle || print_error "Failed to set permissions for sudoers file." - -# Enhanced service configuration -print_status "Enabling essential services..." -chroot "$CHROOT_DIR" systemctl enable sddm NetworkManager ssh || print_error "Failed to enable essential services." -chroot "$CHROOT_DIR" systemctl disable apt-daily.timer apt-daily-upgrade.timer || print_error "Failed to disable apt daily timers." - -# Enhanced apt-ostree configuration -print_status "Configuring apt-ostree..." -chroot "$CHROOT_DIR" mkdir -p /etc/apt-ostree || print_error "Failed to create apt-ostree config directory." -chroot "$CHROOT_DIR" echo 'ref: particleos/desktop/1.0.0' > /etc/apt-ostree/ref || print_error "Failed to configure apt-ostree ref." - -# Clean up -print_status "Cleaning APT caches and temporary files in chroot..." -chroot "$CHROOT_DIR" apt clean || print_error "Failed to clean apt cache in chroot." -chroot "$CHROOT_DIR" rm -rf /var/lib/apt/lists/* /tmp/* || print_error "Failed to remove apt lists or tmp files in chroot." - -print_success "Base system configured" - -print_header "Phase 3: Create ISO" - -# Unmount filesystems from chroot before creating squashfs -cleanup_chroot_mounts - -# 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 - more robust lookup -print_status "Copying kernel and initramfs..." -# Find the latest kernel and initrd -KERNEL_PATH=$(find "$CHROOT_DIR/boot" -maxdepth 1 -name "vmlinuz-*" | sort -V | tail -n 1) -INITRD_PATH=$(find "$CHROOT_DIR/boot" -maxdepth 1 -name "initrd.img-*" | sort -V | tail -n 1) - -if [ -z "$KERNEL_PATH" ]; then - print_error "Kernel (vmlinuz-*) not found in $CHROOT_DIR/boot." -fi -if [ -z "$INITRD_PATH" ]; then - print_error "Initrd (initrd.img-*) not found in $CHROOT_DIR/boot." -fi - -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." -print_success "Kernel and initramfs copied." - -# Create filesystem manifest -print_status "Creating filesystem manifest..." -chroot "$CHROOT_DIR" dpkg-query -W --showformat='${Package} ${Version}\n' > "/filesystem.manifest.tmp" || print_error "Failed to create filesystem manifest in chroot." -mv "/filesystem.manifest.tmp" "$ISO_DIR/casper/filesystem.manifest" || print_error "Failed to move filesystem manifest." -print_success "Filesystem manifest created." - -# Create filesystem size -print_status "Calculating filesystem size..." -du -sx --block-size=1 "$CHROOT_DIR" | cut -f1 > "$ISO_DIR/casper/filesystem.size" || print_error "Failed to create filesystem.size." -print_success "Filesystem size calculated." - -# Create squashfs -print_status "Creating squashfs filesystem..." -# Ensure chroot mounts are truly clean before this step -if mountpoint -q "$CHROOT_DIR/dev" || mountpoint -q "$CHROOT_DIR/run" || mountpoint -q "$CHROOT_DIR/proc" || mountpoint -q "$CHROOT_DIR/sys"; then - print_error "Chroot mounts are still active before mksquashfs. This should not happen." -fi -mksquashfs "$CHROOT_DIR" "$ISO_DIR/casper/filesystem.squashfs" -comp xz -e boot || print_error "Failed to create squashfs filesystem." -print_success "Squashfs created." - -# Create GRUB configuration -cat > "$ISO_DIR/boot/grub/grub.cfg" << 'GRUBEOF' -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 -} -GRUBEOF -print_success "GRUB configuration created." - - -# Create ISOLINUX configuration -cat > "$ISO_DIR/isolinux/isolinux.cfg" << 'ISOLINUXEOF' -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 --- -ISOLINUXEOF -print_success "ISOLINUX configuration created." - -# Copy ISOLINUX boot files - more robust lookup -print_status "Copying ISOLINUX boot files..." -ISOLINUX_BIN_HOST_PATH=$(find /usr/lib/syslinux -name isolinux.bin -print -quit || find /usr/lib/ISOLINUX -name isolinux.bin -print -quit) -BOOT_CAT_HOST_PATH=$(find /usr/lib/syslinux -name boot.cat -print -quit || find /usr/lib/ISOLINUX -name boot.cat -print -quit) -ISOHDPFX_BIN_HOST_PATH=$(find /usr/lib/syslinux -name isohdpfx.bin -print -quit || find /usr/lib/ISOLINUX -name isohdpfx.bin -print -quit) - -if [ -z "$ISOLINUX_BIN_HOST_PATH" ]; then print_error "isolinux.bin not found. Install isolinux."; fi -if [ -z "$BOOT_CAT_HOST_PATH" ]; then print_error "boot.cat not found. Install isolinux."; fi -if [ -z "$ISOHDPFX_BIN_HOST_PATH" ]; then print_error "isohdpfx.bin not found. Install isolinux."; fi - - -cp "$ISOLINUX_BIN_HOST_PATH" "$ISO_DIR/isolinux/" || print_error "Failed to copy isolinux.bin." -cp "$BOOT_CAT_HOST_PATH" "$ISO_DIR/isolinux/" || print_error "Failed to copy boot.cat." -print_success "ISOLINUX boot files copied." - -# Create EFI boot image (for UEFI) using grub-mkimage -print_status "Creating EFI boot image..." -GRUB_EFI_MODULES_DIR="/usr/lib/grub/x86_64-efi" -if [ ! -d "$GRUB_EFI_MODULES_DIR" ]; then - print_error "GRUB EFI modules directory not found at $GRUB_EFI_MODULES_DIR. Cannot create EFI boot image. Check grub-efi-amd64-bin installation." -fi - -# Ensure EFI/BOOT directory exists -mkdir -p "$ISO_DIR/EFI/BOOT" || print_error "Failed to create EFI/BOOT directory" - -# Create EFI boot image directly (bootx64.efi) -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 loopback_luks test configfile search loadenv \ - efi_gop efi_uga all_video gfxterm_menu gfxterm_background \ - chain btrfs zfs iso9660 || print_error "Failed to create EFI boot image (bootx64.efi)." - -# Create a dummy efi.img file for xorriso compatibility -# xorriso -e option expects an El Torito boot image, which is sometimes a separate file. -# Modern UEFI primarily looks for EFI/BOOT/bootx64.efi directly, but this satisfies xorriso. -dd if=/dev/zero of="$ISO_DIR/boot/grub/efi.img" bs=1M count=1 2>/dev/null || print_error "Failed to create dummy efi.img" - -print_success "EFI boot image created." - - -# Create ISO -print_status "Creating bootable ISO using xorriso..." -xorriso -as mkisofs \ - -o "$OUTPUT_DIR/${PROJECT_NAME}-${VERSION}.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 boot/grub/efi.img -no-emul-boot \ - -isohybrid-mbr "$ISOHDPFX_BIN_HOST_PATH" \ - -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}-${VERSION}.iso" - -# Clean up temporary directories within the container -print_status "Cleaning up temporary directories in container..." -rm -rf "$CHROOT_DIR" "$ISO_DIR" || print_error "Failed to remove temporary directories in container." - -print_header "Container Build Complete!" -echo "๐ŸŽ‰ ParticleOS ISO built successfully in container!" -echo "๐Ÿ“ Location: $OUTPUT_DIR/${PROJECT_NAME}-${VERSION}.iso" -EOF - - chmod +x "$BUILD_DIR/build-in-container.sh" - print_success "Container build script created" -} - -# Build the container image -build_container_image() { - print_header "Phase 5: Build Container Image" - - print_status "Building container image '$IMAGE_NAME'..." - cd "$BUILD_DIR" - - # Use || print_error instead of full if/else for cleaner code - $PODMAN_CMD build -t "$IMAGE_NAME" . || print_error "Failed to build container image '$IMAGE_NAME'." - - print_success "Container image built successfully" -} - -# Run the build in the container -run_container_build() { - print_header "Phase 6: Run Build in Container" - - print_status "Starting build in isolated container '$CONTAINER_NAME'..." - - # Run the container with volume mounts - # Added --privileged for mmdebstrap/chroot/mount/mksquashfs within container. - # --userns=host for better compatibility with privileged operations in some cases. - $PODMAN_CMD run \ - --name "$CONTAINER_NAME" \ - --rm \ - --privileged \ - --userns=host \ - -v "$OUTPUT_DIR:/output:Z" \ - "$IMAGE_NAME" || print_error "Container build failed. Check container logs for details." - - print_success "Container build completed successfully" -} - -# Cleanup function (for host-side Podman resources) -cleanup() { - print_status "Cleaning up Podman container and image..." - - # Remove container if it exists - if $PODMAN_CMD container exists "$CONTAINER_NAME" 2>/dev/null; then - print_status "Removing container: $CONTAINER_NAME" - $PODMAN_CMD container rm -f "$CONTAINER_NAME" 2>/dev/null || true - fi - - # Remove image if it exists. Be careful here, sometimes you want to keep the image. - # For a clean build process, removing it ensures fresh start next time. - if $PODMAN_CMD image exists "$IMAGE_NAME" 2>/dev/null; then - print_status "Removing image: $IMAGE_NAME" - $PODMAN_CMD image rm -f "$IMAGE_NAME" 2>/dev/null || true - fi - - print_success "Podman resources cleaned." -} - -# Signal trap for cleanup -trap cleanup EXIT INT TERM - -# Main execution -main() { - echo "๐Ÿš€ ParticleOS ISO Builder (Podman) - HARDENED ULTRA SAFE VERSION" - echo "==============================================================" - echo "Project: $PROJECT_NAME" - echo "Version: $VERSION" - echo "Build Directory: $BUILD_DIR" - echo "Tool: Podman (Containerized)" - echo "" - echo "๐Ÿ›ก๏ธ This build runs in a completely isolated container" - echo "๐Ÿ›ก๏ธ Your host system cannot be affected by this build" - echo "๐Ÿ›ก๏ธ Includes all hardening features from the analysis" - echo "๐Ÿ›ก๏ธ Firefox installed as DEB package (no snap)" - echo "" - - # Call cleanup explicitly at the start to ensure a clean slate before attempting new build - cleanup # This will also remove image from previous failed runs. - - check_podman - check_prerequisites - clean_build # This cleans host-side directories, but cleanup() handles Podman resources. - create_dockerfile - create_container_build_script - build_container_image - run_container_build - - print_header "Build Complete!" - echo "" - echo "๐ŸŽ‰ ParticleOS ISO built successfully!" - echo "๐Ÿ“ Location: $OUTPUT_DIR/${PROJECT_NAME}-${VERSION}.iso" - echo "" - echo "๐Ÿงช Test the ISO:" - echo " qemu-system-x86_64 -m 4G -enable-kvm \\" - echo " -cdrom $OUTPUT_DIR/${PROJECT_NAME}-${VERSION}.iso \\" - echo " -boot d" - echo "" - echo "๐Ÿ›ก๏ธ Your host system is completely safe!" -} - -# Run main function -main "$@" -``` - ------ - -### Summary of Changes and Confidence - - * **Host Script**: Updated the final output messages to consistently refer to `particleos-1.0.0.iso`. - * **`build-in-container.sh`**: - * Added a more robust check for the Forgejo GPG key by attempting to `gpg --dearmor` it. - * Improved the check for `ostree` and `bootc` packages in the Forgejo repository by directly inspecting `Packages.gz`. - * Confirmed the logic for installing specific `ostree`/`bootc` versions then falling back to latest. - * Verified the Firefox installation after setting APT preferences. - * Kept the current EFI boot image strategy, which is generally functional for modern UEFI systems even if the `efi.img` is a dummy. - -This script looks **very solid**. The combination of Podman isolation, thorough error checking, explicit GPG key handling, and the fallback logic for `ostree`/`bootc` versions makes it robust. - -Give it a run\! If you encounter any issues, the detailed logging you've implemented will be invaluable for pinpointing exactly where the problem lies. -``` \ No newline at end of file