mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 15:11:09 -05:00
- 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
465 lines
14 KiB
Bash
465 lines
14 KiB
Bash
#!/bin/bash
|
|
#
|
|
# ThrillWiki Auto-Pull Script
|
|
# Automatically pulls latest changes from Git repository every 10 minutes
|
|
# Designed to run as a cron job on the VM
|
|
#
|
|
|
|
set -e
|
|
|
|
# Configuration
|
|
PROJECT_DIR="/home/thrillwiki/thrillwiki"
|
|
LOG_FILE="/home/thrillwiki/logs/auto-pull.log"
|
|
LOCK_FILE="/tmp/thrillwiki-auto-pull.lock"
|
|
SERVICE_NAME="thrillwiki"
|
|
MAX_LOG_SIZE=10485760 # 10MB
|
|
|
|
# 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 function
|
|
log() {
|
|
echo -e "$(date '+%Y-%m-%d %H:%M:%S') [AUTO-PULL] $1" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "$(date '+%Y-%m-%d %H:%M:%S') ${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
log_success() {
|
|
echo -e "$(date '+%Y-%m-%d %H:%M:%S') ${GREEN}[SUCCESS]${NC} $1" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
log_warning() {
|
|
echo -e "$(date '+%Y-%m-%d %H:%M:%S') ${YELLOW}[WARNING]${NC} $1" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
# Function to rotate log file if it gets too large
|
|
rotate_log() {
|
|
if [ -f "$LOG_FILE" ] && [ $(stat -f%z "$LOG_FILE" 2>/dev/null || stat -c%s "$LOG_FILE" 2>/dev/null || echo 0) -gt $MAX_LOG_SIZE ]; then
|
|
mv "$LOG_FILE" "${LOG_FILE}.old"
|
|
log "Log file rotated due to size limit"
|
|
fi
|
|
}
|
|
|
|
# Function to acquire lock
|
|
acquire_lock() {
|
|
if [ -f "$LOCK_FILE" ]; then
|
|
local lock_pid=$(cat "$LOCK_FILE" 2>/dev/null || echo "")
|
|
if [ -n "$lock_pid" ] && kill -0 "$lock_pid" 2>/dev/null; then
|
|
log_warning "Auto-pull already running (PID: $lock_pid), skipping this run"
|
|
exit 0
|
|
else
|
|
log "Removing stale lock file"
|
|
rm -f "$LOCK_FILE"
|
|
fi
|
|
fi
|
|
|
|
echo $$ > "$LOCK_FILE"
|
|
trap 'rm -f "$LOCK_FILE"' EXIT
|
|
}
|
|
|
|
# Function to setup GitHub authentication
|
|
setup_git_auth() {
|
|
log "🔐 Setting up GitHub authentication..."
|
|
|
|
# Check if GITHUB_TOKEN is available
|
|
if [ -z "${GITHUB_TOKEN:-}" ]; then
|
|
# Try loading from ***REMOVED*** file in project directory
|
|
if [ -f "$PROJECT_DIR/***REMOVED***" ]; then
|
|
source "$PROJECT_DIR/***REMOVED***"
|
|
fi
|
|
|
|
# Try loading from global ***REMOVED***.unraid
|
|
if [ -z "${GITHUB_TOKEN:-}" ] && [ -f "$PROJECT_DIR/../../***REMOVED***.unraid" ]; then
|
|
source "$PROJECT_DIR/../../***REMOVED***.unraid"
|
|
fi
|
|
|
|
# Try loading from parent directory ***REMOVED***.unraid
|
|
if [ -z "${GITHUB_TOKEN:-}" ] && [ -f "$PROJECT_DIR/../***REMOVED***.unraid" ]; then
|
|
source "$PROJECT_DIR/../***REMOVED***.unraid"
|
|
fi
|
|
fi
|
|
|
|
# Verify we have the token
|
|
if [ -z "${GITHUB_TOKEN:-}" ]; then
|
|
log_warning "⚠️ GITHUB_TOKEN not found, trying public access..."
|
|
return 1
|
|
fi
|
|
|
|
# Configure git to use token authentication
|
|
local repo_url="https://github.com/pacnpal/thrillwiki_django_no_react.git"
|
|
local auth_url="https://pacnpal:${GITHUB_TOKEN}@github.com/pacnpal/thrillwiki_django_no_react.git"
|
|
|
|
# Update remote URL to use token
|
|
if git remote get-url origin | grep -q "github.com/pacnpal/thrillwiki_django_no_react"; then
|
|
git remote set-url origin "$auth_url"
|
|
log_success "✅ GitHub authentication configured with token"
|
|
return 0
|
|
else
|
|
log_warning "⚠️ Repository origin URL doesn't match expected GitHub repo"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Function to check if Git repository has changes
|
|
has_remote_changes() {
|
|
# Setup authentication first
|
|
if ! setup_git_auth; then
|
|
log_warning "⚠️ GitHub authentication failed, skipping remote check"
|
|
return 1 # Assume no changes if we can't authenticate
|
|
fi
|
|
|
|
# Fetch latest changes without merging
|
|
log "📡 Fetching latest changes from remote..."
|
|
if ! git fetch origin main --quiet 2>/dev/null; then
|
|
log_error "❌ Failed to fetch from remote repository - authentication or network issue"
|
|
log_warning "⚠️ Auto-pull will skip this cycle due to fetch failure"
|
|
return 1
|
|
fi
|
|
|
|
# Compare local and remote
|
|
local local_commit=$(git rev-parse HEAD)
|
|
local remote_commit=$(git rev-parse origin/main)
|
|
|
|
log "📊 Local commit: ${local_commit:0:8}"
|
|
log "📊 Remote commit: ${remote_commit:0:8}"
|
|
|
|
if [ "$local_commit" != "$remote_commit" ]; then
|
|
log "📥 New changes detected!"
|
|
return 0 # Has changes
|
|
else
|
|
log "✅ Repository is up to date"
|
|
return 1 # No changes
|
|
fi
|
|
}
|
|
|
|
# Function to check service status
|
|
is_service_running() {
|
|
systemctl is-active --quiet "$SERVICE_NAME" 2>/dev/null
|
|
}
|
|
|
|
# Function to restart service safely
|
|
restart_service() {
|
|
log "Restarting ThrillWiki service..."
|
|
|
|
if systemctl is-enabled --quiet "$SERVICE_NAME" 2>/dev/null; then
|
|
if sudo systemctl restart "$SERVICE_NAME"; then
|
|
log_success "Service restarted successfully"
|
|
return 0
|
|
else
|
|
log_error "Failed to restart service"
|
|
return 1
|
|
fi
|
|
else
|
|
log_warning "Service not enabled, attempting manual restart..."
|
|
# Try to start it anyway
|
|
if sudo systemctl start "$SERVICE_NAME" 2>/dev/null; then
|
|
log_success "Service started successfully"
|
|
return 0
|
|
else
|
|
log_warning "Service restart failed, may need manual intervention"
|
|
return 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Function to update Python dependencies
|
|
update_dependencies() {
|
|
log "Checking for dependency updates..."
|
|
|
|
# Check if UV is available
|
|
export PATH="/home/thrillwiki/.cargo/bin:$PATH"
|
|
if command -v uv > /dev/null 2>&1; then
|
|
log "Updating dependencies with UV..."
|
|
if uv sync --quiet; then
|
|
log_success "Dependencies updated with UV"
|
|
return 0
|
|
else
|
|
log_warning "UV sync failed, trying pip..."
|
|
fi
|
|
fi
|
|
|
|
# Fallback to pip if UV fails or isn't available
|
|
if [ -d ".venv" ]; then
|
|
log "Activating virtual environment and updating with pip..."
|
|
source .venv/bin/activate
|
|
if pip install -e . --quiet; then
|
|
log_success "Dependencies updated with pip"
|
|
return 0
|
|
else
|
|
log_warning "Pip install failed"
|
|
return 1
|
|
fi
|
|
else
|
|
log_warning "No virtual environment found, skipping dependency update"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Function to run Django migrations
|
|
run_migrations() {
|
|
log "Running Django migrations..."
|
|
|
|
export PATH="/home/thrillwiki/.cargo/bin:$PATH"
|
|
|
|
# Try with UV first
|
|
if command -v uv > /dev/null 2>&1; then
|
|
if uv run python manage.py migrate --quiet; then
|
|
log_success "Migrations completed with UV"
|
|
return 0
|
|
else
|
|
log_warning "UV migrations failed, trying direct Python..."
|
|
fi
|
|
fi
|
|
|
|
# Fallback to direct Python
|
|
if [ -d ".venv" ]; then
|
|
source .venv/bin/activate
|
|
if python manage.py migrate --quiet; then
|
|
log_success "Migrations completed with Python"
|
|
return 0
|
|
else
|
|
log_warning "Django migrations failed"
|
|
return 1
|
|
fi
|
|
else
|
|
if python3 manage.py migrate --quiet; then
|
|
log_success "Migrations completed"
|
|
return 0
|
|
else
|
|
log_warning "Django migrations failed"
|
|
return 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Function to collect static files
|
|
collect_static() {
|
|
log "Collecting static files..."
|
|
|
|
export PATH="/home/thrillwiki/.cargo/bin:$PATH"
|
|
|
|
# Try with UV first
|
|
if command -v uv > /dev/null 2>&1; then
|
|
if uv run python manage.py collectstatic --noinput --quiet; then
|
|
log_success "Static files collected with UV"
|
|
return 0
|
|
else
|
|
log_warning "UV collectstatic failed, trying direct Python..."
|
|
fi
|
|
fi
|
|
|
|
# Fallback to direct Python
|
|
if [ -d ".venv" ]; then
|
|
source .venv/bin/activate
|
|
if python manage.py collectstatic --noinput --quiet; then
|
|
log_success "Static files collected with Python"
|
|
return 0
|
|
else
|
|
log_warning "Static file collection failed"
|
|
return 1
|
|
fi
|
|
else
|
|
if python3 manage.py collectstatic --noinput --quiet; then
|
|
log_success "Static files collected"
|
|
return 0
|
|
else
|
|
log_warning "Static file collection failed"
|
|
return 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Main auto-pull function
|
|
main() {
|
|
# Setup
|
|
rotate_log
|
|
acquire_lock
|
|
|
|
log "🔄 Starting auto-pull check..."
|
|
|
|
# Ensure logs directory exists
|
|
mkdir -p "$(dirname "$LOG_FILE")"
|
|
|
|
# Change to project directory
|
|
if ! cd "$PROJECT_DIR"; then
|
|
log_error "Failed to change to project directory: $PROJECT_DIR"
|
|
exit 1
|
|
fi
|
|
|
|
# Check if this is a Git repository
|
|
if [ ! -d ".git" ]; then
|
|
log_error "Not a Git repository: $PROJECT_DIR"
|
|
exit 1
|
|
fi
|
|
|
|
# Check for remote changes
|
|
log "📡 Checking for remote changes..."
|
|
if ! has_remote_changes; then
|
|
log "✅ Repository is up to date, no changes to pull"
|
|
exit 0
|
|
fi
|
|
|
|
log "📥 New changes detected, pulling updates..."
|
|
|
|
# Record current service status
|
|
local service_was_running=false
|
|
if is_service_running; then
|
|
service_was_running=true
|
|
log "📊 Service is currently running"
|
|
else
|
|
log "📊 Service is not running"
|
|
fi
|
|
|
|
# Pull the latest changes
|
|
local pull_output
|
|
if pull_output=$(git pull origin main 2>&1); then
|
|
log_success "✅ Git pull completed successfully"
|
|
log "📋 Changes:"
|
|
echo "$pull_output" | grep -E "^\s*(create|modify|delete|rename)" | head -10 | while read line; do
|
|
log " $line"
|
|
done
|
|
else
|
|
log_error "❌ Git pull failed:"
|
|
echo "$pull_output" | head -10 | while read line; do
|
|
log_error " $line"
|
|
done
|
|
exit 1
|
|
fi
|
|
|
|
# Update dependencies if requirements files changed
|
|
if echo "$pull_output" | grep -qE "(pyproject\.toml|requirements.*\.txt|setup\.py)"; then
|
|
log "📦 Dependencies file changed, updating..."
|
|
update_dependencies
|
|
else
|
|
log "📦 No dependency changes detected, skipping update"
|
|
fi
|
|
|
|
# Run migrations if models changed
|
|
if echo "$pull_output" | grep -qE "(models\.py|migrations/)"; then
|
|
log "🗄️ Model changes detected, running migrations..."
|
|
run_migrations
|
|
else
|
|
log "🗄️ No model changes detected, skipping migrations"
|
|
fi
|
|
|
|
# Collect static files if they changed
|
|
if echo "$pull_output" | grep -qE "(static/|templates/|\.css|\.js)"; then
|
|
log "🎨 Static files changed, collecting..."
|
|
collect_static
|
|
else
|
|
log "🎨 No static file changes detected, skipping collection"
|
|
fi
|
|
|
|
# Restart service if it was running
|
|
if $service_was_running; then
|
|
log "🔄 Restarting service due to code changes..."
|
|
if restart_service; then
|
|
# Wait a moment for service to fully start
|
|
sleep 3
|
|
|
|
# Verify service is running
|
|
if is_service_running; then
|
|
log_success "🎉 Auto-pull completed successfully! Service is running."
|
|
else
|
|
log_error "⚠️ Service failed to start after restart"
|
|
exit 1
|
|
fi
|
|
else
|
|
log_error "⚠️ Service restart failed"
|
|
exit 1
|
|
fi
|
|
else
|
|
log_success "🎉 Auto-pull completed successfully! (Service was not running)"
|
|
fi
|
|
|
|
# Health check
|
|
log "🔍 Performing health check..."
|
|
if curl -f http://localhost:8000 > /dev/null 2>&1; then
|
|
log_success "✅ Application health check passed"
|
|
else
|
|
log_warning "⚠️ Application health check failed (may still be starting up)"
|
|
fi
|
|
|
|
log "✨ Auto-pull cycle completed at $(date)"
|
|
}
|
|
|
|
# Handle script arguments
|
|
case "${1:-}" in
|
|
--help|-h)
|
|
echo "ThrillWiki Auto-Pull Script"
|
|
echo ""
|
|
echo "Usage:"
|
|
echo " $0 Run auto-pull check (default)"
|
|
echo " $0 --force Force pull even if no changes detected"
|
|
echo " $0 --status Check auto-pull service status"
|
|
echo " $0 --logs Show recent auto-pull logs"
|
|
echo " $0 --help Show this help"
|
|
exit 0
|
|
;;
|
|
--force)
|
|
log "🚨 Force mode: Pulling regardless of changes"
|
|
# Skip the has_remote_changes check
|
|
cd "$PROJECT_DIR"
|
|
|
|
# Setup authentication and pull
|
|
setup_git_auth
|
|
if git pull origin main; then
|
|
log_success "✅ Force pull completed"
|
|
|
|
# Run standard update procedures
|
|
update_dependencies
|
|
run_migrations
|
|
collect_static
|
|
|
|
# Restart service if it was running
|
|
if is_service_running; then
|
|
restart_service
|
|
fi
|
|
|
|
log_success "🎉 Force update completed successfully!"
|
|
else
|
|
log_error "❌ Force pull failed"
|
|
exit 1
|
|
fi
|
|
;;
|
|
--status)
|
|
if systemctl is-active --quiet crond 2>/dev/null; then
|
|
echo "✅ Cron daemon is running"
|
|
else
|
|
echo "❌ Cron daemon is not running"
|
|
fi
|
|
|
|
if crontab -l 2>/dev/null | grep -q "auto-pull.sh"; then
|
|
echo "✅ Auto-pull cron job is installed"
|
|
echo "📋 Current cron jobs:"
|
|
crontab -l 2>/dev/null | grep -E "(auto-pull|thrillwiki)"
|
|
else
|
|
echo "❌ Auto-pull cron job is not installed"
|
|
fi
|
|
|
|
if [ -f "$LOG_FILE" ]; then
|
|
echo "📄 Last auto-pull log entries:"
|
|
tail -5 "$LOG_FILE"
|
|
else
|
|
echo "📄 No auto-pull logs found"
|
|
fi
|
|
;;
|
|
--logs)
|
|
if [ -f "$LOG_FILE" ]; then
|
|
tail -50 "$LOG_FILE"
|
|
else
|
|
echo "No auto-pull logs found at $LOG_FILE"
|
|
fi
|
|
;;
|
|
*)
|
|
# Default: run main auto-pull
|
|
main
|
|
;;
|
|
esac
|