#!/bin/bash set -euo pipefail # Phase 6, Plan 1, Task 4: Automated Deployment Script # Usage: ./deploy.sh [production|staging|development] [--rebuild] # Purpose: Single-command deployment with Docker Compose, pre-flight checks, and health validation DEPLOYMENT_ENV="${1:-production}" REBUILD_FLAG="${2:---no-rebuild}" # Color output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Logging functions log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; } log_info "=== TFM aInventory Deployment Script ===" log_info "Environment: $DEPLOYMENT_ENV" log_info "Rebuild: $REBUILD_FLAG" # ============================================================================ # STEP 1: Pre-flight checks # ============================================================================ log_info "Step 1/10: Running pre-flight checks..." command -v docker &> /dev/null || log_error "Docker not installed. Please install Docker 24.0+" log_success " ✓ Docker is installed" command -v docker-compose &> /dev/null || log_error "Docker Compose not installed. Please install Docker Compose 2.0+" log_success " ✓ Docker Compose is installed" [[ -f "docker-compose.yml" ]] || log_error "docker-compose.yml not found in current directory" log_success " ✓ docker-compose.yml found" [[ -f "inventory.env" ]] || log_warn "inventory.env not found; attempting to create from template..." if [[ ! -f "inventory.env" ]] && [[ -f "inventory.env.template" ]]; then cp inventory.env.template inventory.env log_success " ✓ inventory.env created from template (review and customize)" elif [[ ! -f "inventory.env" ]]; then log_error "inventory.env not found and no template available. Create inventory.env before deploying." fi # ============================================================================ # STEP 2: Validate environment file # ============================================================================ log_info "Step 2/10: Validating inventory.env..." if [[ ! -f ".env.validation.sh" ]]; then log_warn " .env.validation.sh not found; skipping validation" else bash .env.validation.sh || log_error "Environment validation failed" fi log_success " ✓ Environment variables validated" # ============================================================================ # STEP 3: Check port availability # ============================================================================ log_info "Step 3/10: Checking port availability..." # Source inventory.env to get port values source inventory.env BACKEND_PORT=${BACKEND_PORT:-8000} FRONTEND_PORT=${FRONTEND_PORT:-3000} BACKEND_SSL_PORT=${BACKEND_SSL_PORT:-8918} FRONTEND_SSL_PORT=${FRONTEND_SSL_PORT:-8919} for port in "$BACKEND_PORT" "$FRONTEND_PORT" "$BACKEND_SSL_PORT" "$FRONTEND_SSL_PORT"; do if command -v netstat &> /dev/null; then if netstat -tuln 2>/dev/null | grep -q ":$port "; then log_error "Port $port is already in use. Choose a different port in inventory.env" fi else log_warn " netstat not available; skipping port check (ensure ports are available)" fi done log_success " ✓ All required ports are available" # ============================================================================ # STEP 4: Check disk space # ============================================================================ log_info "Step 4/10: Checking disk space..." AVAILABLE_SPACE=$(df . | awk 'NR==2 {print $4}') REQUIRED_SPACE=$((10 * 1024 * 1024)) # 10GB in KB if [[ $AVAILABLE_SPACE -lt $REQUIRED_SPACE ]]; then log_warn " Available space: $(($AVAILABLE_SPACE / 1024 / 1024))GB (recommended: 10GB+)" else log_success " ✓ Sufficient disk space available ($(($AVAILABLE_SPACE / 1024 / 1024))GB)" fi # ============================================================================ # STEP 5: Build or pull images # ============================================================================ log_info "Step 5/10: Building Docker images..." if [[ "$REBUILD_FLAG" == "--rebuild" ]]; then log_info " Building with --no-cache (full rebuild)..." docker-compose build --no-cache || log_error "Docker build failed" else log_info " Building with layer cache (incremental)..." docker-compose build || log_error "Docker build failed" fi log_success " ✓ Docker images built successfully" # ============================================================================ # STEP 6: Create data directories # ============================================================================ log_info "Step 6/10: Preparing data directories..." mkdir -p data logs config mkdir -p data/caddy_data data/caddy_config chmod -R 777 data logs config log_success " ✓ Data directories created with proper permissions" # ============================================================================ # STEP 7: Initialize database # ============================================================================ log_info "Step 7/10: Checking database initialization..." if [[ ! -f "data/inventory.db" ]]; then log_info " Database not found; will be initialized on first backend startup" log_success " ✓ Database initialization scheduled" else log_success " ✓ Existing database found; reusing" fi # ============================================================================ # STEP 8: Start services # ============================================================================ log_info "Step 8/10: Starting Docker services..." docker-compose up -d || log_error "Failed to start Docker services" log_success " ✓ Services started in background" # ============================================================================ # STEP 9: Wait for health checks # ============================================================================ log_info "Step 9/10: Waiting for services to become healthy (max 60 seconds)..." max_attempts=30 attempt=0 all_healthy=false while [[ $attempt -lt $max_attempts ]]; do # Check if all 3 services are healthy if docker-compose ps | grep -q "healthy.*healthy.*healthy"; then all_healthy=true break fi attempt=$((attempt + 1)) remaining=$((max_attempts - attempt)) log_info " Waiting... ($remaining attempts remaining)" sleep 2 done if [[ "$all_healthy" == true ]]; then log_success " ✓ All services are healthy" else log_warn "Services did not become healthy within timeout. Checking logs..." docker-compose logs --tail=50 || true log_error "Service health check timeout. Review logs above." fi # ============================================================================ # STEP 10: Verify connectivity # ============================================================================ log_info "Step 10/10: Verifying service connectivity..." if curl -sf "http://localhost:${BACKEND_PORT}/health" &> /dev/null; then log_success " ✓ Backend API responding at http://localhost:${BACKEND_PORT}/health" else log_warn " Backend health check failed; services may still be initializing" fi if curl -sf "http://localhost:${FRONTEND_PORT}/" &> /dev/null; then log_success " ✓ Frontend responding at http://localhost:${FRONTEND_PORT}" else log_warn " Frontend check failed; container may still be initializing" fi # ============================================================================ # Summary # ============================================================================ echo "" echo -e "${GREEN}╔════════════════════════════════════════════════════════════╗${NC}" echo -e "${GREEN}║${NC} Deployment completed successfully! ${GREEN}║${NC}" echo -e "${GREEN}╚════════════════════════════════════════════════════════════╝${NC}" echo "" echo "Access points:" echo " Frontend (HTTP): http://localhost:${FRONTEND_PORT}" echo " Backend (HTTP): http://localhost:${BACKEND_PORT}" echo " API Docs: http://localhost:${BACKEND_PORT}/docs" echo " Frontend (HTTPS): https://localhost:${FRONTEND_SSL_PORT}" echo " Backend (HTTPS): https://localhost:${BACKEND_SSL_PORT}" echo "" echo "Useful commands:" echo " View logs: docker-compose logs -f" echo " Stop services: docker-compose down" echo " Restart: docker-compose restart" echo " Status: docker-compose ps" echo "" echo "For deployment in production ($DEPLOYMENT_ENV):" echo " • Review and update JWT_SECRET_KEY in inventory.env" echo " • Configure firewall to expose only required ports" echo " • Set up automated backups (see docs/DEPLOYMENT_QUICKSTART.md)" echo " • Monitor logs regularly: docker-compose logs -f" echo "" log_success "Deployment ready!"