#!/bin/bash # ThrillWiki Deployment Script # Deploys the application to various environments set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Script directory and project root SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../../" && pwd)" # Configuration DEPLOY_ENV="production" DEPLOY_DIR="$PROJECT_ROOT/deploy" BACKUP_DIR="$PROJECT_ROOT/backups" TIMESTAMP=$(date +"%Y%m%d_%H%M%S") # Function to print colored output print_status() { echo -e "${BLUE}[INFO]${NC} $1" } print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" } print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" } print_error() { echo -e "${RED}[ERROR]${NC} $1" } # Function to check if a command exists command_exists() { command -v "$1" >/dev/null 2>&1 } # Function to check deployment requirements check_deployment_requirements() { print_status "Checking deployment requirements..." local missing_deps=() # Check if deployment artifacts exist if [ ! -d "$DEPLOY_DIR" ]; then missing_deps+=("deployment_artifacts") fi if [ ! -f "$DEPLOY_DIR/manifest.json" ]; then missing_deps+=("deployment_manifest") fi # Check for deployment tools if [ "$DEPLOY_METHOD" = "docker" ]; then if ! command_exists docker; then missing_deps+=("docker") fi fi if [ "$DEPLOY_METHOD" = "rsync" ]; then if ! command_exists rsync; then missing_deps+=("rsync") fi fi if [ ${#missing_deps[@]} -ne 0 ]; then print_error "Missing deployment requirements: ${missing_deps[*]}" exit 1 fi print_success "Deployment requirements met!" } # Function to create backup create_backup() { print_status "Creating backup before deployment..." mkdir -p "$BACKUP_DIR" local backup_path="$BACKUP_DIR/backup_$TIMESTAMP" # Create backup directory mkdir -p "$backup_path" # Backup current deployment if it exists if [ -d "$DEPLOY_TARGET" ]; then print_status "Backing up current deployment..." cp -r "$DEPLOY_TARGET" "$backup_path/current" fi # Backup database if requested if [ "$BACKUP_DATABASE" = true ]; then print_status "Backing up database..." # This would depend on your database setup # For SQLite: if [ -f "$PROJECT_ROOT/backend/db.sqlite3" ]; then cp "$PROJECT_ROOT/backend/db.sqlite3" "$backup_path/database.sqlite3" fi fi # Backup environment files if [ -f "$PROJECT_ROOT/.env" ]; then cp "$PROJECT_ROOT/.env" "$backup_path/.env.backup" fi print_success "Backup created: $backup_path" } # Function to prepare deployment artifacts prepare_artifacts() { print_status "Preparing deployment artifacts..." # Check if build artifacts exist if [ ! -d "$DEPLOY_DIR" ]; then print_error "No deployment artifacts found. Please run build-all.sh first." exit 1 fi # Validate manifest if [ -f "$DEPLOY_DIR/manifest.json" ]; then print_status "Validating deployment manifest..." # You could add more validation here cat "$DEPLOY_DIR/manifest.json" | grep -q "build_timestamp" || { print_error "Invalid deployment manifest" exit 1 } fi print_success "Deployment artifacts ready!" } # Function to deploy to local development deploy_local() { print_status "Deploying to local development environment..." local target_dir="$PROJECT_ROOT/deployment" # Create target directory mkdir -p "$target_dir" # Copy artifacts print_status "Copying frontend artifacts..." cp -r "$DEPLOY_DIR/frontend" "$target_dir/" print_status "Copying backend artifacts..." mkdir -p "$target_dir/backend" cp -r "$DEPLOY_DIR/backend/staticfiles" "$target_dir/backend/" # Copy deployment configuration cp "$DEPLOY_DIR/manifest.json" "$target_dir/" print_success "Local deployment completed!" print_status "Deployment available at: $target_dir" } # Function to deploy via rsync deploy_rsync() { print_status "Deploying via rsync..." if [ -z "$DEPLOY_HOST" ]; then print_error "DEPLOY_HOST not set for rsync deployment" exit 1 fi local target="" if [ -n "$DEPLOY_USER" ]; then target="$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH" else target="$DEPLOY_HOST:$DEPLOY_PATH" fi print_status "Syncing files to $target..." # Rsync options: # -a: archive mode (recursive, preserves attributes) # -v: verbose # -z: compress during transfer # --delete: delete files not in source # --exclude: exclude certain files rsync -avz --delete \ --exclude='.git' \ --exclude='node_modules' \ --exclude='__pycache__' \ --exclude='*.log' \ "$DEPLOY_DIR/" "$target" print_success "Rsync deployment completed!" } # Function to deploy via Docker deploy_docker() { print_status "Deploying via Docker..." local image_name="thrillwiki-$DEPLOY_ENV" local container_name="thrillwiki-$DEPLOY_ENV" # Build Docker image print_status "Building Docker image: $image_name" docker build -t "$image_name" \ --build-arg DEPLOY_ENV="$DEPLOY_ENV" \ -f "$PROJECT_ROOT/Dockerfile" \ "$PROJECT_ROOT" # Stop existing container if docker ps -q -f name="$container_name" | grep -q .; then print_status "Stopping existing container..." docker stop "$container_name" fi # Remove existing container if docker ps -a -q -f name="$container_name" | grep -q .; then print_status "Removing existing container..." docker rm "$container_name" fi # Run new container print_status "Starting new container..." docker run -d \ --name "$container_name" \ -p 8080:80 \ -e DEPLOY_ENV="$DEPLOY_ENV" \ "$image_name" print_success "Docker deployment completed!" print_status "Container: $container_name" print_status "URL: http://localhost:8080" } # Function to run post-deployment checks run_post_deploy_checks() { print_status "Running post-deployment checks..." local health_url="" case $DEPLOY_METHOD in "local") health_url="http://localhost:8080/health" ;; "docker") health_url="http://localhost:8080/health" ;; "rsync") if [ -n "$DEPLOY_HOST" ]; then health_url="http://$DEPLOY_HOST/health" fi ;; esac if [ -n "$health_url" ]; then print_status "Checking health endpoint: $health_url" if curl -s -f "$health_url" > /dev/null 2>&1; then print_success "Health check passed!" else print_warning "Health check failed. Please verify deployment." fi fi print_success "Post-deployment checks completed!" } # Function to generate deployment report generate_deployment_report() { print_status "Generating deployment report..." local report_file="$PROJECT_ROOT/deployment-report-$DEPLOY_ENV-$TIMESTAMP.txt" cat > "$report_file" << EOF ThrillWiki Deployment Report ============================ Deployment Information: - Deployment Date: $(date) - Environment: $DEPLOY_ENV - Method: $DEPLOY_METHOD - Project Root: $PROJECT_ROOT Deployment Details: - Source Directory: $DEPLOY_DIR - Target: $DEPLOY_TARGET - Backup Created: $([ "$CREATE_BACKUP" = true ] && echo "Yes" || echo "No") Build Information: $(if [ -f "$DEPLOY_DIR/manifest.json" ]; then cat "$DEPLOY_DIR/manifest.json" else echo "No manifest found" fi) System Information: - Hostname: $(hostname) - User: $(whoami) - OS: $(uname -s) $(uname -r) Deployment Status: SUCCESS Post-Deployment: - Health Check: $([ "$RUN_CHECKS" = true ] && echo "Run" || echo "Skipped") - Backup Location: $([ "$CREATE_BACKUP" = true ] && echo "$BACKUP_DIR/backup_$TIMESTAMP" || echo "None") EOF print_success "Deployment report generated: $report_file" } # Function to show usage show_usage() { cat << EOF Usage: $0 [ENVIRONMENT] [OPTIONS] Deploy ThrillWiki to the specified environment. Environments: dev Development environment staging Staging environment production Production environment Options: -h, --help Show this help message -m, --method METHOD Deployment method (local, rsync, docker) --no-backup Skip backup creation --no-checks Skip post-deployment checks --no-report Skip deployment report generation Examples: $0 production # Deploy to production using default method $0 staging --method docker # Deploy to staging using Docker $0 dev --no-backup # Deploy to dev without backup Environment Variables: DEPLOY_METHOD Deployment method (default: local) DEPLOY_HOST Target host for rsync deployment DEPLOY_USER SSH user for rsync deployment DEPLOY_PATH Target path for rsync deployment CREATE_BACKUP Create backup before deployment (default: true) BACKUP_DATABASE Backup database (default: false) EOF } # Parse command line arguments DEPLOY_METHOD="local" CREATE_BACKUP=true RUN_CHECKS=true SKIP_REPORT=false # Get environment from first argument if [ $# -gt 0 ]; then case $1 in dev|staging|production) DEPLOY_ENV="$1" shift ;; -h|--help) show_usage exit 0 ;; *) print_error "Invalid environment: $1" show_usage exit 1 ;; esac fi # Parse remaining arguments while [[ $# -gt 0 ]]; do case $1 in -h|--help) show_usage exit 0 ;; -m|--method) DEPLOY_METHOD="$2" shift 2 ;; --no-backup) CREATE_BACKUP=false shift ;; --no-checks) RUN_CHECKS=false shift ;; --no-report) SKIP_REPORT=true shift ;; *) print_error "Unknown option: $1" show_usage exit 1 ;; esac done # Override from environment variables if [ ! -z "$DEPLOY_METHOD_ENV" ]; then DEPLOY_METHOD=$DEPLOY_METHOD_ENV fi if [ "$CREATE_BACKUP_ENV" = "false" ]; then CREATE_BACKUP=false fi # Set deployment target based on method case $DEPLOY_METHOD in "local") DEPLOY_TARGET="$PROJECT_ROOT/deployment" ;; "rsync") DEPLOY_TARGET="${DEPLOY_USER:-}$(if [ -n "$DEPLOY_USER" ]; then echo "@"; fi)${DEPLOY_HOST:-localhost}:${DEPLOY_PATH:-/var/www/thrillwiki}" ;; "docker") DEPLOY_TARGET="docker_container" ;; *) print_error "Unsupported deployment method: $DEPLOY_METHOD" exit 1 ;; esac # Print banner echo -e "${GREEN}" echo "==========================================" echo " ThrillWiki Deployment" echo "==========================================" echo -e "${NC}" print_status "Environment: $DEPLOY_ENV" print_status "Method: $DEPLOY_METHOD" print_status "Target: $DEPLOY_TARGET" print_status "Create backup: $CREATE_BACKUP" # Check deployment requirements check_deployment_requirements # Prepare deployment artifacts prepare_artifacts # Create backup if requested if [ "$CREATE_BACKUP" = true ]; then create_backup else print_warning "Skipping backup creation as requested" fi # Deploy based on method case $DEPLOY_METHOD in "local") deploy_local ;; "rsync") deploy_rsync ;; "docker") deploy_docker ;; *) print_error "Unsupported deployment method: $DEPLOY_METHOD" exit 1 ;; esac # Run post-deployment checks if [ "$RUN_CHECKS" = true ]; then run_post_deploy_checks else print_warning "Skipping post-deployment checks as requested" fi # Generate deployment report if [ "$SKIP_REPORT" = false ]; then generate_deployment_report else print_warning "Skipping deployment report generation as requested" fi print_success "Deployment completed successfully!" echo "" print_status "Environment: $DEPLOY_ENV" print_status "Method: $DEPLOY_METHOD" print_status "Target: $DEPLOY_TARGET" echo "" print_status "Deployment report: $PROJECT_ROOT/deployment-report-$DEPLOY_ENV-$TIMESTAMP.txt"