diff --git a/code/websites/pokedex.online/deploy.sh b/code/websites/pokedex.online/deploy.sh new file mode 100644 index 0000000..b4fcf8f --- /dev/null +++ b/code/websites/pokedex.online/deploy.sh @@ -0,0 +1,438 @@ +#!/bin/bash + +############################################################################### +# Pokedex.Online Deployment Automation Script +# +# This script automates the deployment process with pre-deployment checks, +# build verification, and rollback capability. +# +# Usage: +# ./deploy.sh [options] +# +# Options: +# --target Deployment target (default: internal) +# --port Frontend HTTP port (default: 8080) +# --ssl-port Frontend HTTPS port (optional) +# --backend-port Backend port (default: 3000) +# --skip-tests Skip test execution +# --skip-build Skip build step (use existing dist/) +# --no-backup Skip backup creation +# --dry-run Show what would be deployed without deploying +# +# Examples: +# ./deploy.sh --target internal +# ./deploy.sh --target external --port 8080 --backend-port 3000 +# ./deploy.sh --dry-run +############################################################################### + +set -e # Exit on error + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Default configuration +TARGET="internal" +PORT=8080 +SSL_PORT="" +BACKEND_PORT=3000 +SKIP_TESTS=false +SKIP_BUILD=false +NO_BACKUP=false +DRY_RUN=false + +# Script directory +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +PROJECT_ROOT="$SCRIPT_DIR" + +############################################################################### +# Helper Functions +############################################################################### + +log_info() { + echo -e "${BLUE}ℹ${NC} $1" +} + +log_success() { + echo -e "${GREEN}✅${NC} $1" +} + +log_warning() { + echo -e "${YELLOW}⚠${NC} $1" +} + +log_error() { + echo -e "${RED}❌${NC} $1" +} + +log_step() { + echo -e "\n${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo -e "${BLUE}$1${NC}" + echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n" +} + +############################################################################### +# Parse Arguments +############################################################################### + +parse_args() { + while [[ $# -gt 0 ]]; do + case $1 in + --target) + TARGET="$2" + shift 2 + ;; + --port) + PORT="$2" + shift 2 + ;; + --ssl-port) + SSL_PORT="$2" + shift 2 + ;; + --backend-port) + BACKEND_PORT="$2" + shift 2 + ;; + --skip-tests) + SKIP_TESTS=true + shift + ;; + --skip-build) + SKIP_BUILD=true + shift + ;; + --no-backup) + NO_BACKUP=true + shift + ;; + --dry-run) + DRY_RUN=true + shift + ;; + --help) + head -n 30 "$0" | tail -n 25 + exit 0 + ;; + *) + log_error "Unknown option: $1" + echo "Use --help for usage information" + exit 1 + ;; + esac + done +} + +############################################################################### +# Pre-Deployment Checks +############################################################################### + +check_prerequisites() { + log_step "🔍 Checking Prerequisites" + + # Check Node.js + if ! command -v node &> /dev/null; then + log_error "Node.js is not installed" + exit 1 + fi + log_success "Node.js $(node --version)" + + # Check npm + if ! command -v npm &> /dev/null; then + log_error "npm is not installed" + exit 1 + fi + log_success "npm $(npm --version)" + + # Check if dependencies are installed + if [ ! -d "$PROJECT_ROOT/node_modules" ]; then + log_error "Dependencies not installed. Run 'npm install' first" + exit 1 + fi + log_success "Dependencies installed" + + # Check if server dependencies are installed + if [ ! -d "$PROJECT_ROOT/server/node_modules" ]; then + log_error "Server dependencies not installed. Run 'npm install --workspace=server' first" + exit 1 + fi + log_success "Server dependencies installed" +} + +check_environment() { + log_step "🔧 Checking Environment Configuration" + + # Check if .env exists + if [ ! -f "$PROJECT_ROOT/server/.env" ]; then + log_warning ".env file not found in server/" + log_info "Copy server/.env.example to server/.env and configure it" + + if [ "$DRY_RUN" = false ]; then + read -p "Continue anyway? (y/N) " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + exit 1 + fi + fi + else + log_success "Environment configuration found" + fi +} + +run_tests() { + if [ "$SKIP_TESTS" = true ]; then + log_warning "Skipping tests (--skip-tests flag set)" + return + fi + + log_step "🧪 Running Tests" + + log_info "Running frontend tests..." + npm run test:run || { + log_error "Frontend tests failed" + exit 1 + } + log_success "Frontend tests passed" + + log_info "Running backend tests..." + npm run test:run --workspace=server || { + log_error "Backend tests failed" + exit 1 + } + log_success "Backend tests passed" +} + +############################################################################### +# Build +############################################################################### + +build_application() { + if [ "$SKIP_BUILD" = true ]; then + log_warning "Skipping build (--skip-build flag set)" + + # Check if dist exists + if [ ! -d "$PROJECT_ROOT/dist" ]; then + log_error "dist/ directory not found and --skip-build is set" + log_info "Remove --skip-build or run 'npm run build' first" + exit 1 + fi + return + fi + + log_step "🔨 Building Application" + + log_info "Building frontend..." + npm run build:frontend || { + log_error "Frontend build failed" + exit 1 + } + log_success "Frontend built successfully" + + log_info "Verifying build..." + npm run build:verify || { + log_error "Build verification failed" + exit 1 + } + log_success "Build verified" +} + +############################################################################### +# Backup +############################################################################### + +create_backup() { + if [ "$NO_BACKUP" = true ]; then + log_warning "Skipping backup (--no-backup flag set)" + return + fi + + log_step "💾 Creating Backup" + + BACKUP_DIR="$PROJECT_ROOT/backups" + TIMESTAMP=$(date +%Y%m%d_%H%M%S) + BACKUP_FILE="$BACKUP_DIR/backup_${TIMESTAMP}.tar.gz" + + mkdir -p "$BACKUP_DIR" + + log_info "Creating backup of current deployment..." + tar -czf "$BACKUP_FILE" \ + --exclude='node_modules' \ + --exclude='dist' \ + --exclude='.git' \ + --exclude='backups' \ + -C "$PROJECT_ROOT" . || { + log_error "Backup creation failed" + exit 1 + } + + log_success "Backup created: $BACKUP_FILE" + + # Keep only last 5 backups + BACKUP_COUNT=$(ls -1 "$BACKUP_DIR"/backup_*.tar.gz 2>/dev/null | wc -l) + if [ "$BACKUP_COUNT" -gt 5 ]; then + log_info "Cleaning old backups (keeping last 5)..." + ls -1t "$BACKUP_DIR"/backup_*.tar.gz | tail -n +6 | xargs rm -f + fi +} + +############################################################################### +# Deployment +############################################################################### + +deploy_to_server() { + log_step "🚀 Deploying to Server" + + if [ "$DRY_RUN" = true ]; then + log_info "DRY RUN - Would deploy with following configuration:" + log_info " Target: $TARGET" + log_info " Frontend Port: $PORT" + [ -n "$SSL_PORT" ] && log_info " SSL Port: $SSL_PORT" + log_info " Backend Port: $BACKEND_PORT" + log_success "Dry run completed" + return + fi + + # Build deployment command + DEPLOY_CMD="node ../../../utils/deploy-pokedex.js --target $TARGET --port $PORT --backend-port $BACKEND_PORT" + [ -n "$SSL_PORT" ] && DEPLOY_CMD="$DEPLOY_CMD --ssl-port $SSL_PORT" + + log_info "Executing deployment..." + log_info "Command: $DEPLOY_CMD" + + eval "$DEPLOY_CMD" || { + log_error "Deployment failed" + log_info "Check logs above for details" + exit 1 + } + + log_success "Deployment completed successfully" +} + +############################################################################### +# Post-Deployment +############################################################################### + +verify_deployment() { + log_step "🏥 Verifying Deployment" + + if [ "$DRY_RUN" = true ]; then + log_info "DRY RUN - Skipping verification" + return + fi + + # Determine host based on target + if [ "$TARGET" = "internal" ]; then + HOST="10.0.0.81" + else + HOST="home.gregrjacobs.com" + fi + + log_info "Checking frontend health..." + sleep 3 # Give containers time to start + + if curl -f -s "http://$HOST:$PORT/health" > /dev/null; then + log_success "Frontend is responding" + else + log_warning "Frontend health check failed" + fi + + log_info "Checking backend health..." + if curl -f -s "http://$HOST:$BACKEND_PORT/health" > /dev/null; then + log_success "Backend is responding" + else + log_warning "Backend health check failed" + fi +} + +print_summary() { + log_step "📊 Deployment Summary" + + if [ "$DRY_RUN" = true ]; then + log_info "DRY RUN completed - no changes made" + return + fi + + # Determine host based on target + if [ "$TARGET" = "internal" ]; then + HOST="10.0.0.81" + else + HOST="home.gregrjacobs.com" + fi + + echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo -e "${GREEN} 🎉 Deployment Successful!${NC}" + echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo "" + echo -e " ${BLUE}Frontend:${NC} http://$HOST:$PORT" + [ -n "$SSL_PORT" ] && echo -e " ${BLUE}HTTPS:${NC} https://$HOST:$SSL_PORT" + echo -e " ${BLUE}Backend:${NC} http://$HOST:$BACKEND_PORT" + echo -e " ${BLUE}Target:${NC} $TARGET" + echo "" + echo -e " ${YELLOW}Next Steps:${NC}" + echo -e " • Test the application manually" + echo -e " • Check logs: npm run docker:logs" + echo -e " • Monitor backend: curl http://$HOST:$BACKEND_PORT/health" + echo "" + echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +} + +############################################################################### +# Rollback +############################################################################### + +rollback() { + log_step "🔄 Rollback Instructions" + + echo "To rollback to a previous version:" + echo "" + echo "1. List available backups:" + echo " ls -lh backups/" + echo "" + echo "2. Extract backup:" + echo " tar -xzf backups/backup_TIMESTAMP.tar.gz -C /tmp/restore" + echo "" + echo "3. Copy files back:" + echo " rsync -av /tmp/restore/ ./" + echo "" + echo "4. Redeploy:" + echo " ./deploy.sh --skip-tests --target $TARGET" + echo "" + echo "Or use the deployment script's built-in rollback:" + echo " node ../../../utils/deploy-pokedex.js --target $TARGET" + echo " (will auto-rollback on failure)" +} + +############################################################################### +# Main Execution +############################################################################### + +main() { + echo -e "${BLUE}" + echo "╔════════════════════════════════════════════════════════════╗" + echo "║ ║" + echo "║ Pokedex.Online Deployment Automation ║" + echo "║ ║" + echo "╚════════════════════════════════════════════════════════════╝" + echo -e "${NC}\n" + + # Parse command line arguments + parse_args "$@" + + # Run deployment pipeline + check_prerequisites + check_environment + run_tests + build_application + create_backup + deploy_to_server + verify_deployment + print_summary + + log_success "All done! 🚀" +} + +# Run main function +main "$@"