mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 23:41:08 +02:00
201 lines
5.1 KiB
Bash
Executable file
201 lines
5.1 KiB
Bash
Executable file
#!/bin/bash
|
|
|
|
# Deploy to Hetzner server via SSH
|
|
# Usage: ./deploy-hetzner.sh [environment] [service]
|
|
# Example: ./deploy-hetzner.sh staging all
|
|
# Example: ./deploy-hetzner.sh production chat-backend
|
|
|
|
set -e
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m'
|
|
|
|
# Configuration
|
|
ENVIRONMENT=${1:-"staging"}
|
|
SERVICE=${2:-"all"}
|
|
|
|
# Environment-specific variables
|
|
if [ "$ENVIRONMENT" == "production" ]; then
|
|
SSH_HOST=${PRODUCTION_HOST}
|
|
SSH_USER=${PRODUCTION_USER}
|
|
SSH_KEY=${PRODUCTION_SSH_KEY}
|
|
DEPLOY_DIR="~/manacore-production"
|
|
COMPOSE_FILE="docker-compose.production.yml"
|
|
else
|
|
SSH_HOST=${STAGING_HOST}
|
|
SSH_USER=${STAGING_USER}
|
|
SSH_KEY=${STAGING_SSH_KEY}
|
|
DEPLOY_DIR="~/manacore-staging"
|
|
COMPOSE_FILE="docker-compose.staging.yml"
|
|
fi
|
|
|
|
# Function to print colored output
|
|
log_info() {
|
|
echo -e "${GREEN}[INFO]${NC} $1"
|
|
}
|
|
|
|
log_warn() {
|
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
}
|
|
|
|
# Validate required variables
|
|
if [ -z "$SSH_HOST" ] || [ -z "$SSH_USER" ]; then
|
|
log_error "SSH configuration missing for ${ENVIRONMENT}"
|
|
log_error "Please set: ${ENVIRONMENT^^}_HOST and ${ENVIRONMENT^^}_USER"
|
|
exit 1
|
|
fi
|
|
|
|
# SSH command helper
|
|
ssh_exec() {
|
|
if [ -n "$SSH_KEY" ]; then
|
|
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${SSH_USER}@${SSH_HOST}" "$@"
|
|
else
|
|
ssh -o StrictHostKeyChecking=no "${SSH_USER}@${SSH_HOST}" "$@"
|
|
fi
|
|
}
|
|
|
|
# SCP command helper
|
|
scp_copy() {
|
|
if [ -n "$SSH_KEY" ]; then
|
|
scp -i "$SSH_KEY" -o StrictHostKeyChecking=no "$@"
|
|
else
|
|
scp -o StrictHostKeyChecking=no "$@"
|
|
fi
|
|
}
|
|
|
|
log_info "Starting deployment to ${ENVIRONMENT}..."
|
|
log_info "Target: ${SSH_USER}@${SSH_HOST}"
|
|
log_info "Service: ${SERVICE}"
|
|
echo ""
|
|
|
|
# Step 1: Prepare deployment directory
|
|
log_info "Preparing deployment directory..."
|
|
ssh_exec << EOF
|
|
mkdir -p ${DEPLOY_DIR}
|
|
mkdir -p ${DEPLOY_DIR}/logs
|
|
mkdir -p ${DEPLOY_DIR}/backups
|
|
cd ${DEPLOY_DIR}
|
|
EOF
|
|
|
|
# Step 2: Copy docker-compose file
|
|
log_info "Copying docker-compose configuration..."
|
|
scp_copy "${COMPOSE_FILE}" "${SSH_USER}@${SSH_HOST}:${DEPLOY_DIR}/docker-compose.yml"
|
|
|
|
# Step 3: Copy environment file if exists
|
|
if [ -f ".env.${ENVIRONMENT}" ]; then
|
|
log_info "Copying environment configuration..."
|
|
scp_copy ".env.${ENVIRONMENT}" "${SSH_USER}@${SSH_HOST}:${DEPLOY_DIR}/.env"
|
|
else
|
|
log_warn "No .env.${ENVIRONMENT} file found, using existing environment"
|
|
fi
|
|
|
|
# Step 4: Pull latest images
|
|
log_info "Pulling latest Docker images..."
|
|
ssh_exec << EOF
|
|
cd ${DEPLOY_DIR}
|
|
docker compose pull ${SERVICE}
|
|
EOF
|
|
|
|
# Step 5: Run migrations if needed
|
|
if [ "$SERVICE" == "all" ] || [ "$SERVICE" == "mana-core-auth" ]; then
|
|
log_info "Running database migrations..."
|
|
ssh_exec << EOF
|
|
cd ${DEPLOY_DIR}
|
|
docker compose run --rm mana-core-auth pnpm run db:migrate || echo "Migrations completed or skipped"
|
|
EOF
|
|
fi
|
|
|
|
# Step 6: Deploy services
|
|
log_info "Deploying services..."
|
|
if [ "$SERVICE" == "all" ]; then
|
|
# Zero-downtime rolling update for all services
|
|
ssh_exec << 'EOF'
|
|
cd ${DEPLOY_DIR}
|
|
|
|
SERVICES=$(docker compose config --services)
|
|
|
|
for service in $SERVICES; do
|
|
echo "Deploying $service..."
|
|
|
|
# Scale up with new version
|
|
docker compose up -d --no-deps --scale $service=2 $service
|
|
sleep 15
|
|
|
|
# Scale down to single instance
|
|
docker compose up -d --no-deps --scale $service=1 $service
|
|
sleep 5
|
|
done
|
|
|
|
# Cleanup old images
|
|
docker image prune -f
|
|
EOF
|
|
else
|
|
# Deploy single service
|
|
ssh_exec << EOF
|
|
cd ${DEPLOY_DIR}
|
|
docker compose up -d --no-deps ${SERVICE}
|
|
sleep 10
|
|
EOF
|
|
fi
|
|
|
|
# Step 7: Health checks
|
|
log_info "Running health checks..."
|
|
HEALTH_ENDPOINTS=(
|
|
"mana-core-auth:3001:/api/v1/health"
|
|
"maerchenzauber-backend:3002:/health"
|
|
"chat-backend:3002:/api/health"
|
|
)
|
|
|
|
FAILED_CHECKS=0
|
|
|
|
for endpoint in "${HEALTH_ENDPOINTS[@]}"; do
|
|
IFS=':' read -r service port path <<< "$endpoint"
|
|
|
|
log_info "Checking health of ${service}..."
|
|
|
|
if ssh_exec << EOF
|
|
HEALTH=\$(docker compose -f ${DEPLOY_DIR}/docker-compose.yml exec -T ${service} wget -q -O - http://localhost:${port}${path} 2>/dev/null || echo "FAILED")
|
|
|
|
if [[ "\$HEALTH" == *"FAILED"* ]]; then
|
|
echo "Health check failed for ${service}"
|
|
exit 1
|
|
else
|
|
echo "Health check passed for ${service}"
|
|
exit 0
|
|
fi
|
|
EOF
|
|
then
|
|
log_info "✅ ${service} is healthy"
|
|
else
|
|
log_error "❌ ${service} health check failed"
|
|
((FAILED_CHECKS++))
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
|
|
# Step 8: Display service status
|
|
log_info "Current service status:"
|
|
ssh_exec << EOF
|
|
cd ${DEPLOY_DIR}
|
|
docker compose ps
|
|
EOF
|
|
|
|
echo ""
|
|
|
|
# Final result
|
|
if [ $FAILED_CHECKS -eq 0 ]; then
|
|
log_info "Deployment to ${ENVIRONMENT} completed successfully! ✅"
|
|
exit 0
|
|
else
|
|
log_error "Deployment completed with ${FAILED_CHECKS} failed health checks"
|
|
log_warn "Please check service logs with: ssh ${SSH_USER}@${SSH_HOST} 'cd ${DEPLOY_DIR} && docker compose logs'"
|
|
exit 1
|
|
fi
|