1035 lines
36 KiB
Bash
Executable file
1035 lines
36 KiB
Bash
Executable file
#!/bin/bash
|
|
#===============================================================================
|
|
# PATCH OMARCHY ISO FOR DUAL-BOOT
|
|
#
|
|
# This version hooks into the original Omarchy installer (archinstall)
|
|
# We just handle partitioning, then let archinstall do everything else
|
|
#
|
|
# Usage: sudo ./patch-omarchy-dualboot.sh omarchy-3.x.x.iso
|
|
#===============================================================================
|
|
|
|
set -e
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m'
|
|
|
|
log() { echo -e "${GREEN}[✓]${NC} $1"; }
|
|
warn() { echo -e "${YELLOW}[!]${NC} $1"; }
|
|
error() { echo -e "${RED}[✗]${NC} $1"; exit 1; }
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
|
|
[[ $EUID -ne 0 ]] && error "Run as root: sudo $0"
|
|
|
|
# Auto-detect ISO if not provided
|
|
if [[ -z "$1" ]]; then
|
|
SOURCE_ISO=$(find "$SCRIPT_DIR" -maxdepth 1 -name "omarchy-*.iso" ! -name "omarchy-dualboot-*" ! -name "omarchy-installer-*" 2>/dev/null | head -1)
|
|
if [[ -z "$SOURCE_ISO" ]]; then
|
|
error "No Omarchy ISO found in $SCRIPT_DIR
|
|
|
|
Please either:
|
|
1. Place omarchy-*.iso in the same directory as this script
|
|
2. Or specify the path: sudo $0 /path/to/omarchy.iso"
|
|
fi
|
|
log "Auto-detected ISO: $(basename "$SOURCE_ISO")"
|
|
else
|
|
SOURCE_ISO="$1"
|
|
fi
|
|
|
|
[[ ! -f "$SOURCE_ISO" ]] && error "ISO not found: $SOURCE_ISO"
|
|
WORK_DIR="$SCRIPT_DIR/.omarchy-patch-$$"
|
|
OUTPUT_ISO="$SCRIPT_DIR/omarchy-dualboot-$(date +%Y.%m.%d).iso"
|
|
|
|
# Check and install dependencies
|
|
MISSING_PKGS=""
|
|
command -v xorriso &>/dev/null || MISSING_PKGS="$MISSING_PKGS xorriso"
|
|
command -v unsquashfs &>/dev/null || MISSING_PKGS="$MISSING_PKGS squashfs-tools"
|
|
command -v isoinfo &>/dev/null || MISSING_PKGS="$MISSING_PKGS cdrtools"
|
|
|
|
if [[ -n "$MISSING_PKGS" ]]; then
|
|
log "Installing missing dependencies:$MISSING_PKGS"
|
|
pacman -S --noconfirm --needed $MISSING_PKGS || error "Failed to install dependencies"
|
|
log "Dependencies installed"
|
|
fi
|
|
|
|
log "Patching: $SOURCE_ISO"
|
|
|
|
# Cleanup on exit
|
|
cleanup() {
|
|
umount "$WORK_DIR/iso" 2>/dev/null || true
|
|
rm -rf "$WORK_DIR"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
mkdir -p "$WORK_DIR"/{iso,extracted,newiso}
|
|
|
|
#===============================================================================
|
|
# EXTRACT ISO
|
|
#===============================================================================
|
|
|
|
log "Extracting ISO..."
|
|
mount -o loop,ro "$SOURCE_ISO" "$WORK_DIR/iso"
|
|
cp -a "$WORK_DIR/iso/"* "$WORK_DIR/newiso/"
|
|
umount "$WORK_DIR/iso"
|
|
|
|
# Find squashfs
|
|
SQUASHFS="$WORK_DIR/newiso/arch/x86_64/airootfs.sfs"
|
|
[[ ! -f "$SQUASHFS" ]] && error "Squashfs not found at expected path"
|
|
|
|
log "Extracting squashfs (this takes a few minutes)..."
|
|
unsquashfs -d "$WORK_DIR/extracted" "$SQUASHFS"
|
|
|
|
# Verify squashfs structure
|
|
if [[ ! -d "$WORK_DIR/extracted/root" ]]; then
|
|
error "Unexpected squashfs structure: /root directory not found!"
|
|
fi
|
|
|
|
log "Original /root contents:"
|
|
ls -la "$WORK_DIR/extracted/root/" | grep -E '\.(zlogin|zprofile|automated|bash)' || echo " (no matching files)"
|
|
|
|
#===============================================================================
|
|
# CREATE DUAL-BOOT WRAPPER SCRIPT
|
|
#===============================================================================
|
|
|
|
log "Creating dual-boot wrapper..."
|
|
|
|
# This script runs BEFORE the original installer
|
|
# It handles partitioning, then lets archinstall do the rest
|
|
cat > "$WORK_DIR/extracted/root/dualboot-setup.sh" << 'DUALBOOT_SCRIPT'
|
|
#!/bin/bash
|
|
#===============================================================================
|
|
# DUAL-BOOT PARTITION SETUP
|
|
# Creates partitions in free space, then hands off to archinstall
|
|
#===============================================================================
|
|
|
|
set -e
|
|
|
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
|
|
CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
|
|
|
|
log() { echo -e "${GREEN}[✓]${NC} $1"; }
|
|
warn() { echo -e "${YELLOW}[!]${NC} $1"; }
|
|
error() { echo -e "${RED}[✗]${NC} $1"; exit 1; }
|
|
info() { echo -e "${CYAN}[i]${NC} $1"; }
|
|
|
|
header() {
|
|
echo ""
|
|
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
echo -e "${BOLD} $1${NC}"
|
|
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
echo ""
|
|
}
|
|
|
|
cleanup_and_exit() {
|
|
warn "Setup cancelled or failed. Cleaning up..."
|
|
umount -R /mnt 2>/dev/null || true
|
|
cryptsetup close cryptroot 2>/dev/null || true
|
|
exit 1
|
|
}
|
|
|
|
#===============================================================================
|
|
# CLEANUP FUNCTION FOR FAILED INSTALLATIONS
|
|
#===============================================================================
|
|
|
|
cleanup_failed_install() {
|
|
header "CLEANUP FAILED INSTALLATION"
|
|
|
|
echo "This will remove Linux partitions from a failed installation attempt."
|
|
echo "Windows partitions will NOT be touched."
|
|
echo ""
|
|
|
|
echo "Available drives:"
|
|
lsblk -d -o NAME,SIZE,MODEL | grep -E "^(nvme|sd|vd)"
|
|
echo ""
|
|
|
|
read -p "Enter drive to clean (e.g., nvme0n1): " drive_input
|
|
DRIVE="/dev/$drive_input"
|
|
|
|
[[ ! -b "$DRIVE" ]] && error "Drive not found: $DRIVE"
|
|
[[ "$DRIVE" == *"nvme"* ]] && PART_PREFIX="${DRIVE}p" || PART_PREFIX="$DRIVE"
|
|
|
|
echo ""
|
|
echo "Current partitions:"
|
|
lsblk -o NAME,SIZE,FSTYPE,LABEL "$DRIVE"
|
|
echo ""
|
|
|
|
cryptsetup close cryptroot 2>/dev/null || true
|
|
umount -R /mnt 2>/dev/null || true
|
|
|
|
LINUX_EFI=""
|
|
LINUX_ROOT=""
|
|
|
|
for part in ${PART_PREFIX}*; do
|
|
[[ "$part" == "$DRIVE" || "$part" == "${PART_PREFIX}" ]] && continue
|
|
[[ ! -b "$part" ]] && continue
|
|
|
|
LABEL=$(lsblk -n -o LABEL "$part" 2>/dev/null | tr -d ' ')
|
|
FSTYPE=$(lsblk -n -o FSTYPE "$part" 2>/dev/null | tr -d ' ')
|
|
|
|
if [[ "$LABEL" == "LINUXEFI" ]]; then
|
|
LINUX_EFI="$part"
|
|
echo -e "${YELLOW}Found Linux EFI partition: $part${NC}"
|
|
elif [[ "$FSTYPE" == "crypto_LUKS" ]]; then
|
|
LINUX_ROOT="$part"
|
|
echo -e "${YELLOW}Found LUKS partition: $part${NC}"
|
|
fi
|
|
done
|
|
|
|
if [[ -z "$LINUX_EFI" && -z "$LINUX_ROOT" ]]; then
|
|
echo ""
|
|
info "No Linux partitions found from failed installation."
|
|
read -p "Press Enter to continue..." _
|
|
return 1
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "${RED}WARNING: The following partitions will be DELETED:${NC}"
|
|
[[ -n "$LINUX_EFI" ]] && echo " - $LINUX_EFI (Linux EFI)"
|
|
[[ -n "$LINUX_ROOT" ]] && echo " - $LINUX_ROOT (Linux Root/LUKS)"
|
|
echo ""
|
|
|
|
read -p "Type 'DELETE' to confirm removal: " confirm
|
|
if [[ "$confirm" != "DELETE" ]]; then
|
|
echo "Cancelled."
|
|
return 1
|
|
fi
|
|
|
|
if [[ -n "$LINUX_ROOT" ]]; then
|
|
PART_NUM=$(echo "$LINUX_ROOT" | grep -oE '[0-9]+$')
|
|
log "Deleting partition $PART_NUM..."
|
|
sgdisk -d "$PART_NUM" "$DRIVE"
|
|
fi
|
|
|
|
if [[ -n "$LINUX_EFI" ]]; then
|
|
PART_NUM=$(echo "$LINUX_EFI" | grep -oE '[0-9]+$')
|
|
log "Deleting partition $PART_NUM..."
|
|
sgdisk -d "$PART_NUM" "$DRIVE"
|
|
fi
|
|
|
|
partprobe "$DRIVE"
|
|
|
|
for entry in $(efibootmgr 2>/dev/null | grep -i "Omarchy" | sed -n 's/^Boot\([0-9A-Fa-f]*\).*/\1/p'); do
|
|
efibootmgr -b "$entry" -B 2>/dev/null || true
|
|
done
|
|
|
|
log "Cleanup complete!"
|
|
echo ""
|
|
lsblk -o NAME,SIZE,FSTYPE,LABEL "$DRIVE"
|
|
echo ""
|
|
read -p "Press Enter to continue..." _
|
|
return 0
|
|
}
|
|
|
|
#===============================================================================
|
|
# MAIN MENU
|
|
#===============================================================================
|
|
|
|
header "OMARCHY DUAL-BOOT INSTALLER"
|
|
|
|
echo "This installer will set up Omarchy alongside Windows."
|
|
echo "Windows partitions will NOT be touched."
|
|
echo ""
|
|
echo "Options:"
|
|
echo " 1) Install Omarchy (dual-boot with Windows)"
|
|
echo " 2) Clean up failed installation"
|
|
echo " 3) Standard install (wipe entire disk)"
|
|
echo " 4) Exit to shell"
|
|
echo ""
|
|
read -p "Select option [1]: " MENU_CHOICE
|
|
MENU_CHOICE=${MENU_CHOICE:-1}
|
|
|
|
case $MENU_CHOICE in
|
|
2)
|
|
cleanup_failed_install || true
|
|
exec /root/dualboot-setup.sh
|
|
;;
|
|
3)
|
|
# Run original installer
|
|
info "Starting standard Omarchy installer..."
|
|
exec /root/.automated_script.sh.orig
|
|
;;
|
|
4)
|
|
echo "Type '/root/dualboot-setup.sh' to restart installer."
|
|
exit 0
|
|
;;
|
|
1)
|
|
# Continue with dual-boot setup
|
|
;;
|
|
*)
|
|
exec /root/dualboot-setup.sh
|
|
;;
|
|
esac
|
|
|
|
#===============================================================================
|
|
# PRE-FLIGHT CHECKS
|
|
#===============================================================================
|
|
|
|
[[ ! -d /sys/firmware/efi ]] && error "UEFI mode required for dual-boot"
|
|
|
|
header "SELECT DRIVE"
|
|
|
|
echo "Available drives:"
|
|
echo ""
|
|
lsblk -d -o NAME,SIZE,MODEL | grep -E "^(nvme|sd|vd)"
|
|
echo ""
|
|
|
|
mapfile -t DRIVES < <(lsblk -d -n -o NAME,TRAN | awk '$2!="usb" {print $1}' | grep -E "^(nvme|sd|vd)")
|
|
|
|
if [[ ${#DRIVES[@]} -eq 1 ]]; then
|
|
DRIVE="/dev/${DRIVES[0]}"
|
|
info "Found: $DRIVE"
|
|
read -p "Use this drive? (Y/n): " confirm
|
|
[[ "$confirm" =~ ^[Nn]$ ]] && error "Cancelled"
|
|
else
|
|
read -p "Enter drive (e.g., nvme0n1): " drive_input
|
|
DRIVE="/dev/$drive_input"
|
|
fi
|
|
|
|
[[ ! -b "$DRIVE" ]] && error "Drive not found: $DRIVE"
|
|
[[ "$DRIVE" == *"nvme"* ]] && PART_PREFIX="${DRIVE}p" || PART_PREFIX="$DRIVE"
|
|
|
|
echo ""
|
|
echo "Current partitions on $DRIVE:"
|
|
lsblk -o NAME,SIZE,FSTYPE,LABEL "$DRIVE"
|
|
echo ""
|
|
|
|
# Check for Windows
|
|
if lsblk -o FSTYPE "$DRIVE" | grep -q ntfs; then
|
|
log "Windows detected - it will be preserved"
|
|
else
|
|
warn "Windows not detected on this drive"
|
|
read -p "Continue anyway? (y/N): " cont
|
|
[[ ! "$cont" =~ ^[Yy]$ ]] && error "Cancelled"
|
|
fi
|
|
|
|
# Check free space
|
|
FREE_SECTORS=$(sgdisk -p "$DRIVE" 2>/dev/null | grep "Total free space" | awk '{print $5}')
|
|
if [[ "$FREE_SECTORS" =~ ^[0-9]+$ ]]; then
|
|
FREE_GB=$((FREE_SECTORS * 512 / 1024 / 1024 / 1024))
|
|
[[ $FREE_GB -lt 20 ]] && error "Need 20GB+ free space, found ${FREE_GB}GB"
|
|
log "Free space: ${FREE_GB}GB"
|
|
else
|
|
warn "Could not determine free space"
|
|
fi
|
|
|
|
#===============================================================================
|
|
# ENCRYPTION PASSWORD
|
|
#===============================================================================
|
|
|
|
header "DISK ENCRYPTION"
|
|
|
|
echo "Your Linux partition will be encrypted with LUKS2."
|
|
echo "You'll enter your user password in the next step (configurator)."
|
|
echo ""
|
|
|
|
while true; do
|
|
read -s -p "Enter LUKS encryption password: " LUKS_PASS; echo ""
|
|
read -s -p "Confirm password: " LUKS_PASS2; echo ""
|
|
[[ "$LUKS_PASS" == "$LUKS_PASS2" ]] && break
|
|
warn "Passwords don't match, try again"
|
|
done
|
|
|
|
#===============================================================================
|
|
# CONFIRMATION
|
|
#===============================================================================
|
|
|
|
header "CONFIRM PARTITIONING"
|
|
|
|
echo "The following will be created in FREE SPACE on $DRIVE:"
|
|
echo ""
|
|
echo " • 1GB EFI partition (for Linux bootloader)"
|
|
echo " • Rest: LUKS2 encrypted partition (for Linux root)"
|
|
echo ""
|
|
echo -e "${GREEN}Windows partitions will NOT be touched.${NC}"
|
|
echo ""
|
|
read -p "Type 'yes' to continue: " confirm
|
|
[[ "$confirm" != "yes" ]] && error "Cancelled"
|
|
|
|
#===============================================================================
|
|
# CREATE PARTITIONS
|
|
#===============================================================================
|
|
|
|
header "CREATING PARTITIONS"
|
|
|
|
trap cleanup_and_exit ERR
|
|
|
|
DRIVE_NAME=$(basename "$DRIVE")
|
|
LAST_PART=$(lsblk -n -o NAME "$DRIVE" | grep -v "^${DRIVE_NAME}$" | grep -oE '[0-9]+$' | sort -n | tail -1)
|
|
LAST_PART=${LAST_PART:-0}
|
|
|
|
# Create EFI partition
|
|
log "Creating Linux EFI partition (1GB)..."
|
|
NEXT=$((LAST_PART + 1))
|
|
sgdisk -n "${NEXT}:0:+1G" -t "${NEXT}:ef00" "$DRIVE"
|
|
EFI_PART="${PART_PREFIX}${NEXT}"
|
|
partprobe "$DRIVE"; udevadm settle; sleep 2
|
|
mkfs.fat -F32 -n "LINUXEFI" "$EFI_PART"
|
|
log "Created: $EFI_PART"
|
|
|
|
# Create root partition
|
|
log "Creating Linux root partition..."
|
|
NEXT=$((NEXT + 1))
|
|
sgdisk -n "${NEXT}:0:0" -t "${NEXT}:8300" "$DRIVE"
|
|
ROOT_PART="${PART_PREFIX}${NEXT}"
|
|
partprobe "$DRIVE"; udevadm settle; sleep 2
|
|
log "Created: $ROOT_PART"
|
|
|
|
#===============================================================================
|
|
# SETUP ENCRYPTION & FILESYSTEM
|
|
#===============================================================================
|
|
|
|
header "SETTING UP ENCRYPTION"
|
|
|
|
log "Formatting LUKS2..."
|
|
echo -n "$LUKS_PASS" | cryptsetup luksFormat --type luks2 --key-file=- "$ROOT_PART"
|
|
|
|
log "Opening encrypted volume..."
|
|
echo -n "$LUKS_PASS" | cryptsetup open --key-file=- "$ROOT_PART" cryptroot
|
|
LUKS_UUID=$(blkid -s UUID -o value "$ROOT_PART")
|
|
|
|
log "Creating btrfs filesystem..."
|
|
mkfs.btrfs -f /dev/mapper/cryptroot
|
|
|
|
log "Creating subvolumes..."
|
|
mount /dev/mapper/cryptroot /mnt
|
|
btrfs subvolume create /mnt/@
|
|
btrfs subvolume create /mnt/@home
|
|
btrfs subvolume create /mnt/@log
|
|
btrfs subvolume create /mnt/@pkg
|
|
# NOTE: Do NOT create @snapshots - snapper will create .snapshots as nested subvolume
|
|
umount /mnt
|
|
|
|
log "Mounting filesystems..."
|
|
mount -o compress=zstd,subvol=@ /dev/mapper/cryptroot /mnt
|
|
mkdir -p /mnt/{home,var/log,var/cache/pacman/pkg,boot}
|
|
mount -o compress=zstd,subvol=@home /dev/mapper/cryptroot /mnt/home
|
|
mount -o compress=zstd,subvol=@log /dev/mapper/cryptroot /mnt/var/log
|
|
mount -o compress=zstd,subvol=@pkg /dev/mapper/cryptroot /mnt/var/cache/pacman/pkg
|
|
# NOTE: Do NOT mount .snapshots - snapper creates it as nested subvolume in @
|
|
mount "$EFI_PART" /mnt/boot
|
|
|
|
log "Partitions ready!"
|
|
|
|
# Save info for later
|
|
echo "$LUKS_UUID" > /tmp/luks_uuid
|
|
echo "$EFI_PART" > /tmp/efi_part
|
|
echo "$ROOT_PART" > /tmp/root_part
|
|
echo "$DRIVE" > /tmp/install_drive
|
|
|
|
trap - ERR
|
|
|
|
#===============================================================================
|
|
# HAND OFF TO ARCHINSTALL
|
|
#===============================================================================
|
|
|
|
header "STARTING OMARCHY INSTALLER"
|
|
|
|
echo "Partitions are ready. Now the Omarchy installer will collect your"
|
|
echo "user information and install the system."
|
|
echo ""
|
|
info "The disk is already partitioned - just enter your user details."
|
|
echo ""
|
|
read -p "Press Enter to continue to Omarchy setup..." _
|
|
|
|
# Run the configurator to get user info
|
|
cd /root
|
|
source /root/omarchy/install/helpers/all.sh 2>/dev/null || true
|
|
|
|
# Set colors
|
|
if [[ $(tty) == "/dev/tty"* ]]; then
|
|
echo -en "\e]P01a1b26"
|
|
echo -en "\e]P7a9b1d6"
|
|
echo -en "\e]PFc0caf5"
|
|
echo -en "\033[0m"
|
|
clear
|
|
fi
|
|
|
|
# Run configurator
|
|
./configurator
|
|
|
|
# Get username from generated config
|
|
OMARCHY_USER=$(jq -r '.users[0].username' user_credentials.json 2>/dev/null)
|
|
|
|
if [[ -z "$OMARCHY_USER" || "$OMARCHY_USER" == "null" ]]; then
|
|
error "Failed to get username from configurator"
|
|
fi
|
|
|
|
log "User: $OMARCHY_USER"
|
|
|
|
#===============================================================================
|
|
# MODIFY CONFIG FOR PRE-MOUNTED PARTITIONS
|
|
#===============================================================================
|
|
|
|
header "CONFIGURING INSTALLATION"
|
|
|
|
log "Updating configuration for dual-boot..."
|
|
|
|
# Only modify disk_config and remove bootloader, preserve everything else from generated config
|
|
# This keeps the correct kernel, locale, packages, profile, etc.
|
|
# We MUST remove bootloader because archinstall crashes when trying to
|
|
# detect root device with pre_mounted_config - it fails BEFORE installing packages
|
|
# We install limine manually after archinstall completes
|
|
jq '.disk_config = {"config_type": "pre_mounted_config", "mountpoint": "/mnt"} | del(.bootloader)' \
|
|
user_configuration.json > user_configuration.json.tmp
|
|
mv user_configuration.json.tmp user_configuration.json
|
|
|
|
# Extract kernel name for later use in bootloader config
|
|
KERNEL_NAME=$(jq -r '.kernels[0] // "linux"' user_configuration.json)
|
|
echo "$KERNEL_NAME" > /tmp/kernel_name
|
|
log "Using kernel: $KERNEL_NAME"
|
|
|
|
log "Configuration updated for pre-mounted partitions"
|
|
|
|
#===============================================================================
|
|
# RUN ARCHINSTALL
|
|
#===============================================================================
|
|
|
|
header "INSTALLING SYSTEM"
|
|
|
|
log "Running archinstall..."
|
|
info "This will take several minutes..."
|
|
|
|
# Initialize keyring
|
|
pacman-key --init
|
|
pacman-key --populate archlinux
|
|
pacman-key --populate omarchy 2>/dev/null || true
|
|
|
|
# For offline install, ensure we use the offline-only pacman config
|
|
# and skip database sync since the database is already on the ISO
|
|
cat > /tmp/pacman-offline.conf << 'PACCONF'
|
|
[options]
|
|
HoldPkg = pacman glibc
|
|
Architecture = auto
|
|
SigLevel = Required DatabaseOptional
|
|
LocalFileSigLevel = Optional
|
|
|
|
[offline]
|
|
SigLevel = Optional TrustAll
|
|
Server = file:///var/cache/omarchy/mirror/offline/
|
|
PACCONF
|
|
|
|
# Overwrite system pacman.conf with offline-only version
|
|
cp /tmp/pacman-offline.conf /etc/pacman.conf
|
|
|
|
# Debug: verify config
|
|
log "Pacman config set to offline-only:"
|
|
grep -E "^\[|^Server" /etc/pacman.conf
|
|
|
|
# Skip pacman -Sy for offline install - database already exists on ISO
|
|
# Check for either offline.db or offline.db.tar.gz
|
|
if [[ -f /var/cache/omarchy/mirror/offline/offline.db ]] || [[ -f /var/cache/omarchy/mirror/offline/offline.db.tar.gz ]]; then
|
|
log "Offline package database found - skipping sync"
|
|
else
|
|
warn "Offline database not found - attempting sync..."
|
|
pacman --config /tmp/pacman-offline.conf -Sy --noconfirm
|
|
fi
|
|
|
|
# Run archinstall with our config (same flags as original Omarchy installer)
|
|
# bootloader=none prevents archinstall from crashing on pre_mounted_config
|
|
archinstall \
|
|
--config user_configuration.json \
|
|
--creds user_credentials.json \
|
|
--silent \
|
|
--skip-ntp \
|
|
--skip-wkd \
|
|
--skip-wifi-check
|
|
|
|
# Verify archinstall created the user
|
|
if [[ ! -d /mnt/home/$OMARCHY_USER ]]; then
|
|
error "archinstall failed - user $OMARCHY_USER not created"
|
|
fi
|
|
|
|
log "Base system installed!"
|
|
|
|
# Install limine bootloader package (archinstall didn't because we removed bootloader config)
|
|
log "Installing limine bootloader..."
|
|
# Copy offline pacman config to chroot and mount offline mirror first
|
|
cp /tmp/pacman-offline.conf /mnt/etc/pacman.conf
|
|
mkdir -p /mnt/var/cache/omarchy/mirror/offline
|
|
mount --bind /var/cache/omarchy/mirror/offline /mnt/var/cache/omarchy/mirror/offline
|
|
# Install limine using offline repo (no -Sy needed, packages are local)
|
|
arch-chroot /mnt pacman --noconfirm --needed -S limine
|
|
|
|
#===============================================================================
|
|
# POST-INSTALL CONFIGURATION
|
|
#===============================================================================
|
|
|
|
header "CONFIGURING BOOTLOADER"
|
|
|
|
LUKS_UUID=$(cat /tmp/luks_uuid)
|
|
KERNEL_NAME=$(cat /tmp/kernel_name)
|
|
|
|
# Configure mkinitcpio for encryption using Omarchy's drop-in approach
|
|
log "Configuring initramfs for encryption..."
|
|
|
|
# Install required packages
|
|
log "Installing plymouth and limine tools..."
|
|
arch-chroot /mnt pacman --noconfirm --needed -S plymouth limine-snapper-sync limine-mkinitcpio-hook || warn "Some packages may already be installed"
|
|
|
|
# Create Omarchy-style mkinitcpio drop-in config with encryption support
|
|
# This uses the same hooks as Omarchy but includes encrypt for LUKS
|
|
mkdir -p /mnt/etc/mkinitcpio.conf.d
|
|
cat > /mnt/etc/mkinitcpio.conf.d/omarchy_hooks.conf << 'MKINIT'
|
|
HOOKS=(base udev plymouth keyboard autodetect microcode modconf kms keymap consolefont block encrypt filesystems fsck btrfs-overlayfs)
|
|
MKINIT
|
|
|
|
cat > /mnt/etc/mkinitcpio.conf.d/thunderbolt_module.conf << 'MKINIT'
|
|
MODULES+=(thunderbolt)
|
|
MKINIT
|
|
|
|
# Rebuild initramfs (warnings about missing firmware are OK)
|
|
log "Building initramfs (warnings about missing firmware are normal)..."
|
|
if ! arch-chroot /mnt mkinitcpio -P; then
|
|
warn "mkinitcpio reported warnings - checking if initramfs was created..."
|
|
fi
|
|
|
|
# Verify the initramfs was actually created for our kernel
|
|
if [[ -f /mnt/boot/initramfs-${KERNEL_NAME}.img ]]; then
|
|
log "initramfs-${KERNEL_NAME}.img created successfully"
|
|
elif [[ -f /mnt/boot/initramfs-linux.img ]]; then
|
|
log "initramfs-linux.img created successfully"
|
|
else
|
|
error "initramfs was not created! Check mkinitcpio output above."
|
|
fi
|
|
|
|
# Verify kernel was installed
|
|
if [[ ! -f /mnt/boot/vmlinuz-$KERNEL_NAME ]]; then
|
|
error "Kernel vmlinuz-$KERNEL_NAME not found in /mnt/boot/"
|
|
fi
|
|
log "Found kernel: vmlinuz-$KERNEL_NAME"
|
|
|
|
# Configure Limine bootloader using Omarchy's approach
|
|
log "Configuring Limine bootloader..."
|
|
mkdir -p /mnt/boot/EFI/BOOT /mnt/boot/EFI/limine
|
|
|
|
# Copy Limine EFI files
|
|
cp /mnt/usr/share/limine/BOOTX64.EFI /mnt/boot/EFI/BOOT/
|
|
cp /mnt/usr/share/limine/BOOTX64.EFI /mnt/boot/EFI/limine/
|
|
|
|
# Create /etc/default/limine for limine-update (Omarchy style)
|
|
CRYPT_CMDLINE="cryptdevice=UUID=$LUKS_UUID:cryptroot root=/dev/mapper/cryptroot rootflags=subvol=@"
|
|
cat > /mnt/etc/default/limine << LIMINEDEF
|
|
TARGET_OS_NAME="Omarchy"
|
|
|
|
ESP_PATH="/boot"
|
|
|
|
KERNEL_CMDLINE[default]="$CRYPT_CMDLINE rw quiet splash"
|
|
|
|
ENABLE_UKI=yes
|
|
CUSTOM_UKI_NAME="omarchy"
|
|
|
|
ENABLE_LIMINE_FALLBACK=yes
|
|
|
|
# Find and add other bootloaders (Windows)
|
|
FIND_BOOTLOADERS=yes
|
|
|
|
BOOT_ORDER="*, *fallback, Snapshots"
|
|
|
|
MAX_SNAPSHOT_ENTRIES=5
|
|
|
|
SNAPSHOT_FORMAT_CHOICE=5
|
|
LIMINEDEF
|
|
|
|
# Create base limine.conf (limine-update will add entries)
|
|
cat > /mnt/boot/limine.conf << 'LIMINE'
|
|
default_entry: 2
|
|
interface_branding: Omarchy Bootloader
|
|
interface_branding_color: 2
|
|
hash_mismatch_panic: no
|
|
|
|
term_background: 1a1b26
|
|
backdrop: 1a1b26
|
|
|
|
term_palette: 15161e;f7768e;9ece6a;e0af68;7aa2f7;bb9af7;7dcfff;a9b1d6
|
|
term_palette_bright: 414868;f7768e;9ece6a;e0af68;7aa2f7;bb9af7;7dcfff;c0caf5
|
|
|
|
term_foreground: c0caf5
|
|
term_foreground_bright: c0caf5
|
|
term_background_bright: 24283b
|
|
LIMINE
|
|
|
|
# Run limine-update to generate boot entries (including snapshots)
|
|
log "Running limine-update to generate boot entries..."
|
|
arch-chroot /mnt limine-update || warn "limine-update had issues"
|
|
|
|
# Enable btrfs quota for space-aware snapshot cleanup
|
|
log "Enabling btrfs quota..."
|
|
btrfs quota enable /mnt || warn "Could not enable btrfs quota"
|
|
|
|
#===============================================================================
|
|
# INSTALL OMARCHY
|
|
#===============================================================================
|
|
|
|
header "INSTALLING OMARCHY PACKAGES"
|
|
|
|
# Mount offline mirror if available
|
|
if [[ -d /var/cache/omarchy/mirror/offline ]]; then
|
|
mkdir -p /mnt/var/cache/omarchy/mirror/offline
|
|
mount --bind /var/cache/omarchy/mirror/offline /mnt/var/cache/omarchy/mirror/offline
|
|
fi
|
|
|
|
if [[ -d /opt/packages ]]; then
|
|
mkdir -p /mnt/opt/packages
|
|
mount --bind /opt/packages /mnt/opt/packages
|
|
fi
|
|
|
|
# Backup system pacman.conf and use ISO's version temporarily for offline install
|
|
cp /mnt/etc/pacman.conf /mnt/etc/pacman.conf.bak
|
|
cp /etc/pacman.conf /mnt/etc/pacman.conf
|
|
|
|
# Copy omarchy to user's home
|
|
mkdir -p /mnt/home/$OMARCHY_USER/.local/share/
|
|
cp -a /root/omarchy /mnt/home/$OMARCHY_USER/.local/share/
|
|
# Ensure all scripts are executable
|
|
chmod -R +x /mnt/home/$OMARCHY_USER/.local/share/omarchy/bin/
|
|
chmod -R +x /mnt/home/$OMARCHY_USER/.local/share/omarchy/install/
|
|
find /mnt/home/$OMARCHY_USER/.local/share/omarchy -name "*.sh" -exec chmod +x {} \;
|
|
arch-chroot /mnt chown -R $OMARCHY_USER:$OMARCHY_USER /home/$OMARCHY_USER/.local/
|
|
|
|
# Get user info from configurator-generated files
|
|
USER_FULL_NAME=""
|
|
USER_EMAIL=""
|
|
[[ -f /root/user_full_name.txt ]] && USER_FULL_NAME=$(<user_full_name.txt)
|
|
[[ -f /root/user_email_address.txt ]] && USER_EMAIL=$(<user_email_address.txt)
|
|
OMARCHY_MIRROR=""
|
|
[[ -f /root/omarchy_mirror ]] && OMARCHY_MIRROR=$(<omarchy_mirror)
|
|
|
|
# Run omarchy installer in chroot (with proper environment like original installer)
|
|
log "Installing Omarchy packages and configuration..."
|
|
arch-chroot /mnt /bin/bash -c "
|
|
export HOME=/home/$OMARCHY_USER
|
|
export USER=$OMARCHY_USER
|
|
export OMARCHY_CHROOT_INSTALL=1
|
|
export OMARCHY_USER_NAME='$USER_FULL_NAME'
|
|
export OMARCHY_USER_EMAIL='$USER_EMAIL'
|
|
export OMARCHY_MIRROR='$OMARCHY_MIRROR'
|
|
cd /home/$OMARCHY_USER/.local/share/omarchy
|
|
sudo -u $OMARCHY_USER -E bash -c 'source install.sh' || true
|
|
" || warn "Omarchy install script had some issues (this may be OK)"
|
|
|
|
# Set up proper pacman.conf for the installed system
|
|
log "Configuring pacman for installed system..."
|
|
cat > /mnt/etc/pacman.conf << 'PACMANCONF'
|
|
[options]
|
|
Color
|
|
ILoveCandy
|
|
VerbosePkgLists
|
|
HoldPkg = pacman glibc
|
|
Architecture = auto
|
|
CheckSpace
|
|
ParallelDownloads = 5
|
|
DownloadUser = alpm
|
|
|
|
SigLevel = Required DatabaseOptional
|
|
LocalFileSigLevel = Optional
|
|
|
|
[core]
|
|
Include = /etc/pacman.d/mirrorlist
|
|
|
|
[extra]
|
|
Include = /etc/pacman.d/mirrorlist
|
|
|
|
[multilib]
|
|
Include = /etc/pacman.d/mirrorlist
|
|
|
|
[omarchy]
|
|
SigLevel = Optional TrustAll
|
|
Server = https://pkgs.omarchy.org/stable/$arch
|
|
PACMANCONF
|
|
|
|
# Set up mirrorlist
|
|
cat > /mnt/etc/pacman.d/mirrorlist << 'MIRRORLIST'
|
|
Server = https://stable-mirror.omarchy.org/$repo/os/$arch
|
|
MIRRORLIST
|
|
|
|
log "Pacman configured with Omarchy stable repos"
|
|
|
|
# Now that Omarchy is installed, set Plymouth theme and rebuild initramfs
|
|
log "Setting Omarchy Plymouth theme..."
|
|
if [[ -d /mnt/usr/share/plymouth/themes/omarchy ]]; then
|
|
arch-chroot /mnt plymouth-set-default-theme omarchy
|
|
log "Rebuilding initramfs with Omarchy Plymouth theme..."
|
|
arch-chroot /mnt mkinitcpio -P
|
|
else
|
|
warn "Omarchy Plymouth theme not found"
|
|
fi
|
|
|
|
#===============================================================================
|
|
# FINAL SETUP
|
|
#===============================================================================
|
|
|
|
header "FINAL CONFIGURATION"
|
|
|
|
# Enable services
|
|
log "Enabling services..."
|
|
arch-chroot /mnt systemctl enable sddm 2>/dev/null || true
|
|
arch-chroot /mnt systemctl enable NetworkManager 2>/dev/null || true
|
|
arch-chroot /mnt systemctl enable iwd 2>/dev/null || true
|
|
arch-chroot /mnt systemctl enable bluetooth 2>/dev/null || true
|
|
|
|
# Snapper configuration
|
|
log "Installing and configuring snapper..."
|
|
arch-chroot /mnt pacman --noconfirm --needed -S snapper 2>/dev/null || true
|
|
|
|
# Create snapper config for root - this creates .snapshots as nested subvolume in @
|
|
log "Creating snapper root config..."
|
|
arch-chroot /mnt snapper -c root create-config / || warn "snapper create-config had issues"
|
|
|
|
# Enable snapper services
|
|
arch-chroot /mnt systemctl enable snapper-cleanup.timer 2>/dev/null || true
|
|
arch-chroot /mnt systemctl enable snapper-timeline.timer 2>/dev/null || true
|
|
arch-chroot /mnt systemctl enable limine-snapper-sync.service 2>/dev/null || true
|
|
|
|
# Create initial snapshot
|
|
log "Creating initial snapshot..."
|
|
arch-chroot /mnt snapper -c root create -d "Fresh install" || warn "Could not create initial snapshot"
|
|
|
|
# Sync snapshots to limine boot menu
|
|
log "Syncing snapshots to limine..."
|
|
arch-chroot /mnt limine-snapper-sync 2>/dev/null || warn "limine-snapper-sync had issues (may need to run after first boot)"
|
|
|
|
log "Snapper configured with bootable snapshots"
|
|
|
|
# Configure SDDM auto-login (Omarchy uses hyprland-uwsm session)
|
|
log "Configuring auto-login for $OMARCHY_USER..."
|
|
mkdir -p /mnt/etc/sddm.conf.d
|
|
cat > /mnt/etc/sddm.conf.d/autologin.conf << SDDMCONF
|
|
[Autologin]
|
|
User=$OMARCHY_USER
|
|
Session=hyprland-uwsm
|
|
[Theme]
|
|
Current=breeze
|
|
SDDMCONF
|
|
|
|
# Create UEFI boot entry
|
|
log "Creating UEFI boot entry..."
|
|
DRIVE=$(cat /tmp/install_drive)
|
|
EFI_PART=$(cat /tmp/efi_part)
|
|
EFI_PART_NUM=$(echo "$EFI_PART" | grep -oE '[0-9]+$')
|
|
|
|
# Remove any existing Omarchy or Limine entries to avoid duplicates
|
|
for entry in $(efibootmgr 2>/dev/null | grep -iE "Omarchy|Limine" | sed -n 's/^Boot\([0-9A-Fa-f]*\).*/\1/p'); do
|
|
log "Removing old boot entry: $entry"
|
|
efibootmgr -b "$entry" -B 2>/dev/null || true
|
|
done
|
|
|
|
# Create single Omarchy entry
|
|
efibootmgr --create --disk "$DRIVE" --part "$EFI_PART_NUM" \
|
|
--loader '\\EFI\\BOOT\\BOOTX64.EFI' --label 'Omarchy' 2>/dev/null || \
|
|
warn "Could not create UEFI entry - use F12 boot menu"
|
|
|
|
#===============================================================================
|
|
# CLEANUP
|
|
#===============================================================================
|
|
|
|
header "CLEANING UP"
|
|
|
|
umount /mnt/var/cache/omarchy/mirror/offline 2>/dev/null || true
|
|
umount /mnt/opt/packages 2>/dev/null || true
|
|
umount -R /mnt
|
|
cryptsetup close cryptroot
|
|
|
|
rm -f /tmp/luks_uuid /tmp/efi_part /tmp/root_part /tmp/install_drive /tmp/kernel_name
|
|
|
|
#===============================================================================
|
|
# DONE
|
|
#===============================================================================
|
|
|
|
header "INSTALLATION COMPLETE!"
|
|
|
|
echo ""
|
|
echo -e "${GREEN}╔══════════════════════════════════════════════════════════════╗${NC}"
|
|
echo -e "${GREEN}║ ║${NC}"
|
|
echo -e "${GREEN}║ Omarchy has been installed alongside Windows! ║${NC}"
|
|
echo -e "${GREEN}║ ║${NC}"
|
|
echo -e "${GREEN}╚══════════════════════════════════════════════════════════════╝${NC}"
|
|
echo ""
|
|
echo "To boot:"
|
|
echo " 1. Reboot your computer"
|
|
echo " 2. Press F12/F8/DEL at startup for boot menu"
|
|
echo " 3. Select 'Omarchy' for Linux"
|
|
echo " 4. Select 'Windows Boot Manager' for Windows"
|
|
echo ""
|
|
echo " Username: $OMARCHY_USER"
|
|
echo ""
|
|
read -p "Press Enter to reboot..." _
|
|
reboot
|
|
DUALBOOT_SCRIPT
|
|
|
|
chmod +x "$WORK_DIR/extracted/root/dualboot-setup.sh"
|
|
|
|
#===============================================================================
|
|
# MODIFY BOOT SEQUENCE
|
|
#===============================================================================
|
|
|
|
log "Configuring boot sequence..."
|
|
|
|
# Backup original automated script
|
|
if [[ -f "$WORK_DIR/extracted/root/.automated_script.sh" ]]; then
|
|
mv "$WORK_DIR/extracted/root/.automated_script.sh" "$WORK_DIR/extracted/root/.automated_script.sh.orig"
|
|
fi
|
|
|
|
# Replace .zlogin to run our dual-boot setup
|
|
cat > "$WORK_DIR/extracted/root/.zlogin" << 'EOF'
|
|
# Omarchy Dual-Boot Installer
|
|
if [[ $(tty) == "/dev/tty1" ]]; then
|
|
/root/dualboot-setup.sh
|
|
fi
|
|
EOF
|
|
|
|
log "Boot sequence configured"
|
|
|
|
#===============================================================================
|
|
# REPACK SQUASHFS
|
|
#===============================================================================
|
|
|
|
log "Repacking squashfs (this takes several minutes)..."
|
|
rm "$SQUASHFS"
|
|
mksquashfs "$WORK_DIR/extracted" "$SQUASHFS" -comp zstd -Xcompression-level 3
|
|
|
|
if [[ ! -f "$SQUASHFS" ]]; then
|
|
error "Failed to create squashfs!"
|
|
fi
|
|
log "New squashfs created: $(du -h "$SQUASHFS" | cut -f1)"
|
|
|
|
#===============================================================================
|
|
# UPDATE BOOT CONFIGS
|
|
#===============================================================================
|
|
|
|
log "Creating new ISO..."
|
|
|
|
ISO_LABEL=$(isoinfo -d -i "$SOURCE_ISO" 2>/dev/null | grep "Volume id:" | cut -d: -f2 | tr -d ' ' || echo "OMARCHY")
|
|
log "ISO label: $ISO_LABEL"
|
|
|
|
# Update boot configs to use label instead of UUID
|
|
log "Updating boot configurations..."
|
|
for cfg in "$WORK_DIR/newiso/boot/syslinux/"*.cfg \
|
|
"$WORK_DIR/newiso/loader/entries/"*.conf \
|
|
"$WORK_DIR/newiso/boot/grub/grub.cfg" \
|
|
"$WORK_DIR/newiso/EFI/BOOT/grub.cfg"; do
|
|
if [[ -f "$cfg" ]]; then
|
|
sed -i "s/archisosearchuuid=[^ ]*/archisolabel=$ISO_LABEL/g" "$cfg"
|
|
fi
|
|
done
|
|
|
|
#===============================================================================
|
|
# EXTRACT EFI BOOT IMAGE FROM ORIGINAL ISO
|
|
#===============================================================================
|
|
|
|
log "Extracting EFI boot image from original ISO..."
|
|
|
|
# Get the xorriso parameters from the original ISO to replicate its boot setup
|
|
XORRISO_PARAMS=$(xorriso -indev "$SOURCE_ISO" -report_el_torito as_mkisofs 2>/dev/null)
|
|
|
|
# Extract the appended EFI partition from the original ISO
|
|
# Format: -append_partition 2 0xef --interval:local_fs:STARTd-ENDd::
|
|
EFI_LINE=$(echo "$XORRISO_PARAMS" | grep "append_partition 2 0xef")
|
|
if [[ -n "$EFI_LINE" ]]; then
|
|
log "Found EFI partition in original ISO"
|
|
# Extract start and end from the interval (format: NUMBERd-NUMBERd)
|
|
EFI_RANGE=$(echo "$EFI_LINE" | grep -oE '[0-9]+d-[0-9]+d')
|
|
EFI_START=$(echo "$EFI_RANGE" | cut -d'-' -f1 | tr -d 'd')
|
|
EFI_END=$(echo "$EFI_RANGE" | cut -d'-' -f2 | tr -d 'd')
|
|
|
|
if [[ -n "$EFI_START" && -n "$EFI_END" ]]; then
|
|
EFI_SIZE=$((EFI_END - EFI_START + 1))
|
|
log "Extracting EFI partition: start=$EFI_START end=$EFI_END size=$EFI_SIZE sectors"
|
|
# The 'd' values from xorriso are in 512-byte sectors
|
|
dd if="$SOURCE_ISO" of="$WORK_DIR/efi.img" bs=512 skip="$EFI_START" count="$EFI_SIZE" status=progress
|
|
|
|
if [[ -f "$WORK_DIR/efi.img" ]]; then
|
|
EFI_IMG_SIZE=$(stat -c%s "$WORK_DIR/efi.img")
|
|
log "EFI image created: $((EFI_IMG_SIZE / 1024 / 1024))MB"
|
|
else
|
|
warn "Failed to extract EFI image"
|
|
fi
|
|
else
|
|
warn "Could not parse EFI partition range"
|
|
fi
|
|
else
|
|
warn "No EFI partition found in original ISO"
|
|
fi
|
|
|
|
#===============================================================================
|
|
# CREATE ISO
|
|
#===============================================================================
|
|
|
|
if [[ -f "$WORK_DIR/newiso/boot/syslinux/isolinux.bin" ]]; then
|
|
log "Creating hybrid BIOS/UEFI ISO..."
|
|
|
|
if [[ -f "$WORK_DIR/efi.img" ]]; then
|
|
# Use extracted EFI image for proper UEFI boot
|
|
xorriso -as mkisofs \
|
|
-iso-level 3 \
|
|
-full-iso9660-filenames \
|
|
-rational-rock \
|
|
-volid "$ISO_LABEL" \
|
|
-eltorito-boot boot/syslinux/isolinux.bin \
|
|
-eltorito-catalog boot/syslinux/boot.cat \
|
|
-no-emul-boot \
|
|
-boot-load-size 4 \
|
|
-boot-info-table \
|
|
-isohybrid-mbr "$WORK_DIR/newiso/boot/syslinux/isohdpfx.bin" \
|
|
-eltorito-alt-boot \
|
|
-e --interval:appended_partition_2:all:: \
|
|
-no-emul-boot \
|
|
-append_partition 2 0xef "$WORK_DIR/efi.img" \
|
|
-isohybrid-gpt-basdat \
|
|
-o "$OUTPUT_ISO" \
|
|
"$WORK_DIR/newiso"
|
|
else
|
|
warn "EFI image not found, using fallback method..."
|
|
xorriso -as mkisofs \
|
|
-iso-level 3 \
|
|
-full-iso9660-filenames \
|
|
-rational-rock \
|
|
-volid "$ISO_LABEL" \
|
|
-eltorito-boot boot/syslinux/isolinux.bin \
|
|
-eltorito-catalog boot/syslinux/boot.cat \
|
|
-no-emul-boot \
|
|
-boot-load-size 4 \
|
|
-boot-info-table \
|
|
-isohybrid-mbr "$WORK_DIR/newiso/boot/syslinux/isohdpfx.bin" \
|
|
-eltorito-alt-boot \
|
|
-e EFI/BOOT/BOOTX64.EFI \
|
|
-no-emul-boot \
|
|
-isohybrid-gpt-basdat \
|
|
-o "$OUTPUT_ISO" \
|
|
"$WORK_DIR/newiso"
|
|
fi
|
|
else
|
|
log "Creating UEFI-only ISO..."
|
|
xorriso -as mkisofs \
|
|
-iso-level 3 \
|
|
-full-iso9660-filenames \
|
|
-volid "$ISO_LABEL" \
|
|
-e EFI/BOOT/BOOTX64.EFI \
|
|
-no-emul-boot \
|
|
-isohybrid-gpt-basdat \
|
|
-o "$OUTPUT_ISO" \
|
|
"$WORK_DIR/newiso"
|
|
fi
|
|
|
|
#===============================================================================
|
|
# DONE
|
|
#===============================================================================
|
|
|
|
echo ""
|
|
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
echo -e "${GREEN} SUCCESS!${NC}"
|
|
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
echo ""
|
|
echo " Created: $OUTPUT_ISO"
|
|
echo " Size: $(du -h "$OUTPUT_ISO" | cut -f1)"
|
|
echo ""
|
|
echo " This ISO provides:"
|
|
echo " • Option 1: Dual-boot install (preserves Windows)"
|
|
echo " • Option 3: Standard install (original Omarchy installer)"
|
|
echo ""
|
|
echo " Next steps:"
|
|
echo " 1. Copy ISO to Ventoy USB drive"
|
|
echo " 2. Boot from Ventoy"
|
|
echo " 3. Select option 1 for dual-boot"
|
|
echo ""
|