#!/bin/bash # ============================================================================== # Application Deployment Script # ============================================================================== # Deploys application to staging or production environment # Usage: ./deploy.sh [options] # ============================================================================== set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" cd "$PROJECT_ROOT" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Function to print colored output print_info() { echo -e "${BLUE}ℹ${NC} $1" } print_success() { echo -e "${GREEN}✅${NC} $1" } print_warning() { echo -e "${YELLOW}⚠️${NC} $1" } print_error() { echo -e "${RED}❌${NC} $1" } # Parse arguments ENVIRONMENT=$1 BUILD_IMAGES=${2:-false} if [ -z "$ENVIRONMENT" ]; then print_error "Usage: $0 [build]" print_info "Environments: staging, production" print_info "Options: build - Build Docker images before deployment" exit 1 fi if [ "$ENVIRONMENT" != "staging" ] && [ "$ENVIRONMENT" != "production" ]; then print_error "Invalid environment: $ENVIRONMENT" print_info "Valid environments: staging, production" exit 1 fi # Set compose files COMPOSE_BASE="docker-compose.base.yml" if [ "$ENVIRONMENT" = "staging" ]; then COMPOSE_ENV="docker-compose.staging.yml" elif [ "$ENVIRONMENT" = "production" ]; then COMPOSE_ENV="docker-compose.prod.yml" fi COMPOSE_FILES="-f $COMPOSE_BASE -f $COMPOSE_ENV" print_info "Deploying to $ENVIRONMENT environment..." # Check if secrets exist SECRETS_DIR="deployment/secrets/$ENVIRONMENT" if [ ! -d "$SECRETS_DIR" ]; then print_warning "Secrets directory not found: $SECRETS_DIR" print_info "Creating secrets directory..." mkdir -p "$SECRETS_DIR" fi MISSING_SECRETS=() REQUIRED_SECRETS=("db_password.txt" "redis_password.txt" "app_key.txt") for secret_file in "${REQUIRED_SECRETS[@]}"; do if [ ! -f "$SECRETS_DIR/$secret_file" ]; then MISSING_SECRETS+=("$secret_file") fi done if [ ${#MISSING_SECRETS[@]} -gt 0 ]; then print_error "Missing required secrets:" for secret in "${MISSING_SECRETS[@]}"; do print_error " - $SECRETS_DIR/$secret" done print_info "See deployment/infrastructure/SECRETS.md for instructions" exit 1 fi # Check if infrastructure networks exist print_info "Checking infrastructure networks..." if ! docker network ls | grep -q "traefik-public"; then print_error "traefik-public network not found" print_info "Please deploy infrastructure stacks first:" print_info " cd deployment/infrastructure && ./deploy.sh traefik" exit 1 fi if ! docker network ls | grep -q "app-internal"; then print_error "app-internal network not found" print_info "Please deploy infrastructure stacks first:" print_info " cd deployment/infrastructure && ./deploy.sh postgresql" exit 1 fi # Build images if requested if [ "$BUILD_IMAGES" = "build" ]; then print_info "Building Docker images..." docker compose $COMPOSE_FILES build fi # Pull latest images print_info "Pulling latest images..." docker compose $COMPOSE_FILES pull || print_warning "Failed to pull some images, continuing..." # Stop and remove existing containers to prevent name conflicts print_info "Stopping existing containers..." docker compose $COMPOSE_FILES down --remove-orphans || print_warning "No existing containers to stop" # Remove any orphaned containers with conflicting names for container in nginx php redis scheduler queue-worker; do if docker ps -a --format '{{.Names}}' | grep -q "^${container}$"; then print_warning "Removing orphaned container: $container" docker rm -f "$container" 2>/dev/null || true fi done # Deploy stack print_info "Deploying application stack..." docker compose $COMPOSE_FILES up -d --force-recreate --remove-orphans # Wait for services to be healthy print_info "Waiting for services to be healthy..." sleep 10 # Check service status print_info "Checking service status..." docker compose $COMPOSE_FILES ps # Health checks print_info "Running health checks..." HEALTH_CHECK_FAILED=0 # Check PHP service if docker compose $COMPOSE_FILES exec -T php php -v > /dev/null 2>&1; then print_success "PHP service is healthy" else print_error "PHP service health check failed" HEALTH_CHECK_FAILED=1 fi # Check Redis service if docker compose $COMPOSE_FILES exec -T redis redis-cli ping > /dev/null 2>&1; then print_success "Redis service is healthy" else print_error "Redis service health check failed" HEALTH_CHECK_FAILED=1 fi # Check Nginx service if docker compose $COMPOSE_FILES exec -T nginx wget --quiet --tries=1 --spider http://localhost/health > /dev/null 2>&1; then print_success "Nginx service is healthy" else print_warning "Nginx health check endpoint not available (this may be normal)" fi if [ $HEALTH_CHECK_FAILED -eq 1 ]; then print_error "Some health checks failed. Check logs:" print_info " docker compose $COMPOSE_FILES logs" exit 1 fi print_success "Deployment to $ENVIRONMENT completed successfully!" # Show service URLs if [ "$ENVIRONMENT" = "production" ]; then print_info "Application URL: https://michaelschiemer.de" elif [ "$ENVIRONMENT" = "staging" ]; then print_info "Application URL: https://staging.michaelschiemer.de" fi print_info "View logs: docker compose $COMPOSE_FILES logs -f" print_info "View status: docker compose $COMPOSE_FILES ps"