Files
thrillwiki_django_no_react/scripts/vm/auto-pull.sh
pacnpal c26414ff74 Add comprehensive tests for Parks API and models
- Implemented extensive test cases for the Parks API, covering endpoints for listing, retrieving, creating, updating, and deleting parks.
- Added tests for filtering, searching, and ordering parks in the API.
- Created tests for error handling in the API, including malformed JSON and unsupported methods.
- Developed model tests for Park, ParkArea, Company, and ParkReview models, ensuring validation and constraints are enforced.
- Introduced utility mixins for API and model testing to streamline assertions and enhance test readability.
- Included integration tests to validate complete workflows involving park creation, retrieval, updating, and deletion.
2025-08-17 19:36:20 -04:00

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