mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 18:31:09 -05:00
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
This commit is contained in:
464
shared/scripts/vm/auto-pull.sh
Normal file
464
shared/scripts/vm/auto-pull.sh
Normal file
@@ -0,0 +1,464 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user