#!/usr/bin/env bash # Omeron - Modular System Setup Framework # Usage: ./install.sh [--fresh] [--modules m1,m2] [--skip-packages] [--with-sddm] [--help] set -euo pipefail OMERON_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" export OMERON_ROOT export OMERON_PROJECT_DIR="$OMERON_ROOT" source "$OMERON_ROOT/lib/log.sh" source "$OMERON_ROOT/lib/tui.sh" source "$OMERON_ROOT/lib/tui-fs.sh" source "$OMERON_ROOT/lib/utils.sh" source "$OMERON_ROOT/lib/config.sh" source "$OMERON_ROOT/lib/modules.sh" OMERON_FRESH_INSTALL="${OMERON_FRESH_INSTALL:-0}" export OMERON_FRESH_INSTALL DEFAULT_MODULES=( "core/preflight" "core/packages" "core/ags" "core/dotfiles" "core/services" "homelab/setup" "optional/install" "post/apply-theme" "core/sddm" ) FRESH_MODULES=( "core/packages" "core/ags" "core/dotfiles" "core/services" "homelab/setup" "optional/install" "post/apply-theme" "core/sddm" ) RUN_MODULES=() SKIP_MODULES=() WITH_SDDM=0 usage() { cat </dev/null && declare -F "module_description" >/dev/null 2>&1; then description="$(module_description)" fi printf " %-30s %s\n" "$rel_path" "$description" done exit 0 } parse_args() { while [[ $# -gt 0 ]]; do case "$1" in --fresh) OMERON_FRESH_INSTALL=1; shift ;; --modules) IFS=',' read -ra RUN_MODULES <<< "$2"; shift 2 ;; --skip) IFS=',' read -ra SKIP_MODULES <<< "$2"; shift 2 ;; --skip-packages) SKIP_MODULES+=("core/packages"); shift ;; --with-sddm) WITH_SDDM=1; shift ;; --list-modules) list_modules ;; -h|--help) usage ;; *) echo "Unknown option: $1" >&2; usage ;; esac done } detect_environment() { tui_detect if ((!OMERON_HAS_GUM)) && have pacman; then tui_fs_show_msg "Installing gum for interactive TUI..." tui_install_gum 2>/dev/null || true tui_detect if ((OMERON_HAS_GUM)); then tui_fs_show_ok "gum installed" else tui_fs_show_msg "using terminal prompts" fi fi if ((!OMERON_FRESH_INSTALL)) && ((${#RUN_MODULES[@]} == 0)); then tui_fs_show_msg "Checking for fresh install..." if is_fresh_install; then OMERON_FRESH_INSTALL=1 export OMERON_FRESH_INSTALL fi fi if ((OMERON_FRESH_INSTALL)); then if ((!OMERON_HAS_GUM)) && have pacman; then tui_install_gum 2>/dev/null || true tui_detect fi if ! have paru && ! have yay; then tui_fs_show_msg "Installing AUR helper..." install_aur_helper tui_fs_show_ok "AUR helper ready" fi fi } collect_modules() { if ((${#RUN_MODULES[@]})); then for mod in "${RUN_MODULES[@]}"; do local module_file="$OMERON_MODULE_DIR/$mod.sh" [[ -f "$module_file" ]] && printf '%s\n' "$module_file" done return fi local modules=() if ((OMERON_FRESH_INSTALL)); then modules=("${FRESH_MODULES[@]}") else modules=("${DEFAULT_MODULES[@]}") fi for mod in "${modules[@]}"; do local skip=0 for skipped in "${SKIP_MODULES[@]}"; do if [[ "$mod" == "$skipped" || "$mod" == "core/$skipped" ]]; then skip=1 break fi done [[ "$mod" == "core/sddm" && "$WITH_SDDM" -eq 0 && "$OMERON_FRESH_INSTALL" -eq 0 ]] && skip=1 ((skip)) && continue local module_file="$OMERON_MODULE_DIR/$mod.sh" [[ -f "$module_file" ]] && printf '%s\n' "$module_file" done } collect_all_interactive() { exec 3>&1 exec 1>&2 local modules=() if ((OMERON_FRESH_INSTALL)); then modules=("${FRESH_MODULES[@]}") else modules=("${DEFAULT_MODULES[@]}") fi local module_order=() local idx=1 local total=${#modules[@]} for mod in "${modules[@]}"; do local module_file="$OMERON_MODULE_DIR/$mod.sh" [[ -f "$module_file" ]] || continue source "$module_file" 2>/dev/null || continue local description="" if declare -F "module_description" >/dev/null 2>&1; then description="$(module_description)" fi local required=0 if declare -F "module_required" >/dev/null 2>&1; then if module_required; then required=1 fi fi if ((required)); then tui_fs_select_module "$idx" "$total" "$mod" "$description" 1 module_order+=("$module_file") ((idx++)) continue fi if tui_fs_select_module "$idx" "$total" "$mod" "$description" 0; then module_order+=("$module_file") fi ((idx++)) done if ! tui_fs_confirm_start "${#module_order[@]}"; then exec 3>&- return 1 fi printf '%s\n' "${module_order[@]}" >&3 exec 3>&- } main() { OMERON_LOG_FILE="${OMERON_LOG_FILE:-$HOME/.local/share/omeron/install-$(date +%Y%m%d-%H%M%S).log}" export OMERON_LOG_FILE parse_args "$@" mkdir -p "$(dirname "$OMERON_LOG_FILE")" log_info "Omeron installation started" log_info "Log file: $OMERON_LOG_FILE" log_info "Project: $OMERON_ROOT" sudo_init trap 'sudo_cleanup; tui_fs_exit' EXIT tui_fs_init detect_environment local modules_to_run=() if ((${#RUN_MODULES[@]})); then mapfile -t modules_to_run < <(collect_modules) else local collected collected="$(collect_all_interactive)" || { tui_fs_exit exit 0 } mapfile -t modules_to_run <<< "$collected" fi if ((${#modules_to_run[@]} == 0)); then tui_fs_show_msg "No modules selected. Nothing to do." tui_fs_exit exit 0 fi tui_fs_start_execution local total=${#modules_to_run[@]} local idx=1 for module_path in "${modules_to_run[@]}"; do local description="" if source "$module_path" 2>/dev/null && declare -F "module_description" >/dev/null 2>&1; then description="$(module_description)" fi tui_fs_set_step "$idx" "$total" "${description:-$(basename "$module_path" .sh)}" printf '\n' log_step "$idx" "$total" "${description:-$(basename "$module_path" .sh)}" module_run "$module_path" || { local rc=$? if declare -F "module_required" >/dev/null 2>&1; then source "$module_path" if module_required 2>/dev/null; then tui_fs_exit tui_error "Required module failed. Aborting." exit $rc fi fi tui_fs_show_msg "Module completed with warnings" } ((idx++)) done tui_fs_exit printf '\n' tui_header "O M E R O N — Done" tui_success "Installation Complete!" printf '\n' tui_info "Log: ${OMERON_LOG_FILE}" printf '\n' if ((OMERON_FRESH_INSTALL)); then tui_bold "Your system is ready for Hyprland!" tui_list \ "Start Hyprland: Hyprland" \ "Or enable SDDM: sudo systemctl enable --now sddm" \ "Reload config: hyprctl reload" else tui_bold "What next?" tui_list \ "Reload config: hyprctl reload" \ "Re-run installer: ./install.sh" fi printf '\n' if have notify-send; then notify-send "Omeron" "Installation complete!" >/dev/null 2>&1 || true fi } main "$@"