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:
399
scripts/enhanced-setup/generalize.sh
Executable file
399
scripts/enhanced-setup/generalize.sh
Executable file
@@ -0,0 +1,399 @@
|
||||
#!/bin/bash
|
||||
# EZ-Homelab Enhanced Setup Scripts - Configuration Generalization
|
||||
# Reverse localization by restoring template variables from backups
|
||||
|
||||
SCRIPT_NAME="generalize"
|
||||
SCRIPT_VERSION="1.0.0"
|
||||
|
||||
# Load common library
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/lib/common.sh"
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/lib/ui.sh"
|
||||
|
||||
# =============================================================================
|
||||
# SCRIPT CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
# Template variables that were replaced
|
||||
TEMPLATE_VARS=("DOMAIN" "TZ" "PUID" "PGID" "DUCKDNS_TOKEN" "JWT_SECRET" "SESSION_SECRET" "ENCRYPTION_KEY" "AUTHELIA_ADMIN_PASSWORD" "AUTHELIA_ADMIN_EMAIL" "PLEX_CLAIM_TOKEN" "DEPLOYMENT_TYPE" "SERVER_HOSTNAME" "DOCKER_SOCKET_PATH")
|
||||
|
||||
# =============================================================================
|
||||
# GENERALIZATION FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
# Load environment variables
|
||||
load_environment() {
|
||||
local env_file="$EZ_HOME/.env"
|
||||
|
||||
if [[ ! -f "$env_file" ]]; then
|
||||
print_error ".env file not found at $env_file"
|
||||
print_error "Cannot generalize without environment context"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Source the .env file
|
||||
set -a
|
||||
source "$env_file"
|
||||
set +a
|
||||
|
||||
print_success "Environment loaded from $env_file"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Find backup template files
|
||||
find_backup_files() {
|
||||
local service="${1:-}"
|
||||
|
||||
if [[ -n "$service" ]]; then
|
||||
# Process specific service
|
||||
local service_dir="$EZ_HOME/docker-compose/$service"
|
||||
if [[ -d "$service_dir" ]]; then
|
||||
find "$service_dir" -name "*.template" -type f 2>/dev/null
|
||||
else
|
||||
print_error "Service directory not found: $service_dir"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
# Process all services
|
||||
find "$EZ_HOME/docker-compose" -name "*.template" -type f 2>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
# Restore template file from backup
|
||||
restore_template_file() {
|
||||
local backup_file="$1"
|
||||
local original_file="${backup_file%.template}"
|
||||
|
||||
print_info "Restoring: $original_file"
|
||||
|
||||
if [[ ! -f "$backup_file" ]]; then
|
||||
print_error "Backup file not found: $backup_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Confirm destructive operation
|
||||
if ui_available; then
|
||||
if ! ui_yesno "Restore $original_file from backup? This will overwrite current changes."; then
|
||||
print_info "Skipped $original_file"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Backup current version (safety)
|
||||
backup_file "$original_file"
|
||||
|
||||
# Restore from template backup
|
||||
cp "$backup_file" "$original_file"
|
||||
|
||||
print_success "Restored $original_file from $backup_file"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Generalize processed file (reverse engineer values)
|
||||
generalize_processed_file() {
|
||||
local file="$1"
|
||||
local backup_file="${file}.template"
|
||||
|
||||
print_info "Generalizing: $file"
|
||||
|
||||
if [[ ! -f "$file" ]]; then
|
||||
print_error "File not found: $file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Create backup if it doesn't exist
|
||||
if [[ ! -f "$backup_file" ]]; then
|
||||
cp "$file" "$backup_file"
|
||||
print_info "Created backup: $backup_file"
|
||||
fi
|
||||
|
||||
# Process template variables in reverse
|
||||
local temp_file
|
||||
temp_file=$(mktemp)
|
||||
|
||||
cp "$file" "$temp_file"
|
||||
|
||||
for var in "${TEMPLATE_VARS[@]}"; do
|
||||
local value="${!var:-}"
|
||||
if [[ -n "$value" ]]; then
|
||||
# Escape special characters in value for sed
|
||||
local escaped_value
|
||||
escaped_value=$(printf '%s\n' "$value" | sed 's/[[\.*^$()+?{|]/\\&/g')
|
||||
|
||||
# Replace actual values back to ${VAR} format
|
||||
sed -i "s|$escaped_value|\${$var}|g" "$temp_file"
|
||||
log_debug "Generalized \${$var} in $file"
|
||||
fi
|
||||
done
|
||||
|
||||
# Move generalized file back
|
||||
mv "$temp_file" "$file"
|
||||
|
||||
print_success "Generalized $file"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Clean up backup files
|
||||
cleanup_backups() {
|
||||
local service="${1:-}"
|
||||
|
||||
print_info "Cleaning up backup files..."
|
||||
|
||||
local backup_files
|
||||
mapfile -t backup_files < <(find_backup_files "$service")
|
||||
|
||||
if [[ ${#backup_files[@]} -eq 0 ]]; then
|
||||
print_info "No backup files to clean up"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local cleaned=0
|
||||
for backup in "${backup_files[@]}"; do
|
||||
if ui_available; then
|
||||
if ui_yesno "Delete backup file: $backup?"; then
|
||||
rm -f "$backup"
|
||||
((cleaned++))
|
||||
print_info "Deleted: $backup"
|
||||
fi
|
||||
else
|
||||
rm -f "$backup"
|
||||
((cleaned++))
|
||||
log_info "Deleted backup: $backup"
|
||||
fi
|
||||
done
|
||||
|
||||
print_success "Cleaned up $cleaned backup file(s)"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# UI FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
# Show generalization options
|
||||
show_generalization_menu() {
|
||||
local text="Select generalization method:"
|
||||
local items=(
|
||||
"restore" "Restore from .template backups" "on"
|
||||
"reverse" "Reverse engineer from current files" "off"
|
||||
"cleanup" "Clean up backup files" "off"
|
||||
)
|
||||
|
||||
ui_radiolist "$text" "$UI_HEIGHT" "$UI_WIDTH" "${items[@]}"
|
||||
}
|
||||
|
||||
# Show progress for batch processing
|
||||
show_generalization_progress() {
|
||||
local total="$1"
|
||||
local current="$2"
|
||||
local file="$3"
|
||||
|
||||
local percent=$(( current * 100 / total ))
|
||||
ui_gauge "Generalizing configurations... ($current/$total)" "$percent"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# MAIN FUNCTION
|
||||
# =============================================================================
|
||||
|
||||
main() {
|
||||
local service=""
|
||||
local method=""
|
||||
local non_interactive=false
|
||||
local verbose=false
|
||||
local cleanup=false
|
||||
|
||||
# Parse command line arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-h|--help)
|
||||
cat << EOF
|
||||
EZ-Homelab Configuration Generalization
|
||||
|
||||
USAGE:
|
||||
$SCRIPT_NAME [OPTIONS] [SERVICE]
|
||||
|
||||
ARGUMENTS:
|
||||
SERVICE Specific service to generalize (optional, processes all if not specified)
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Show this help message
|
||||
-v, --verbose Enable verbose logging
|
||||
--method METHOD Generalization method: restore, reverse, cleanup
|
||||
--cleanup Clean up backup files after generalization
|
||||
--no-ui Run without interactive UI
|
||||
|
||||
METHODS:
|
||||
restore Restore files from .template backups (safe)
|
||||
reverse Reverse engineer template variables from current values (advanced)
|
||||
cleanup Remove .template backup files
|
||||
|
||||
EXAMPLES:
|
||||
$SCRIPT_NAME --method restore # Restore all from backups
|
||||
$SCRIPT_NAME --method reverse traefik # Reverse engineer Traefik
|
||||
$SCRIPT_NAME --cleanup # Clean up all backups
|
||||
|
||||
WARNING:
|
||||
Generalization can be destructive. Always backup important data first.
|
||||
|
||||
EOF
|
||||
exit 0
|
||||
;;
|
||||
-v|--verbose)
|
||||
verbose=true
|
||||
;;
|
||||
--method)
|
||||
shift
|
||||
method="$1"
|
||||
;;
|
||||
--cleanup)
|
||||
cleanup=true
|
||||
;;
|
||||
--no-ui)
|
||||
non_interactive=true
|
||||
;;
|
||||
-*)
|
||||
print_error "Unknown option: $1"
|
||||
echo "Use --help for usage information"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
if [[ -z "$service" ]]; then
|
||||
service="$1"
|
||||
else
|
||||
print_error "Multiple services specified. Use only one service name."
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# Initialize script
|
||||
init_script "$SCRIPT_NAME"
|
||||
|
||||
if $verbose; then
|
||||
set -x
|
||||
fi
|
||||
|
||||
print_info "Starting EZ-Homelab configuration generalization..."
|
||||
|
||||
# Load environment
|
||||
if ! load_environment; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Determine method
|
||||
if [[ -z "$method" ]]; then
|
||||
if ui_available && ! $non_interactive; then
|
||||
method=$(show_generalization_menu) || exit 1
|
||||
else
|
||||
print_error "Method must be specified with --method when running non-interactively"
|
||||
echo "Available methods: restore, reverse, cleanup"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
case "$method" in
|
||||
"restore")
|
||||
# Find backup files
|
||||
local backup_files
|
||||
mapfile -t backup_files < <(find_backup_files "$service")
|
||||
if [[ ${#backup_files[@]} -eq 0 ]]; then
|
||||
print_warning "No backup files found"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
print_info "Found ${#backup_files[@]} backup file(s)"
|
||||
|
||||
# Restore files
|
||||
local restored=0
|
||||
local total=${#backup_files[@]}
|
||||
|
||||
for backup in "${backup_files[@]}"; do
|
||||
if ui_available && ! $non_interactive; then
|
||||
show_generalization_progress "$total" "$restored" "$backup"
|
||||
fi
|
||||
|
||||
if restore_template_file "$backup"; then
|
||||
((restored++))
|
||||
fi
|
||||
done
|
||||
|
||||
# Close progress gauge
|
||||
if ui_available && ! $non_interactive; then
|
||||
ui_gauge "Restoration complete!" 100
|
||||
sleep 1
|
||||
fi
|
||||
|
||||
print_success "Restored $restored file(s) from backups"
|
||||
;;
|
||||
|
||||
"reverse")
|
||||
print_warning "Reverse engineering is experimental and may not be perfect"
|
||||
print_warning "Make sure you have backups of important data"
|
||||
|
||||
if ui_available && ! $non_interactive; then
|
||||
if ! ui_yesno "Continue with reverse engineering? This may modify your configuration files."; then
|
||||
print_info "Operation cancelled"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Find processed files (those with actual values instead of templates)
|
||||
local processed_files
|
||||
mapfile -t processed_files < <(find "$EZ_HOME/docker-compose${service:+/$service}" -name "*.yml" -o -name "*.yaml" -o -name "*.json" -o -name "*.conf" -o -name "*.cfg" -o -name "*.env" 2>/dev/null)
|
||||
|
||||
if [[ ${#processed_files[@]} -eq 0 ]]; then
|
||||
print_warning "No configuration files found"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
print_info "Found ${#processed_files[@]} file(s) to generalize"
|
||||
|
||||
# Generalize files
|
||||
local generalized=0
|
||||
local total=${#processed_files[@]}
|
||||
|
||||
for file in "${processed_files[@]}"; do
|
||||
if ui_available && ! $non_interactive; then
|
||||
show_generalization_progress "$total" "$generalized" "$file"
|
||||
fi
|
||||
|
||||
if generalize_processed_file "$file"; then
|
||||
((generalized++))
|
||||
fi
|
||||
done
|
||||
|
||||
# Close progress gauge
|
||||
if ui_available && ! $non_interactive; then
|
||||
ui_gauge "Generalization complete!" 100
|
||||
sleep 1
|
||||
fi
|
||||
|
||||
print_success "Generalized $generalized file(s)"
|
||||
;;
|
||||
|
||||
"cleanup")
|
||||
cleanup_backups "$service"
|
||||
;;
|
||||
|
||||
*)
|
||||
print_error "Unknown method: $method"
|
||||
echo "Available methods: restore, reverse, cleanup"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Optional cleanup
|
||||
if $cleanup && [[ "$method" != "cleanup" ]]; then
|
||||
cleanup_backups "$service"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
print_success "Configuration generalization complete!"
|
||||
print_info "Use ./validate.sh to check the results"
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user