diff --git a/INSTALLATION.md b/INSTALLATION.md index 020db62..4df0a0b 100644 --- a/INSTALLATION.md +++ b/INSTALLATION.md @@ -34,7 +34,7 @@ The installation script installs the following tools to `/usr/local/bin/`: | `composefs-alternative.sh` | `composefs` | ComposeFS image management | | `bootc-alternative.sh` | `bootc` | Bootable container management | | `bootupd-alternative.sh` | `bootupd` | Bootloader management | -| `orchestrator.sh` | `particle-orchestrator` | System orchestration | +| `../orchestrator/orchestrator.sh` | `particle-orchestrator` | System orchestration | | `oci-integration.sh` | `particle-oci` | OCI integration | | `particle-logrotate.sh` | `particle-logrotate` | Log rotation management | diff --git a/SCRIPT_INVENTORY.md b/SCRIPT_INVENTORY.md index 907ab10..cc3e552 100644 --- a/SCRIPT_INVENTORY.md +++ b/SCRIPT_INVENTORY.md @@ -5,11 +5,11 @@ This document catalogs all scripts in the tools directory and their purposes. ## Core Scripts (KEEP) ### Main Tools -- **apt-layer.sh** - Main apt-layer tool (compiled from scriptlets) +- **apt-layer.sh** - Main apt-layer tool. Mimicks rpm-ostree but for deb packages. (compiled from scriptlets) - **composefs-alternative.sh** - ComposeFS management tool (compiled from scriptlets) - **bootc-alternative.sh** - BootC management tool (compiled from scriptlets) - **bootupd-alternative.sh** - BootUpd management tool (compiled from scriptlets) -- **orchestrator.sh** - Main orchestrator for all tools +- **../orchestrator/orchestrator.sh** - Main orchestrator for all tools (moved to orchestrator directory) - **particle-config.sh** - Configuration file for all tools - **particle-logrotate.sh** - Log rotation configuration - **oci-integration.sh** - OCI container integration diff --git a/install-particle-os.sh b/install-particle-os.sh index 93429fa..4f5bd52 100644 --- a/install-particle-os.sh +++ b/install-particle-os.sh @@ -22,7 +22,7 @@ declare -A SCRIPTS=( ["composefs-alternative.sh"]="composefs" ["bootc-alternative.sh"]="bootc" ["bootupd-alternative.sh"]="bootupd" - ["orchestrator.sh"]="particle-orchestrator" + ["../orchestrator/orchestrator.sh"]="particle-orchestrator" ["oci-integration.sh"]="particle-oci" ["particle-logrotate.sh"]="particle-logrotate" ) diff --git a/orchestrator.sh b/orchestrator.sh deleted file mode 100644 index a70b332..0000000 --- a/orchestrator.sh +++ /dev/null @@ -1,824 +0,0 @@ -#!/bin/bash - -# orchestrator.sh - Particle-OS System Orchestrator -# This script acts as the central hub for managing an immutable Particle-OS system -# by orchestrating operations across apt-layer.sh, composefs-alternative.sh, -# fsverity-alternative.sh, and bootupd-alternative.sh. - -set -euo pipefail - -# --- Configuration --- -# Load Particle-OS configuration if available -if [[ -f "/usr/local/etc/particle-config.sh" ]]; then - source "/usr/local/etc/particle-config.sh" -else - # Fallback configuration if particle-config.sh is not available - PARTICLE_WORKSPACE="/var/lib/particle-os" - PARTICLE_CONFIG_DIR="/usr/local/etc/particle-os" - PARTICLE_LOG_DIR="/var/log/particle-os" - PARTICLE_CACHE_DIR="/var/cache/particle-os" -fi - -# Define the root directory for all Particle-OS related data -PARTICLE_OS_ROOT="${PARTICLE_WORKSPACE:-/var/lib/particle-os}" - -# Paths to your alternative scripts (standardized installation) -APT_LAYER_SCRIPT="/usr/local/bin/apt-layer" -COMPOSEFS_SCRIPT="/usr/local/bin/composefs" -FSVERITY_SCRIPT="fsverity" -BOOTUPD_SCRIPT="/usr/local/bin/bootupd" - -# Transaction log and state files (centralized for the orchestrator) -TRANSACTION_LOG="${PARTICLE_LOG_DIR:-/var/log/particle-os}/orchestrator_transaction.log" -TRANSACTION_STATE="${PARTICLE_WORKSPACE:-/var/lib/particle-os}/orchestrator_transaction.state" - -# --- Global Transaction State Variables --- -TRANSACTION_ID="" -TRANSACTION_OPERATION="" -TRANSACTION_TARGET="" -TRANSACTION_PHASE="" -TRANSACTION_BACKUP_PATH="" # Path to a backup created during a critical step -TRANSACTION_TEMP_DIRS=() # List of temporary directories to clean up - -# --- Colors for Output --- -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -PURPLE='\033[0;35m' -CYAN='\033[0;36m' -NC='\033[0m' # No Color - -# --- Logging Functions --- -log_info() { - echo -e "${BLUE}[INFO]${NC} [$(date +'%H:%M:%S')] $1" | tee -a "$TRANSACTION_LOG" -} - -log_debug() { - echo -e "${YELLOW}[DEBUG]${NC} [$(date +'%H:%M:%S')] $1" | tee -a "$TRANSACTION_LOG" -} - -log_error() { - echo -e "${RED}[ERROR]${NC} [$(date +'%H:%M:%S')] $1" | tee -a "$TRANSACTION_LOG" -} - -log_warning() { - echo -e "${YELLOW}[WARNING]${NC} [$(date +'%H:%M:%S')] $1" | tee -a "$TRANSACTION_LOG" -} - -log_success() { - echo -e "${GREEN}[SUCCESS]${NC} [$(date +'%H:%M:%S')] $1" | tee -a "$TRANSACTION_LOG" -} - -log_orchestrator() { - echo -e "${PURPLE}[ORCHESTRATOR]${NC} [$(date +'%H:%M:%S')] $1" | tee -a "$TRANSACTION_LOG" -} - -# --- Transaction Management Functions --- - -# Starts a new transaction -start_transaction() { - local operation="$1" - local target="$2" - - TRANSACTION_ID=$(date +%Y%m%d_%H%M%S)_$$ - TRANSACTION_OPERATION="$operation" - TRANSACTION_TARGET="$target" - TRANSACTION_PHASE="started" - TRANSACTION_BACKUP_PATH="" - TRANSACTION_TEMP_DIRS=() - - log_orchestrator "Starting transaction $TRANSACTION_ID: $operation -> $target" - - # Write initial state to file - save_transaction_state - - log_orchestrator "Transaction $TRANSACTION_ID started successfully." -} - -# Updates the current phase of the transaction -update_transaction_phase() { - local phase="$1" - TRANSACTION_PHASE="$phase" - log_orchestrator "Transaction $TRANSACTION_ID phase: $phase" - save_transaction_state -} - -# Saves the current transaction state to file -save_transaction_state() { - cat > "$TRANSACTION_STATE" << EOF -TRANSACTION_ID=$TRANSACTION_ID -OPERATION=$TRANSACTION_OPERATION -TARGET=$TRANSACTION_TARGET -PHASE=$TRANSACTION_PHASE -BACKUP_PATH=$TRANSACTION_BACKUP_PATH -TEMP_DIRS=${TRANSACTION_TEMP_DIRS[*]} -START_TIME=$(date -Iseconds) -EOF -} - -# Clears the transaction state files -clear_transaction_state() { - TRANSACTION_ID="" - TRANSACTION_OPERATION="" - TRANSACTION_TARGET="" - TRANSACTION_PHASE="" - TRANSACTION_BACKUP_PATH="" - TRANSACTION_TEMP_DIRS=() - rm -f "$TRANSACTION_STATE" - log_orchestrator "Transaction state cleared." -} - -# Commits the transaction, indicating success -commit_transaction() { - if [[ -n "$TRANSACTION_ID" ]]; then - update_transaction_phase "committed" - echo "END_TIME=$(date -Iseconds)" >> "$TRANSACTION_LOG" - echo "STATUS=success" >> "$TRANSACTION_LOG" - log_success "Transaction $TRANSACTION_ID completed successfully." - clear_transaction_state - fi -} - -# Rolls back the transaction in case of failure -rollback_transaction() { - if [[ -n "$TRANSACTION_ID" ]]; then - log_warning "Rolling back transaction $TRANSACTION_ID (Operation: $TRANSACTION_OPERATION, Phase: $TRANSACTION_PHASE)" - - # --- Rollback logic based on phase --- - case "$TRANSACTION_OPERATION" in - "install_packages"|"rebase_system") - case "$TRANSACTION_PHASE" in - "build_rootfs") - log_info "Build was interrupted. Temporary build directory will be cleaned." - # No specific rollback needed beyond temp dir cleanup - ;; - "create_composefs_image") - log_info "ComposeFS image creation failed. Attempting to remove partial image." - # If image creation failed, try to remove the partial image - # TODO: composefs-alternative.sh needs a 'remove-partial' or 'cleanup-failed-image' command - # For now, rely on cleanup_on_exit for temp dirs. - ;; - "verify_image") - log_info "Image verification failed. Image might be present but untrusted." - # No specific rollback needed beyond cleanup_on_exit - ;; - "deploy_bootloader") - log_info "Bootloader deployment failed. Attempting to revert boot entry." - # TODO: bootupd-alternative.sh needs a 'rollback' or 'revert-default' command - # This would revert the default boot entry to the previous known good one. - # For now, user might need manual intervention or rely on previous boot entries. - ;; - *) - log_warning "No specific rollback action defined for phase: $TRANSACTION_PHASE" - ;; - esac - ;; - # Add more operation types here - *) - log_warning "No specific rollback action defined for operation: $TRANSACTION_OPERATION" - ;; - esac - - # Clean up all temporary directories regardless of phase - for temp_dir in "${TRANSACTION_TEMP_DIRS[@]}"; do - if [[ -d "$temp_dir" ]]; then - log_debug "Cleaning up temporary directory: $temp_dir" - rm -rf "$temp_dir" 2>/dev/null || true - fi - done - - # Update transaction log - echo "END_TIME=$(date -Iseconds)" >> "$TRANSACTION_LOG" - echo "STATUS=rolled_back" >> "$TRANSACTION_LOG" - - log_orchestrator "Transaction $TRANSACTION_ID rolled back." - clear_transaction_state - fi -} - -# Checks for incomplete transactions on startup and prompts user -check_incomplete_transactions() { - if [[ -f "$TRANSACTION_STATE" ]]; then - log_warning "Found incomplete transaction state from previous run." - - # Source the state file to load transaction details - source "$TRANSACTION_STATE" - - log_info "Incomplete transaction ID: $TRANSACTION_ID" - log_info "Operation: $TRANSACTION_OPERATION" - log_info "Target: $TRANSACTION_TARGET" - log_info "Last completed phase: $TRANSACTION_PHASE" - - echo - echo "Options:" - echo "1. Attempt to resume transaction (if supported for this phase)" - echo "2. Rollback transaction (discard changes and clean up)" - echo "3. Clear transaction state (manual cleanup might be required)" - echo "4. Exit (resolve manually)" - echo - read -p "Choose option (1-4): " choice - - case $choice in - 1) - log_info "Attempting to resume transaction..." - # Resume logic needs to be implemented per operation and phase - log_error "Resume functionality not fully implemented for this operation/phase. Please choose another option." - exit 1 # For now, force user to choose another option - ;; - 2) - log_info "Rolling back transaction..." - rollback_transaction - ;; - 3) - log_info "Clearing transaction state..." - clear_transaction_state - ;; - 4) - log_error "Exiting due to incomplete transaction. Please resolve manually." - exit 1 - ;; - *) - log_error "Invalid choice. Exiting." - exit 1 - ;; - esac - fi -} - -# --- Helper Functions --- - -# Check if running as root -check_root() { - if [[ $EUID -ne 0 ]]; then - log_error "This script must be run as root." - exit 1 - fi -} - -# Check if all required scripts exist and are executable -check_script_dependencies() { - log_orchestrator "Checking core Particle-OS script dependencies..." - - # Check if Particle-OS configuration is available - if [[ ! -f "/usr/local/etc/particle-config.sh" ]]; then - log_warning "Particle-OS configuration not found at /usr/local/etc/particle-config.sh" - log_info "Using fallback configuration paths" - fi - - local missing=() - - # Check Particle-OS scripts - for script in "$APT_LAYER_SCRIPT" "$COMPOSEFS_SCRIPT" "$BOOTUPD_SCRIPT"; do - if [[ ! -f "$script" ]]; then - missing+=("$script (file not found)") - elif [[ ! -x "$script" ]]; then - missing+=("$script (not executable)") - fi - done - - # Check system fsverity command - if ! command -v fsverity >/dev/null 2>&1; then - missing+=("fsverity (system command not found)") - log_info "fsverity not found. Install with: sudo apt install -y fsverity" - fi - - if [[ ${#missing[@]} -gt 0 ]]; then - log_error "Missing or non-executable core scripts: ${missing[*]}" - log_error "Please ensure all Particle-OS alternative scripts are in /usr/local/bin and are executable." - log_error "For fsverity, install with: sudo apt install -y fsverity" - log_error "Run 'sudo apt-layer --init' to initialize the Particle-OS environment." - exit 1 - fi - log_success "All core script dependencies found and are executable." -} - -# Initialize the Particle-OS workspace -init_workspace() { - log_orchestrator "Initializing Particle-OS workspace..." - - # Create main workspace directory - mkdir -p "$PARTICLE_OS_ROOT" - - # Create subdirectories using Particle-OS configuration - mkdir -p "${PARTICLE_BUILD_DIR:-$PARTICLE_OS_ROOT/build}" - mkdir -p "${PARTICLE_TEMP_DIR:-$PARTICLE_OS_ROOT/temp}" - mkdir -p "${PARTICLE_LAYERS_DIR:-$PARTICLE_OS_ROOT/layers}" - mkdir -p "${PARTICLE_LOG_DIR:-/var/log/particle-os}" - mkdir -p "${PARTICLE_CACHE_DIR:-/var/cache/particle-os}" - - # Ensure transaction log directory exists and is writable - mkdir -p "$(dirname "$TRANSACTION_LOG")" - touch "$TRANSACTION_LOG" - chmod 644 "$TRANSACTION_LOG" # Allow non-root to read logs - - log_success "Workspace initialized at $PARTICLE_OS_ROOT" -} - -# Run a sub-script and check its exit code -run_script() { - local script_path="$1" - shift - local script_args=("$@") - - log_debug "Running: $script_path ${script_args[*]}" - - # Execute the script, redirecting its output to our log - if ! "$script_path" "${script_args[@]}" >> "$TRANSACTION_LOG" 2>&1; then - log_error "Script '$script_path' failed with arguments: ${script_args[*]}" - return 1 - fi - log_debug "Script '$script_path' completed successfully." - return 0 -} - -# Get the current booted Particle-OS image ID -get_current_particle_os_image_id() { - # TODO: This needs to be implemented based on how bootupd-alternative.sh - # tracks the currently booted composefs image. - # For now, return a placeholder or an error if not implemented. - log_warning "get_current_particle_os_image_id is a placeholder. Returning dummy ID." - # Dummy IDs reflecting the new naming conventions - # Particle-OS Corona: KDE Plasma desktop - # Particle-OS Apex: GNOME desktop - echo "particle-os-base-24.04-corona" -} - -# Enhanced package installation with dpkg support -install_packages_enhanced() { - local base_image_name="$1" - local use_dpkg="${2:-false}" - shift 2 - local packages=("$@") - - if [[ -z "$base_image_name" || ${#packages[@]} -eq 0 ]]; then - log_error "Usage: install [--dpkg] [package2]..." - exit 1 - fi - - local new_image_name="particle-os-custom-$(date +%Y%m%d%H%M%S)" - local temp_rootfs_dir="${PARTICLE_BUILD_DIR:-$PARTICLE_OS_ROOT/build}/temp-rootfs-${TRANSACTION_ID}" - - log_orchestrator "Starting enhanced package installation for '$new_image_name' based on '$base_image_name'." - if [[ "$use_dpkg" == "true" ]]; then - log_info "Using dpkg-based installation for better performance" - fi - start_transaction "install_packages_enhanced" "$new_image_name" - - # Add temp_rootfs_dir to cleanup list - TRANSACTION_TEMP_DIRS+=("$temp_rootfs_dir") - mkdir -p "$temp_rootfs_dir" - - # Phase 1: Build the new root filesystem with packages using apt-layer.sh - update_transaction_phase "build_rootfs" - log_orchestrator "Building new root filesystem with packages: ${packages[*]}..." - - # Check if base image exists in composefs-alternative.sh - if ! "$COMPOSEFS_SCRIPT" list-images | grep -q "$base_image_name"; then - log_error "Base image '$base_image_name' not found in composefs-alternative.sh." - exit 1 - fi - - # Mount the base image to get its content - local base_mount_point="${PARTICLE_TEMP_DIR:-$PARTICLE_OS_ROOT/temp}/temp_base_mount_${TRANSACTION_ID}" - TRANSACTION_TEMP_DIRS+=("$base_mount_point") - mkdir -p "$base_mount_point" - if ! run_script "$COMPOSEFS_SCRIPT" mount "$base_image_name" "$base_mount_point"; then - log_error "Failed to mount base image '$base_image_name'." - exit 1 - fi - - # Copy base image content to temp_rootfs_dir - log_info "Copying base image content to temporary build directory..." - if ! rsync -a "$base_mount_point/" "$temp_rootfs_dir/"; then - log_error "Failed to copy base image content." - exit 1 - fi - - # Unmount base image (cleanup) - if ! run_script "$COMPOSEFS_SCRIPT" unmount "$base_mount_point"; then - log_warning "Failed to unmount temporary base image mount point: $base_mount_point" - fi - - # Install packages using the appropriate method - if [[ "$use_dpkg" == "true" ]]; then - log_info "Using dpkg-based package installation..." - # Use apt-layer's dpkg functionality - if ! run_script "$APT_LAYER_SCRIPT" --dpkg-install "${packages[@]}"; then - log_error "dpkg-based package installation failed." - exit 1 - fi - else - log_info "Using traditional apt-based package installation..." - # Traditional apt-get installation - if ! chroot "$temp_rootfs_dir" apt-get update; then - log_error "apt-get update failed in chroot." - exit 1 - fi - if ! chroot "$temp_rootfs_dir" apt-get install -y "${packages[@]}"; then - log_error "apt-get install failed in chroot." - exit 1 - fi - chroot "$temp_rootfs_dir" apt-get clean - chroot "$temp_rootfs_dir" apt-get autoremove -y - fi - - log_success "Root filesystem built in: $temp_rootfs_dir" - - # Phase 2: Create the ComposeFS image from the new rootfs - update_transaction_phase "create_composefs_image" - log_orchestrator "Creating ComposeFS image '$new_image_name' from '$temp_rootfs_dir'..." - if ! run_script "$COMPOSEFS_SCRIPT" create "$new_image_name" "$temp_rootfs_dir"; then - log_error "Failed to create ComposeFS image." - exit 1 - fi - log_success "ComposeFS image '$new_image_name' created." - - # Phase 3: Verify and sign the new ComposeFS image - update_transaction_phase "verify_image" - log_orchestrator "Verifying and signing ComposeFS image '$new_image_name'..." - if ! run_script "$FSVERITY_SCRIPT" enable "${PARTICLE_IMAGES_DIR:-$PARTICLE_OS_ROOT/images}/$new_image_name" sha256; then - log_error "Failed to verify/sign ComposeFS image." - exit 1 - fi - log_success "ComposeFS image '$new_image_name' verified/signed." - - # Phase 4: Deploy the new image via the bootloader - update_transaction_phase "deploy_bootloader" - log_orchestrator "Deploying new image '$new_image_name' via bootloader..." - if ! run_script "$BOOTUPD_SCRIPT" set-default "$new_image_name"; then - log_error "Failed to deploy image via bootloader." - exit 1 - fi - log_success "Image '$new_image_name' deployed as default boot entry." - - # Commit the transaction - commit_transaction - log_orchestrator "Enhanced package installation and system update completed for '$new_image_name'." - log_info "A reboot is recommended to apply changes." -} - -# --- Core Orchestration Workflows --- - -# Installs packages by building a new rootfs, creating a composefs image, -# verifying it, and deploying it via the bootloader. -install_packages() { - local base_image_name="$1" - shift - local packages=("$@") - - if [[ -z "$base_image_name" || ${#packages[@]} -eq 0 ]]; then - log_error "Usage: install [package2]..." - exit 1 - fi - - local new_image_name="particle-os-custom-$(date +%Y%m%d%H%M%S)" - # Example for specific desktop images: - # local new_image_name="particle-os-corona-$(date +%Y%m%d%H%M%S)" # For KDE Plasma - # local new_image_name="particle-os-apex-$(date +%Y%m%d%H%M%S)" # For GNOME - local temp_rootfs_dir="${PARTICLE_BUILD_DIR:-$PARTICLE_OS_ROOT/build}/temp-rootfs-${TRANSACTION_ID}" - - log_orchestrator "Starting package installation for '$new_image_name' based on '$base_image_name'." - start_transaction "install_packages" "$new_image_name" - - # Add temp_rootfs_dir to cleanup list - TRANSACTION_TEMP_DIRS+=("$temp_rootfs_dir") - mkdir -p "$temp_rootfs_dir" - - # Phase 1: Build the new root filesystem with packages using apt-layer.sh - update_transaction_phase "build_rootfs" - log_orchestrator "Building new root filesystem with packages: ${packages[*]}..." - - # Check if base image exists in composefs-alternative.sh - if ! "$COMPOSEFS_SCRIPT" list-images | grep -q "$base_image_name"; then - log_error "Base image '$base_image_name' not found in composefs-alternative.sh." - exit 1 - fi - - # Mount the base image to get its content (simulating apt-layer's initial checkout) - local base_mount_point="${PARTICLE_TEMP_DIR:-$PARTICLE_OS_ROOT/temp}/temp_base_mount_${TRANSACTION_ID}" - TRANSACTION_TEMP_DIRS+=("$base_mount_point") - mkdir -p "$base_mount_point" - if ! run_script "$COMPOSEFS_SCRIPT" mount "$base_image_name" "$base_mount_point"; then - log_error "Failed to mount base image '$base_image_name'." - exit 1 - fi - - # Copy base image content to temp_rootfs_dir (this is where apt-layer would work) - log_info "Copying base image content to temporary build directory..." - if ! rsync -a "$base_mount_point/" "$temp_rootfs_dir/"; then - log_error "Failed to copy base image content." - exit 1 - fi - - # Unmount base image (cleanup) - if ! run_script "$COMPOSEFS_SCRIPT" unmount "$base_mount_point"; then - log_warning "Failed to unmount temporary base image mount point: $base_mount_point" - fi - - # Now, simulate apt-layer.sh installing packages into temp_rootfs_dir - log_info "Simulating package installation into $temp_rootfs_dir (chroot apt-get)..." - # This is a placeholder for apt-layer's actual package installation logic - # In reality, apt-layer.sh --build-rootfs would handle this. - if ! chroot "$temp_rootfs_dir" apt-get update; then - log_error "Simulated apt-get update failed in chroot." - exit 1 - fi - if ! chroot "$temp_rootfs_dir" apt-get install -y "${packages[@]}"; then - log_error "Simulated apt-get install failed in chroot." - exit 1 - fi - chroot "$temp_rootfs_dir" apt-get clean - chroot "$temp_rootfs_dir" apt-get autoremove -y - - log_success "Root filesystem built in: $temp_rootfs_dir" - - # Phase 2: Create the ComposeFS image from the new rootfs - update_transaction_phase "create_composefs_image" - log_orchestrator "Creating ComposeFS image '$new_image_name' from '$temp_rootfs_dir'..." - if ! run_script "$COMPOSEFS_SCRIPT" create "$new_image_name" "$temp_rootfs_dir"; then - log_error "Failed to create ComposeFS image." - exit 1 - fi - log_success "ComposeFS image '$new_image_name' created." - - # Phase 3: Verify and sign the new ComposeFS image - update_transaction_phase "verify_image" - log_orchestrator "Verifying and signing ComposeFS image '$new_image_name'..." - # TODO: fsverity-alternative.sh needs a command to verify a composefs image by name - # and potentially sign it. Assuming 'enable' can work on a created image. - if ! run_script "$FSVERITY_SCRIPT" enable "${PARTICLE_IMAGES_DIR:-$PARTICLE_OS_ROOT/images}/$new_image_name" sha256; then # Assuming fsverity works on the image dir - log_error "Failed to verify/sign ComposeFS image." - exit 1 - fi - log_success "ComposeFS image '$new_image_name' verified/signed." - - # Phase 4: Deploy the new image via the bootloader - update_transaction_phase "deploy_bootloader" - log_orchestrator "Deploying new image '$new_image_name' via bootloader..." - # TODO: bootupd-alternative.sh needs a command to register and set default - # a composefs image by name/ID. Assuming 'set-default' can take an image name. - if ! run_script "$BOOTUPD_SCRIPT" set-default "$new_image_name"; then - log_error "Failed to deploy image via bootloader." - exit 1 - fi - log_success "Image '$new_image_name' deployed as default boot entry." - - # Commit the transaction - commit_transaction - log_orchestrator "Package installation and system update completed for '$new_image_name'." - log_info "A reboot is recommended to apply changes." -} - -# Rebases the system to a new base image -rebase_system() { - local new_base_image="$1" - - if [[ -z "$new_base_image" ]]; then - log_error "Usage: rebase " - exit 1 - fi - - log_orchestrator "Starting rebase operation to '$new_base_image'." - start_transaction "rebase_system" "$new_base_image" - - # Phase 1: Check if new base image exists - update_transaction_phase "check_base_image" - log_orchestrator "Checking if new base image '$new_base_image' exists..." - if ! "$COMPOSEFS_SCRIPT" list-images | grep -q "$new_base_image"; then - log_error "New base image '$new_base_image' not found in composefs-alternative.sh." - exit 1 - fi - log_success "New base image '$new_base_image' found." - - # Phase 2: Get current layered packages and re-apply them on the new base - update_transaction_phase "reapply_layers" - log_orchestrator "Re-applying existing layers on top of '$new_base_image'..." - # This is highly complex. It would involve: - # 1. Identifying packages/changes in the *current* system's layered image. - # 2. Calling apt-layer.sh --build-rootfs with the new_base_image and these identified packages. - # This part needs significant design and implementation to handle package manifests. - log_warning "Re-applying layers is a complex feature and is not yet implemented." - log_warning "For now, rebase will just switch the base image, potentially losing layered packages." - - local temp_rootfs_dir="${PARTICLE_BUILD_DIR:-$PARTICLE_OS_ROOT/build}/temp-rebase-rootfs-${TRANSACTION_ID}" - TRANSACTION_TEMP_DIRS+=("$temp_rootfs_dir") - mkdir -p "$temp_rootfs_dir" - - # Mount the new base image to get its content - local base_mount_point="${PARTICLE_TEMP_DIR:-$PARTICLE_OS_ROOT/temp}/temp_rebase_base_mount_${TRANSACTION_ID}" - TRANSACTION_TEMP_DIRS+=("$base_mount_point") - mkdir -p "$base_mount_point" - if ! run_script "$COMPOSEFS_SCRIPT" mount "$new_base_image" "$base_mount_point"; then - log_error "Failed to mount new base image '$new_base_image'." - exit 1 - fi - - # Copy new base image content to temp_rootfs_dir - log_info "Copying new base image content to temporary rebase directory..." - if ! rsync -a "$base_mount_point/" "$temp_rootfs_dir/"; then - log_error "Failed to copy new base image content." - exit 1 - fi - - # Unmount base image (cleanup) - if ! run_script "$COMPOSEFS_SCRIPT" unmount "$base_mount_point"; then - log_warning "Failed to unmount temporary base image mount point: $base_mount_point" - fi - - # Phase 3: Create new ComposeFS image for the rebased system - update_transaction_phase "create_rebased_image" - local rebased_image_name="${new_base_image}-rebased-$(date +%Y%m%d%H%M%S)" - log_orchestrator "Creating rebased ComposeFS image '$rebased_image_name'..." - if ! run_script "$COMPOSEFS_SCRIPT" create "$rebased_image_name" "$temp_rootfs_dir"; then - log_error "Failed to create rebased ComposeFS image." - exit 1 - fi - log_success "Rebased ComposeFS image '$rebased_image_name' created." - - # Phase 4: Verify and sign the rebased image - update_transaction_phase "verify_rebased_image" - log_orchestrator "Verifying and signing rebased ComposeFS image '$rebased_image_name'..." - if ! run_script "$FSVERITY_SCRIPT" enable "${PARTICLE_IMAGES_DIR:-$PARTICLE_OS_ROOT/images}/$rebased_image_name" sha256; then - log_error "Failed to verify/sign rebased ComposeFS image." - exit 1 - fi - log_success "Rebased ComposeFS image '$rebased_image_name' verified/signed." - - # Phase 5: Deploy the rebased image via the bootloader - update_transaction_phase "deploy_rebased_bootloader" - log_orchestrator "Deploying rebased image '$rebased_image_name' via bootloader..." - if ! run_script "$BOOTUPD_SCRIPT" set-default "$rebased_image_name"; then - log_error "Failed to deploy rebased image via bootloader." - exit 1 - fi - log_success "Image '$rebased_image_name' deployed as default boot entry." - - # Commit the transaction - commit_transaction - log_orchestrator "System rebased to '$rebased_image_name'. A reboot is recommended." -} - -# Rolls back the system to a previous deployment -rollback_system() { - local target_image_name="$1" # Optional: specific image to rollback to - - log_orchestrator "Starting system rollback operation." - start_transaction "rollback_system" "${target_image_name:-last_good}" - - update_transaction_phase "identify_target" - log_orchestrator "Identifying rollback target..." - # TODO: bootupd-alternative.sh needs a 'list-deployments' or 'get-previous' command - # to identify the previous bootable image or a specific target image. - - local rollback_target_id - if [[ -n "$target_image_name" ]]; then - rollback_target_id="$target_image_name" - if ! "$COMPOSEFS_SCRIPT" list-images | grep -q "$rollback_target_id"; then - log_error "Rollback target image '$rollback_target_id' not found." - exit 1 - fi - else - # Simulate getting previous good image from bootupd-alternative.sh - log_warning "No specific target provided. Simulating rollback to previous image." - # This would be a call to bootupd-alternative.sh to get the *previous* boot entry - # For now, we'll just pick a dummy previous one. - rollback_target_id="particle-os-dummy-previous-image" # Placeholder - log_info "Identified previous image for rollback: $rollback_target_id" - fi - - update_transaction_phase "deploy_rollback" - log_orchestrator "Setting bootloader default to '$rollback_target_id'..." - if ! run_script "$BOOTUPD_SCRIPT" set-default "$rollback_target_id"; then - log_error "Failed to set bootloader default for rollback." - exit 1 - fi - log_success "Bootloader default set to '$rollback_target_id'." - - # Commit the transaction - commit_transaction - log_orchestrator "System rollback initiated. A reboot is required to complete." -} - -# Shows the current status of the Particle-OS system -show_status() { - log_orchestrator "Gathering Particle-OS system status..." - - echo "--- Particle-OS System Status ---" - echo - - echo "1. Orchestrator Transaction Status:" - if [[ -f "$TRANSACTION_STATE" ]]; then - echo " Active: Yes" - cat "$TRANSACTION_STATE" | sed 's/^/ /' - else - echo " Active: No" - fi - echo - - echo "2. ComposeFS Images Status:" - run_script "$COMPOSEFS_SCRIPT" list-images - echo - - echo "3. ComposeFS Mounts Status:" - run_script "$COMPOSEFS_SCRIPT" list-mounts - echo - - echo "4. ComposeFS Layers Status:" - run_script "$COMPOSEFS_SCRIPT" list-layers - echo - - echo "5. Bootloader Status (via bootupd-alternative.sh):" - run_script "$BOOTUPD_SCRIPT" status - echo - - echo "6. File Integrity Status (via fsverity-alternative.sh):" - run_script "$FSVERITY_SCRIPT" status - echo - - echo "7. Live Overlay Status (via apt-layer.sh):" - # TODO: apt-layer.sh needs a 'status' command for live overlay - run_script "$APT_LAYER_SCRIPT" --live-overlay status - echo - - log_orchestrator "Status report complete." -} - -# --- Main Command Dispatch --- -main() { - check_root - init_workspace - check_script_dependencies - - # Check for incomplete transactions on startup - check_incomplete_transactions - - local command="${1:-help}" - shift || true # Shift arguments, allow no arguments for 'help' - - case "$command" in - "install") - install_packages "$@" - ;; - "install-dpkg") - # Enhanced installation with dpkg support - local base_image="$1" - shift - install_packages_enhanced "$base_image" "true" "$@" - ;; - "rebase") - rebase_system "$@" - ;; - "rollback") - rollback_system "$@" - ;; - "status") - show_status - ;; - "help"|"-h"|"--help") - cat << EOF -Particle-OS System Orchestrator - -This script orchestrates the Particle-OS alternative tools to provide an atomic, immutable -Ubuntu experience similar to Fedora Silverblue/Kinoite. - -Available Desktop Images: - - Particle-OS Corona (KDE Plasma): A radiant and expansive desktop experience. - - Particle-OS Apex (GNOME): A nimble, powerful, and adaptable desktop for power users. - - Particle-OS Base: Minimal base system for custom builds. - -Usage: $0 [options] - -Commands: - install [package2]... Install packages and create new system image - install-dpkg [package2]... Install packages using dpkg (faster, more controlled) - rebase Rebase the system to a new base image - rollback [target_image_name] Rollback to a previous system deployment - status Show comprehensive Particle-OS system status - help Show this help message - -Examples: - $0 install particle-os-base-24.04 firefox steam # Install packages on a base image - $0 install-dpkg particle-os-base-24.04 firefox steam # Install packages using dpkg (optimized) - $0 rebase particle-os-base-25.04 # Rebase to a new Particle-OS base - $0 rollback # Rollback to the previous deployment - $0 status # Show current system status - -Desktop Variants: - particle-os-corona-24.04 # KDE Plasma desktop - particle-os-apex-24.04 # GNOME desktop - particle-os-base-24.04 # Minimal base system -EOF - ;; - *) - log_error "Unknown command: $command" - echo "Run '$0 help' for usage." - exit 1 - ;; - esac -} - -# Run main function with all arguments -if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - main "$@" -fi \ No newline at end of file