From 9a0a6fef00ffc65cc9d6be54db1cf25107a76058 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Mon, 11 May 2026 21:34:43 -0400 Subject: [PATCH] Update install-stormux script. --- x86_64/airootfs/usr/local/bin/install-stormux | 480 +++++++++++++----- 1 file changed, 356 insertions(+), 124 deletions(-) diff --git a/x86_64/airootfs/usr/local/bin/install-stormux b/x86_64/airootfs/usr/local/bin/install-stormux index 8b96e6f..2e24b74 100755 --- a/x86_64/airootfs/usr/local/bin/install-stormux +++ b/x86_64/airootfs/usr/local/bin/install-stormux @@ -3,7 +3,7 @@ # install-stormux: Interactive Arch Linux installer for Stormux # Supports UEFI/BIOS, multiple partition layouts, and accessibility-first configuration -set -euo pipefail +set -Eeuo pipefail # Global variables logFile="/tmp/install-stormux.log" @@ -25,6 +25,11 @@ enableSsh="no" # "yes" or "no" installLinuxGameManager="no" # "yes" or "no" installAudiogameManager="no" # "yes" or "no" autoLoginUser="" # User to auto-login for desktop environments +minHomePartitionMiB=1024 +errorCount=0 +warningCount=0 +currentStep="initialization" +cleanupOnError=false # @@ -36,10 +41,12 @@ log() { } log_error() { + errorCount=$((errorCount + 1)) echo "ERROR: $*" | tee -a "$logFile" >&2 } log_warning() { + warningCount=$((warningCount + 1)) echo "WARNING: $*" | tee -a "$logFile" >&2 } @@ -47,6 +54,22 @@ log_info() { echo "$*" | tee -a "$logFile" } +handle_unexpected_failure() { + local exitCode="$1" + + if [[ "$exitCode" -eq 0 ]]; then + return 0 + fi + + log_error "Installation aborted during step: $currentStep (exit code $exitCode)" + + if [[ "$cleanupOnError" == true ]]; then + cleanup_and_unmount + fi + + exit "$exitCode" +} + # # Audio feedback functions # @@ -131,8 +154,24 @@ preflight_checks() { # list_available_disks() { - # List disks excluding loop devices, the current root device, and ISO - lsblk -dno NAME,SIZE,TYPE | grep -E "^[^l].*disk$" | grep -v "$(df / | tail -1 | cut -d' ' -f1 | sed 's|/dev/||;s|[0-9]*$||')" || true + # List disks excluding loop devices and the disk backing the live root filesystem. + local rootSource rootDiskName + rootSource=$(findmnt -n -o SOURCE --target / 2>/dev/null || true) + + if [[ "$rootSource" == /dev/* ]]; then + rootDiskName=$(lsblk -no PKNAME "$rootSource" 2>/dev/null | head -n 1 || true) + + if [[ -z "$rootDiskName" ]]; then + rootDiskName=$(basename "$rootSource") + rootDiskName=$(echo "$rootDiskName" | sed -E 's/p?[0-9]+$//') + fi + fi + + if [[ -n "${rootDiskName:-}" ]]; then + lsblk -dno NAME,SIZE,TYPE | awk -v excludedDisk="$rootDiskName" '$1 != excludedDisk && $3 == "disk"' + else + lsblk -dno NAME,SIZE,TYPE | awk '$3 == "disk"' + fi } select_disk() { @@ -165,6 +204,55 @@ select_disk() { done } +get_disk_size_mib() { + local disk="$1" + local diskSizeBytes + + diskSizeBytes=$(lsblk -bdno SIZE "$disk" 2>/dev/null | head -n 1 || true) + if [[ -z "$diskSizeBytes" || ! "$diskSizeBytes" =~ ^[0-9]+$ ]]; then + log_error "Could not determine disk size for $disk" + return 1 + fi + + echo $((diskSizeBytes / 1024 / 1024)) +} + +validate_home_disk_size() { + local disk="$1" + local diskSizeMiB + + diskSizeMiB=$(get_disk_size_mib "$disk") || return 1 + + if [[ "$diskSizeMiB" -lt "$minHomePartitionMiB" ]]; then + log_warning "Selected home disk $disk is too small (${diskSizeMiB}MiB)" + log_warning "Separate home disk requires at least ${minHomePartitionMiB}MiB" + return 1 + fi + + return 0 +} + +select_separate_home_disk() { + while true; do + if ! select_disk homeDisk "Select disk for /home partition:"; then + return 1 + fi + + if [[ "$homeDisk" == "$targetDisk" ]]; then + log_warning "Home disk must be different from target disk in separate home disk layout" + echo "Please choose a different disk." + continue + fi + + if ! validate_home_disk_size "$homeDisk"; then + echo "Please choose a different disk for /home." + continue + fi + + return 0 + done +} + select_install_target() { echo "Select installation target:" @@ -262,6 +350,51 @@ select_partition_layout() { done } +show_partition_plan() { + log "" + log "=== Planned Disk Layout ===" + log "Boot mode: $bootMode" + log "Target disk: $targetDisk" + + case "$partitionLayout" in + single) + if [[ "$bootMode" == "uefi" ]]; then + log " ${targetDisk}1 -> EFI (FAT32) mounted at /boot" + log " ${targetDisk}2 -> root (ext4) mounted at /" + else + log " ${targetDisk}1 -> root (ext4, boot flag) mounted at /" + fi + log " /home will be inside root filesystem" + ;; + separate_home) + if [[ "$bootMode" == "uefi" ]]; then + log " ${targetDisk}1 -> EFI (FAT32) mounted at /boot" + log " ${targetDisk}2 -> root (ext4) mounted at /" + log " ${targetDisk}3 -> home (ext4) mounted at /home" + else + log " ${targetDisk}1 -> root (ext4, boot flag) mounted at /" + log " ${targetDisk}2 -> home (ext4) mounted at /home" + fi + ;; + separate_disk) + if [[ "$bootMode" == "uefi" ]]; then + log " ${targetDisk}1 -> EFI (FAT32) mounted at /boot" + log " ${targetDisk}2 -> root (ext4) mounted at /" + else + log " ${targetDisk}1 -> root (ext4, boot flag) mounted at /" + fi + log "Home disk: $homeDisk" + log " ${homeDisk}1 -> home (ext4) mounted at /home" + ;; + esac + + if [[ "$partitionLayout" == "separate_disk" ]]; then + log "WARNING: ALL DATA on $targetDisk and $homeDisk will be destroyed." + else + log "WARNING: ALL DATA on $targetDisk will be destroyed." + fi +} + # # Partitioning functions # @@ -357,6 +490,10 @@ partition_disk_uefi_home_only() { local disk="$1" log_info "Creating home partition on $disk" + if ! validate_home_disk_size "$disk"; then + return 1 + fi + # Wipe existing partition table wipefs -af "$disk" @@ -450,6 +587,10 @@ partition_disk_bios_home_only() { local disk="$1" log_info "Creating home partition on $disk (BIOS)" + if ! validate_home_disk_size "$disk"; then + return 1 + fi + # Wipe existing partition table wipefs -af "$disk" @@ -523,12 +664,31 @@ get_partition_device() { fi } +get_home_partition_number() { + case "$partitionLayout" in + separate_home) + if [[ "$bootMode" == "uefi" ]]; then + echo 3 + else + echo 2 + fi + ;; + separate_disk) + echo 1 + ;; + *) + return 1 + ;; + esac +} + create_filesystems() { log_info "=== Creating Filesystems ===" local rootPart local homePart local efiPart + local homePartNum if [[ "$bootMode" == "uefi" ]]; then efiPart=$(get_partition_device "$targetDisk" 1) @@ -545,12 +705,14 @@ create_filesystems() { case "$partitionLayout" in separate_home) - homePart=$(get_partition_device "$targetDisk" 3) + homePartNum=$(get_home_partition_number) || return 1 + homePart=$(get_partition_device "$targetDisk" "$homePartNum") log_info "Creating home filesystem on $homePart" mkfs.ext4 -F "$homePart" ;; separate_disk) - homePart=$(get_partition_device "$homeDisk" 1) + homePartNum=$(get_home_partition_number) || return 1 + homePart=$(get_partition_device "$homeDisk" "$homePartNum") log_info "Creating home filesystem on $homePart" mkfs.ext4 -F "$homePart" ;; @@ -565,6 +727,7 @@ mount_filesystems() { local rootPart local homePart local efiPart + local homePartNum # Determine partition devices if [[ "$bootMode" == "uefi" ]]; then @@ -589,13 +752,15 @@ mount_filesystems() { # Mount home if separate case "$partitionLayout" in separate_home) - homePart=$(get_partition_device "$targetDisk" 3) + homePartNum=$(get_home_partition_number) || return 1 + homePart=$(get_partition_device "$targetDisk" "$homePartNum") log_info "Mounting home partition $homePart to $mountPoint/home" mkdir -p "$mountPoint/home" mount "$homePart" "$mountPoint/home" ;; separate_disk) - homePart=$(get_partition_device "$homeDisk" 1) + homePartNum=$(get_home_partition_number) || return 1 + homePart=$(get_partition_device "$homeDisk" "$homePartNum") log_info "Mounting home partition $homePart to $mountPoint/home" mkdir -p "$mountPoint/home" mount "$homePart" "$mountPoint/home" @@ -709,6 +874,32 @@ get_root_partition_device() { fi } +resolve_parent_disk_device() { + local currentDevice="$1" + local maxHops=20 + local hop=0 + + while [[ "$hop" -lt "$maxHops" ]]; do + local currentType parentName + currentType=$(lsblk -no TYPE "$currentDevice" 2>/dev/null | head -n 1 || true) + + if [[ "$currentType" == "disk" ]]; then + echo "$currentDevice" + return 0 + fi + + parentName=$(lsblk -no PKNAME "$currentDevice" 2>/dev/null | head -n 1 || true) + if [[ -z "$parentName" ]]; then + return 1 + fi + + currentDevice="/dev/$parentName" + hop=$((hop + 1)) + done + + return 1 +} + get_bios_install_disk() { if [[ "$useExistingMount" == false ]]; then echo "$targetDisk" @@ -727,28 +918,15 @@ get_bios_install_disk() { return 1 fi - local rootType - rootType=$(lsblk -no TYPE "$rootPart" 2>/dev/null | head -n 1 || true) + local installDisk + installDisk=$(resolve_parent_disk_device "$rootPart" || true) + if [[ -z "$installDisk" ]]; then + log_error "Could not determine BIOS install disk from root source: $rootPart" + log_error "Set root on a block device that resolves to a physical disk (for example /dev/sdX or /dev/nvmeXnY)" + return 1 + fi - case "$rootType" in - part) - local parentDisk - parentDisk=$(lsblk -no PKNAME "$rootPart" 2>/dev/null | head -n 1 || true) - if [[ -z "$parentDisk" ]]; then - log_error "Could not determine parent disk for root partition: $rootPart" - return 1 - fi - echo "/dev/$parentDisk" - ;; - disk) - echo "$rootPart" - ;; - *) - log_error "Unsupported root device type for BIOS bootloader installation: $rootType" - log_error "Automatic disk detection only supports direct partition or disk root devices" - return 1 - ;; - esac + echo "$installDisk" } # @@ -1107,10 +1285,18 @@ install_base_system() { return 1 fi - # Generate fstab - log_info "Generating fstab" - log_info "Generating fstab" - genfstab -U "$mountPoint" > "$mountPoint/etc/fstab" + # Generate or preserve fstab + if [[ "$useExistingMount" == true ]]; then + if [[ -s "$mountPoint/etc/fstab" ]]; then + log_info "Manual mount mode: preserving existing /etc/fstab" + else + log_warning "Manual mount mode: /etc/fstab not found, generating one" + genfstab -U "$mountPoint" > "$mountPoint/etc/fstab" + fi + else + log_info "Generating fstab" + genfstab -U "$mountPoint" > "$mountPoint/etc/fstab" + fi log_info "Base system installed" log_info "Base system installation complete" @@ -1324,7 +1510,9 @@ echo \"Created user: $username\" stormuxPackagesCmd=" # Install Stormux-specific packages echo \"Installing Stormux-specific packages: ${stormuxPackages[*]}\" -pacman -Sy --noconfirm --needed ${stormuxPackages[*]} 2>/dev/null || true +if ! pacman -Sy --noconfirm --needed ${stormuxPackages[*]}; then + echo \"WARNING: Failed to install one or more Stormux-specific packages: ${stormuxPackages[*]}\" +fi " fi @@ -1337,14 +1525,18 @@ echo \"Installing packages for audiogame-manager...\" # Critical packages: wine, p7zip, curl, dialog, sox, cabextract, unzip, xz # Optional packages: winetricks, wine_gecko, wine-mono, translate-shell, gawk, perl, xclip, xdotool # Audio packages: gst-plugins for multimedia support -pacman -S --noconfirm --needed wine winetricks wine_gecko wine-mono p7zip curl dialog sox cabextract unzip xz translate-shell gawk perl xclip xdotool gst-plugins-bad gst-plugins-good gst-plugins-ugly gst-libav 2>/dev/null || true +if ! pacman -S --noconfirm --needed wine winetricks wine_gecko wine-mono p7zip curl dialog sox cabextract unzip xz translate-shell gawk perl xclip xdotool gst-plugins-bad gst-plugins-good gst-plugins-ugly gst-libav; then + echo \"WARNING: Failed to install one or more audiogame-manager dependencies\" +fi " fi if [[ "$installLinuxGameManager" == "yes" ]]; then gameManagerPackagesCmd+=" # Install packages for linux-game-manager echo \"Installing packages for linux-game-manager...\" -pacman -S --noconfirm --needed p7zip curl dialog yad unzip 2>/dev/null || true +if ! pacman -S --noconfirm --needed p7zip curl dialog yad unzip; then + echo \"WARNING: Failed to install one or more linux-game-manager dependencies\" +fi " fi @@ -1352,12 +1544,14 @@ pacman -S --noconfirm --needed p7zip curl dialog yad unzip 2>/dev/null || true local pipewireConfigCmd="" for username in "${userNames[@]}"; do pipewireConfigCmd+=" -sudo -u $(printf %q "$username") /usr/share/fenrirscreenreader/tools/configure_pipewire.sh 2>/dev/null || true +if ! sudo -u $(printf %q "$username") /usr/share/fenrirscreenreader/tools/configure_pipewire.sh; then + echo \"WARNING: Failed to configure PipeWire for user $(printf %q "$username")\" +fi " done # Run configuration in chroot using heredoc (like pi4 script) - if ! arch-chroot "$mountPoint" /bin/bash <&1 | tee -a "$logFile" set -euo pipefail # Set timezone @@ -1409,7 +1603,9 @@ echo '%wheel ALL=(ALL) ALL' > /etc/sudoers.d/wheel chmod 440 /etc/sudoers.d/wheel # Disable systemd-networkd in favor of NetworkManager -systemctl disable systemd-networkd.service systemd-networkd.socket 2>/dev/null || true +if ! systemctl disable systemd-networkd.service systemd-networkd.socket; then + echo "WARNING: Could not disable systemd-networkd services (may already be disabled)" +fi # Enable system services systemctl enable NetworkManager.service @@ -1419,7 +1615,9 @@ systemctl enable cronie.service systemctl enable ssh-login-monitor.service # Enable bluetooth if present -systemctl enable bluetooth.service 2>/dev/null || true +if ! systemctl enable bluetooth.service; then + echo "WARNING: Could not enable bluetooth.service" +fi # Enable SSH if requested if [[ "${enableSsh}" == "yes" ]]; then @@ -1435,9 +1633,11 @@ if [[ "${desktopEnvironment}" == "i3" ]]; then mkdir -p /home/\$firstUser/git chown \$firstUser:users /home/\$firstUser/git - # Clone I38 to ~/git/I38 - echo "Cloning I38 accessibility configuration..." - sudo -u \$firstUser git clone https://git.stormux.org/storm/I38 /home/\$firstUser/git/I38 2>/dev/null || true +# Clone I38 to ~/git/I38 +echo "Cloning I38 accessibility configuration..." +if ! sudo -u \$firstUser git clone https://git.stormux.org/storm/I38 /home/\$firstUser/git/I38; then + echo "WARNING: Failed to clone I38 repository" +fi # Run I38 setup to generate accessible i3 configuration if [[ -d /home/\$firstUser/git/I38 ]]; then @@ -1447,14 +1647,20 @@ if [[ "${desktopEnvironment}" == "i3" ]]; then # Ensure xdotool is available for I38 setup if ! command -v xdotool >/dev/null 2>&1; then echo "Installing xdotool for I38..." - pacman -Sy --noconfirm --needed xdotool 2>/dev/null || true + if ! pacman -Sy --noconfirm --needed xdotool; then + echo "WARNING: Failed to install xdotool for I38 setup" + fi fi # Run I38 setup scripts as the user # -x generates xinitrc and xprofile # Main script generates i3 config with accessibility features - sudo -u \$firstUser ./i38.sh -x || true - sudo -u \$firstUser ./i38.sh || true + if ! sudo -u \$firstUser ./i38.sh -x; then + echo "WARNING: I38 xinit/xprofile setup reported an error" + fi + if ! sudo -u \$firstUser ./i38.sh; then + echo "WARNING: I38 main setup reported an error" + fi cd - > /dev/null || exit 1 fi @@ -1498,16 +1704,24 @@ export QT_LINUX_ACCESSIBILITY_ALWAYS_ON=1 export SAL_USE_VCLPLUGIN=gtk3 # Enable Orca screen reader -gsettings set org.gnome.desktop.a11y.applications screen-reader-enabled true 2>/dev/null || true -gsettings set org.mate.interface accessibility true 2>/dev/null || true -gsettings set org.mate.applications-at-visual startup true 2>/dev/null || true +if ! gsettings set org.gnome.desktop.a11y.applications screen-reader-enabled true; then + echo "WARNING: Could not set GNOME screen-reader-enabled flag" +fi +if ! gsettings set org.mate.interface accessibility true; then + echo "WARNING: Could not set MATE accessibility flag" +fi +if ! gsettings set org.mate.applications-at-visual startup true; then + echo "WARNING: Could not set MATE startup applications accessibility flag" +fi XPROFILE_MATE_EOF chmod 755 /home/\$firstUser/.xprofile chown \$firstUser:users /home/\$firstUser/.xprofile # Configure speech-dispatcher for MATE if [[ ! -d /home/\$firstUser/.config/speech-dispatcher ]]; then - sudo -u \$firstUser spd-conf -n 2>/dev/null || true + if ! sudo -u \$firstUser spd-conf -n; then + echo "WARNING: Failed to initialize speech-dispatcher configuration for \$firstUser" + fi fi fi @@ -1517,8 +1731,12 @@ pacman-key --populate archlinux # Import Stormux key if present if [[ -f /usr/share/pacman/keyrings/stormux.gpg ]]; then - pacman-key --add /usr/share/pacman/keyrings/stormux.gpg 2>/dev/null || true - pacman-key --lsign-key 52ADA49000F1FF0456F8AEEFB4CDE1CD56EF8E82 2>/dev/null || true + if ! pacman-key --add /usr/share/pacman/keyrings/stormux.gpg; then + echo "WARNING: Could not add Stormux signing key to pacman keyring" + fi + if ! pacman-key --lsign-key 52ADA49000F1FF0456F8AEEFB4CDE1CD56EF8E82; then + echo "WARNING: Could not locally sign Stormux pacman key" + fi fi # Install Stormux-specific packages (built dynamically before chroot) @@ -1532,7 +1750,9 @@ systemctl --global enable pipewire.service pipewire-pulse.service wireplumber.se # Configure pipewire for Fenrir screen reader (run as root first) if [[ -x /usr/share/fenrirscreenreader/tools/configure_pipewire.sh ]]; then - /usr/share/fenrirscreenreader/tools/configure_pipewire.sh 2>/dev/null || true + if ! /usr/share/fenrirscreenreader/tools/configure_pipewire.sh; then + echo "WARNING: Failed to configure PipeWire for root/Fenrir" + fi fi # Configure pipewire for each user (built dynamically before chroot) @@ -1788,6 +2008,7 @@ log_info "=== Stormux Installer Started ===" log_info "Installation log: $logFile" # Pre-flight checks +currentStep="pre-flight checks" if ! preflight_checks; then log_error "Pre-flight checks failed" exit 1 @@ -1795,19 +2016,15 @@ fi # Disk selection log_info "=== Disk Selection ===" +currentStep="disk selection" if ! select_install_target; then log_error "Target selection cancelled" exit 1 fi if [[ "$useExistingMount" == false ]]; then - if ! confirm_disk_destruction "$targetDisk"; then - log_error "Disk destruction not confirmed" - echo "Installation cancelled." - exit 1 - fi - # Partition layout selection + currentStep="partition layout selection" if ! select_partition_layout; then log_error "Partition layout selection cancelled" exit 1 @@ -1815,11 +2032,25 @@ if [[ "$useExistingMount" == false ]]; then # If separate disk layout, select home disk if [[ "$partitionLayout" == "separate_disk" ]]; then - if ! select_disk homeDisk "Select disk for /home partition:"; then + currentStep="home disk selection" + if ! select_separate_home_disk; then log_error "Home disk selection cancelled" exit 1 fi + fi + currentStep="partition plan summary" + show_partition_plan + + currentStep="target disk confirmation" + if ! confirm_disk_destruction "$targetDisk"; then + log_error "Disk destruction not confirmed" + echo "Installation cancelled." + exit 1 + fi + + if [[ "$partitionLayout" == "separate_disk" ]]; then + currentStep="home disk confirmation" if ! confirm_disk_destruction "$homeDisk"; then log_error "Home disk destruction not confirmed" echo "Installation cancelled." @@ -1827,6 +2058,7 @@ if [[ "$useExistingMount" == false ]]; then fi fi else + currentStep="existing mount validation" if ! validate_existing_mountpoint; then log_error "Existing mountpoint validation failed" exit 1 @@ -1834,102 +2066,102 @@ else fi # Gather system information +currentStep="system configuration prompts" if ! gather_system_info; then log_error "System information gathering failed" exit 1 fi +trap 'handle_unexpected_failure "$?"' ERR + if [[ "$useExistingMount" == false ]]; then # Partition disks - if ! partition_disks; then - log_error "Disk partitioning failed" - echo "ERROR: Disk partitioning failed" - exit 1 - fi + currentStep="disk partitioning" + partition_disks # Create filesystems - if ! create_filesystems; then - log_error "Filesystem creation failed" - echo "ERROR: Filesystem creation failed" - exit 1 - fi + currentStep="filesystem creation" + create_filesystems # Mount filesystems - if ! mount_filesystems; then - log_error "Filesystem mounting failed" - echo "ERROR: Filesystem mounting failed" - exit 1 - fi + cleanupOnError=true + currentStep="filesystem mounting" + mount_filesystems else + cleanupOnError=true log_info "Skipping partitioning, filesystem creation, and mounting (using existing $mountPoint)" fi # Install base system -if ! install_base_system; then - log_error "Base system installation failed" - cleanup_and_unmount - exit 1 -fi +currentStep="base system installation" +install_base_system # Install audio configurations -if ! install_audio_configs; then - log_error "Audio configuration failed" - cleanup_and_unmount - exit 1 -fi +currentStep="audio configuration" +install_audio_configs # Install SSH login monitor files (script + service) -if ! install_ssh_login_monitor; then - log_error "SSH login monitor installation failed" - cleanup_and_unmount - exit 1 -fi +currentStep="ssh login monitor installation" +install_ssh_login_monitor # Configure system -if ! configure_system; then - log_error "System configuration failed" - cleanup_and_unmount - exit 1 -fi +currentStep="chroot system configuration" +configure_system # Install bootloader -if ! install_bootloader; then - log_error "Bootloader installation failed" - cleanup_and_unmount - exit 1 -fi +currentStep="bootloader installation" +install_bootloader # Install game managers -if ! install_game_managers; then - log_warning "Game manager installation had issues (non-fatal)" -fi +currentStep="game manager installation" +install_game_managers # Cleanup and unmount +currentStep="cleanup" cleanup_and_unmount +cleanupOnError=false -# Success message -log_info "=== Stormux Installer Completed Successfully ===" -log "" -log "╔════════════════════════════════════════════╗" -log "║ Installation Complete Successfully! ║" -log "╚════════════════════════════════════════════╝" -log "" -log "Next steps:" -log " 1. Remove the installation media" -log " 2. Reboot your system" -log " 3. Log in with one of your user accounts: ${userNames[*]}" +summaryWarningCount=$(grep -c '^WARNING:' "$logFile" 2>/dev/null || true) +summaryErrorCount=$(grep -c '^ERROR:' "$logFile" 2>/dev/null || true) -if [[ "$desktopEnvironment" == "i3" ]]; then - log " 4. Your i3 desktop is configured with I38 for accessibility" - log " - I38 source is available in ~/git/I38 for customization" - log " - Press Alt+Shift+F1 for I38 help after login" +log "" +log "=== Installation Summary ===" +log " Errors: ${summaryErrorCount:-0}" +log " Warnings: ${summaryWarningCount:-0}" +log " Log file: $logFile" +log "" + +if [[ "$errorCount" -gt 0 ]]; then + log_warning "Installation completed with ${summaryErrorCount:-$errorCount} error(s)." + log_warning "Review the installation log before rebooting: $logFile" + finalExitCode=1 +else + # Success message + log_info "=== Stormux Installer Completed Successfully ===" + log "" + log "╔════════════════════════════════════════════╗" + log "║ Installation Complete Successfully! ║" + log "╚════════════════════════════════════════════╝" + log "" + log "Next steps:" + log " 1. Remove the installation media" + log " 2. Reboot your system" + log " 3. Log in with one of your user accounts: ${userNames[*]}" + + if [[ "$desktopEnvironment" == "i3" ]]; then + log " 4. Your i3 desktop is configured with I38 for accessibility" + log " - I38 source is available in ~/git/I38 for customization" + log " - Press Alt+Shift+F1 for I38 help after login" + fi + + log "" + log "Installation log saved to: $logFile" + log "" + + # Play success sound + play_success_sound + finalExitCode=0 fi -log "" -log "Installation log saved to: $logFile" -log "" - -# Play success sound -play_success_sound - read -rp "Press Enter to exit..." +exit "$finalExitCode"