Files
thrillwiki_django_no_react/shared/scripts/unraid/setup-template-automation.sh
pacnpal d504d41de2 feat: complete monorepo structure with frontend and shared resources
- Add complete backend/ directory with full Django application
- Add frontend/ directory with Vite + TypeScript setup ready for Next.js
- Add comprehensive shared/ directory with:
  - Complete documentation and memory-bank archives
  - Media files and avatars (letters, park/ride images)
  - Deployment scripts and automation tools
  - Shared types and utilities
- Add architecture/ directory with migration guides
- Configure pnpm workspace for monorepo development
- Update .gitignore to exclude .django_tailwind_cli/ build artifacts
- Preserve all historical documentation in shared/docs/memory-bank/
- Set up proper structure for full-stack development with shared resources
2025-08-23 18:40:07 -04:00

2263 lines
86 KiB
Bash
Executable File
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# ThrillWiki Template-Based Complete Unraid Automation Setup
# This script automates the entire template-based VM creation and deployment process on Unraid
#
# Usage:
# ./setup-template-automation.sh # Standard template-based setup
# ./setup-template-automation.sh --reset # Delete VM and config, start completely fresh
# ./setup-template-automation.sh --reset-vm # Delete VM only, keep configuration
# ./setup-template-automation.sh --reset-config # Delete config only, keep VM
# Function to show help
show_help() {
echo "ThrillWiki Template-Based CI/CD Automation Setup"
echo ""
echo "This script sets up FAST template-based VM deployment using pre-configured Ubuntu templates."
echo "Template VMs deploy in 2-5 minutes instead of 20-30 minutes with autoinstall."
echo ""
echo "Usage:"
echo " $0 Set up or update ThrillWiki template automation"
echo " $0 -y Non-interactive mode, use saved configuration"
echo " $0 --reset Delete VM and config, start completely fresh"
echo " $0 --reset-vm Delete VM only, keep configuration"
echo " $0 --reset-config Delete config only, keep VM"
echo " $0 --help Show this help message"
echo ""
echo "Template Benefits:"
echo " ⚡ Speed: 2-5 min deployment vs 20-30 min with autoinstall"
echo " 🔒 Reliability: Pre-tested template eliminates installation failures"
echo " 💾 Efficiency: Copy-on-write disk format saves space"
echo ""
echo "Options:"
echo " -y, --yes Non-interactive mode - use saved configuration"
echo " and passwords without prompting. Requires existing"
echo " configuration file with saved settings."
echo ""
echo "Reset Options:"
echo " --reset Completely removes existing VM, disks, and config"
echo " before starting fresh template-based installation"
echo " --reset-vm Removes only the VM and disks, preserves saved"
echo " configuration to avoid re-entering settings"
echo " --reset-config Removes only the saved configuration, preserves"
echo " VM and prompts for fresh configuration input"
echo " --help Display this help and exit"
echo ""
echo "Examples:"
echo " $0 # Normal template-based setup/update"
echo " $0 -y # Non-interactive setup with saved config"
echo " $0 --reset # Complete fresh template installation"
echo " $0 --reset-vm # Fresh VM with saved settings"
echo " $0 --reset-config # Re-configure existing VM"
exit 0
}
# Check for help flag
if [[ "$1" == "--help" || "$1" == "-h" ]]; then
show_help
fi
# Parse command line flags
RESET_ALL=false
RESET_VM_ONLY=false
RESET_CONFIG_ONLY=false
NON_INTERACTIVE=false
# Process all arguments
while [[ $# -gt 0 ]]; do
case $1 in
-y|--yes)
NON_INTERACTIVE=true
echo "🤖 NON-INTERACTIVE MODE: Using saved configuration only"
shift
;;
--reset)
RESET_ALL=true
echo "🔄 COMPLETE RESET MODE: Will delete VM and configuration"
shift
;;
--reset-vm)
RESET_VM_ONLY=true
echo "🔄 VM RESET MODE: Will delete VM only, keep configuration"
shift
;;
--reset-config)
RESET_CONFIG_ONLY=true
echo "🔄 CONFIG RESET MODE: Will delete configuration only, keep VM"
shift
;;
--help|-h)
show_help
;;
*)
echo "Unknown option: $1"
show_help
;;
esac
done
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
log() {
echo -e "${BLUE}[TEMPLATE-AUTOMATION]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_template() {
echo -e "${CYAN}[TEMPLATE]${NC} $1"
}
# Configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
LOG_DIR="$PROJECT_DIR/logs"
# Default values
DEFAULT_UNRAID_HOST=""
DEFAULT_VM_NAME="thrillwiki-vm"
DEFAULT_VM_MEMORY="4096"
DEFAULT_VM_VCPUS="2"
DEFAULT_VM_DISK_SIZE="50"
DEFAULT_WEBHOOK_PORT="9000"
TEMPLATE_VM_NAME="thrillwiki-template-ubuntu"
# Configuration files
CONFIG_FILE="$PROJECT_DIR/.thrillwiki-template-config"
TOKEN_FILE="$PROJECT_DIR/.thrillwiki-github-token"
# Function to save configuration
save_config() {
log "Saving template configuration to $CONFIG_FILE..."
cat > "$CONFIG_FILE" << EOF
# ThrillWiki Template-Based Automation Configuration
# This file stores your settings to avoid re-entering them each time
# Unraid Server Configuration
UNRAID_HOST="$UNRAID_HOST"
UNRAID_USER="$UNRAID_USER"
VM_NAME="$VM_NAME"
VM_MEMORY="$VM_MEMORY"
VM_VCPUS="$VM_VCPUS"
VM_DISK_SIZE="$VM_DISK_SIZE"
# Template Configuration
TEMPLATE_VM_NAME="$TEMPLATE_VM_NAME"
DEPLOYMENT_TYPE="template-based"
# Network Configuration
VM_IP="$VM_IP"
VM_GATEWAY="$VM_GATEWAY"
VM_NETMASK="$VM_NETMASK"
VM_NETWORK="$VM_NETWORK"
# GitHub Configuration
REPO_URL="$REPO_URL"
GITHUB_USERNAME="$GITHUB_USERNAME"
GITHUB_API_ENABLED="$GITHUB_API_ENABLED"
GITHUB_AUTH_METHOD="$GITHUB_AUTH_METHOD"
# Webhook Configuration
WEBHOOK_PORT="$WEBHOOK_PORT"
WEBHOOK_ENABLED="$WEBHOOK_ENABLED"
# SSH Configuration (path to key, not the key content)
SSH_KEY_PATH="$HOME/.ssh/thrillwiki_vm"
EOF
log_success "Template configuration saved to $CONFIG_FILE"
}
# Function to save GitHub token securely - OVERWRITE THE OLD ONE COMPLETELY
save_github_token() {
if [ -n "$GITHUB_TOKEN" ]; then
log "🔒 OVERWRITING GitHub token (new token will REPLACE old one)..."
# Force remove any existing token file first
rm -f "$TOKEN_FILE" 2>/dev/null || true
# Write new token - this COMPLETELY OVERWRITES any old token
echo "$GITHUB_TOKEN" > "$TOKEN_FILE"
chmod 600 "$TOKEN_FILE" # Restrict to owner read/write only
log_success "✅ NEW GitHub token saved securely (OLD TOKEN COMPLETELY REPLACED)"
log "Token file: $TOKEN_FILE"
else
log_error "No GITHUB_TOKEN to save!"
fi
}
# Function to load GitHub token
load_github_token() {
if [ -f "$TOKEN_FILE" ]; then
GITHUB_TOKEN=$(cat "$TOKEN_FILE")
if [ -n "$GITHUB_TOKEN" ]; then
log "🔓 Loaded saved GitHub token for reuse"
return 0
fi
fi
return 1
}
# Function to load configuration
load_config() {
if [ -f "$CONFIG_FILE" ]; then
log "Loading existing template configuration from $CONFIG_FILE..."
source "$CONFIG_FILE"
return 0
else
return 1
fi
}
# Function for non-interactive configuration loading
load_non_interactive_config() {
log "=== Non-Interactive Template Configuration Loading ==="
# Load saved configuration
if ! load_config; then
log_error "No saved template configuration found. Cannot run in non-interactive mode."
log_error "Please run the script without -y flag first to create initial configuration."
exit 1
fi
log_success "Loaded saved template configuration successfully"
# Check for required environment variables for passwords
if [ -z "${UNRAID_PASSWORD:-}" ]; then
log_error "UNRAID_PASSWORD environment variable not set."
log_error "For non-interactive mode, set: export UNRAID_PASSWORD='your_password'"
exit 1
fi
# Handle GitHub authentication based on saved method
if [ -n "$GITHUB_USERNAME" ] && [ "$GITHUB_API_ENABLED" = "true" ]; then
# Personal access token method - try authentication script first
log "Attempting to get PAT token from authentication script..."
if GITHUB_TOKEN=$(python3 "$SCRIPT_DIR/../github-auth.py" token 2>/dev/null) && [ -n "$GITHUB_TOKEN" ]; then
log_success "Token obtained from authentication script"
elif [ -n "${GITHUB_TOKEN:-}" ]; then
log "Using token from environment variable"
else
log_error "No GitHub PAT token available. Either:"
log_error "1. Run setup interactively to configure token"
log_error "2. Set GITHUB_TOKEN environment variable: export GITHUB_TOKEN='your_token'"
exit 1
fi
fi
# Handle webhook secret
if [ "$WEBHOOK_ENABLED" = "true" ]; then
if [ -z "${WEBHOOK_SECRET:-}" ]; then
log_error "WEBHOOK_SECRET environment variable not set."
log_error "For non-interactive mode, set: export WEBHOOK_SECRET='your_secret'"
exit 1
fi
fi
log_success "All required credentials loaded from environment variables"
log "Template configuration summary:"
echo " Unraid Host: $UNRAID_HOST"
echo " VM Name: $VM_NAME"
echo " Template VM: $TEMPLATE_VM_NAME"
echo " VM IP: $VM_IP"
echo " Repository: $REPO_URL"
echo " GitHub Auth: $GITHUB_AUTH_METHOD"
echo " Webhook Enabled: $WEBHOOK_ENABLED"
echo " Deployment Type: template-based ⚡"
}
# Function to stop and clean up existing VM before reset
stop_existing_vm_for_reset() {
local vm_name="$1"
local unraid_host="$2"
local unraid_user="$3"
if [ -z "$vm_name" ] || [ -z "$unraid_host" ] || [ -z "$unraid_user" ]; then
log_warning "Missing VM connection details for VM shutdown"
log "VM Name: ${vm_name:-'not set'}"
log "Unraid Host: ${unraid_host:-'not set'}"
log "Unraid User: ${unraid_user:-'not set'}"
return 0
fi
log "🔍 Checking if VM '$vm_name' exists and needs to be stopped..."
# Test connection first
if ! ssh -o ConnectTimeout=10 "$unraid_user@$unraid_host" "echo 'Connected'" > /dev/null 2>&1; then
log_warning "Cannot connect to Unraid server at $unraid_host - skipping VM shutdown"
return 0
fi
# Check VM status
local vm_status=$(ssh "$unraid_user@$unraid_host" "virsh domstate $vm_name 2>/dev/null || echo 'not defined'")
if [ "$vm_status" = "not defined" ]; then
log "VM '$vm_name' does not exist - no need to stop"
return 0
elif [ "$vm_status" = "shut off" ]; then
log "VM '$vm_name' is already stopped - good for reset"
return 0
elif [ "$vm_status" = "running" ]; then
log_warning "⚠️ VM '$vm_name' is currently RUNNING!"
log_warning "VM must be stopped before reset to avoid conflicts."
echo
if [ "$NON_INTERACTIVE" = "true" ]; then
log "Non-interactive mode: Automatically stopping VM..."
stop_choice="y"
else
echo "Options:"
echo " 1. Stop the VM gracefully before reset (recommended)"
echo " 2. Force stop the VM before reset"
echo " 3. Skip VM shutdown (may cause issues)"
echo " 4. Cancel reset"
echo
read -p "What would you like to do? (1-4): " stop_choice
fi
case $stop_choice in
1|y|Y)
log "Stopping VM '$vm_name' gracefully before reset..."
# Try graceful shutdown first
log "Attempting graceful shutdown..."
if ssh "$unraid_user@$unraid_host" "virsh shutdown $vm_name"; then
log "Shutdown command sent, waiting for VM to stop..."
# Wait up to 60 seconds for graceful shutdown
local wait_count=0
local max_wait=12 # 60 seconds (12 * 5 seconds)
while [ $wait_count -lt $max_wait ]; do
sleep 5
local current_status=$(ssh "$unraid_user@$unraid_host" "virsh domstate $vm_name 2>/dev/null || echo 'not defined'")
if [ "$current_status" != "running" ]; then
log_success "✅ VM '$vm_name' stopped gracefully (status: $current_status)"
return 0
fi
((wait_count++))
log "Waiting for graceful shutdown... ($((wait_count * 5))s)"
done
# If graceful shutdown didn't work, ask about force stop
log_warning "Graceful shutdown took too long. VM is still running."
if [ "$NON_INTERACTIVE" = "true" ]; then
log "Non-interactive mode: Force stopping VM..."
force_choice="y"
else
echo
read -p "Force stop the VM? (y/n): " force_choice
fi
if [ "$force_choice" = "y" ] || [ "$force_choice" = "Y" ]; then
log "Force stopping VM '$vm_name'..."
if ssh "$unraid_user@$unraid_host" "virsh destroy $vm_name"; then
log_success "✅ VM '$vm_name' force stopped"
return 0
else
log_error "❌ Failed to force stop VM"
return 1
fi
else
log_error "VM is still running. Cannot proceed safely with reset."
return 1
fi
else
log_error "❌ Failed to send shutdown command to VM"
return 1
fi
;;
2)
log "Force stopping VM '$vm_name' before reset..."
if ssh "$unraid_user@$unraid_host" "virsh destroy $vm_name"; then
log_success "✅ VM '$vm_name' force stopped"
return 0
else
log_error "❌ Failed to force stop VM"
return 1
fi
;;
3)
log_warning "⚠️ Continuing with running VM (NOT RECOMMENDED)"
log_warning "This may cause conflicts during VM recreation!"
return 0
;;
4|n|N|"")
log "VM reset cancelled by user"
exit 0
;;
*)
log_error "Invalid choice. Please select 1, 2, 3, or 4."
return 1
;;
esac
else
log "VM '$vm_name' status: $vm_status - continuing with reset"
return 0
fi
}
# Function to gracefully stop template VM if running
stop_template_vm_if_running() {
local template_status=$(ssh "$UNRAID_USER@$UNRAID_HOST" "virsh domstate $TEMPLATE_VM_NAME 2>/dev/null || echo 'not defined'")
if [ "$template_status" = "running" ]; then
log_warning "⚠️ Template VM '$TEMPLATE_VM_NAME' is currently RUNNING!"
log_warning "Template VMs must be stopped to create new instances safely."
echo
if [ "$NON_INTERACTIVE" = "true" ]; then
log "Non-interactive mode: Automatically stopping template VM..."
stop_choice="y"
else
echo "Options:"
echo " 1. Stop the template VM gracefully (recommended)"
echo " 2. Continue anyway (may cause issues)"
echo " 3. Cancel setup"
echo
read -p "What would you like to do? (1/2/3): " stop_choice
fi
case $stop_choice in
1|y|Y)
log "Stopping template VM gracefully..."
# Try graceful shutdown first
log "Attempting graceful shutdown..."
if ssh "$UNRAID_USER@$UNRAID_HOST" "virsh shutdown $TEMPLATE_VM_NAME"; then
log "Shutdown command sent, waiting for VM to stop..."
# Wait up to 60 seconds for graceful shutdown
local wait_count=0
local max_wait=12 # 60 seconds (12 * 5 seconds)
while [ $wait_count -lt $max_wait ]; do
sleep 5
local current_status=$(ssh "$UNRAID_USER@$UNRAID_HOST" "virsh domstate $TEMPLATE_VM_NAME 2>/dev/null || echo 'not defined'")
if [ "$current_status" != "running" ]; then
log_success "✅ Template VM stopped gracefully (status: $current_status)"
return 0
fi
((wait_count++))
log "Waiting for graceful shutdown... ($((wait_count * 5))s)"
done
# If graceful shutdown didn't work, ask about force stop
log_warning "Graceful shutdown took too long. Template VM is still running."
if [ "$NON_INTERACTIVE" = "true" ]; then
log "Non-interactive mode: Force stopping template VM..."
force_choice="y"
else
echo
read -p "Force stop the template VM? (y/n): " force_choice
fi
if [ "$force_choice" = "y" ] || [ "$force_choice" = "Y" ]; then
log "Force stopping template VM..."
if ssh "$UNRAID_USER@$UNRAID_HOST" "virsh destroy $TEMPLATE_VM_NAME"; then
log_success "✅ Template VM force stopped"
return 0
else
log_error "❌ Failed to force stop template VM"
return 1
fi
else
log_error "Template VM is still running. Cannot proceed safely."
return 1
fi
else
log_error "❌ Failed to send shutdown command to template VM"
return 1
fi
;;
2)
log_warning "⚠️ Continuing with running template VM (NOT RECOMMENDED)"
log_warning "This may cause disk corruption or deployment issues!"
return 0
;;
3|n|N|"")
log "Setup cancelled by user"
exit 0
;;
*)
log_error "Invalid choice. Please select 1, 2, or 3."
return 1
;;
esac
fi
return 0
}
# Function to check template VM availability
check_template_vm() {
log_template "Checking template VM availability..."
# Test connection first
if ! ssh -o ConnectTimeout=10 "$UNRAID_USER@$UNRAID_HOST" "echo 'Connected'" > /dev/null 2>&1; then
log_error "Cannot connect to Unraid server at $UNRAID_HOST"
log_error "Please verify:"
log_error "1. Unraid server IP address is correct"
log_error "2. SSH key authentication is set up"
log_error "3. Network connectivity"
return 1
fi
# Check if template VM disk exists
if ssh "$UNRAID_USER@$UNRAID_HOST" "test -f /mnt/user/domains/$TEMPLATE_VM_NAME/vdisk1.qcow2"; then
log_template "✅ Template VM disk found: /mnt/user/domains/$TEMPLATE_VM_NAME/vdisk1.qcow2"
# Get template info
template_info=$(ssh "$UNRAID_USER@$UNRAID_HOST" "qemu-img info /mnt/user/domains/$TEMPLATE_VM_NAME/vdisk1.qcow2 | grep 'virtual size' || echo 'Size info not available'")
log_template "📋 Template info: $template_info"
# Check and handle template VM status
template_status=$(ssh "$UNRAID_USER@$UNRAID_HOST" "virsh domstate $TEMPLATE_VM_NAME 2>/dev/null || echo 'not defined'")
if [ "$template_status" = "running" ]; then
log_template "Template VM status: $template_status (needs to be stopped)"
# Stop the template VM if running
if ! stop_template_vm_if_running; then
log_error "Failed to stop template VM. Cannot proceed safely."
return 1
fi
else
log_template "✅ Template VM status: $template_status (good for template use)"
fi
return 0
else
log_error "❌ Template VM disk not found!"
log_error "Expected location: /mnt/user/domains/$TEMPLATE_VM_NAME/vdisk1.qcow2"
log_error ""
log_error "To create the template VM:"
log_error "1. Create a VM named '$TEMPLATE_VM_NAME' on your Unraid server"
log_error "2. Install Ubuntu 24.04 LTS with required packages"
log_error "3. Configure it with Python, PostgreSQL, Nginx, etc."
log_error "4. Shut it down to use as a template"
log_error ""
log_error "See README-template-deployment.md for detailed setup instructions"
return 1
fi
}
# Function to prompt for configuration
prompt_template_config() {
# In non-interactive mode, use saved config only
if [ "$NON_INTERACTIVE" = "true" ]; then
load_non_interactive_config
return 0
fi
log "=== ThrillWiki Template-Based VM Configuration ==="
echo
log_template "🚀 This setup uses TEMPLATE-BASED deployment for ultra-fast VM creation!"
echo
# Try to load existing config first
if load_config; then
log_success "Loaded existing template configuration"
echo "Current settings:"
echo " Unraid Host: $UNRAID_HOST"
echo " VM Name: $VM_NAME"
echo " Template VM: $TEMPLATE_VM_NAME"
echo " VM IP: $VM_IP"
echo " Repository: $REPO_URL"
echo " Deployment: template-based ⚡"
echo
read -p "Use existing configuration? (y/n): " use_existing
if [ "$use_existing" = "y" ] || [ "$use_existing" = "Y" ]; then
# Still need to get sensitive info that we don't save
read -s -p "Enter Unraid [PASSWORD-REMOVED]
echo
# Handle GitHub authentication based on saved method
if [ -n "$GITHUB_USERNAME" ] && [ "$GITHUB_API_ENABLED" = "true" ]; then
# Try different sources for the token in order of preference
log "Loading GitHub PAT token..."
# 1. Try authentication script first
if GITHUB_TOKEN=$(python3 "$SCRIPT_DIR/../github-auth.py" token 2>/dev/null) && [ -n "$GITHUB_TOKEN" ]; then
log_success "Token obtained from authentication script"
log "Using existing PAT token from authentication script"
# Validate token and repository access immediately
log "🔍 Validating GitHub token and repository access..."
if ! validate_github_access; then
log_error "❌ GitHub token validation failed. Please check your token and repository access."
log "Please try entering a new token or check your repository URL."
return 1
fi
# 2. Try saved token file
elif load_github_token; then
log_success "Token loaded from secure storage (reusing for VM reset)"
# Validate token and repository access immediately
log "🔍 Validating GitHub token and repository access..."
if ! validate_github_access; then
log_error "❌ GitHub token validation failed. Please check your token and repository access."
log "Please try entering a new token or check your repository URL."
return 1
fi
else
log "No token found in authentication script or saved storage"
read -s -p "Enter GitHub personal access token: " GITHUB_TOKEN
echo
# Validate the new token immediately
if [ -n "$GITHUB_TOKEN" ]; then
log "🔍 Validating new GitHub token..."
if ! validate_github_access; then
log_error "❌ GitHub token validation failed. Please check your token and repository access."
log "Please try running the setup again with a valid token."
return 1
fi
fi
# Save the new token for future VM resets
save_github_token
fi
fi
if [ "$WEBHOOK_ENABLED" = "true" ]; then
read -s -p "Enter GitHub webhook secret: " WEBHOOK_SECRET
echo
fi
# Check template VM before proceeding
if ! check_template_vm; then
log_error "Template VM check failed. Please set up your template VM first."
exit 1
fi
return 0
fi
fi
# Prompt for new configuration
read -p "Enter your Unraid server IP address: " UNRAID_HOST
read -p "Enter Unraid username (default: root): " UNRAID_USER
UNRAID_USER=${UNRAID_USER:-root}
read -s -p "Enter Unraid [PASSWORD-REMOVED]
echo
# Note: Password not saved for security
# Check template VM availability early
log_template "Verifying template VM setup..."
if ! check_template_vm; then
log_error "Template VM setup is required before proceeding."
echo
read -p "Do you want to continue setup anyway? (y/n): " continue_anyway
if [ "$continue_anyway" != "y" ] && [ "$continue_anyway" != "Y" ]; then
log "Setup cancelled. Please set up your template VM first."
log "See README-template-deployment.md for instructions."
exit 1
fi
log_warning "Continuing setup without verified template VM..."
else
log_success "Template VM verified and ready!"
fi
read -p "Enter VM name (default: $DEFAULT_VM_NAME): " VM_NAME
VM_NAME=${VM_NAME:-$DEFAULT_VM_NAME}
read -p "Enter VM memory in MB (default: $DEFAULT_VM_MEMORY): " VM_MEMORY
VM_MEMORY=${VM_MEMORY:-$DEFAULT_VM_MEMORY}
read -p "Enter VM vCPUs (default: $DEFAULT_VM_VCPUS): " VM_VCPUS
VM_VCPUS=${VM_VCPUS:-$DEFAULT_VM_VCPUS}
read -p "Enter VM disk size in GB (default: $DEFAULT_VM_DISK_SIZE): " VM_DISK_SIZE
VM_DISK_SIZE=${VM_DISK_SIZE:-$DEFAULT_VM_DISK_SIZE}
# Template VM name (usually fixed)
read -p "Enter template VM name (default: $TEMPLATE_VM_NAME): " TEMPLATE_VM_NAME_INPUT
TEMPLATE_VM_NAME=${TEMPLATE_VM_NAME_INPUT:-$TEMPLATE_VM_NAME}
read -p "Enter GitHub repository URL: " REPO_URL
# GitHub API Configuration - PAT Only
echo
log "=== GitHub Personal Access Token Configuration ==="
echo "This setup requires a GitHub Personal Access Token (PAT) for repository access."
echo "Both classic tokens and fine-grained tokens are supported."
echo ""
echo "Required token permissions:"
echo " - Repository access (read/write)"
echo " - Contents (read/write)"
echo " - Metadata (read)"
echo ""
# Try to get token from authentication script first
log "Checking for existing GitHub token..."
if GITHUB_TOKEN=$(python3 "$SCRIPT_DIR/../github-auth.py" token 2>/dev/null) && [ -n "$GITHUB_TOKEN" ]; then
# Get username from authentication script if possible
if GITHUB_USERNAME=$(python3 "$SCRIPT_DIR/../github-auth.py" whoami 2>/dev/null | grep "You are authenticated as:" | cut -d: -f2 | xargs) && [ -n "$GITHUB_USERNAME" ]; then
log_success "Found existing token and username from authentication script"
echo "Username: $GITHUB_USERNAME"
echo "Token: ${GITHUB_TOKEN:0:8}... (masked)"
echo
read -p "Use this existing token? (y/n): " use_existing_token
if [ "$use_existing_token" != "y" ] && [ "$use_existing_token" != "Y" ]; then
GITHUB_TOKEN=""
GITHUB_USERNAME=""
fi
else
log "Found token but no username, need to get username..."
read -p "Enter GitHub username: " GITHUB_USERNAME
fi
fi
# If no token found or user chose not to use existing, prompt for manual entry
if [ -z "$GITHUB_TOKEN" ]; then
log "Enter your GitHub credentials manually:"
read -p "Enter GitHub username: " GITHUB_USERNAME
read -s -p "Enter GitHub Personal Access Token (classic or fine-grained): " GITHUB_TOKEN
echo
fi
# Validate that we have both username and token
if [ -n "$GITHUB_USERNAME" ] && [ -n "$GITHUB_TOKEN" ]; then
GITHUB_API_ENABLED=true
GITHUB_AUTH_METHOD="token"
log_success "Personal access token configured for user: $GITHUB_USERNAME"
# Test the token quickly
log "Testing GitHub token access..."
if curl -sf -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/user >/dev/null 2>&1; then
log_success "✅ GitHub token validated successfully"
else
log_warning "⚠️ Could not validate GitHub token (API may be rate-limited)"
log "Proceeding anyway - token will be tested during repository operations"
fi
else
log_error "Both username and token are required for GitHub access"
log_error "Repository cloning and auto-pull functionality will not work without proper authentication"
exit 1
fi
# Webhook Configuration
echo
read -s -p "Enter GitHub webhook secret (optional, press Enter to skip): " WEBHOOK_SECRET
echo
# If no webhook secret provided, disable webhook functionality
if [ -z "$WEBHOOK_SECRET" ]; then
log "No webhook secret provided - webhook functionality will be disabled"
WEBHOOK_ENABLED=false
else
WEBHOOK_ENABLED=true
fi
read -p "Enter webhook port (default: $DEFAULT_WEBHOOK_PORT): " WEBHOOK_PORT
WEBHOOK_PORT=${WEBHOOK_PORT:-$DEFAULT_WEBHOOK_PORT}
# Get VM network configuration preference
echo
log "=== Network Configuration ==="
echo "Choose network configuration method:"
echo "1. DHCP (automatic IP assignment - recommended)"
echo "2. Static IP (manual IP configuration)"
while true; do
read -p "Select option (1-2): " network_choice
case $network_choice in
1)
log "Using DHCP network configuration..."
VM_IP="dhcp"
VM_GATEWAY="192.168.20.1"
VM_NETMASK="255.255.255.0"
VM_NETWORK="192.168.20.0/24"
NETWORK_MODE="dhcp"
break
;;
2)
log "Using static IP network configuration..."
# Get VM IP address with proper range validation
while true; do
read -p "Enter VM IP address (192.168.20.10-192.168.20.100): " VM_IP
if [[ "$VM_IP" =~ ^192\.168\.20\.([1-9][0-9]|100)$ ]]; then
local ip_last_octet="${BASH_REMATCH[1]}"
if [ "$ip_last_octet" -ge 10 ] && [ "$ip_last_octet" -le 100 ]; then
break
fi
fi
echo "Invalid IP address. Please enter an IP in the range 192.168.20.10-192.168.20.100"
done
VM_GATEWAY="192.168.20.1"
VM_NETMASK="255.255.255.0"
VM_NETWORK="192.168.20.0/24"
NETWORK_MODE="static"
break
;;
*)
echo "Invalid option. Please select 1 or 2."
;;
esac
done
# Save configuration and GitHub token
save_config
save_github_token # Save token for VM resets
log_success "Template configuration saved - setup complete!"
}
# Function to update SSH config with actual VM IP address
update_ssh_config_with_ip() {
local vm_name="$1"
local vm_ip="$2"
local ssh_config_path="$HOME/.ssh/config"
log "Updating SSH config with actual IP: $vm_ip"
# Check if SSH config exists and has our VM entry
if [ -f "$ssh_config_path" ] && grep -q "Host $vm_name" "$ssh_config_path"; then
# Update the HostName to use actual IP instead of %h placeholder
if grep -A 10 "Host $vm_name" "$ssh_config_path" | grep -q "HostName %h"; then
# Replace %h with actual IP
sed -i.bak "/Host $vm_name/,/^Host\|^$/s/HostName %h/HostName $vm_ip/" "$ssh_config_path"
log_success "SSH config updated: $vm_name now points to $vm_ip"
elif grep -A 10 "Host $vm_name" "$ssh_config_path" | grep -q "HostName "; then
# Update existing IP
sed -i.bak "/Host $vm_name/,/^Host\|^$/s/HostName .*/HostName $vm_ip/" "$ssh_config_path"
log_success "SSH config updated: $vm_name IP changed to $vm_ip"
else
# Add HostName line after Host line
sed -i.bak "/Host $vm_name/a\\
HostName $vm_ip" "$ssh_config_path"
log_success "SSH config updated: Added IP $vm_ip for $vm_name"
fi
# Show the updated config section
log "Updated SSH config for $vm_name:"
grep -A 6 "Host $vm_name" "$ssh_config_path" | head -7
else
log_warning "SSH config entry for $vm_name not found, cannot update IP"
fi
}
# Generate SSH keys for VM access
setup_ssh_keys() {
log "Setting up SSH keys for template VM access..."
local ssh_key_path="$HOME/.ssh/thrillwiki_vm"
local ssh_config_path="$HOME/.ssh/config"
if [ ! -f "$ssh_key_path" ]; then
ssh-keygen -t rsa -b 4096 -f "$ssh_key_path" -N "" -C "thrillwiki-template-vm-access"
log_success "SSH key generated: $ssh_key_path"
else
log "SSH key already exists: $ssh_key_path"
fi
# Add SSH config entry
if ! grep -q "Host $VM_NAME" "$ssh_config_path" 2>/dev/null; then
cat >> "$ssh_config_path" << EOF
# ThrillWiki Template VM
Host $VM_NAME
HostName %h
User thrillwiki
IdentityFile $ssh_key_path
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
EOF
log_success "SSH config updated for template VM"
fi
# Store public key for VM setup
SSH_PUBLIC_KEY=$(cat "$ssh_key_path.pub")
export SSH_PUBLIC_KEY
}
# Setup Unraid host access
setup_unraid_access() {
log "Setting up Unraid server access..."
local unraid_key_path="$HOME/.ssh/unraid_access"
if [ ! -f "$unraid_key_path" ]; then
ssh-keygen -t rsa -b 4096 -f "$unraid_key_path" -N "" -C "unraid-template-access"
log "Please add this public key to your Unraid server:"
echo "---"
cat "$unraid_key_path.pub"
echo "---"
echo
log "Add this to /root/.ssh/***REMOVED*** on your Unraid server"
read -p "Press Enter when you've added the key..."
fi
# Test Unraid connection
log "Testing Unraid connection..."
if ssh -i "$unraid_key_path" -o ConnectTimeout=5 -o StrictHostKeyChecking=no "$UNRAID_USER@$UNRAID_HOST" "echo 'Connected to Unraid successfully'"; then
log_success "Unraid connection test passed"
else
log_error "Unraid connection test failed"
exit 1
fi
# Update SSH config for Unraid
if ! grep -q "Host unraid" "$HOME/.ssh/config" 2>/dev/null; then
cat >> "$HOME/.ssh/config" << EOF
# Unraid Server
Host unraid
HostName $UNRAID_HOST
User $UNRAID_USER
IdentityFile $unraid_key_path
StrictHostKeyChecking no
EOF
fi
}
# Create environment files for template deployment
create_environment_files() {
log "Creating template deployment environment files..."
log "🔄 NEW TOKEN WILL BE WRITTEN TO ALL ENVIRONMENT FILES (overwriting any old tokens)"
# Force remove old environment files first
rm -f "$PROJECT_DIR/***REMOVED***.unraid" "$PROJECT_DIR/***REMOVED***.webhook" 2>/dev/null || true
# Get SSH public key content safely
local ssh_key_path="$HOME/.ssh/thrillwiki_vm.pub"
local ssh_public_key=""
if [ -f "$ssh_key_path" ]; then
ssh_public_key=$(cat "$ssh_key_path")
fi
# Template-based Unraid VM environment - COMPLETELY NEW FILE WITH NEW TOKEN
cat > "$PROJECT_DIR/***REMOVED***.unraid" << EOF
# ThrillWiki Template-Based VM Configuration
UNRAID_HOST=$UNRAID_HOST
UNRAID_USER=$UNRAID_USER
UNRAID_PASSWORD=$UNRAID_PASSWORD
VM_NAME=$VM_NAME
VM_MEMORY=$VM_MEMORY
VM_VCPUS=$VM_VCPUS
VM_DISK_SIZE=$VM_DISK_SIZE
SSH_PUBLIC_KEY="$ssh_public_key"
# Template Configuration
TEMPLATE_VM_NAME=$TEMPLATE_VM_NAME
DEPLOYMENT_TYPE=template-based
# Network Configuration
VM_IP=$VM_IP
VM_GATEWAY=$VM_GATEWAY
VM_NETMASK=$VM_NETMASK
VM_NETWORK=$VM_NETWORK
# GitHub Configuration
REPO_URL=$REPO_URL
GITHUB_USERNAME=$GITHUB_USERNAME
GITHUB_TOKEN=$GITHUB_TOKEN
GITHUB_API_ENABLED=$GITHUB_API_ENABLED
EOF
# Webhook environment (updated with VM info)
cat > "$PROJECT_DIR/***REMOVED***.webhook" << EOF
# ThrillWiki Template-Based Webhook Configuration
WEBHOOK_PORT=$WEBHOOK_PORT
WEBHOOK_SECRET=$WEBHOOK_SECRET
WEBHOOK_ENABLED=$WEBHOOK_ENABLED
VM_HOST=$VM_IP
VM_PORT=22
VM_USER=thrillwiki
VM_KEY_PATH=$HOME/.ssh/thrillwiki_vm
VM_PROJECT_PATH=/home/thrillwiki/thrillwiki
REPO_URL=$REPO_URL
DEPLOY_BRANCH=main
# Template Configuration
TEMPLATE_VM_NAME=$TEMPLATE_VM_NAME
DEPLOYMENT_TYPE=template-based
# GitHub API Configuration
GITHUB_USERNAME=$GITHUB_USERNAME
GITHUB_TOKEN=$GITHUB_TOKEN
GITHUB_API_ENABLED=$GITHUB_API_ENABLED
EOF
log_success "Template deployment environment files created"
}
# Install required tools
install_dependencies() {
log "Installing required dependencies for template deployment..."
# Check for required tools
local missing_tools=()
local mac_tools=()
command -v python3 >/dev/null 2>&1 || missing_tools+=("python3")
command -v ssh >/dev/null 2>&1 || missing_tools+=("openssh-client")
command -v scp >/dev/null 2>&1 || missing_tools+=("openssh-client")
# Install missing tools based on platform
if [ ${#missing_tools[@]} -gt 0 ]; then
log "Installing missing tools: ${missing_tools[*]}"
if command -v apt-get >/dev/null 2>&1; then
sudo apt-get update
sudo apt-get install -y "${missing_tools[@]}"
elif command -v yum >/dev/null 2>&1; then
sudo yum install -y "${missing_tools[@]}"
elif command -v dnf >/dev/null 2>&1; then
sudo dnf install -y "${missing_tools[@]}"
elif command -v brew >/dev/null 2>&1; then
# macOS with Homebrew
for tool in "${missing_tools[@]}"; do
case $tool in
python3) brew install python3 ;;
openssh-client) log "OpenSSH should be available on macOS" ;;
esac
done
else
log_error "Package manager not found. Please install: ${missing_tools[*]}"
exit 1
fi
fi
# Install Python dependencies
if [ -f "$PROJECT_DIR/pyproject.toml" ]; then
log "Installing Python dependencies with UV..."
if ! command -v uv >/dev/null 2>&1; then
curl -LsSf https://astral.sh/uv/install.sh | sh
source ~/.cargo/env
fi
cd "$PROJECT_DIR"
uv sync
fi
log_success "Dependencies installed for template deployment"
}
# Create VM using the template-based VM manager
create_template_vm() {
log "Creating VM from template on Unraid server..."
# Export all environment variables from the file
set -a # automatically export all variables
source "$PROJECT_DIR/***REMOVED***.unraid"
set +a # turn off automatic export
# Run template-based VM setup
cd "$PROJECT_DIR"
python3 scripts/unraid/main_template.py setup
if [ $? -eq 0 ]; then
log_success "Template-based VM setup completed successfully ⚡"
log_template "VM deployed in minutes instead of 30+ minutes!"
else
log_error "Template-based VM setup failed"
exit 1
fi
}
# Wait for template VM to be ready and get IP
wait_for_template_vm() {
log "🔍 Getting VM IP address from guest agent..."
log_template "Template VMs should get IP immediately via guest agent!"
# Export all environment variables from the file
set -a # automatically export all variables
source "$PROJECT_DIR/***REMOVED***.unraid"
set +a # turn off automatic export
# Check for IP immediately - template VMs should have guest agent running
local max_attempts=12 # 3 minutes max wait (much shorter)
local attempt=1
log "🔍 Phase 1: Checking guest agent for IP address..."
while [ $attempt -le $max_attempts ]; do
log "🔍 Attempt $attempt/$max_attempts: Querying guest agent on VM '$VM_NAME'..."
# Add timeout to the IP detection to prevent hanging
VM_IP_RESULT=""
VM_IP=""
# Use timeout command to prevent hanging (30 seconds max per attempt)
if command -v timeout >/dev/null 2>&1; then
VM_IP_RESULT=$(timeout 30 python3 scripts/unraid/main_template.py ip 2>&1 || echo "TIMEOUT")
elif command -v gtimeout >/dev/null 2>&1; then
# macOS with coreutils installed
VM_IP_RESULT=$(gtimeout 30 python3 scripts/unraid/main_template.py ip 2>&1 || echo "TIMEOUT")
else
# Fallback for systems without timeout command - use background process with kill
log "⚠️ No timeout command available, using background process method..."
VM_IP_RESULT=$(python3 scripts/unraid/main_template.py ip 2>&1 &
PID=$!
(
sleep 30
if kill -0 $PID 2>/dev/null; then
kill $PID 2>/dev/null
echo "TIMEOUT"
fi
) &
wait $PID 2>/dev/null || echo "TIMEOUT")
fi
# Check if we got a timeout
if echo "$VM_IP_RESULT" | grep -q "TIMEOUT"; then
log "⚠️ IP detection timed out after 30 seconds - guest agent may not be ready"
elif [ -n "$VM_IP_RESULT" ]; then
# Show what we got from the query
log "📝 Guest agent response: $(echo "$VM_IP_RESULT" | head -1)"
# Extract IP from successful response
VM_IP=$(echo "$VM_IP_RESULT" | grep "VM IP:" | cut -d' ' -f3)
else
log "⚠️ No response from guest agent query"
fi
if [ -n "$VM_IP" ] && [ "$VM_IP" != "None" ] && [ "$VM_IP" != "null" ] && [ "$VM_IP" != "TIMEOUT" ]; then
log_success "✅ Template VM got IP address: $VM_IP"
# Update SSH config with actual IP
update_ssh_config_with_ip "$VM_NAME" "$VM_IP"
# Update webhook environment with IP
sed -i.bak "s/VM_HOST=$VM_NAME/VM_HOST=$VM_IP/" "$PROJECT_DIR/***REMOVED***.webhook"
break
fi
# Much shorter wait time since template VMs should be fast
if [ $attempt -le 3 ]; then
log "⏳ No IP yet, waiting 5 seconds... (VM may still be booting)"
sleep 5 # Very short wait for first few attempts
else
log "⏳ Still waiting for IP... ($(($attempt * 15))s elapsed, checking every 15s)"
# Show VM status to help debug - also with timeout
log "🔍 Checking VM status for debugging..."
if command -v timeout >/dev/null 2>&1; then
VM_STATUS=$(timeout 15 python3 scripts/unraid/main_template.py status 2>&1 | head -1 || echo "Status check timed out")
else
VM_STATUS=$(python3 scripts/unraid/main_template.py status 2>&1 | head -1)
fi
if [ -n "$VM_STATUS" ]; then
log "📊 VM Status: $VM_STATUS"
fi
sleep 15
fi
((attempt++))
done
if [ -z "$VM_IP" ] || [ "$VM_IP" = "None" ] || [ "$VM_IP" = "null" ]; then
log_error "❌ Template VM failed to get IP address after $((max_attempts * 15)) seconds"
log_error "Guest agent may not be running or network configuration issue"
log_error "Check VM console on Unraid: virsh console $VM_NAME"
exit 1
fi
# Phase 2: Wait for SSH connectivity (should be very fast for templates)
log "🔍 Phase 2: Testing SSH connectivity to $VM_IP..."
wait_for_ssh_connectivity "$VM_IP"
}
# Wait for SSH connectivity to be available
wait_for_ssh_connectivity() {
local vm_ip="$1"
local max_ssh_attempts=20 # 5 minutes max wait for SSH
local ssh_attempt=1
while [ $ssh_attempt -le $max_ssh_attempts ]; do
log "🔑 Testing SSH connection to $vm_ip... (attempt $ssh_attempt/$max_ssh_attempts)"
# Test SSH connectivity with a simple command
if ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no -o BatchMode=yes "$VM_NAME" "echo 'SSH connection successful'" >/dev/null 2>&1; then
log_success "✅ SSH connectivity established to template VM! 🚀"
return 0
fi
# More detailed error for first few attempts
if [ $ssh_attempt -le 3 ]; then
log "⏳ SSH not ready yet - VM may still be booting or initializing SSH service..."
else
log "⏳ Still waiting for SSH... ($(($ssh_attempt * 15))s elapsed)"
fi
sleep 15
((ssh_attempt++))
done
log_error "❌ SSH connection failed after $((max_ssh_attempts * 15)) seconds"
log_error "VM IP: $vm_ip"
log_error "Try manually: ssh $VM_NAME"
log_error "Check VM console on Unraid for boot issues"
exit 1
}
# Configure VM for ThrillWiki using template-optimized deployment
configure_template_vm() {
log "🚀 Deploying ThrillWiki to template VM..."
log "This will sync the project files and set up the application"
# First, sync the current project files to the VM
deploy_project_files
# Then run the setup script on the VM
run_vm_setup_script
log_success "✅ Template VM configured and application deployed! ⚡"
}
# Configure passwordless sudo for required operations
configure_passwordless_sudo() {
log "⚙️ Configuring passwordless sudo for deployment operations..."
# Create sudoers configuration file for thrillwiki user
local sudoers_config="/tmp/thrillwiki-sudoers"
cat > "$sudoers_config" << 'EOF'
# ThrillWiki deployment sudo configuration
# Allow thrillwiki user to run specific commands without password
# File system operations for deployment
thrillwiki ALL=(ALL) NOPASSWD: /bin/rm, /bin/mkdir, /bin/chown, /bin/chmod
# Package management for updates
thrillwiki ALL=(ALL) NOPASSWD: /usr/bin/apt, /usr/bin/apt-get, /usr/bin/apt-cache
# System service management
thrillwiki ALL=(ALL) NOPASSWD: /bin/systemctl
# PostgreSQL management
thrillwiki ALL=(ALL) NOPASSWD: /usr/bin/sudo -u postgres *
# Service file management
thrillwiki ALL=(ALL) NOPASSWD: /bin/cp [AWS-SECRET-REMOVED]emd/* /etc/systemd/system/
thrillwiki ALL=(ALL) NOPASSWD: /bin/sed -i * /etc/systemd/system/thrillwiki.service
EOF
# Copy sudoers file to VM and install it
log "📋 Copying sudoers configuration to VM..."
scp "$sudoers_config" "$VM_NAME:/tmp/"
# Install sudoers configuration (this requires password once)
log "Installing sudo configuration (may require password this one time)..."
if ssh -t "$VM_NAME" "sudo cp /tmp/thrillwiki-sudoers /etc/sudoers.d/thrillwiki && sudo chmod 440 /etc/sudoers.d/thrillwiki && sudo visudo -c"; then
log_success "✅ Passwordless sudo configured successfully"
else
log_error "Failed to configure passwordless sudo. Setup will continue but may prompt for passwords."
# Continue anyway, as the user might have already configured this
fi
# Cleanup
rm -f "$sudoers_config"
ssh "$VM_NAME" "rm -f /tmp/thrillwiki-sudoers"
}
# Validate GitHub token and repository access
validate_github_access() {
log "🔍 Validating GitHub token and repository access..."
# Extract repository path from REPO_URL
local repo_path=$(echo "$REPO_URL" | sed 's|^https://github.com/||' | sed 's|/$||')
if [ -z "$repo_path" ]; then
repo_path="pacnpal/thrillwiki_django_no_react" # fallback
log_warning "Using fallback repository path: $repo_path"
fi
# Test GitHub API authentication
log "Testing GitHub API authentication..."
if ! curl -sf -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/user" > /dev/null; then
log_error "❌ GitHub token authentication failed!"
log_error "The token cannot authenticate with GitHub API."
if [ "$NON_INTERACTIVE" = "true" ]; then
log_error "Non-interactive mode: Cannot prompt for new token."
log_error "Please update your GITHUB_TOKEN environment variable with a valid token."
exit 1
fi
echo
echo "❌ Your GitHub token is invalid or expired!"
echo "Please create a new Personal Access Token at: https://github.com/settings/tokens"
echo "Required permissions: repo (full control of private repositories)"
echo
read -s -p "Enter a new GitHub Personal Access Token: " GITHUB_TOKEN
echo
if [ -z "$GITHUB_TOKEN" ]; then
log_error "No token provided. Cannot continue."
return 1
fi
# Save the new token
save_github_token
# Test the new token
if ! curl -sf -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/user" > /dev/null; then
log_error "❌ New token is also invalid. Please check your token and try again."
return 1
fi
log_success "✅ New GitHub token validated successfully"
else
log_success "✅ GitHub token authentication successful"
fi
# Test repository access
log "Testing repository access: $repo_path"
local repo_response=$(curl -sf -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/repos/$repo_path")
if [ $? -ne 0 ] || [ -z "$repo_response" ]; then
log_error "❌ Cannot access repository: $repo_path"
log_error "This could be due to:"
log_error "1. Repository doesn't exist"
log_error "2. Repository is private and token lacks access"
log_error "3. Token doesn't have 'repo' permissions"
if [ "$NON_INTERACTIVE" = "true" ]; then
log_error "Non-interactive mode: Cannot prompt for new repository."
log_error "Please update your repository URL or token permissions."
return 1
fi
echo
echo "❌ Cannot access repository: $REPO_URL"
echo "Current repository path: $repo_path"
echo
echo "The token has these scopes: $(curl -sf -H "Authorization: token $GITHUB_TOKEN" -I "https://api.github.com/user" | grep -i "x-oauth-scopes:" | cut -d: -f2 | xargs || echo "unknown")"
echo "Required scope: 'repo' (full control of private repositories)"
echo
echo "Options:"
echo "1. Enter a new GitHub token with 'repo' permissions"
echo "2. Enter a different repository URL"
echo "3. Exit and fix token permissions at https://github.com/settings/tokens"
echo
read -p "Select option (1-3): " repo_access_choice
case $repo_access_choice in
1)
echo
echo "Please create a new GitHub Personal Access Token:"
echo "1. Go to: https://github.com/settings/tokens/new"
echo "2. Give it a name like 'ThrillWiki Template Automation'"
echo "3. Check the 'repo' scope (full control of private repositories)"
echo "4. Click 'Generate token'"
echo "5. Copy the new token"
echo
read -s -p "Enter new GitHub Personal Access Token: " new_github_token
echo
if [ -z "$new_github_token" ]; then
log_error "No token provided. Cannot continue."
return 1
fi
# Test the new token
log "Testing new GitHub token..."
if ! curl -sf -H "Authorization: token $new_github_token" "https://api.github.com/user" > /dev/null; then
log_error "❌ New token authentication failed. Please check your token."
return 1
fi
# Test repository access with new token
log "Testing repository access with new token: $repo_path"
local new_repo_response=$(curl -sf -H "Authorization: token $new_github_token" "https://api.github.com/repos/$repo_path")
if [ $? -ne 0 ] || [ -z "$new_repo_response" ]; then
log_error "❌ New token still cannot access the repository."
log_error "Please ensure the token has 'repo' scope and try again."
return 1
fi
# Token works! Update it
GITHUB_TOKEN="$new_github_token"
log_success "✅ New GitHub token validated successfully"
# Show new token scopes
local new_scopes=$(curl -sf -H "Authorization: token $GITHUB_TOKEN" -I "https://api.github.com/user" | grep -i "x-oauth-scopes:" | cut -d: -f2 | xargs || echo "unknown")
log "New token scopes: $new_scopes"
# Save the new token
save_github_token
# Continue with validation using the new token
repo_response="$new_repo_response"
;;
2)
echo
read -p "Enter new repository URL: " new_repo_url
if [ -z "$new_repo_url" ]; then
log "Setup cancelled by user"
exit 0
fi
REPO_URL="$new_repo_url"
# Extract new repo path and test again
repo_path=$(echo "$REPO_URL" | sed 's|^https://github.com/||' | sed 's|/$||')
log "Testing new repository: $repo_path"
repo_response=$(curl -sf -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/repos/$repo_path")
if [ $? -ne 0 ] || [ -z "$repo_response" ]; then
log_error "❌ New repository is also inaccessible. Please check the URL and token permissions."
return 1
fi
log_success "✅ New repository validated successfully"
# Update saved configuration with new repo URL
save_config
;;
3|"")
log "Setup cancelled by user"
echo "Please update your token permissions at: https://github.com/settings/tokens"
return 1
;;
*)
log_error "Invalid choice. Please select 1, 2, or 3."
return 1
;;
esac
else
log_success "✅ Repository access confirmed: $repo_path"
fi
# Show repository info
local repo_name=$(echo "$repo_response" | python3 -c "import sys, json; print(json.load(sys.stdin).get('full_name', 'Unknown'))" 2>/dev/null || echo "$repo_path")
local repo_private=$(echo "$repo_response" | python3 -c "import sys, json; print(json.load(sys.stdin).get('private', False))" 2>/dev/null || echo "Unknown")
log "📊 Repository info:"
echo " Name: $repo_name"
echo " Private: $repo_private"
echo " URL: $REPO_URL"
}
# Clone project from GitHub using PAT authentication
deploy_project_files() {
log "🔄 Cloning project from GitHub repository..."
# Validate GitHub access before attempting clone
if ! validate_github_access; then
log_error "❌ GitHub token validation failed during deployment."
log_error "Cannot proceed with repository cloning without valid GitHub access."
exit 1
fi
# First, configure passwordless sudo for required operations
configure_passwordless_sudo
# Remove any existing directory first
ssh "$VM_NAME" "sudo rm -rf /home/thrillwiki/thrillwiki"
# Create parent directory
ssh "$VM_NAME" "sudo mkdir -p /home/thrillwiki && sudo chown thrillwiki:thrillwiki /home/thrillwiki"
# Clone the repository using PAT authentication
# Extract repository path from REPO_URL (already validated)
local repo_path=$(echo "$REPO_URL" | sed 's|^https://github.com/||' | sed 's|/$||')
local auth_url="https://${GITHUB_USERNAME}:${GITHUB_TOKEN}@github.com/${repo_path}.git"
log "Cloning repository: $REPO_URL"
if ssh "$VM_NAME" "cd /home/thrillwiki && git clone '$auth_url' thrillwiki"; then
log_success "✅ Repository cloned successfully from GitHub!"
else
log_error "❌ Failed to clone repository from GitHub"
log_error "Repository access was validated, but clone failed. This may be due to:"
log_error "1. Network connectivity issues from VM to GitHub"
log_error "2. Git not installed on VM"
log_error "3. Disk space issues on VM"
log_error "Try manually: ssh $VM_NAME 'git --version && df -h'"
exit 1
fi
# Set proper ownership
ssh "$VM_NAME" "sudo chown -R thrillwiki:thrillwiki /home/thrillwiki/thrillwiki"
# Show repository info
local commit_info=$(ssh "$VM_NAME" "cd /home/thrillwiki/thrillwiki && git log -1 --oneline")
log "📊 Cloned repository at commit: $commit_info"
# Remove the authentication URL from git config for security
ssh "$VM_NAME" "cd /home/thrillwiki/thrillwiki && git remote set-url origin $REPO_URL"
log "🔒 Cleaned up authentication URL from git configuration"
}
# Run setup script on the VM after files are synchronized
run_vm_setup_script() {
log "⚙️ Running application setup on template VM..."
# Create optimized VM setup script for template VMs
local vm_setup_script="/tmp/template_vm_thrillwiki_setup.sh"
cat > "$vm_setup_script" << 'EOF'
#!/bin/bash
set -e
echo "🚀 Setting up ThrillWiki on template VM (optimized for pre-configured templates)..."
# Navigate to project directory
cd /home/thrillwiki/thrillwiki
# Template VMs should already have most packages - just update security
echo "📦 Quick system update (template optimization)..."
sudo apt update >/dev/null 2>&1
if sudo apt list --upgradable 2>/dev/null | grep -q security; then
echo "🔒 Installing security updates..."
sudo apt upgrade -y --with-new-pkgs -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" >/dev/null 2>&1
else
echo "✅ No security updates needed"
fi
# UV should already be installed in template
echo "🔧 Checking UV installation..."
# Check multiple possible UV locations
export PATH="/home/thrillwiki/.local/bin:/home/thrillwiki/.cargo/bin:$PATH"
if ! command -v uv > /dev/null 2>&1; then
echo "📥 Installing UV (not found in template)..."
curl -LsSf https://astral.sh/uv/install.sh | sh
# UV installer may put it in .local/bin or .cargo/bin
if [ -f ~/.cargo/env ]; then
source ~/.cargo/env
fi
# Add both possible paths
export PATH="/home/thrillwiki/.local/bin:/home/thrillwiki/.cargo/bin:$PATH"
# Verify installation worked
if command -v uv > /dev/null 2>&1; then
echo "✅ UV installed successfully at: $(which uv)"
else
echo "❌ UV installation failed or not in PATH"
echo "Current PATH: $PATH"
echo "Checking possible locations:"
ls -la ~/.local/bin/ 2>/dev/null || echo "~/.local/bin/ not found"
ls -la ~/.cargo/bin/ 2>/dev/null || echo "~/.cargo/bin/ not found"
exit 1
fi
else
echo "✅ UV already installed at: $(which uv)"
fi
# PostgreSQL should already be configured in template
echo "🗄️ Checking PostgreSQL..."
if ! sudo systemctl is-active --quiet postgresql; then
echo "▶️ Starting PostgreSQL..."
sudo systemctl start postgresql
sudo systemctl enable postgresql
else
echo "✅ PostgreSQL already running"
fi
# Configure database if not already done
echo "🔧 Setting up database..."
sudo -u postgres createdb thrillwiki 2>/dev/null || echo "📋 Database may already exist"
sudo -u postgres createuser thrillwiki_user 2>/dev/null || echo "👤 User may already exist"
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE thrillwiki TO thrillwiki_user;" 2>/dev/null || echo "🔑 Privileges may already be set"
# Install Python dependencies with UV
echo "📦 Installing Python dependencies..."
UV_CMD="$(which uv)"
if [ -n "$UV_CMD" ] && "$UV_CMD" sync; then
echo "✅ UV sync completed successfully"
else
echo "⚠️ UV sync failed, falling back to pip..."
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
fi
# Create necessary directories
echo "📁 Creating directories..."
mkdir -p logs backups static media
# Make scripts executable
echo "⚡ Making scripts executable..."
find scripts -name "*.sh" -exec chmod +x {} \; 2>/dev/null || echo " No shell scripts found"
# Run Django setup
echo "🌍 Running Django setup..."
UV_CMD="$(which uv)"
echo " 🔄 Running migrations..."
if [ -n "$UV_CMD" ] && "$UV_CMD" run python manage.py migrate; then
echo " ✅ Migrations completed"
else
echo " ⚠️ UV run failed, trying direct Python..."
python3 manage.py migrate
fi
echo " 📦 Collecting static files..."
if [ -n "$UV_CMD" ] && "$UV_CMD" run python manage.py collectstatic --noinput; then
echo " ✅ Static files collected"
else
echo " ⚠️ UV run failed, trying direct Python..."
python3 manage.py collectstatic --noinput
fi
# Install systemd services if available
if [ -f scripts/systemd/thrillwiki.service ]; then
echo "🔧 Installing systemd service..."
sudo cp scripts/systemd/thrillwiki.service /etc/systemd/system/
# Fix the home directory path for thrillwiki user
sudo sed -i 's|/home/ubuntu|/home/thrillwiki|g' /etc/systemd/system/thrillwiki.service
sudo systemctl daemon-reload
sudo systemctl enable thrillwiki.service
if sudo systemctl start thrillwiki.service; then
echo "✅ ThrillWiki service started successfully"
else
echo "⚠️ Service start failed, checking logs..."
sudo systemctl status thrillwiki.service --no-pager -l
fi
else
echo " No systemd service files found, ThrillWiki ready for manual start"
echo "💡 You can start it manually with: uv run python manage.py runserver 0.0.0.0:8000"
fi
# Test the application
echo "🧪 Testing application..."
sleep 3
if curl -f http://localhost:8000 >/dev/null 2>&1; then
echo "✅ ThrillWiki is responding on port 8000!"
else
echo "⚠️ ThrillWiki may not be responding yet (this is normal for first start)"
fi
# Setup auto-pull functionality
echo "🔄 Setting up auto-pull functionality..."
# Create ***REMOVED*** file with GitHub token for auto-pull authentication
if [ -n "${GITHUB_TOKEN:-}" ]; then
echo "GITHUB_TOKEN=$GITHUB_TOKEN" > ***REMOVED***
echo "✅ GitHub token configured for auto-pull"
else
echo "⚠️ GITHUB_TOKEN not found - auto-pull will use fallback mode"
echo "# GitHub token not available during setup" > ***REMOVED***
fi
# Ensure scripts/vm directory exists and make auto-pull script executable
if [ -f "scripts/vm/auto-pull.sh" ]; then
chmod +x scripts/vm/auto-pull.sh
# Create cron job for auto-pull (every 10 minutes)
echo "⏰ Installing cron job for auto-pull (every 10 minutes)..."
# Create cron entry
CRON_ENTRY="*/10 * * * * [AWS-SECRET-REMOVED]uto-pull.sh >> /home/thrillwiki/logs/cron.log 2>&1"
# Install cron job if not already present
if ! crontab -l 2>/dev/null | grep -q "auto-pull.sh"; then
# Add to existing crontab or create new one
(crontab -l 2>/dev/null || echo "") | {
cat
echo "# ThrillWiki Auto-Pull - Update repository every 10 minutes"
echo "$CRON_ENTRY"
} | crontab -
echo "✅ Auto-pull cron job installed successfully"
echo "📋 Cron job: $CRON_ENTRY"
else
echo "✅ Auto-pull cron job already exists"
fi
# Ensure cron service is running
if ! systemctl is-active --quiet cron 2>/dev/null; then
echo "▶️ Starting cron service..."
sudo systemctl start cron
sudo systemctl enable cron
else
echo "✅ Cron service is already running"
fi
# Test auto-pull script
echo "🧪 Testing auto-pull script..."
if timeout 30 ./scripts/vm/auto-pull.sh --status; then
echo "✅ Auto-pull script test successful"
else
echo "⚠️ Auto-pull script test failed or timed out (this may be normal)"
fi
echo "📋 Auto-pull setup completed:"
echo " - Script: [AWS-SECRET-REMOVED]uto-pull.sh"
echo " - Schedule: Every 10 minutes"
echo " - Logs: /home/thrillwiki/logs/auto-pull.log"
echo " - Status: Run './scripts/vm/auto-pull.sh --status' to check"
else
echo "⚠️ Auto-pull script not found, skipping auto-pull setup"
fi
echo "🎉 Template VM ThrillWiki setup completed successfully! ⚡"
echo "🌐 Application should be available at http://$(hostname -I | awk '{print $1}'):8000"
echo "🔄 Auto-pull: Repository will be updated every 10 minutes automatically"
EOF
# Copy setup script to VM with progress
log "📋 Copying setup script to VM..."
scp "$vm_setup_script" "$VM_NAME:/tmp/"
# Make it executable and run it
ssh "$VM_NAME" "chmod +x /tmp/template_vm_thrillwiki_setup.sh"
log "⚡ Executing setup script on VM (this may take a few minutes)..."
if ssh "$VM_NAME" "bash /tmp/template_vm_thrillwiki_setup.sh"; then
log_success "✅ Application setup completed successfully!"
else
log_error "❌ Application setup failed"
log "Try debugging with: ssh $VM_NAME 'journalctl -u thrillwiki -f'"
exit 1
fi
# Cleanup
rm -f "$vm_setup_script"
}
# Start services
start_template_services() {
log "Starting ThrillWiki services on template VM..."
# Start VM service
ssh "$VM_NAME" "sudo systemctl start thrillwiki 2>/dev/null || echo 'Service may need manual start'"
# Verify service is running
if ssh "$VM_NAME" "systemctl is-active --quiet thrillwiki 2>/dev/null"; then
log_success "ThrillWiki service started successfully on template VM ⚡"
else
log_warning "ThrillWiki service may need manual configuration"
log "Try: ssh $VM_NAME 'systemctl status thrillwiki'"
fi
# Get service status
log "Template VM service status:"
ssh "$VM_NAME" "systemctl status thrillwiki --no-pager -l 2>/dev/null || echo 'Service status not available'"
}
# Setup webhook listener
setup_template_webhook_listener() {
log "Setting up webhook listener for template deployments..."
# Create webhook start script
cat > "$PROJECT_DIR/start-template-webhook.sh" << 'EOF'
#!/bin/bash
cd "$(dirname "$0")"
source ***REMOVED***.webhook
echo "Starting webhook listener for template-based deployments ⚡"
python3 scripts/webhook-listener.py
EOF
chmod +x "$PROJECT_DIR/start-template-webhook.sh"
log_success "Template webhook listener configured"
log "You can start the webhook listener with: ./start-template-webhook.sh"
}
# Perform end-to-end test
test_template_deployment() {
log "Performing end-to-end template deployment test..."
# Test VM connectivity
if ssh "$VM_NAME" "echo 'Template VM connectivity test passed'"; then
log_success "Template VM connectivity test passed ⚡"
else
log_error "Template VM connectivity test failed"
return 1
fi
# Test ThrillWiki service
if ssh "$VM_NAME" "curl -f http://localhost:8000 >/dev/null 2>&1"; then
log_success "ThrillWiki service test passed on template VM ⚡"
else
log_warning "ThrillWiki service test failed - checking logs..."
ssh "$VM_NAME" "journalctl -u thrillwiki --no-pager -l | tail -20 2>/dev/null || echo 'Service logs not available'"
fi
# Test template deployment script
log "Testing template deployment capabilities..."
cd "$PROJECT_DIR/scripts/unraid"
./template-utils.sh check && log_success "Template utilities working ⚡"
log_success "End-to-end template deployment test completed ⚡"
}
# Generate final instructions for template deployment
generate_template_instructions() {
log "Generating final template deployment instructions..."
cat > "$PROJECT_DIR/TEMPLATE_SETUP_COMPLETE.md" << EOF
# ThrillWiki Template-Based Automation - Setup Complete! 🚀⚡
Your ThrillWiki template-based CI/CD system has been fully automated and deployed!
## Template Deployment Benefits ⚡
- **Speed**: 2-5 minute VM deployment vs 20-30 minutes with autoinstall
- **Reliability**: Pre-configured template eliminates installation failures
- **Efficiency**: Copy-on-write disk format saves space
## VM Information
- **VM Name**: $VM_NAME
- **Template VM**: $TEMPLATE_VM_NAME
- **VM IP**: $VM_IP
- **SSH Access**: \`ssh $VM_NAME\`
- **Deployment Type**: Template-based ⚡
## Services Status
- **ThrillWiki Service**: Running on template VM
- **Database**: PostgreSQL configured in template
- **Web Server**: Available at http://$VM_IP:8000
## Next Steps
### 1. Start Template Webhook Listener
\`\`\`bash
./start-template-webhook.sh
\`\`\`
### 2. Configure GitHub Webhook
- Go to your repository: $REPO_URL
- Settings → Webhooks → Add webhook
- **Payload URL**: http://YOUR_PUBLIC_IP:$WEBHOOK_PORT/webhook
- **Content type**: application/json
- **Secret**: (your webhook secret)
- **Events**: Just the push event
### 3. Test the Template System
\`\`\`bash
# Test template VM connection
ssh $VM_NAME
# Test service status
ssh $VM_NAME "systemctl status thrillwiki"
# Test template utilities
cd scripts/unraid
./template-utils.sh check
./template-utils.sh info
# Deploy another VM from template (fast!)
./template-utils.sh deploy test-vm-2
# Make a test commit to trigger automatic deployment
git add .
git commit -m "Test automated template deployment"
git push origin main
\`\`\`
## Template Management Commands
### Template VM Management
\`\`\`bash
# Check template status and info
./scripts/unraid/template-utils.sh status
./scripts/unraid/template-utils.sh info
# List all template-based VMs
./scripts/unraid/template-utils.sh list
# Deploy new VM from template (2-5 minutes!)
./scripts/unraid/template-utils.sh deploy VM_NAME
# Copy template to new VM
./scripts/unraid/template-utils.sh copy VM_NAME
\`\`\`
### Python Template Scripts
\`\`\`bash
# Template-based deployment
python3 scripts/unraid/main_template.py deploy
# Template management
python3 scripts/unraid/main_template.py template info
python3 scripts/unraid/main_template.py template check
python3 scripts/unraid/main_template.py template list
# VM operations (fast with templates!)
python3 scripts/unraid/main_template.py setup
python3 scripts/unraid/main_template.py start
python3 scripts/unraid/main_template.py ip
python3 scripts/unraid/main_template.py status
\`\`\`
### Service Management on Template VM
\`\`\`bash
# Check service status
ssh $VM_NAME "systemctl status thrillwiki"
# Restart service
ssh $VM_NAME "sudo systemctl restart thrillwiki"
# View logs
ssh $VM_NAME "journalctl -u thrillwiki -f"
\`\`\`
## Template Maintenance
### Updating Your Template VM
\`\`\`bash
# Get update instructions
./scripts/unraid/template-utils.sh update
# After updating template VM manually:
./scripts/unraid/template-utils.sh check
\`\`\`
### Creating Additional Template VMs
You can create multiple template VMs for different purposes:
- Development: \`thrillwiki-template-dev\`
- Staging: \`thrillwiki-template-staging\`
- Production: \`thrillwiki-template-prod\`
## Troubleshooting
### Template VM Issues
1. **Template not found**: Verify template VM exists and is stopped
2. **Template VM running**: Stop template before creating instances
3. **Deployment slow**: Template should be 5-10x faster than autoinstall
### Common Commands
\`\`\`bash
# Check if template is ready
./scripts/unraid/template-utils.sh check
# Test template VM connectivity
ssh root@unraid-server "virsh domstate $TEMPLATE_VM_NAME"
# Force stop template VM if needed
ssh root@unraid-server "virsh shutdown $TEMPLATE_VM_NAME"
\`\`\`
### Support Files
- Template Configuration: \`.thrillwiki-template-config\`
- Environment: \`***REMOVED***.unraid\`, \`***REMOVED***.webhook\`
- Logs: \`logs/\` directory
- Documentation: \`scripts/unraid/README-template-deployment.md\`
## Performance Comparison
| Operation | Autoinstall | Template | Improvement |
|-----------|------------|----------|-------------|
| VM Creation | 20-30 min | 2-5 min | **5-6x faster** |
| Boot Time | Full install | Instant | **Instant** |
| Reliability | ISO issues | Pre-tested | **Much higher** |
| Total Deploy | 45+ min | ~10 min | **4-5x faster** |
**Your template-based automated CI/CD system is now ready!** 🚀⚡
Every push to the main branch will automatically deploy to your template VM in minutes, not hours!
EOF
log_success "Template setup instructions saved to TEMPLATE_SETUP_COMPLETE.md"
}
# Main automation function
main() {
log "🚀⚡ Starting ThrillWiki Template-Based Complete Unraid Automation"
echo "[AWS-SECRET-REMOVED]=========================="
echo
log_template "Template deployment is 5-10x FASTER than autoinstall approach!"
echo
# Create logs directory
mkdir -p "$LOG_DIR"
# Handle reset modes
if [[ "$RESET_ALL" == "true" ]]; then
log "🔄 Complete reset mode - deleting VM and configuration"
echo
# Load configuration first to get connection details for VM deletion
if [[ -f "$CONFIG_FILE" ]]; then
source "$CONFIG_FILE"
log_success "Loaded existing configuration for VM deletion"
else
log_warning "No configuration file found, will skip VM deletion"
fi
# Delete existing VM if config exists
if [[ -f "$CONFIG_FILE" ]]; then
log "🗑️ Deleting existing template VM..."
# Check if ***REMOVED***.unraid file exists
if [ -f "$PROJECT_DIR/***REMOVED***.unraid" ]; then
log "Loading environment from ***REMOVED***.unraid..."
set -a
source "$PROJECT_DIR/***REMOVED***.unraid" 2>/dev/null || true
set +a
else
log_warning "***REMOVED***.unraid file not found - VM deletion may not work properly"
log "The VM may not exist or may have been deleted manually"
fi
# Stop existing VM if running before deletion (for complete reset)
log "🛑 Ensuring VM is stopped before deletion..."
if [ -n "${VM_NAME:-}" ] && [ -n "${UNRAID_HOST:-}" ] && [ -n "${UNRAID_USER:-}" ]; then
if ! stop_existing_vm_for_reset "$VM_NAME" "$UNRAID_HOST" "$UNRAID_USER"; then
log_warning "Failed to stop VM '$VM_NAME' - continuing anyway for complete reset"
log_warning "VM may be forcibly deleted during reset process"
fi
else
log_warning "Missing VM connection details - skipping VM shutdown check"
fi
# Debug environment loading
log "Debug: VM_NAME=${VM_NAME:-'not set'}"
log "Debug: UNRAID_HOST=${UNRAID_HOST:-'not set'}"
# Check if main_template.py exists
if [ ! -f "$SCRIPT_DIR/main_template.py" ]; then
log_error "main_template.py not found at: $SCRIPT_DIR/main_template.py"
log "Available files in $SCRIPT_DIR:"
ls -la "$SCRIPT_DIR"
log "Skipping VM deletion due to missing script..."
elif [ -z "${VM_NAME:-}" ] || [ -z "${UNRAID_HOST:-}" ]; then
log_warning "Missing required environment variables for VM deletion"
log "VM_NAME: ${VM_NAME:-'not set'}"
log "UNRAID_HOST: ${UNRAID_HOST:-'not set'}"
log "Skipping VM deletion - VM may not exist or was deleted manually"
else
log "Found main_template.py at: $SCRIPT_DIR/main_template.py"
# Run delete with timeout and better error handling
log "Attempting VM deletion with timeout..."
if timeout 60 python3 "$SCRIPT_DIR/main_template.py" delete 2>&1; then
log_success "Template VM deleted successfully"
else
deletion_exit_code=$?
if [ $deletion_exit_code -eq 124 ]; then
log_error "⚠️ VM deletion timed out after 60 seconds"
else
log "⚠️ Template VM deletion failed (exit code: $deletion_exit_code) or VM didn't exist"
fi
# Continue anyway since this might be expected
log "Continuing with script execution..."
fi
fi
fi
# Remove configuration files
if [[ -f "$CONFIG_FILE" ]]; then
rm "$CONFIG_FILE"
log_success "Template configuration file removed"
fi
# Remove GitHub token file
if [[ -f "$TOKEN_FILE" ]]; then
rm "$TOKEN_FILE"
log_success "GitHub token file removed"
fi
# Remove environment files
rm -f "$PROJECT_DIR/***REMOVED***.unraid" "$PROJECT_DIR/***REMOVED***.webhook"
log_success "Environment files removed"
log_success "Complete reset finished - continuing with fresh template setup"
echo
elif [[ "$RESET_VM_ONLY" == "true" ]]; then
log "🔄 VM-only reset mode - deleting VM, preserving configuration"
echo
# Load configuration to get connection details
if [[ -f "$CONFIG_FILE" ]]; then
source "$CONFIG_FILE"
log_success "Loaded existing configuration"
else
log_error "No configuration file found. Cannot reset VM without connection details."
echo " Run the script without reset flags first to create initial configuration."
exit 1
fi
# Stop existing VM if running before deletion
log "🛑 Ensuring VM is stopped before deletion..."
if ! stop_existing_vm_for_reset "$VM_NAME" "$UNRAID_HOST" "$UNRAID_USER"; then
log_error "Failed to stop VM '$VM_NAME'. Cannot proceed safely with VM deletion."
log_error "Please manually stop the VM or resolve the connection issue."
exit 1
fi
# Delete existing VM
log "🗑️ Deleting existing template VM..."
# Check if ***REMOVED***.unraid file exists
if [ -f "$PROJECT_DIR/***REMOVED***.unraid" ]; then
log "Loading environment from ***REMOVED***.unraid..."
set -a
source "$PROJECT_DIR/***REMOVED***.unraid" 2>/dev/null || true
set +a
else
log_warning "***REMOVED***.unraid file not found - VM deletion may not work properly"
log "The VM may not exist or may have been deleted manually"
fi
# Debug environment loading
log "Debug: VM_NAME=${VM_NAME:-'not set'}"
log "Debug: UNRAID_HOST=${UNRAID_HOST:-'not set'}"
# Check if main_template.py exists
if [ ! -f "$SCRIPT_DIR/main_template.py" ]; then
log_error "main_template.py not found at: $SCRIPT_DIR/main_template.py"
log "Available files in $SCRIPT_DIR:"
ls -la "$SCRIPT_DIR"
log "Skipping VM deletion due to missing script..."
elif [ -z "${VM_NAME:-}" ] || [ -z "${UNRAID_HOST:-}" ]; then
log_warning "Missing required environment variables for VM deletion"
log "VM_NAME: ${VM_NAME:-'not set'}"
log "UNRAID_HOST: ${UNRAID_HOST:-'not set'}"
log "Skipping VM deletion - VM may not exist or was deleted manually"
else
log "Found main_template.py at: $SCRIPT_DIR/main_template.py"
# Run delete with timeout and better error handling
log "Attempting VM deletion with timeout..."
if timeout 60 python3 "$SCRIPT_DIR/main_template.py" delete 2>&1; then
log_success "Template VM deleted successfully"
else
deletion_exit_code=$?
if [ $deletion_exit_code -eq 124 ]; then
log_error "⚠️ VM deletion timed out after 60 seconds"
else
log "⚠️ Template VM deletion failed (exit code: $deletion_exit_code) or VM didn't exist"
fi
# Continue anyway since this might be expected
log "Continuing with script execution..."
fi
fi
# Remove only environment files, keep main config
rm -f "$PROJECT_DIR/***REMOVED***.unraid" "$PROJECT_DIR/***REMOVED***.webhook"
log_success "Environment files removed, configuration preserved"
# Check if GitHub token is available for VM recreation
if [ "$GITHUB_API_ENABLED" = "true" ] && [ -n "$GITHUB_USERNAME" ]; then
log "🔍 Checking for GitHub token availability..."
# Try to load token from saved file
if load_github_token; then
log_success "✅ GitHub token loaded from secure storage"
elif GITHUB_TOKEN=$(python3 "$SCRIPT_DIR/../github-auth.py" token 2>/dev/null) && [ -n "$GITHUB_TOKEN" ]; then
log_success "✅ GitHub token obtained from authentication script"
# Validate the token can access the repository immediately
log "🔍 Validating token can access repository..."
if ! validate_github_access; then
log_error "❌ GitHub token validation failed during VM reset."
log_error "Please check your token and repository access before recreating the VM."
return 1
fi
# Save the token for future use
save_github_token
else
log_warning "⚠️ No GitHub token found - you'll need to provide it"
echo "GitHub authentication is required for repository cloning and auto-pull."
echo
if [ "$NON_INTERACTIVE" = "true" ]; then
if [ -n "${GITHUB_TOKEN:-}" ]; then
log "Using token from environment variable"
save_github_token
else
log_error "GITHUB_TOKEN environment variable not set for non-interactive mode"
log_error "Set: export GITHUB_TOKEN='your_token'"
exit 1
fi
else
read -s -p "Enter GitHub Personal Access Token: " GITHUB_TOKEN
echo
if [ -n "$GITHUB_TOKEN" ]; then
save_github_token
log_success "✅ GitHub token saved for VM recreation"
else
log_error "GitHub token is required for repository operations"
exit 1
fi
fi
fi
fi
log_success "VM reset complete - will recreate VM with saved configuration"
echo
elif [[ "$RESET_CONFIG_ONLY" == "true" ]]; then
log "🔄 Config-only reset mode - deleting configuration, preserving VM"
echo
# Remove configuration files
if [[ -f "$CONFIG_FILE" ]]; then
rm "$CONFIG_FILE"
log_success "Template configuration file removed"
fi
# Remove environment files
rm -f "$PROJECT_DIR/***REMOVED***.unraid" "$PROJECT_DIR/***REMOVED***.webhook"
log_success "Environment files removed"
log_success "Configuration reset complete - will prompt for fresh configuration"
echo
fi
# Collect configuration
prompt_template_config
# Setup steps
setup_ssh_keys
setup_unraid_access
create_environment_files
install_dependencies
create_template_vm
wait_for_template_vm
configure_template_vm
start_template_services
setup_template_webhook_listener
test_template_deployment
generate_template_instructions
echo
log_success "🎉⚡ Template-based complete automation setup finished!"
echo
log "Your ThrillWiki template VM is running at: http://$VM_IP:8000"
log "Start the webhook listener: ./start-template-webhook.sh"
log "See TEMPLATE_SETUP_COMPLETE.md for detailed instructions"
echo
log_template "🚀 Template deployment is 5-10x FASTER than traditional autoinstall!"
log "The system will now automatically deploy in MINUTES when you push to GitHub!"
}
# Run main function and log output
main "$@" 2>&1 | tee "$LOG_DIR/template-automation.log"