From 9d26f04af68a042a721b48d42cab503f011dbfc2 Mon Sep 17 00:00:00 2001 From: nevaforget Date: Sun, 29 Mar 2026 15:03:44 +0200 Subject: [PATCH] Add transform script for existing Arch+Wayland systems Extract shared helpers into lib.sh (log, err, confirm, path constants) and refactor post-install.sh + update.sh to source it. New transform.sh converts an existing Arch+Wayland system to Moonarch: pre-flight summary, config backup, DM conflict resolution, PulseAudio removal, full package install, and hard overwrite of all configs. Also migrate kanshi from niri spawn-at-startup to systemd user service. --- README.md | 43 ++- defaults/user/systemd/user/kanshi.service | 17 + defaults/xdg/niri/config.kdl | 2 +- scripts/lib.sh | 45 +++ scripts/post-install.sh | 47 ++- scripts/transform.sh | 367 ++++++++++++++++++++++ scripts/update.sh | 30 +- 7 files changed, 489 insertions(+), 62 deletions(-) create mode 100644 defaults/user/systemd/user/kanshi.service create mode 100755 scripts/lib.sh create mode 100755 scripts/transform.sh diff --git a/README.md b/README.md index 07f2cf3..c9040d2 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ desktop that can be rebuilt from scratch in minutes. ## Quick Start -### Install +### Fresh Install (Bare Metal) 1. Boot the [Arch ISO](https://archlinux.org/download/) and download the config: ```bash @@ -53,6 +53,28 @@ The archinstall config clones this repo to `/opt/moonarch` via custom-commands. post-install.sh handles the remaining ~100 packages, all configs, themes, services, and user setup. +### Transform (Existing Arch+Wayland System) + +Already running Arch with a Wayland compositor (Sway, Hyprland, GNOME Wayland, etc.)? +Transform converts your system to Moonarch without reinstalling. + +**Prerequisites:** Active Wayland session, git installed. + +```bash +git clone https://gitea.moonarch.de/nevaforget/moonarch.git /opt/moonarch +/opt/moonarch/scripts/transform.sh +``` + +The script will: +1. Show a pre-flight summary of all changes +2. Back up your `~/.config/`, `~/.zshrc`, and `/etc/xdg/` to timestamped tar archives +3. Disable conflicting display managers (SDDM, GDM, LightDM, etc.) +4. Remove conflicting packages (e.g. PulseAudio → PipeWire) +5. Install all Moonarch packages and configs (**hard overwrite** of all user configs) +6. Enable greetd, firewall, and system services + +After completion, **reboot** (do not log out — your previous DM is already disabled). + ### Update ```bash @@ -74,7 +96,9 @@ packages/ aur.txt AUR packages (~15), one per line scripts/ + lib.sh Shared helpers sourced by all scripts post-install.sh Main automation (packages, configs, themes, services) + transform.sh Convert existing Arch+Wayland system to Moonarch install-themes.sh Cursor theme installer (Sweet-cursors) update.sh System updater (symlinked to moonarch-update) @@ -113,6 +137,7 @@ defaults/ shell/zshrc Zsh config: prompt, aliases, FZF, completion etc/greetd/ greetd daemon + greeter Niri config etc/moongreet/ moongreet configuration + user/systemd/user/ Systemd user services (kanshi) user/waybar/ Per-user Waybar overrides (only copied if missing) backgrounds/wallpaper.jpg Default wallpaper (shared by desktop, greeter, lock screen) ``` @@ -125,10 +150,12 @@ greetd ─► niri (greeter instance) ─► moongreet ─► user authenticates ▼ niri (user session) │ - ┌─────────┬──────────┬────────┼────────┬──────────┐ - ▼ ▼ ▼ ▼ ▼ ▼ - kanshi waybar dunst foot waypaper cliphist - (outputs) (bar) (notify) (server) (wallpaper) (clipboard) + ┌──────────┬────────┬────────┬──────────┐ + ▼ ▼ ▼ ▼ ▼ + waybar dunst foot waypaper cliphist + (bar) (notify) (server) (wallpaper) (clipboard) + + kanshi runs as a systemd user service (graphical-session.target) ``` ## Keybinds (Default) @@ -186,6 +213,12 @@ are part of the system and updated via `moonarch-update`. | ufw | Firewall (deny incoming, allow outgoing) | | auto-cpufreq | CPU frequency scaling (AC: performance, battery: powersave) | +### User Services (systemd --user) + +| Service | Purpose | +|---------|---------| +| kanshi | Dynamic display configuration (auto-switch output profiles on hotplug) | + ## Moonarch Ecosystem Moonarch is the deployment and configuration layer. Three companion projects provide diff --git a/defaults/user/systemd/user/kanshi.service b/defaults/user/systemd/user/kanshi.service new file mode 100644 index 0000000..d059223 --- /dev/null +++ b/defaults/user/systemd/user/kanshi.service @@ -0,0 +1,17 @@ +# ABOUTME: systemd user service for kanshi dynamic display configuration. +# ABOUTME: Starts kanshi as part of the graphical session, with automatic restart. + +[Unit] +Description=Dynamic display configuration +Documentation=man:kanshi(1) +PartOf=graphical-session.target +After=graphical-session.target + +[Service] +Type=simple +ExecStart=/usr/bin/kanshi +Restart=on-failure +RestartSec=3 + +[Install] +WantedBy=graphical-session.target diff --git a/defaults/xdg/niri/config.kdl b/defaults/xdg/niri/config.kdl index c8e863b..aa5969d 100644 --- a/defaults/xdg/niri/config.kdl +++ b/defaults/xdg/niri/config.kdl @@ -77,7 +77,7 @@ layout { } // xwayland-satellite is managed automatically since niri 25.08 -spawn-at-startup "kanshi" +// kanshi is managed via systemd user service (kanshi.service) spawn-at-startup "waybar" spawn-at-startup "dunst" spawn-at-startup "/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1" diff --git a/scripts/lib.sh b/scripts/lib.sh new file mode 100755 index 0000000..96577f7 --- /dev/null +++ b/scripts/lib.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# ABOUTME: Shared helper functions and constants for Moonarch scripts. +# ABOUTME: Sourced by post-install.sh, update.sh and transform.sh. + +# Path constants — BASH_SOURCE[1] resolves to the calling script, not lib.sh itself. +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[1]}")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" +OFFICIAL_PACKAGES="$PROJECT_DIR/packages/official.txt" +AUR_PACKAGES="$PROJECT_DIR/packages/aur.txt" +DEFAULTS_DIR="$PROJECT_DIR/defaults" + +# --- Helper functions --- + +log() { + echo -e "\e[1;34m[Moonarch]\e[0m $*" +} + +err() { + echo -e "\e[1;31m[Moonarch ERROR]\e[0m $*" >&2 +} + +read_packages() { + grep -v '^\s*#' "$1" | grep -v '^\s*$' +} + +confirm() { + read -r -p "$1 [y/N] " response + [[ "$response" =~ ^[yY]$ ]] +} + +# --- Prerequisite checks --- + +check_not_root() { + if [[ $EUID -eq 0 ]]; then + err "Do NOT run as root. The script uses sudo where needed." + exit 1 + fi +} + +check_pacman() { + if ! command -v pacman &>/dev/null; then + err "pacman not found — is this really Arch Linux?" + exit 1 + fi +} diff --git a/scripts/post-install.sh b/scripts/post-install.sh index f452769..e1eb205 100755 --- a/scripts/post-install.sh +++ b/scripts/post-install.sh @@ -4,37 +4,12 @@ set -euo pipefail -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_DIR="$(dirname "$SCRIPT_DIR")" -OFFICIAL_PACKAGES="$PROJECT_DIR/packages/official.txt" -AUR_PACKAGES="$PROJECT_DIR/packages/aur.txt" -DEFAULTS_DIR="$PROJECT_DIR/defaults" - -# --- Helper functions --- - -log() { - echo -e "\e[1;34m[Moonarch]\e[0m $*" -} - -err() { - echo -e "\e[1;31m[Moonarch ERROR]\e[0m $*" >&2 -} - -read_packages() { - grep -v '^\s*#' "$1" | grep -v '^\s*$' -} +source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib.sh" # --- Prerequisites --- -if [[ $EUID -eq 0 ]]; then - err "Do NOT run as root. The script uses sudo where needed." - exit 1 -fi - -if ! command -v pacman &>/dev/null; then - err "pacman not found — is this really Arch Linux?" - exit 1 -fi +check_not_root +check_pacman # --- Install official packages --- @@ -251,6 +226,22 @@ if [[ -d "$USER_DEFAULTS_DIR" ]]; then done fi +# --- Enable systemd user services --- + +log "Enabling systemd user services..." +USER_SERVICES=( + "kanshi" +) + +for service in "${USER_SERVICES[@]}"; do + if systemctl --user list-unit-files "${service}.service" &>/dev/null; then + systemctl --user enable "$service" + log " + $service (user)" + else + log " ~ $service (user) not found, skipped." + fi +done + # --- Screenshots directory --- mkdir -p "$HOME/Pictures/Screenshots" diff --git a/scripts/transform.sh b/scripts/transform.sh new file mode 100755 index 0000000..355b3d2 --- /dev/null +++ b/scripts/transform.sh @@ -0,0 +1,367 @@ +#!/bin/bash +# ABOUTME: Transforms an existing Arch+Wayland system into a Moonarch system. +# ABOUTME: Backs up configs, installs packages, deploys all defaults with hard overwrite. + +set -euo pipefail + +source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib.sh" + +# ============================================================ +# 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 "" +log "============================================" +log " Moonarch Transform — Pre-flight Summary" +log "============================================" +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. Backup ~/.config/, ~/.zshrc, /etc/xdg/ to ~/moonarch-backup-.tar.gz" +log " 2. Install official + AUR packages" +log " 3. Disable conflicting DMs, enable greetd" +log " 4. Overwrite ALL system configs (/etc/xdg/, /etc/greetd/, ...)" +log " 5. Overwrite ALL user configs (~/.config/)" +log " 6. Configure zsh, GTK themes, firewall, services" +echo "" +err "This will REPLACE your current desktop configuration." +echo "" + +if ! confirm "Proceed?"; then + log "Transform cancelled." + exit 0 +fi + +# ============================================================ +# Phase 3: Backup +# ============================================================ + +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)" + +# ============================================================ +# 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 Packages +# ============================================================ + +log "Installing official packages..." +if [[ -f "$OFFICIAL_PACKAGES" ]]; then + # shellcheck disable=SC2046 + sudo pacman -S --needed --noconfirm $(read_packages "$OFFICIAL_PACKAGES") +else + err "Package list not found: $OFFICIAL_PACKAGES" + exit 1 +fi + +# Install paru if not present +if ! command -v paru &>/dev/null; then + log "Installing paru..." + PARU_TMPDIR=$(mktemp -d) + git clone https://aur.archlinux.org/paru.git "$PARU_TMPDIR/paru" + (cd "$PARU_TMPDIR/paru" && makepkg -si --noconfirm) + rm -rf "$PARU_TMPDIR" +else + log "paru already installed." +fi + +log "Installing AUR packages..." +if [[ -f "$AUR_PACKAGES" ]]; then + # shellcheck disable=SC2046 + paru -S --needed --noconfirm $(read_packages "$AUR_PACKAGES") +else + err "AUR package list not found: $AUR_PACKAGES" + exit 1 +fi + +# Moonarch custom paru repo +log "Setting up Moonarch paru repo..." +PARU_CONF="$HOME/.config/paru/paru.conf" +mkdir -p "$(dirname "$PARU_CONF")" +if ! grep -q '\[moonarch\]' "$PARU_CONF" 2>/dev/null; then + cat >> "$PARU_CONF" <<'EOCONF' +[moonarch] +Url = https://gitea.moonarch.de/nevaforget/moonarch-pkgbuilds.git +EOCONF + log " + Moonarch repo added to paru.conf." +else + log " ~ Moonarch repo already in paru.conf." +fi + +paru -Sy --pkgbuilds --noconfirm +paru -S --needed --noconfirm moonset-git moonlock-git moongreet-git + +# Themes +log "Installing themes..." +"$SCRIPT_DIR/install-themes.sh" + +# ============================================================ +# Phase 7: Deploy System Configs (Hard Overwrite) +# ============================================================ + +log "Deploying XDG defaults to /etc/xdg/..." +sudo cp -r "$DEFAULTS_DIR/xdg/"* /etc/xdg/ +sudo find /etc/xdg/rofi -name "*.sh" -exec chmod +x {} \; + +# GTK4 theme symlinks +THEME_NAME="Colloid-Catppuccin" +THEME_GTK4="/usr/share/themes/$THEME_NAME/gtk-4.0" +GTK4_XDG="/etc/xdg/gtk-4.0" + +if [[ -d "$THEME_GTK4" ]]; then + log "Creating system-wide GTK4 symlinks for $THEME_NAME..." + sudo ln -sf "$THEME_GTK4/gtk-dark.css" "$GTK4_XDG/gtk.css" + sudo ln -sf "$THEME_GTK4/gtk-dark.css" "$GTK4_XDG/gtk-dark.css" + sudo rm -f "$GTK4_XDG/assets" + sudo ln -s "$THEME_GTK4/assets" "$GTK4_XDG/assets" + + # libadwaita only reads user CSS from ~/.config/gtk-4.0/ + 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' + +# Helper scripts +log "Installing Moonarch helper scripts to /usr/local/bin/..." +sudo install -m 755 "$DEFAULTS_DIR/bin/moonarch-"* /usr/local/bin/ + +# awww compatibility symlinks +if command -v awww &>/dev/null && ! command -v swww &>/dev/null; then + log "Creating swww -> awww compatibility symlinks..." + sudo ln -s /usr/bin/awww /usr/local/bin/swww + sudo ln -s /usr/bin/awww-daemon /usr/local/bin/swww-daemon +fi + +# Zsh configuration +log "Installing Zsh default config..." +sudo cp "$DEFAULTS_DIR/shell/zshrc" /etc/zsh/zshrc.moonarch + +if ! grep -q "zshrc.moonarch" /etc/zsh/zshrc 2>/dev/null; then + echo '# Moonarch defaults (overridden by ~/.zshrc)' | sudo tee -a /etc/zsh/zshrc > /dev/null + echo '[[ ! -f "$HOME/.zshrc" ]] && source /etc/zsh/zshrc.moonarch' | sudo tee -a /etc/zsh/zshrc > /dev/null +fi + +# For transform: 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" + +# greetd / moongreet +log "Configuring greetd + moongreet..." +sudo mkdir -p /etc/greetd +sudo cp "$DEFAULTS_DIR/etc/greetd/config.toml" /etc/greetd/config.toml +sudo mkdir -p /etc/moongreet +sudo cp "$DEFAULTS_DIR/etc/moongreet/moongreet.toml" /etc/moongreet/moongreet.toml + +# Niri greeter config (if present) +if [[ -f "$DEFAULTS_DIR/etc/greetd/niri-greeter.kdl" ]]; then + sudo cp "$DEFAULTS_DIR/etc/greetd/niri-greeter.kdl" /etc/greetd/niri-greeter.kdl +fi + +# Wallpaper +log "Installing default wallpaper..." +sudo mkdir -p /usr/share/moonarch +sudo cp "$DEFAULTS_DIR/backgrounds/wallpaper.jpg" /usr/share/moonarch/wallpaper.jpg + +# ============================================================ +# Phase 8: Deploy User Configs (Hard Overwrite) +# ============================================================ + +# Replace user-level XDG configs for all apps Moonarch manages +log "Deploying XDG configs to ~/.config/ (overwrite)..." +for src_dir in "$DEFAULTS_DIR/xdg/"*/; do + app_name="$(basename "$src_dir")" + # gtk-4.0 is handled separately with Colloid-Catppuccin theme symlinks (Phase 7) + [[ "$app_name" == "gtk-4.0" ]] && continue + dest_dir="$HOME/.config/$app_name" + rm -rf "$dest_dir" + cp -r "$src_dir" "$dest_dir" + log " + $app_name/" +done + +# Deploy user defaults (overwrite, no existence check) +log "Deploying user config defaults to ~/.config/ (overwrite)..." +USER_DEFAULTS_DIR="$DEFAULTS_DIR/user" +if [[ -d "$USER_DEFAULTS_DIR" ]]; then + for src_dir in "$USER_DEFAULTS_DIR"/*/; do + app_name="$(basename "$src_dir")" + dest_dir="$HOME/.config/$app_name" + mkdir -p "$dest_dir" + find "$src_dir" -type f -print0 | while IFS= read -r -d '' src_file; do + rel_path="${src_file#"$src_dir"}" + dest_file="$dest_dir/$rel_path" + mkdir -p "$(dirname "$dest_file")" + cp "$src_file" "$dest_file" + log " + $app_name/$rel_path" + done + done +fi + +# ============================================================ +# Phase 9: Services & Finalization +# ============================================================ + +log "Enabling services..." +SERVICES=( + "NetworkManager" + "bluetooth" + "docker" + "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 + +# Docker group +if ! groups | grep -q docker; then + log "Adding user to docker group..." + sudo usermod -aG docker "$USER" +fi + +# Directories +mkdir -p "$HOME/Pictures/Screenshots" +mkdir -p "$HOME/Pictures/Wallpaper" + +# moonarch-update symlink +log "Creating moonarch-update command..." +sudo ln -sf "$PROJECT_DIR/scripts/update.sh" /usr/local/bin/moonarch-update + +# ============================================================ +# Phase 10: Done +# ============================================================ + +echo "" +log "============================================" +log " Moonarch transform complete!" +log "============================================" +echo "" +log "Your previous config is backed up at:" +log " $BACKUP_FILE" +if [[ -n "${SYSTEM_BACKUP:-}" ]]; then + log " $SYSTEM_BACKUP" +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 "" diff --git a/scripts/update.sh b/scripts/update.sh index ba749b1..4ee782d 100755 --- a/scripts/update.sh +++ b/scripts/update.sh @@ -4,37 +4,11 @@ set -euo pipefail -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_DIR="$(dirname "$SCRIPT_DIR")" -OFFICIAL_PACKAGES="$PROJECT_DIR/packages/official.txt" -AUR_PACKAGES="$PROJECT_DIR/packages/aur.txt" -DEFAULTS_DIR="$PROJECT_DIR/defaults" - -# --- Helper functions --- - -log() { - echo -e "\e[1;34m[Moonarch]\e[0m $*" -} - -err() { - echo -e "\e[1;31m[Moonarch ERROR]\e[0m $*" >&2 -} - -read_packages() { - grep -v '^\s*#' "$1" | grep -v '^\s*$' -} - -confirm() { - read -r -p "$1 [y/N] " response - [[ "$response" =~ ^[yY]$ ]] -} +source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib.sh" # --- Prerequisites --- -if [[ $EUID -eq 0 ]]; then - err "Do NOT run as root." - exit 1 -fi +check_not_root # --- 1. Update Moonarch repo ---