#!/bin/bash # ๐Ÿงช Deployment Validation Script # Validates complete deployment with all environment variables and health checks set -e echo "๐Ÿš€ Starting deploymif docker compose -f docker/docker-compose.yaml -p rxminder-validation ps | grep -q "Up"; then print_success "Docker Compose setup completed successfully!" else print_error "Docker Compose services failed to start" docker compose -f docker/docker-compose.yaml -p rxminder-validation logsalidation..." # 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_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" } # Cleanup function cleanup() { print_status "Cleaning up test containers..." docker stop rxminder-validation-test 2>/dev/null || true docker rm rxminder-validation-test 2>/dev/null || true docker compose -f docker/docker-compose.yaml -p rxminder-validation down 2>/dev/null || true } # Set trap for cleanup trap cleanup EXIT print_status "1. Validating environment files..." # Check if required environment files exist if [[ ! -f .env ]]; then print_error ".env file not found. Run 'cp .env.example .env' and configure it." exit 1 fi if [[ ! -f .env.example ]]; then print_error ".env.example file not found." exit 1 fi print_success "Environment files exist" # Validate environment consistency print_status "2. Checking environment variable consistency..." ./validate-env.sh print_status "3. Setting up Docker Buildx..." # Ensure buildx is available if ! docker buildx version >/dev/null 2>&1; then print_error "Docker Buildx is not available. Please update Docker to a version that supports Buildx." exit 1 fi # Create a new builder instance if it doesn't exist if ! docker buildx ls | grep -q "rxminder-builder"; then print_status "Creating new buildx builder instance..." docker buildx create --name rxminder-builder --driver docker-container --bootstrap fi # Use the builder docker buildx use rxminder-builder print_status "4. Building multi-platform Docker image with buildx..." # Build the image with buildx for multiple platforms docker buildx build --no-cache \ --platform linux/amd64,linux/arm64 \ --build-arg COUCHDB_USER="${COUCHDB_USER:-admin}" \ --build-arg COUCHDB_PASSWORD="${COUCHDB_PASSWORD:-change-this-secure-password}" \ --build-arg VITE_COUCHDB_URL="${VITE_COUCHDB_URL:-http://localhost:5984}" \ --build-arg VITE_COUCHDB_USER="${VITE_COUCHDB_USER:-admin}" \ --build-arg VITE_COUCHDB_PASSWORD="${VITE_COUCHDB_PASSWORD:-change-this-secure-password}" \ --build-arg APP_BASE_URL="${APP_BASE_URL:-http://localhost:8080}" \ --build-arg VITE_GOOGLE_CLIENT_ID="${VITE_GOOGLE_CLIENT_ID:-}" \ --build-arg VITE_GITHUB_CLIENT_ID="${VITE_GITHUB_CLIENT_ID:-}" \ --build-arg MAILGUN_API_KEY="${MAILGUN_API_KEY:-}" \ --build-arg MAILGUN_DOMAIN="${MAILGUN_DOMAIN:-}" \ --build-arg MAILGUN_FROM_EMAIL="${MAILGUN_FROM_EMAIL:-}" \ --build-arg NODE_ENV="${NODE_ENV:-production}" \ -t rxminder-validation \ --load \ . print_success "Docker image built successfully" print_status "5. Testing container startup and health..." # Run container in background docker run --rm -d \ -p 8083:80 \ --name rxminder-validation-test \ rxminder-validation # Wait for container to start sleep 5 # Check if container is running if ! docker ps | grep -q rxminder-validation-test; then print_error "Container failed to start" docker logs rxminder-validation-test exit 1 fi print_success "Container started successfully" # Test health endpoint print_status "5. Testing health endpoint..." for i in {1..10}; do if curl -s -f http://localhost:8083/health > /dev/null; then print_success "Health endpoint responding" break elif [[ $i -eq 10 ]]; then print_error "Health endpoint not responding after 10 attempts" exit 1 else print_warning "Health endpoint not ready, retrying... ($i/10)" sleep 2 fi done # Test main application print_status "6. Testing main application..." HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8083) if [[ $HTTP_CODE -eq 200 ]]; then print_success "Main application responding (HTTP $HTTP_CODE)" else print_error "Main application not responding properly (HTTP $HTTP_CODE)" exit 1 fi # Test docker-compose build print_status "7. Testing Docker Compose build..." docker compose -f docker/docker-compose.yaml build frontend --no-cache print_success "Docker Compose build successful" # Test docker-compose with validation project name print_status "8. Testing Docker Compose deployment..." docker compose -f docker/docker-compose.yaml -p rxminder-validation up -d --build # Wait for services to start sleep 10 # Check service health if docker compose -f docker/docker-compose.yaml -p meds-validation ps | grep -q "Up"; then print_success "Docker Compose services started successfully" else print_error "Docker Compose services failed to start" docker compose -f docker/docker-compose.yaml -p meds-validation logs exit 1 fi # Test health of compose deployment if curl -s -f http://localhost:8080/health > /dev/null; then print_success "Docker Compose health endpoint responding" else print_warning "Docker Compose health endpoint not responding (may need CouchDB)" fi print_status "9. Checking image size..." IMAGE_SIZE=$(docker image inspect rxminder-validation --format='{{.Size}}' | numfmt --to=iec) print_success "Image size: $IMAGE_SIZE" print_status "10. Validating security configuration..." # Check if image runs as non-root USER_INFO=$(docker run --rm rxminder-validation whoami) if [[ "$USER_INFO" != "root" ]]; then print_success "Container runs as non-root user: $USER_INFO" else print_warning "Container runs as root user (security consideration)" fi # Check nginx configuration if docker run --rm rxminder-validation nginx -t 2>/dev/null; then print_success "Nginx configuration is valid" else print_error "Nginx configuration has issues" exit 1 fi print_status "11. Final validation complete!" echo echo "๐ŸŽ‰ Deployment validation successful!" echo echo "Summary:" echo "โœ… Environment files validated" echo "โœ… Docker image builds successfully" echo "โœ… Container starts and runs healthy" echo "โœ… Health endpoints respond correctly" echo "โœ… Docker Compose deployment works" echo "โœ… Security configuration validated" echo "โœ… Image size optimized ($IMAGE_SIZE)" echo echo "Your deployment is ready for production! ๐Ÿš€" echo echo "Next steps:" echo "1. Configure production environment variables in .env" echo "2. Run './deploy.sh production' for production deployment" echo "3. Set up monitoring and backups" echo "4. Configure SSL/TLS certificates" echo # Load Gitea-specific environment variables REGISTRY=${GITEA_SERVER_URL#https://} IMAGE_NAME=${GITEA_REPOSITORY} IMAGE_TAG=${GITEA_SHA:0:8} print_status "Registry: $REGISTRY" print_status "Image: $IMAGE_NAME:$IMAGE_TAG" fi # Check if .env file exists if [ ! -f ".env" ]; then print_warning ".env file not found, using defaults" # Create minimal .env for Gitea deployment cat > .env << EOF COUCHDB_USER=admin COUCHDB_PASSWORD=change-this-secure-password VITE_COUCHDB_URL=http://couchdb:5984 VITE_COUCHDB_USER=admin VITE_COUCHDB_PASSWORD=change-this-secure-password APP_BASE_URL=http://localhost:8080 NODE_ENV=production EOF print_warning "Created default .env file - please update with your credentials" fi # Load environment variables print_status "Loading environment variables from .env file..." export $(cat .env | grep -v '^#' | xargs) # Validate required environment variables REQUIRED_VARS=("COUCHDB_USER" "COUCHDB_PASSWORD" "VITE_COUCHDB_URL") for var in "${REQUIRED_VARS[@]}"; do if [ -z "${!var}" ]; then print_error "Required environment variable $var is not set" exit 1 fi done print_success "Environment variables validated" # Function to deploy via Docker Compose deploy_compose() { print_status "Deploying with Docker Compose..." # Export image variables for compose export IMAGE_TAG export REGISTRY export IMAGE_NAME # Use the built image from registry if available if [ "$GITEA_ACTIONS" = "true" ]; then # Override the image in docker-compose export FRONTEND_IMAGE="$REGISTRY/$IMAGE_NAME:$IMAGE_TAG" print_status "Using Gitea Actions built image: $FRONTEND_IMAGE" fi # Pull the latest images print_status "Pulling latest images..." docker-compose -f docker/docker-compose.yaml pull || print_warning "Failed to pull some images" # Start services print_status "Starting services..." docker-compose -f docker/docker-compose.yaml up -d # Wait for services print_status "Waiting for services to be ready..." sleep 10 # Health check print_status "Checking service health..." if curl -f http://localhost:8080/health > /dev/null 2>&1; then print_success "Frontend service is healthy" else print_warning "Frontend health check failed, checking logs..." docker-compose -f docker/docker-compose.yaml logs frontend fi if curl -f http://localhost:5984/_up > /dev/null 2>&1; then print_success "CouchDB service is healthy" else print_warning "CouchDB health check failed" fi } # Function to deploy via Kubernetes deploy_k8s() { print_status "Deploying to Kubernetes..." if ! command -v kubectl &> /dev/null; then print_error "kubectl is not installed" exit 1 fi # Update image in k8s manifests if [ "$GITEA_ACTIONS" = "true" ]; then print_status "Updating Kubernetes manifests with new image..." sed -i "s|image:.*rxminder.*|image: $REGISTRY/$IMAGE_NAME:$IMAGE_TAG|g" k8s/frontend-deployment.yaml fi # Apply manifests print_status "Applying Kubernetes manifests..." kubectl apply -f k8s/ # Wait for rollout print_status "Waiting for deployment rollout..." kubectl rollout status deployment/frontend-deployment print_success "Kubernetes deployment completed" } # Main deployment logic case "$ENVIRONMENT" in "production"|"prod") print_status "Deploying to production environment" deploy_compose ;; "kubernetes"|"k8s") print_status "Deploying to Kubernetes environment" deploy_k8s ;; "staging") print_status "Deploying to staging environment" # Use staging-specific configurations export APP_BASE_URL="http://staging.localhost:8080" deploy_compose ;; *) print_error "Unknown environment: $ENVIRONMENT" echo "Available environments: production, kubernetes, staging" exit 1 ;; esac print_success "Deployment to $ENVIRONMENT completed successfully! ๐ŸŽ‰" # Post-deployment tasks print_status "Running post-deployment tasks..." # Cleanup old images (optional) if [ "$CLEANUP_OLD_IMAGES" = "true" ]; then print_status "Cleaning up old Docker images..." docker image prune -f --filter "until=72h" || print_warning "Image cleanup failed" fi # Send notification (if configured) if [ -n "$DEPLOYMENT_WEBHOOK_URL" ]; then print_status "Sending deployment notification..." curl -X POST "$DEPLOYMENT_WEBHOOK_URL" \ -H "Content-Type: application/json" \ -d "{\"text\":\"โœ… RxMinder deployed to $ENVIRONMENT\", \"environment\":\"$ENVIRONMENT\", \"image\":\"$REGISTRY/$IMAGE_NAME:$IMAGE_TAG\"}" \ || print_warning "Failed to send notification" fi print_success "All tasks completed! ๐Ÿš€"