439 lines
13 KiB
Bash
Executable File
439 lines
13 KiB
Bash
Executable File
#!/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 <internal|external> Deployment target (default: internal)
|
||
# --port <number> Frontend HTTP port (default: 8080)
|
||
# --ssl-port <number> Frontend HTTPS port (optional)
|
||
# --backend-port <number> 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=8099
|
||
SSL_PORT=""
|
||
BACKEND_PORT=3099
|
||
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 (workspaces hoist to root node_modules)
|
||
# Check for key server dependencies in root node_modules
|
||
if [ ! -d "$PROJECT_ROOT/node_modules/express" ] || [ ! -d "$PROJECT_ROOT/node_modules/cors" ]; then
|
||
log_error "Server dependencies not installed. Run 'npm install' 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_warning "Backend tests failed or not found - continuing anyway"
|
||
}
|
||
log_success "Backend checks completed"
|
||
}
|
||
|
||
###############################################################################
|
||
# 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 "$@"
|