#!/bin/bash # ABOUTME: Transforms an existing Arch+Wayland system into a Moonarch system. # ABOUTME: Backs up configs, installs moonarch-git package, deploys user configs with hard overwrite. set -euo pipefail source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib.sh" # --- Parse arguments --- DRY_RUN=false if [[ "${1:-}" == "--dry-run" ]]; then DRY_RUN=true fi # ============================================================ # Phase 1: Prerequisites & Detection # ============================================================ check_not_root check_pacman # Require active Wayland session if [[ "${XDG_SESSION_TYPE:-}" != "wayland" ]] && [[ -z "${WAYLAND_DISPLAY:-}" ]]; then err "No active Wayland session detected. Transform requires Wayland." exit 1 fi # Detect conflicting display managers CONFLICTING_DMS=() for dm in sddm gdm lightdm ly lemurs; do if systemctl is-enabled "${dm}.service" &>/dev/null; then CONFLICTING_DMS+=("$dm") fi done # Detect conflicting packages CONFLICTING_PKGS=() if pacman -Qq pulseaudio &>/dev/null && ! pacman -Qq pipewire-pulse &>/dev/null; then CONFLICTING_PKGS+=("pulseaudio") fi # ============================================================ # Phase 2: Pre-flight Summary # ============================================================ echo "" if $DRY_RUN; then log "============================================" log " Moonarch Transform — Dry Run" log "============================================" else log "============================================" log " Moonarch Transform — Pre-flight Summary" log "============================================" fi echo "" log "Wayland session: detected" if [[ ${#CONFLICTING_DMS[@]} -gt 0 ]]; then log "Display managers: ${CONFLICTING_DMS[*]} (will be disabled)" else log "Display managers: none conflicting" fi if [[ ${#CONFLICTING_PKGS[@]} -gt 0 ]]; then log "Package conflicts: ${CONFLICTING_PKGS[*]} (will be removed)" else log "Package conflicts: none" fi echo "" log "Actions:" log " 1. (Optional) Backup ~/.config/, ~/.zshrc, /etc/xdg/" log " 2. Install moonarch-git package (pulls in all dependencies)" log " 3. Disable conflicting DMs, enable greetd" log " 4. Overwrite ALL user configs (~/.config/)" log " 5. Configure GTK themes, firewall, services" echo "" # Show package diff MISSING_OFFICIAL=$(comm -23 <(read_packages "$OFFICIAL_PACKAGES" | sort) <(pacman -Qqe | sort) 2>/dev/null || true) MISSING_AUR="" if [[ -f "$AUR_PACKAGES" ]]; then MISSING_AUR=$(comm -23 <(read_packages "$AUR_PACKAGES" | sort) <(pacman -Qqe | sort) 2>/dev/null || true) fi if [[ -n "$MISSING_OFFICIAL" ]] || [[ -n "$MISSING_AUR" ]]; then log "Packages to install:" if [[ -n "$MISSING_OFFICIAL" ]]; then OFFICIAL_COUNT=$(echo "$MISSING_OFFICIAL" | wc -l) log " Official ($OFFICIAL_COUNT):" echo "$MISSING_OFFICIAL" | sed 's/^/ /' fi if [[ -n "$MISSING_AUR" ]]; then AUR_COUNT=$(echo "$MISSING_AUR" | wc -l) log " AUR ($AUR_COUNT):" echo "$MISSING_AUR" | sed 's/^/ /' fi else log "Packages: all already installed" fi # Show config diff summary CHANGED_XDG=0 CHANGED_BIN=0 for src_dir in "$DEFAULTS_DIR/xdg/"*/; do app_name="$(basename "$src_dir")" dest_dir="$HOME/.config/$app_name" if [[ -d "$dest_dir" ]]; then if ! diff -rq "$src_dir" "$dest_dir" &>/dev/null 2>&1; then ((CHANGED_XDG++)) || true fi else ((CHANGED_XDG++)) || true fi done for bin in "$DEFAULTS_DIR/bin/moonarch-"*; do name=$(basename "$bin") if ! cmp -s "$bin" "/usr/bin/$name" 2>/dev/null; then ((CHANGED_BIN++)) || true fi done log "Config changes: $CHANGED_XDG XDG app(s), $CHANGED_BIN helper script(s)" echo "" err "This will REPLACE your current desktop configuration." echo "" if $DRY_RUN; then log "Dry run complete — no changes were made." exit 0 fi if ! confirm "Proceed?"; then log "Transform cancelled." exit 0 fi # ============================================================ # Phase 3: Backup (optional) # ============================================================ BACKUP_FILE="" SYSTEM_BACKUP="" if confirm "Create backup of current configs before overwriting?"; then BACKUP_FILE="$HOME/moonarch-backup-$(date +%Y%m%d-%H%M%S).tar.gz" log "Creating backup: $BACKUP_FILE" # Build list of paths that actually exist BACKUP_PATHS=() [[ -d "$HOME/.config" ]] && BACKUP_PATHS+=(".config") [[ -f "$HOME/.zshrc" ]] && BACKUP_PATHS+=(".zshrc") [[ -d "$HOME/.zshrc.d" ]] && BACKUP_PATHS+=(".zshrc.d") if [[ ${#BACKUP_PATHS[@]} -gt 0 ]]; then tar czf "$BACKUP_FILE" -C "$HOME" "${BACKUP_PATHS[@]}" log " + User configs backed up." fi # Backup system XDG separately (needs sudo) if [[ -d /etc/xdg ]]; then SYSTEM_BACKUP="$HOME/moonarch-backup-system-$(date +%Y%m%d-%H%M%S).tar.gz" sudo tar czf "$SYSTEM_BACKUP" -C / etc/xdg sudo chown "$USER:$USER" "$SYSTEM_BACKUP" log " + System configs backed up: $SYSTEM_BACKUP" fi log "Backup complete: $(du -h "$BACKUP_FILE" | cut -f1)" else log "Skipping backup." fi # ============================================================ # Phase 4: Disable Conflicting Display Managers # ============================================================ if [[ ${#CONFLICTING_DMS[@]} -gt 0 ]]; then log "Disabling conflicting display managers..." for dm in "${CONFLICTING_DMS[@]}"; do sudo systemctl disable "$dm" log " - $dm disabled" done fi # ============================================================ # Phase 5: Remove Conflicting Packages # ============================================================ if pacman -Qq pulseaudio &>/dev/null; then log "Removing PulseAudio (replaced by PipeWire)..." sudo pacman -Rdd --noconfirm pulseaudio pulseaudio-alsa pulseaudio-bluetooth 2>/dev/null || true fi # ============================================================ # Phase 6: Install moonarch-git Package # ============================================================ # Install paru if not present if ! command -v paru &>/dev/null; then log "Installing paru..." sudo pacman -S --needed --noconfirm paru else log "paru already installed." fi # Moonarch package registry log "Setting up Moonarch package registry..." if ! grep -q '\[moonarch\]' /etc/pacman.conf 2>/dev/null; then sudo tee -a /etc/pacman.conf > /dev/null <<'EOCONF' [moonarch] SigLevel = Required DatabaseOptional Server = https://gitea.moonarch.de/api/packages/nevaforget/arch/$repo/$arch EOCONF log " + Moonarch repo added to pacman.conf." else log " ~ Moonarch repo already in pacman.conf." fi log "Importing Moonarch registry signing key..." EXPECTED_FINGERPRINT="9B02C596A4652C40CA768E75B90C8B82EA30A131" KEY_FILE=$(mktemp) trap 'rm -f "$KEY_FILE"' EXIT curl -sf https://gitea.moonarch.de/api/packages/nevaforget/arch/repository.key -o "$KEY_FILE" if [[ ! -s "$KEY_FILE" ]]; then err "Failed to download registry key (empty response)." exit 1 fi KEY_FPR=$(gpg --show-keys --with-colons "$KEY_FILE" 2>/dev/null | awk -F: '/^fpr/{print $10; exit}') if [[ "$KEY_FPR" != "$EXPECTED_FINGERPRINT" ]]; then err "Registry key fingerprint mismatch! Expected $EXPECTED_FINGERPRINT, got ${KEY_FPR:-}" exit 1 fi KEY_ID=$(gpg --show-keys --with-colons "$KEY_FILE" 2>/dev/null | awk -F: '/^pub/{print $5}') if [[ -n "$KEY_ID" ]] && ! sudo pacman-key --list-keys "$KEY_ID" &>/dev/null; then sudo pacman-key --add "$KEY_FILE" sudo pacman-key --lsign-key "$KEY_ID" log " + Registry key $KEY_ID imported and locally signed." else log " ~ Registry key already imported." fi rm -f "$KEY_FILE" trap - EXIT # Install/update moonarch-git (paru repo config is set up by moonarch.install hook) paru -Syu --pkgbuilds --noconfirm log "Installing moonarch-git package..." paru -S --needed --noconfirm moonarch-git # Install packages from package lists if [[ -n "$MISSING_OFFICIAL" ]]; then log "Installing official packages..." echo "$MISSING_OFFICIAL" | paru -S --needed --noconfirm - fi if [[ -n "$MISSING_AUR" ]]; then log "Installing AUR packages..." echo "$MISSING_AUR" | paru -S --needed --noconfirm - fi # ============================================================ # Phase 7: User-Level Configuration # ============================================================ # User-level GTK4 symlinks for libadwaita apps THEME_NAME="Colloid-Grey-Dark-Catppuccin" THEME_GTK4="/usr/share/themes/$THEME_NAME/gtk-4.0" if [[ -d "$THEME_GTK4" ]]; then log "Creating user-level GTK4 symlinks for $THEME_NAME..." USER_GTK4="$HOME/.config/gtk-4.0" mkdir -p "$USER_GTK4" ln -sf "$THEME_GTK4/gtk-dark.css" "$USER_GTK4/gtk.css" ln -sf "$THEME_GTK4/gtk-dark.css" "$USER_GTK4/gtk-dark.css" rm -f "$USER_GTK4/assets" ln -s "$THEME_GTK4/assets" "$USER_GTK4/assets" else err "GTK4 theme not found: $THEME_GTK4 — libadwaita apps will use fallback theme." fi # gsettings log "Setting gsettings for GTK theme..." gsettings set org.gnome.desktop.interface gtk-theme "$THEME_NAME" gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark' gsettings set org.gnome.desktop.interface icon-theme 'Colloid-Grey-Catppuccin-Dark' gsettings set org.gnome.desktop.interface font-name 'UbuntuSans Nerd Font 11' # ============================================================ # Phase 8: Deploy User Configs (Hard Overwrite) # ============================================================ # Replace user-level XDG configs from /etc/xdg/ (deployed by moonarch-git) log "Deploying XDG configs to ~/.config/ (overwrite)..." for src_dir in /etc/xdg/*/; do app_name="$(basename "$src_dir")" # Only overwrite apps that moonarch manages [[ -d "$DEFAULTS_DIR/xdg/$app_name" ]] || continue # gtk-4.0 is handled separately with Colloid-Grey-Dark-Catppuccin theme symlinks [[ "$app_name" == "gtk-4.0" ]] && continue # kanshi profiles are user-specific (display setup) — only seed, never overwrite [[ "$app_name" == "kanshi" ]] && [[ -d "$HOME/.config/kanshi" ]] && continue dest_dir="$HOME/.config/$app_name" rm -rf "$dest_dir" 2>/dev/null || sudo rm -rf "$dest_dir" cp -r --no-preserve=ownership "$src_dir" "$dest_dir" log " + $app_name/" done # Overwrite configs owned by other packages with moonarch versions log "Deploying moonarch config overrides..." cp /usr/share/moonarch/walker-config.toml "$HOME/.config/walker/config.toml" log " + walker/config.toml" # Zsh: always create a fresh .zshrc that sources Moonarch defaults log "Creating ~/.zshrc with Moonarch defaults..." mkdir -p "$HOME/.zshrc.d" echo "# Load Moonarch defaults, add custom overrides in ~/.zshrc.d/ or below" > "$HOME/.zshrc" echo "source /etc/zsh/zshrc.moonarch" >> "$HOME/.zshrc" # ============================================================ # Phase 9: Services & Finalization # ============================================================ # Enable systemd user services log "Enabling systemd user services..." USER_SERVICES=( "kanshi" "stasis" "cliphist-text" "cliphist-image" ) for service in "${USER_SERVICES[@]}"; do if systemctl --user cat "${service}.service" &>/dev/null; then systemctl --user enable "$service" log " + $service (user)" else log " ~ $service (user) not found, skipped." fi done log "Enabling services..." SERVICES=( "NetworkManager" "bluetooth" "greetd" "systemd-timesyncd" "ufw" "auto-cpufreq" ) for service in "${SERVICES[@]}"; do if systemctl list-unit-files "${service}.service" &>/dev/null; then sudo systemctl enable "$service" log " + $service" else log " ~ $service not found, skipped." fi done # Set shell to zsh if [[ "$SHELL" != */zsh ]]; then log "Setting default shell to zsh..." chsh -s "$(which zsh)" fi # Firewall log "Configuring UFW..." sudo ufw default deny incoming sudo ufw default allow outgoing sudo ufw --force enable # Directories mkdir -p "$HOME/Pictures/Screenshots" mkdir -p "$HOME/Pictures/Wallpaper" # ============================================================ # Phase 10: Done # ============================================================ echo "" log "============================================" log " Moonarch transform complete!" log "============================================" echo "" if [[ -n "$BACKUP_FILE" ]]; then log "Your previous config is backed up at:" log " $BACKUP_FILE" if [[ -n "$SYSTEM_BACKUP" ]]; then log " $SYSTEM_BACKUP" fi fi echo "" log "Next steps:" log " 1. Reboot (greetd replaces your previous display manager)" log " 2. Place wallpapers in ~/Pictures/Wallpaper/" log " 3. rustup default stable" log " 4. User overrides in ~/.config/ or ~/.zshrc.d/" echo "" err "Do NOT log out — your previous DM is disabled. Reboot instead." echo ""