collect_all_interactive wurde via <(...) in einer Subshell ausgeführt, deren
stdout an mapfile gebunden war. ALLE Ausgaben (log_step, tui_confirm-Prompts,
tui_info etc.) landeten im modules_to_run-Array → mapfile las Müllzeilen wie
'━━━ [4/7] ... ━━━' als Modul-Pfade → module_run('━━━ ... ━━━') → 'not found'.
Fix: exec 1>&2 zu Beginn der Subshell, nur die tatsächlichen Modul-Pfade
werden über erhaltenen fd 3 (> original stdout) an mapfile gegeben.
Prompt sagt jetzt auch welcher Step ('Run Modulbeschreibung?' statt
'Run this step?').
336 lines
8.6 KiB
Bash
Executable File
336 lines
8.6 KiB
Bash
Executable File
#!/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/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/dotfiles"
|
||
"core/services"
|
||
"homelab/setup"
|
||
"optional/install"
|
||
"core/sddm"
|
||
"post/apply-theme"
|
||
)
|
||
|
||
FRESH_MODULES=(
|
||
"core/packages"
|
||
"core/dotfiles"
|
||
"core/services"
|
||
"homelab/setup"
|
||
"optional/install"
|
||
"core/sddm"
|
||
"post/apply-theme"
|
||
)
|
||
|
||
RUN_MODULES=()
|
||
SKIP_MODULES=()
|
||
WITH_SDDM=0
|
||
|
||
usage() {
|
||
cat <<EOF
|
||
Omeron - Modular System Setup Framework
|
||
|
||
Usage: ./install.sh [OPTIONS]
|
||
|
||
Options:
|
||
--fresh Full system setup (Hyprland + GPU drivers + all deps)
|
||
--modules m1,m2 Run only specific modules (comma-separated)
|
||
--skip m1,m2 Skip specific modules (comma-separated)
|
||
--skip-packages Skip package installation
|
||
--with-sddm Include SDDM theme installation
|
||
--list-modules List all available modules
|
||
--help Show this help message
|
||
|
||
Examples:
|
||
./install.sh Interactive (detects fresh install automatically)
|
||
./install.sh --fresh Force full setup on a running system
|
||
./install.sh --modules core/dotfiles,post/apply-theme
|
||
./install.sh --skip core/packages
|
||
EOF
|
||
exit 0
|
||
}
|
||
|
||
list_modules() {
|
||
echo "Available modules:"
|
||
for module in $(module_list); do
|
||
local rel_path="${module#$OMERON_MODULE_DIR/}"
|
||
rel_path="${rel_path%.sh}"
|
||
local description=""
|
||
if source "$module" 2>/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
|
||
printf '\033[1;36m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m\n'
|
||
printf '\033[1;36m Ꮎ Ꮇ Ꭼ Ꮢ Ꮎ Ꮑ — Modular System Setup Framework\033[0m\n'
|
||
printf '\033[1;36m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m\n'
|
||
printf '\n'
|
||
printf ' \033[1;33mgum is not installed.\033[0m Installing it enables the full TUI experience.\n'
|
||
printf '\n'
|
||
|
||
if tui_install_gum; then
|
||
printf ' \033[1;32m✓ gum installed!\033[0m\n'
|
||
else
|
||
if ((OMERON_HAS_WHIPTAIL)); then
|
||
printf ' \033[1;33musing whiptail as fallback\033[0m\n'
|
||
else
|
||
printf ' \033[1;33musing basic text mode (install gum for UI: pacman -S gum)\033[0m\n'
|
||
fi
|
||
fi
|
||
printf '\n'
|
||
fi
|
||
|
||
if ((!OMERON_FRESH_INSTALL)) && ((${#RUN_MODULES[@]} == 0)); then
|
||
log_info "Checking for fresh install..."
|
||
if is_fresh_install; then
|
||
OMERON_FRESH_INSTALL=1
|
||
export OMERON_FRESH_INSTALL
|
||
fi
|
||
fi
|
||
|
||
if ((OMERON_FRESH_INSTALL)) && ((!OMERON_HAS_GUM)) && have pacman; then
|
||
tui_install_gum 2>/dev/null || true
|
||
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 total=${#modules[@]}
|
||
local idx=1
|
||
local module_order=()
|
||
|
||
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
|
||
|
||
printf '\n'
|
||
log_step "$idx" "$total" "${description:-$mod}"
|
||
|
||
if declare -F "module_required" >/dev/null 2>&1; then
|
||
if module_required; then
|
||
tui_info "Required module — will run"
|
||
module_order+=("$module_file")
|
||
((idx++))
|
||
continue
|
||
fi
|
||
fi
|
||
|
||
if tui_confirm "${description:-$mod}?"; then
|
||
module_order+=("$module_file")
|
||
else
|
||
log_info "Skipped"
|
||
fi
|
||
((idx++))
|
||
done
|
||
|
||
printf '%s\n' "${module_order[@]}" >&3
|
||
exec 3>&-
|
||
}
|
||
|
||
show_banner() {
|
||
tui_header " Ꮎ Ꮇ Ꭼ Ꮢ Ꮎ Ꮑ "
|
||
tui_info "Modular System Setup Framework"
|
||
tui_info "Arch / Hyprland / CachyOS"
|
||
printf '\n'
|
||
|
||
if ((OMERON_FRESH_INSTALL)); then
|
||
tui_warn "FRESH INSTALL MODE"
|
||
tui_info "Will install Hyprland, GPU drivers, and all dependencies."
|
||
printf '\n'
|
||
fi
|
||
}
|
||
|
||
show_summary() {
|
||
local modules=("$@")
|
||
|
||
tui_separator
|
||
tui_bold "Installation Summary"
|
||
tui_info "$(tui_bold "${#modules[@]}") module(s) to run"
|
||
|
||
local gpu
|
||
gpu="$(detect_gpu)"
|
||
tui_info "Detected GPU: $(tui_bold "$gpu")"
|
||
|
||
if ((${#modules[@]})); then
|
||
printf '\n'
|
||
tui_bold "Steps:"
|
||
local i=1
|
||
for mod in "${modules[@]}"; do
|
||
printf ' \033[1;36m%d.\033[0m %s\n' "$i" "$(basename "$mod" .sh)"
|
||
((i++))
|
||
done
|
||
fi
|
||
|
||
printf '\n'
|
||
if ! tui_confirm "Proceed with installation?"; then
|
||
tui_bold "Installation cancelled."
|
||
exit 0
|
||
fi
|
||
}
|
||
|
||
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"
|
||
|
||
detect_environment
|
||
|
||
show_banner
|
||
|
||
local modules_to_run=()
|
||
if ((${#RUN_MODULES[@]})); then
|
||
mapfile -t modules_to_run < <(collect_modules)
|
||
else
|
||
mapfile -t modules_to_run < <(collect_all_interactive)
|
||
fi
|
||
|
||
if ((${#modules_to_run[@]} == 0)); then
|
||
tui_warn "No modules selected. Nothing to do."
|
||
exit 0
|
||
fi
|
||
|
||
show_summary "${modules_to_run[@]}"
|
||
|
||
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
|
||
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_error "Required module failed. Aborting."
|
||
exit $rc
|
||
fi
|
||
fi
|
||
tui_warn "Module completed with warnings"
|
||
}
|
||
((idx++))
|
||
done
|
||
|
||
printf '\n'
|
||
tui_header " Ꮎ Ꮇ Ꭼ Ꮢ Ꮎ Ꮑ "
|
||
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 "$@"
|