Add EZ-Homelab Enhanced Setup System
- Complete modular bash-based setup system replacing Python TUI - Phase 1-4 implementation: Core Infrastructure, Configuration Management, Deployment Engine, Service Orchestration & Management - 9 production-ready scripts: preflight.sh, setup.sh, pre-deployment-wizard.sh, localize.sh, generalize.sh, validate.sh, deploy.sh, service.sh, monitor.sh, backup.sh, update.sh - Shared libraries: common.sh (utilities), ui.sh (text interface) - Template-based configuration system with environment variable substitution - Comprehensive documentation: PRD, standards, and quick reference guides - Automated backup, monitoring, and update management capabilities - Cross-platform compatibility with robust error handling and logging
This commit is contained in:
373
scripts/enhanced-setup/lib/common.sh
Executable file
373
scripts/enhanced-setup/lib/common.sh
Executable file
@@ -0,0 +1,373 @@
|
||||
#!/bin/bash
|
||||
# EZ-Homelab Enhanced Setup Scripts - Common Library
|
||||
# Shared variables, utility functions, and constants
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# =============================================================================
|
||||
# SHARED VARIABLES
|
||||
# =============================================================================
|
||||
|
||||
# Repository and paths
|
||||
EZ_HOME="${EZ_HOME:-/home/kelin/EZ-Homelab}"
|
||||
STACKS_DIR="${STACKS_DIR:-/opt/stacks}"
|
||||
LOG_DIR="${LOG_DIR:-$HOME/.ez-homelab/logs}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
# User and system
|
||||
EZ_USER="${EZ_USER:-$USER}"
|
||||
EZ_UID="${EZ_UID:-$(id -u)}"
|
||||
EZ_GID="${EZ_GID:-$(id -g)}"
|
||||
|
||||
# Architecture detection
|
||||
ARCH="$(uname -m)"
|
||||
IS_ARM64=false
|
||||
[[ "$ARCH" == "aarch64" ]] && IS_ARM64=true
|
||||
|
||||
# System information
|
||||
OS_NAME="$(lsb_release -si 2>/dev/null || echo "Unknown")"
|
||||
OS_VERSION="$(lsb_release -sr 2>/dev/null || echo "Unknown")"
|
||||
KERNEL_VERSION="$(uname -r)"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# =============================================================================
|
||||
# LOGGING FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
# Initialize logging
|
||||
init_logging() {
|
||||
local script_name="${1:-unknown}"
|
||||
mkdir -p "$LOG_DIR"
|
||||
LOG_FILE="$LOG_DIR/${script_name}.log"
|
||||
touch "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Log message with timestamp and level
|
||||
log() {
|
||||
local level="$1"
|
||||
local message="$2"
|
||||
local timestamp
|
||||
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
echo "$timestamp [$SCRIPT_NAME] $level: $message" >> "$LOG_FILE"
|
||||
echo "$timestamp [$SCRIPT_NAME] $level: $message" >&2
|
||||
}
|
||||
|
||||
# Convenience logging functions
|
||||
log_info() { log "INFO" "$1"; }
|
||||
log_warn() { log "WARN" "$1"; }
|
||||
log_error() { log "ERROR" "$1"; }
|
||||
log_debug() { log "DEBUG" "$1"; }
|
||||
|
||||
# =============================================================================
|
||||
# UTILITY FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
# Check if command exists
|
||||
command_exists() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Check if running as root
|
||||
is_root() {
|
||||
[[ $EUID -eq 0 ]]
|
||||
}
|
||||
|
||||
# Get available disk space in GB
|
||||
get_disk_space() {
|
||||
local path="${1:-/}"
|
||||
df -BG "$path" 2>/dev/null | tail -1 | awk '{print $4}' | sed 's/G//' || echo "0"
|
||||
}
|
||||
|
||||
# Get total memory in MB
|
||||
get_total_memory() {
|
||||
free -m 2>/dev/null | awk 'NR==2{printf "%.0f", $2}' || echo "0"
|
||||
}
|
||||
|
||||
# Get available memory in MB
|
||||
get_available_memory() {
|
||||
free -m 2>/dev/null | awk 'NR==2{printf "%.0f", $7}' || echo "0"
|
||||
}
|
||||
|
||||
# Check if service is running (systemd)
|
||||
service_running() {
|
||||
local service="$1"
|
||||
systemctl is-active --quiet "$service" 2>/dev/null
|
||||
}
|
||||
|
||||
# Check if Docker is installed and running
|
||||
docker_available() {
|
||||
command_exists docker && service_running docker
|
||||
}
|
||||
|
||||
# Check network connectivity
|
||||
check_network() {
|
||||
ping -c 1 -W 5 8.8.8.8 >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Validate YAML file syntax
|
||||
validate_yaml() {
|
||||
local file="$1"
|
||||
if command_exists python3 && python3 -c "import yaml" 2>/dev/null; then
|
||||
python3 -c "import yaml; yaml.safe_load(open('$file'))" 2>/dev/null
|
||||
elif command_exists yq; then
|
||||
yq eval '.' "$file" >/dev/null 2>/dev/null
|
||||
elif command_exists docker && docker compose version >/dev/null 2>&1; then
|
||||
# Fallback to docker compose config
|
||||
local dir=$(dirname "$file")
|
||||
local base=$(basename "$file")
|
||||
(cd "$dir" && docker compose -f "$base" config >/dev/null 2>&1)
|
||||
else
|
||||
# No validation tools available, assume valid
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
# Backup file with timestamp
|
||||
backup_file() {
|
||||
local file="$1"
|
||||
local backup="${file}.bak.$(date +%Y%m%d_%H%M%S)"
|
||||
cp "$file" "$backup"
|
||||
log_info "Backed up $file to $backup"
|
||||
}
|
||||
|
||||
# Clean up old backups (keep last 5)
|
||||
cleanup_backups() {
|
||||
local file="$1"
|
||||
local backups
|
||||
mapfile -t backups < <(ls -t "${file}.bak."* 2>/dev/null | tail -n +6)
|
||||
for backup in "${backups[@]}"; do
|
||||
rm -f "$backup"
|
||||
log_debug "Cleaned up old backup: $backup"
|
||||
done
|
||||
}
|
||||
|
||||
# Display colored message
|
||||
print_color() {
|
||||
local color="$1"
|
||||
local message="$2"
|
||||
echo -e "${color}${message}${NC}"
|
||||
}
|
||||
|
||||
# Display success message
|
||||
print_success() {
|
||||
print_color "$GREEN" "✓ $1"
|
||||
}
|
||||
|
||||
# Display warning message
|
||||
print_warning() {
|
||||
print_color "$YELLOW" "⚠ $1"
|
||||
}
|
||||
|
||||
# Display error message
|
||||
print_error() {
|
||||
print_color "$RED" "✗ $1"
|
||||
}
|
||||
|
||||
# Display info message
|
||||
print_info() {
|
||||
print_color "$BLUE" "ℹ $1"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# VALIDATION FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
# Validate OS compatibility
|
||||
validate_os() {
|
||||
case "$OS_NAME" in
|
||||
"Ubuntu"|"Debian"|"Raspbian")
|
||||
if [[ "$OS_NAME" == "Ubuntu" && "$OS_VERSION" =~ ^(20|22|24) ]]; then
|
||||
return 0
|
||||
elif [[ "$OS_NAME" == "Debian" && "$OS_VERSION" =~ ^(11|12) ]]; then
|
||||
return 0
|
||||
elif [[ "$OS_NAME" == "Raspbian" ]]; then
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
return 1
|
||||
}
|
||||
|
||||
# Validate architecture
|
||||
validate_arch() {
|
||||
[[ "$ARCH" == "x86_64" || "$ARCH" == "aarch64" ]]
|
||||
}
|
||||
|
||||
# Validate minimum requirements
|
||||
validate_requirements() {
|
||||
local min_disk=20 # GB
|
||||
local min_memory=1024 # MB
|
||||
|
||||
local disk_space
|
||||
disk_space=$(get_disk_space)
|
||||
local total_memory
|
||||
total_memory=$(get_total_memory)
|
||||
|
||||
if (( disk_space < min_disk )); then
|
||||
log_error "Insufficient disk space: ${disk_space}GB available, ${min_disk}GB required"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if (( total_memory < min_memory )); then
|
||||
log_error "Insufficient memory: ${total_memory}MB available, ${min_memory}MB required"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# DEPENDENCY CHECKS
|
||||
# =============================================================================
|
||||
|
||||
# Check if required packages are installed
|
||||
check_dependencies() {
|
||||
local deps=("curl" "wget" "jq" "git")
|
||||
local missing=()
|
||||
|
||||
for dep in "${deps[@]}"; do
|
||||
if ! command_exists "$dep"; then
|
||||
missing+=("$dep")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#missing[@]} -gt 0 ]]; then
|
||||
log_warn "Missing dependencies: ${missing[*]}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Install missing dependencies
|
||||
install_dependencies() {
|
||||
if ! check_dependencies; then
|
||||
log_info "Installing missing dependencies..."
|
||||
if is_root; then
|
||||
apt update && apt install -y curl wget jq git
|
||||
else
|
||||
sudo apt update && sudo apt install -y curl wget jq git
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# SCRIPT INITIALIZATION
|
||||
# =============================================================================
|
||||
|
||||
# Initialize script environment
|
||||
init_script() {
|
||||
local script_name="$1"
|
||||
SCRIPT_NAME="$script_name"
|
||||
init_logging "$script_name"
|
||||
|
||||
log_info "Starting $script_name on $OS_NAME $OS_VERSION ($ARCH)"
|
||||
|
||||
# Set trap for cleanup
|
||||
trap 'log_error "Script interrupted"; exit 1' INT TERM
|
||||
|
||||
# Validate basic requirements
|
||||
if ! validate_os; then
|
||||
print_error "Unsupported OS: $OS_NAME $OS_VERSION"
|
||||
log_error "Unsupported OS: $OS_NAME $OS_VERSION"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! validate_arch; then
|
||||
print_error "Unsupported architecture: $ARCH"
|
||||
log_error "Unsupported architecture: $ARCH"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# DOCKER UTILITIES
|
||||
# =============================================================================
|
||||
|
||||
# Check if Docker is available
|
||||
docker_available() {
|
||||
command_exists "docker" && docker info >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Get services in a stack
|
||||
get_stack_services() {
|
||||
local stack="$1"
|
||||
local compose_file="$EZ_HOME/docker-compose/$stack/docker-compose.yml"
|
||||
|
||||
if [[ ! -f "$compose_file" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Extract service names from docker-compose.yml
|
||||
# Look for lines that start at column 0 followed by a service name
|
||||
sed -n '/^services:/,/^[^ ]/p' "$compose_file" 2>/dev/null | \
|
||||
grep '^ [a-zA-Z0-9_-]\+:' | \
|
||||
sed 's/^\s*//' | sed 's/:.*$//' || true
|
||||
}
|
||||
|
||||
# Check if a service is running
|
||||
is_service_running() {
|
||||
local service="$1"
|
||||
|
||||
docker ps --filter "name=$service" --filter "status=running" --format "{{.Names}}" | grep -q "^${service}$"
|
||||
}
|
||||
|
||||
# Find all available services across all stacks
|
||||
find_all_services() {
|
||||
local services=()
|
||||
|
||||
# Get all docker-compose directories
|
||||
local compose_dirs
|
||||
mapfile -t compose_dirs < <(find "$EZ_HOME/docker-compose" -name "docker-compose.yml" -type f -exec dirname {} \; 2>/dev/null)
|
||||
|
||||
for dir in "${compose_dirs[@]}"; do
|
||||
local stack_services
|
||||
mapfile -t stack_services < <(get_stack_services "$(basename "$dir")")
|
||||
|
||||
for service in "${stack_services[@]}"; do
|
||||
# Avoid duplicates
|
||||
if [[ ! " ${services[*]} " =~ " ${service} " ]]; then
|
||||
services+=("$service")
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
printf '%s\n' "${services[@]}" | sort
|
||||
}
|
||||
|
||||
# Find which stack a service belongs to
|
||||
find_service_stack() {
|
||||
local service="$1"
|
||||
|
||||
local compose_dirs
|
||||
mapfile -t compose_dirs < <(find "$EZ_HOME/docker-compose" -name "docker-compose.yml" -type f -exec dirname {} \; 2>/dev/null)
|
||||
|
||||
for dir in "${compose_dirs[@]}"; do
|
||||
local stack_services
|
||||
mapfile -t stack_services < <(get_stack_services "$(basename "$dir")")
|
||||
|
||||
for stack_service in "${stack_services[@]}"; do
|
||||
if [[ "$stack_service" == "$service" ]]; then
|
||||
echo "$dir"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Get service compose file
|
||||
get_service_compose_file() {
|
||||
local service="$1"
|
||||
local stack_dir
|
||||
|
||||
stack_dir=$(find_service_stack "$service")
|
||||
[[ -n "$stack_dir" ]] && echo "$stack_dir/docker-compose.yml"
|
||||
}
|
||||
324
scripts/enhanced-setup/lib/ui.sh
Executable file
324
scripts/enhanced-setup/lib/ui.sh
Executable file
@@ -0,0 +1,324 @@
|
||||
#!/bin/bash
|
||||
# EZ-Homelab Enhanced Setup Scripts - UI Library
|
||||
# Dialog/whiptail helper functions for consistent user interface
|
||||
|
||||
# Detect available UI tool
|
||||
if command_exists whiptail; then
|
||||
UI_TOOL="whiptail"
|
||||
elif command_exists dialog; then
|
||||
UI_TOOL="dialog"
|
||||
else
|
||||
echo "Error: Neither whiptail nor dialog is installed. Please install one of them."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# UI configuration
|
||||
UI_HEIGHT=20
|
||||
UI_WIDTH=70
|
||||
UI_TITLE="EZ-Homelab Setup"
|
||||
UI_BACKTITLE="EZ-Homelab Enhanced Setup Scripts v1.0"
|
||||
|
||||
# Colors (for dialog)
|
||||
if [[ "$UI_TOOL" == "dialog" ]]; then
|
||||
export DIALOGRC="$SCRIPT_DIR/lib/dialogrc"
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# BASIC UI FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
# Display a message box
|
||||
ui_msgbox() {
|
||||
local text="$1"
|
||||
local height="${2:-$UI_HEIGHT}"
|
||||
local width="${3:-$UI_WIDTH}"
|
||||
|
||||
"$UI_TOOL" --backtitle "$UI_BACKTITLE" --title "$UI_TITLE" \
|
||||
--msgbox "$text" "$height" "$width"
|
||||
}
|
||||
|
||||
# Display a yes/no question
|
||||
ui_yesno() {
|
||||
local text="$1"
|
||||
local height="${2:-$UI_HEIGHT}"
|
||||
local width="${3:-$UI_WIDTH}"
|
||||
|
||||
"$UI_TOOL" --backtitle "$UI_BACKTITLE" --title "$UI_TITLE" \
|
||||
--yesno "$text" "$height" "$width"
|
||||
}
|
||||
|
||||
# Get user input
|
||||
ui_inputbox() {
|
||||
local text="$1"
|
||||
local default="${2:-}"
|
||||
local height="${3:-$UI_HEIGHT}"
|
||||
local width="${4:-$UI_WIDTH}"
|
||||
|
||||
"$UI_TOOL" --backtitle "$UI_BACKTITLE" --title "$UI_TITLE" \
|
||||
--inputbox "$text" "$height" "$width" "$default" 2>&1
|
||||
}
|
||||
|
||||
# Display a menu
|
||||
ui_menu() {
|
||||
local text="$1"
|
||||
local height="${2:-$UI_HEIGHT}"
|
||||
local width="${3:-$UI_WIDTH}"
|
||||
shift 2
|
||||
|
||||
local menu_items=("$@")
|
||||
local menu_height=$(( ${#menu_items[@]} / 2 ))
|
||||
|
||||
"$UI_TOOL" --backtitle "$UI_BACKTITLE" --title "$UI_TITLE" \
|
||||
--menu "$text" "$height" "$width" "$menu_height" \
|
||||
"${menu_items[@]}" 2>&1
|
||||
}
|
||||
|
||||
# Display a checklist
|
||||
ui_checklist() {
|
||||
local text="$1"
|
||||
local height="${2:-$UI_HEIGHT}"
|
||||
local width="${3:-$UI_WIDTH}"
|
||||
shift 2
|
||||
|
||||
local checklist_items=("$@")
|
||||
local list_height=$(( ${#checklist_items[@]} / 3 ))
|
||||
|
||||
"$UI_TOOL" --backtitle "$UI_BACKTITLE" --title "$UI_TITLE" \
|
||||
--checklist "$text" "$height" "$width" "$list_height" \
|
||||
"${checklist_items[@]}" 2>&1
|
||||
}
|
||||
|
||||
# Display a radiolist
|
||||
ui_radiolist() {
|
||||
local text="$1"
|
||||
local height="${2:-$UI_HEIGHT}"
|
||||
local width="${3:-$UI_WIDTH}"
|
||||
shift 2
|
||||
|
||||
local radiolist_items=("$@")
|
||||
local list_height=$(( ${#radiolist_items[@]} / 3 ))
|
||||
|
||||
"$UI_TOOL" --backtitle "$UI_BACKTITLE" --title "$UI_TITLE" \
|
||||
--radiolist "$text" "$height" "$width" "$list_height" \
|
||||
"${radiolist_items[@]}" 2>&1
|
||||
}
|
||||
|
||||
# Display progress gauge
|
||||
ui_gauge() {
|
||||
local text="$1"
|
||||
local percent="${2:-0}"
|
||||
local height="${3:-$UI_HEIGHT}"
|
||||
local width="${4:-$UI_WIDTH}"
|
||||
|
||||
{
|
||||
echo "$percent"
|
||||
echo "$text"
|
||||
} | "$UI_TOOL" --backtitle "$UI_BACKTITLE" --title "$UI_TITLE" \
|
||||
--gauge "$text" "$height" "$width" 0
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# ADVANCED UI FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
# Display progress with updating percentage
|
||||
ui_progress() {
|
||||
local title="$1"
|
||||
local command="$2"
|
||||
local height="${3:-$UI_HEIGHT}"
|
||||
local width="${4:-$UI_WIDTH}"
|
||||
|
||||
{
|
||||
eval "$command" | while IFS= read -r line; do
|
||||
# Try to extract percentage from output
|
||||
if [[ "$line" =~ ([0-9]+)% ]]; then
|
||||
echo "${BASH_REMATCH[1]}"
|
||||
fi
|
||||
echo "$line" >&2
|
||||
done
|
||||
echo "100"
|
||||
} 2>&1 | "$UI_TOOL" --backtitle "$UI_BACKTITLE" --title "$UI_TITLE" \
|
||||
--gauge "$title" "$height" "$width" 0
|
||||
}
|
||||
|
||||
# Display a form with multiple fields
|
||||
ui_form() {
|
||||
local text="$1"
|
||||
local height="${2:-$UI_HEIGHT}"
|
||||
local width="${3:-$UI_WIDTH}"
|
||||
shift 2
|
||||
|
||||
local form_items=("$@")
|
||||
local form_height=$(( ${#form_items[@]} / 2 ))
|
||||
|
||||
"$UI_TOOL" --backtitle "$UI_BACKTITLE" --title "$UI_TITLE" \
|
||||
--form "$text" "$height" "$width" "$form_height" \
|
||||
"${form_items[@]}" 2>&1
|
||||
}
|
||||
|
||||
# Display password input (hidden)
|
||||
ui_password() {
|
||||
local text="$1"
|
||||
local height="${2:-$UI_HEIGHT}"
|
||||
local width="${3:-$UI_WIDTH}"
|
||||
|
||||
"$UI_TOOL" --backtitle "$UI_BACKTITLE" --title "$UI_TITLE" \
|
||||
--passwordbox "$text" "$height" "$width" 2>&1
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# EZ-HOMELAB SPECIFIC UI FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
# Display deployment type selection
|
||||
ui_select_deployment_type() {
|
||||
local text="Select your deployment type:"
|
||||
local items=(
|
||||
"core" "Core Only" "off"
|
||||
"single" "Single Server (Core + Infrastructure + Services)" "on"
|
||||
"remote" "Remote Server (Infrastructure + Services only)" "off"
|
||||
)
|
||||
|
||||
ui_radiolist "$text" "$UI_HEIGHT" "$UI_WIDTH" "${items[@]}"
|
||||
}
|
||||
|
||||
# Display service selection checklist
|
||||
ui_select_services() {
|
||||
local deployment_type="$1"
|
||||
local text="Select services to deploy:"
|
||||
local items=()
|
||||
|
||||
case "$deployment_type" in
|
||||
"core")
|
||||
items=(
|
||||
"duckdns" "DuckDNS (Dynamic DNS)" "on"
|
||||
"traefik" "Traefik (Reverse Proxy)" "on"
|
||||
"authelia" "Authelia (SSO Authentication)" "on"
|
||||
"gluetun" "Gluetun (VPN Client)" "on"
|
||||
"sablier" "Sablier (Lazy Loading)" "on"
|
||||
)
|
||||
;;
|
||||
"single")
|
||||
items=(
|
||||
"core" "Core Services" "on"
|
||||
"infrastructure" "Infrastructure (Dockge, Pi-hole)" "on"
|
||||
"dashboards" "Dashboards (Homepage, Homarr)" "on"
|
||||
"media" "Media Services (Plex, Jellyfin)" "off"
|
||||
"media-management" "Media Management (*arr services)" "off"
|
||||
"homeassistant" "Home Assistant Stack" "off"
|
||||
"productivity" "Productivity (Nextcloud, Gitea)" "off"
|
||||
"monitoring" "Monitoring (Grafana, Prometheus)" "off"
|
||||
"utilities" "Utilities (Duplicati, FreshRSS)" "off"
|
||||
)
|
||||
;;
|
||||
"remote")
|
||||
items=(
|
||||
"infrastructure" "Infrastructure (Dockge, Pi-hole)" "on"
|
||||
"dashboards" "Dashboards (Homepage, Homarr)" "on"
|
||||
"media" "Media Services (Plex, Jellyfin)" "off"
|
||||
"media-management" "Media Management (*arr services)" "off"
|
||||
"homeassistant" "Home Assistant Stack" "off"
|
||||
"productivity" "Productivity (Nextcloud, Gitea)" "off"
|
||||
"monitoring" "Monitoring (Grafana, Prometheus)" "off"
|
||||
"utilities" "Utilities (Duplicati, FreshRSS)" "off"
|
||||
)
|
||||
;;
|
||||
esac
|
||||
|
||||
ui_checklist "$text" "$UI_HEIGHT" "$UI_WIDTH" "${items[@]}"
|
||||
}
|
||||
|
||||
# Display environment configuration form
|
||||
ui_configure_environment() {
|
||||
local text="Configure your environment:"
|
||||
local items=(
|
||||
"Domain" 1 1 "" 1 20 50 0
|
||||
"Timezone" 2 1 "America/New_York" 2 20 50 0
|
||||
"PUID" 3 1 "1000" 3 20 50 0
|
||||
"PGID" 4 1 "1000" 4 20 50 0
|
||||
)
|
||||
|
||||
ui_form "$text" "$UI_HEIGHT" "$UI_WIDTH" "${items[@]}"
|
||||
}
|
||||
|
||||
# Display confirmation dialog
|
||||
ui_confirm_action() {
|
||||
local action="$1"
|
||||
local details="${2:-}"
|
||||
local text="Confirm $action?"
|
||||
|
||||
if [[ -n "$details" ]]; then
|
||||
text="$text\n\n$details"
|
||||
fi
|
||||
|
||||
ui_yesno "$text"
|
||||
}
|
||||
|
||||
# Display error and offer retry
|
||||
ui_error_retry() {
|
||||
local error="$1"
|
||||
local suggestion="${2:-}"
|
||||
|
||||
local text="Error: $error"
|
||||
if [[ -n "$suggestion" ]]; then
|
||||
text="$text\n\nSuggestion: $suggestion"
|
||||
fi
|
||||
text="$text\n\nWould you like to retry?"
|
||||
|
||||
ui_yesno "$text"
|
||||
}
|
||||
|
||||
# Display success message
|
||||
ui_success() {
|
||||
local message="$1"
|
||||
ui_msgbox "Success!\n\n$message"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# UTILITY FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
# Check if UI is available (for non-interactive mode)
|
||||
ui_available() {
|
||||
[[ -n "${DISPLAY:-}" ]] || [[ -n "${TERM:-}" ]] && [[ "$TERM" != "dumb" ]]
|
||||
}
|
||||
|
||||
# Run command with UI progress if available
|
||||
run_with_progress() {
|
||||
local title="$1"
|
||||
local command="$2"
|
||||
|
||||
if ui_available; then
|
||||
ui_progress "$title" "$command"
|
||||
else
|
||||
print_info "$title"
|
||||
eval "$command"
|
||||
fi
|
||||
}
|
||||
|
||||
# Display help text
|
||||
ui_show_help() {
|
||||
local script_name="$1"
|
||||
local help_text="
|
||||
EZ-Homelab $script_name
|
||||
|
||||
USAGE:
|
||||
$script_name [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Show this help message
|
||||
-v, --verbose Enable verbose logging
|
||||
-y, --yes Assume yes for all prompts
|
||||
--no-ui Run without interactive UI
|
||||
|
||||
EXAMPLES:
|
||||
$script_name # Interactive mode
|
||||
$script_name --no-ui # Non-interactive mode
|
||||
$script_name --help # Show help
|
||||
|
||||
For more information, visit:
|
||||
https://github.com/your-repo/EZ-Homelab
|
||||
"
|
||||
|
||||
echo "$help_text" | ui_msgbox "Help - $script_name" 20 70
|
||||
}
|
||||
Reference in New Issue
Block a user