Successfully build ParticleOS ISO with ostree, bootc, and apt-ostree
This commit is contained in:
parent
370df6255d
commit
9b3d530b9d
12 changed files with 1524 additions and 1900 deletions
924
todo.md
924
todo.md
|
|
@ -1,5 +1,90 @@
|
|||
# ParticleOS Installer - TODO List
|
||||
Just remove completed tasks to keep the file tidy.
|
||||
|
||||
Remove completed tasks to keep the file tidy.
|
||||
|
||||
We are building an installer for an atomic desktop based on Ubuntu
|
||||
|
||||
bootc is REQUIRED
|
||||
|
||||
Move to using calmares?
|
||||
see file calmares_plan.md
|
||||
|
||||
```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
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
Discuss adding apt-cacher-ng support
|
||||
|
||||
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
|
||||
|
|
@ -122,4 +207,839 @@ Discuss removing AppArmor and use SELinux?
|
|||
- **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
|
||||
- **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.
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue