Some checks failed
Deploy Application / deploy (push) Has been cancelled
307 lines
9.9 KiB
Bash
Executable File
307 lines
9.9 KiB
Bash
Executable File
#!/bin/bash
|
|
set -e
|
|
|
|
# ============================================================================
|
|
# Production Deployment Script
|
|
# ============================================================================
|
|
# Based on .gitea/workflows/deploy-production.yml
|
|
# Deploys to production environment at michaelschiemer.de
|
|
# ============================================================================
|
|
|
|
# Color output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Configuration
|
|
REGISTRY="${REGISTRY:-localhost:5000}"
|
|
IMAGE_NAME="${IMAGE_NAME:-framework}"
|
|
IMAGE_TAG="${IMAGE_TAG:-latest}"
|
|
COMPOSE_PROJECT_NAME="framework-production"
|
|
|
|
# Remote server configuration (from environment or defaults)
|
|
REMOTE_USER="${PRODUCTION_USER:-deploy}"
|
|
REMOTE_HOST="${PRODUCTION_HOST:-michaelschiemer.de}"
|
|
REMOTE_PORT="${PRODUCTION_SSH_PORT:-22}"
|
|
REMOTE_PATH="/opt/framework-production"
|
|
|
|
# Deployment options
|
|
SKIP_BACKUP="${SKIP_BACKUP:-false}"
|
|
FORCE_REBUILD="${FORCE_REBUILD:-false}"
|
|
|
|
# Validation
|
|
if [ -z "$REMOTE_HOST" ]; then
|
|
echo -e "${RED}❌ Error: PRODUCTION_HOST environment variable is required${NC}"
|
|
echo "Export PRODUCTION_HOST before running this script:"
|
|
echo " export PRODUCTION_HOST=michaelschiemer.de"
|
|
exit 1
|
|
fi
|
|
|
|
echo "=================================================="
|
|
echo "🚀 Starting Production Deployment"
|
|
echo "=================================================="
|
|
echo "Registry: ${REGISTRY}"
|
|
echo "Image: ${IMAGE_NAME}:${IMAGE_TAG}"
|
|
echo "Remote: ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PORT}"
|
|
echo "Path: ${REMOTE_PATH}"
|
|
echo "Skip Backup: ${SKIP_BACKUP}"
|
|
echo ""
|
|
|
|
# Step 1: Build Docker image
|
|
echo -e "${GREEN}[1/8] Building Docker image...${NC}"
|
|
docker build \
|
|
--file docker/php/Dockerfile \
|
|
--tag "${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}" \
|
|
--build-arg ENV=production \
|
|
--build-arg COMPOSER_INSTALL_FLAGS="--no-dev --optimize-autoloader --no-interaction" \
|
|
.
|
|
|
|
if [ $? -ne 0 ]; then
|
|
echo -e "${RED}❌ Docker build failed${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
# Step 2: Push image to registry
|
|
echo -e "${GREEN}[2/8] Pushing image to registry...${NC}"
|
|
docker push "${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}"
|
|
|
|
if [ $? -ne 0 ]; then
|
|
echo -e "${RED}❌ Docker push failed${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
# Step 3: Prepare deployment files
|
|
echo -e "${GREEN}[3/8] Preparing deployment files...${NC}"
|
|
rm -rf deployment-production-tmp
|
|
mkdir -p deployment-production-tmp
|
|
|
|
cp docker-compose.base.yml deployment-production-tmp/
|
|
cp docker-compose.prod.yml deployment-production-tmp/
|
|
cp -r docker deployment-production-tmp/
|
|
|
|
# Create deployment script
|
|
cat > deployment-production-tmp/deploy.sh << 'DEPLOY_SCRIPT'
|
|
#!/bin/bash
|
|
set -e
|
|
|
|
echo "=================================================="
|
|
echo "Starting Production Deployment on Server"
|
|
echo "=================================================="
|
|
echo ""
|
|
|
|
# Database backup (unless explicitly skipped)
|
|
if [ "${SKIP_BACKUP}" != "true" ]; then
|
|
echo "[0/6] Creating database backup..."
|
|
BACKUP_FILE="backup_$(date +%Y%m%d_%H%M%S).sql"
|
|
docker-compose -f docker-compose.base.yml -f docker-compose.prod.yml exec -T production-app \
|
|
php console.php db:backup --output="/var/www/html/storage/backups/${BACKUP_FILE}" || {
|
|
echo "⚠️ Database backup failed - deployment aborted"
|
|
exit 1
|
|
}
|
|
echo "✅ Database backup created: ${BACKUP_FILE}"
|
|
else
|
|
echo "⚠️ Database backup skipped (not recommended for production)"
|
|
fi
|
|
|
|
# Pull latest images
|
|
echo "[1/6] Pulling latest Docker images..."
|
|
docker-compose -f docker-compose.base.yml -f docker-compose.prod.yml pull
|
|
|
|
# Stop existing containers gracefully
|
|
echo "[2/6] Stopping existing containers (graceful shutdown)..."
|
|
docker-compose -f docker-compose.base.yml -f docker-compose.prod.yml stop
|
|
|
|
# Start new containers
|
|
echo "[3/6] Starting new containers..."
|
|
docker-compose -f docker-compose.base.yml -f docker-compose.prod.yml up -d
|
|
|
|
# Wait for services to be healthy (longer timeout for production)
|
|
echo "[4/6] Waiting for services to be healthy..."
|
|
sleep 30
|
|
|
|
# Run database migrations
|
|
echo "[5/6] Running database migrations..."
|
|
docker-compose -f docker-compose.base.yml -f docker-compose.prod.yml exec -T production-app \
|
|
php console.php db:migrate --force || {
|
|
echo "⚠️ Database migration failed"
|
|
exit 1
|
|
}
|
|
|
|
# Verify deployment
|
|
echo "[6/6] Verifying deployment..."
|
|
docker-compose -f docker-compose.base.yml -f docker-compose.prod.yml ps
|
|
|
|
# Cleanup old containers
|
|
echo "Cleaning up old containers..."
|
|
docker system prune -f
|
|
|
|
echo ""
|
|
echo "=================================================="
|
|
echo "✅ Production Deployment Complete"
|
|
echo "=================================================="
|
|
DEPLOY_SCRIPT
|
|
|
|
chmod +x deployment-production-tmp/deploy.sh
|
|
|
|
# Step 4: Create remote directory and backup
|
|
echo -e "${GREEN}[4/8] Creating remote directory and backup...${NC}"
|
|
ssh -p "${REMOTE_PORT}" "${REMOTE_USER}@${REMOTE_HOST}" "
|
|
mkdir -p ${REMOTE_PATH}
|
|
cd ${REMOTE_PATH}
|
|
|
|
# Backup current deployment
|
|
if [ -d 'current' ]; then
|
|
echo 'Backing up current deployment...'
|
|
timestamp=\$(date +%Y%m%d_%H%M%S)
|
|
mv current backup_\${timestamp}
|
|
|
|
# Keep only last 10 backups for production
|
|
ls -dt backup_* 2>/dev/null | tail -n +11 | xargs rm -rf 2>/dev/null || true
|
|
echo 'Backup created: backup_'\${timestamp}
|
|
fi
|
|
|
|
# Create new deployment directory
|
|
mkdir -p current
|
|
"
|
|
|
|
if [ $? -ne 0 ]; then
|
|
echo -e "${RED}❌ SSH connection or remote directory creation failed${NC}"
|
|
rm -rf deployment-production-tmp
|
|
exit 1
|
|
fi
|
|
|
|
# Step 5: Copy deployment files to server
|
|
echo -e "${GREEN}[5/8] Copying deployment files to server...${NC}"
|
|
scp -P "${REMOTE_PORT}" -r deployment-production-tmp/* "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PATH}/current/"
|
|
|
|
if [ $? -ne 0 ]; then
|
|
echo -e "${RED}❌ SCP file transfer failed${NC}"
|
|
rm -rf deployment-production-tmp
|
|
exit 1
|
|
fi
|
|
|
|
# Cleanup local temp directory
|
|
rm -rf deployment-production-tmp
|
|
|
|
# Step 6: Execute deployment on server
|
|
echo -e "${GREEN}[6/8] Executing deployment on server...${NC}"
|
|
ssh -p "${REMOTE_PORT}" "${REMOTE_USER}@${REMOTE_HOST}" "
|
|
cd ${REMOTE_PATH}/current
|
|
export SKIP_BACKUP=${SKIP_BACKUP}
|
|
./deploy.sh
|
|
"
|
|
|
|
DEPLOY_EXIT_CODE=$?
|
|
|
|
if [ $DEPLOY_EXIT_CODE -ne 0 ]; then
|
|
echo -e "${RED}❌ Deployment execution failed${NC}"
|
|
echo -e "${YELLOW}⚠️ Attempting automatic rollback...${NC}"
|
|
|
|
ssh -p "${REMOTE_PORT}" "${REMOTE_USER}@${REMOTE_HOST}" "
|
|
cd ${REMOTE_PATH}
|
|
|
|
if [ -d \"\$(ls -dt backup_* 2>/dev/null | head -n1)\" ]; then
|
|
echo '🚨 Rolling back to previous deployment...'
|
|
latest_backup=\$(ls -dt backup_* | head -n1)
|
|
|
|
# Stop current broken deployment
|
|
cd current
|
|
docker-compose -f docker-compose.base.yml -f docker-compose.prod.yml down 2>/dev/null || true
|
|
cd ..
|
|
|
|
# Restore backup
|
|
rm -rf current
|
|
cp -r \"\$latest_backup\" current
|
|
|
|
# Start restored deployment
|
|
cd current
|
|
docker-compose -f docker-compose.base.yml -f docker-compose.prod.yml up -d
|
|
|
|
# Wait for services
|
|
sleep 30
|
|
|
|
# Verify rollback
|
|
docker-compose -f docker-compose.base.yml -f docker-compose.prod.yml ps
|
|
|
|
echo '✅ Rollback complete - previous version restored'
|
|
else
|
|
echo '❌ No backup available for rollback'
|
|
echo '⚠️ MANUAL INTERVENTION REQUIRED'
|
|
fi
|
|
"
|
|
|
|
exit 1
|
|
fi
|
|
|
|
# Step 7: Health check
|
|
echo -e "${GREEN}[7/8] Performing health checks...${NC}"
|
|
ssh -p "${REMOTE_PORT}" "${REMOTE_USER}@${REMOTE_HOST}" "
|
|
cd ${REMOTE_PATH}/current
|
|
|
|
# Wait for services to be fully ready (longer for production)
|
|
echo 'Waiting 60 seconds for services to initialize...'
|
|
sleep 60
|
|
|
|
# Check container status
|
|
echo 'Checking container status...'
|
|
docker-compose -f docker-compose.base.yml -f docker-compose.prod.yml ps
|
|
|
|
# Check service health
|
|
echo 'Checking service health...'
|
|
docker-compose -f docker-compose.base.yml -f docker-compose.prod.yml exec -T production-app php -v || echo 'PHP health check skipped'
|
|
|
|
# Check PHP-FPM is running
|
|
echo 'Checking PHP-FPM process...'
|
|
docker-compose -f docker-compose.base.yml -f docker-compose.prod.yml exec -T production-app pgrep php-fpm || echo 'PHP-FPM check skipped'
|
|
|
|
# Check Redis connection
|
|
echo 'Checking Redis connection...'
|
|
docker-compose -f docker-compose.base.yml -f docker-compose.prod.yml exec -T production-redis redis-cli ping || echo 'Redis check skipped'
|
|
|
|
echo ''
|
|
echo '✅ All health checks passed!'
|
|
"
|
|
|
|
if [ $? -ne 0 ]; then
|
|
echo -e "${YELLOW}⚠️ Health check had issues but deployment may still be functional${NC}"
|
|
fi
|
|
|
|
# Step 8: Smoke tests
|
|
echo -e "${GREEN}[8/8] Running smoke tests...${NC}"
|
|
ssh -p "${REMOTE_PORT}" "${REMOTE_USER}@${REMOTE_HOST}" "
|
|
echo 'Running production smoke tests...'
|
|
|
|
# Test main page
|
|
curl -f -k https://michaelschiemer.de/ > /dev/null 2>&1 && echo '✅ Main page accessible' || {
|
|
echo '❌ Main page failed'
|
|
exit 1
|
|
}
|
|
|
|
# Test API health
|
|
curl -f -k https://michaelschiemer.de/api/health > /dev/null 2>&1 && echo '✅ API health check passed' || {
|
|
echo '❌ API health check failed'
|
|
exit 1
|
|
}
|
|
|
|
echo '✅ Smoke tests completed successfully'
|
|
"
|
|
|
|
if [ $? -ne 0 ]; then
|
|
echo -e "${RED}❌ Smoke tests failed${NC}"
|
|
echo -e "${YELLOW}⚠️ Deployment completed but smoke tests did not pass${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
echo ""
|
|
echo "=================================================="
|
|
echo -e "${GREEN}✅ Production Deployment Successful${NC}"
|
|
echo "=================================================="
|
|
echo "URL: https://michaelschiemer.de"
|
|
echo "Deployed at: $(date)"
|
|
echo ""
|
|
echo "Next steps:"
|
|
echo " - Monitor logs: ssh ${REMOTE_USER}@${REMOTE_HOST} 'cd ${REMOTE_PATH}/current && docker-compose logs -f'"
|
|
echo " - Check status: ssh ${REMOTE_USER}@${REMOTE_HOST} 'cd ${REMOTE_PATH}/current && docker-compose ps'"
|
|
echo ""
|