name: Build & Deploy KLZ Cables on: push: branches: [main] jobs: build-and-deploy: runs-on: docker steps: # ═══════════════════════════════════════════════════════════════════════════════ # LOGGING: Workflow Start - Full Transparency # ═══════════════════════════════════════════════════════════════════════════════ - name: 📋 Log Workflow Start run: | echo "╔══════════════════════════════════════════════════════════════════════════════╗" echo "║ KLZ Cables Deployment Workflow Started ║" echo "╚══════════════════════════════════════════════════════════════════════════════╝" echo "" echo "📋 Workflow Information:" echo " • Repository: ${{ github.repository }}" echo " • Branch: ${{ github.ref }}" echo " • Commit: ${{ github.sha }}" echo " • Actor: ${{ github.actor }}" echo " • Run ID: ${{ github.run_id }}" echo " • Timestamp: $(date -u +'%Y-%m-%d %H:%M:%S UTC')" echo "" echo "🔍 Environment Details:" echo " • Runner OS: ${{ runner.os }}" echo " • Workspace: ${{ github.workspace }}" echo "" - name: Checkout repository uses: actions/checkout@v4 # ═══════════════════════════════════════════════════════════════════════════════ # LOGGING: Registry Login Phase # ═══════════════════════════════════════════════════════════════════════════════ - name: 🔐 Login to private registry run: | echo "╔══════════════════════════════════════════════════════════════════════════════╗" echo "║ Step: Registry Login ║" echo "╚══════════════════════════════════════════════════════════════════════════════╝" echo "" echo "🔐 Authenticating with private registry..." echo " Registry: registry.infra.mintel.me" echo " User: ${{ secrets.REGISTRY_USER != '' && '***' || 'NOT SET' }}" echo "" # Execute login with error handling if echo "${{ secrets.REGISTRY_PASS }}" | docker login registry.infra.mintel.me -u "${{ secrets.REGISTRY_USER }}" --password-stdin 2>&1; then echo "✅ Registry login successful" else echo "❌ Registry login failed" exit 1 fi echo "" # ═══════════════════════════════════════════════════════════════════════════════ # LOGGING: Build Phase # ═══════════════════════════════════════════════════════════════════════════════ - name: 🏗️ Build Docker image run: | echo "╔══════════════════════════════════════════════════════════════════════════════╗" echo "║ Step: Build Docker Image ║" echo "╚══════════════════════════════════════════════════════════════════════════════╝" echo "" echo "🏗️ Building Docker image with buildx..." echo " Platform: linux/arm64" echo " Target: registry.infra.mintel.me/mintel/klz-cables.com:latest" echo "" echo "📦 Build Arguments (NEXT_PUBLIC_* only - baked into client bundle):" echo " • NEXT_PUBLIC_BASE_URL: ${{ secrets.NEXT_PUBLIC_BASE_URL != '' && '***' || 'NOT SET' }}" echo " • NEXT_PUBLIC_UMAMI_WEBSITE_ID: ${{ secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID != '' && '***' || 'NOT SET' }}" echo " • NEXT_PUBLIC_UMAMI_SCRIPT_URL: ${{ secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL != '' && '***' || 'NOT SET' }}" echo "" echo "⏱️ Build started at: $(date -u +'%Y-%m-%d %H:%M:%S UTC')" echo "" # Execute build with detailed logging set -e docker buildx build \ --pull \ --platform linux/arm64 \ --build-arg NEXT_PUBLIC_BASE_URL="${{ secrets.NEXT_PUBLIC_BASE_URL }}" \ --build-arg NEXT_PUBLIC_UMAMI_WEBSITE_ID="${{ secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID }}" \ --build-arg NEXT_PUBLIC_UMAMI_SCRIPT_URL="${{ secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL }}" \ -t registry.infra.mintel.me/mintel/klz-cables.com:latest \ --push . BUILD_EXIT_CODE=$? if [ $BUILD_EXIT_CODE -eq 0 ]; then echo "" echo "✅ Build completed successfully at: $(date -u +'%Y-%m-%d %H:%M:%S UTC')" echo "" echo "📊 Image Details:" IMAGE_SIZE=$(docker inspect registry.infra.mintel.me/mintel/klz-cables.com:latest --format='{{.Size}}') IMAGE_SIZE_MB=$((IMAGE_SIZE / 1024 / 1024)) echo " • Size: ${IMAGE_SIZE_MB}MB" docker inspect registry.infra.mintel.me/mintel/klz-cables.com:latest --format=' • Created: {{.Created}}' docker inspect registry.infra.mintel.me/mintel/klz-cables.com:latest --format=' • Architecture: {{.Architecture}}' else echo "" echo "❌ Build failed with exit code: $BUILD_EXIT_CODE" exit $BUILD_EXIT_CODE fi echo "" # ═══════════════════════════════════════════════════════════════════════════════ # LOGGING: Deployment Phase # ═══════════════════════════════════════════════════════════════════════════════ - name: 🚀 Deploy to production server run: | echo "╔══════════════════════════════════════════════════════════════════════════════╗" echo "║ Step: Deploy to Production Server ║" echo "╚══════════════════════════════════════════════════════════════════════════════╝" echo "" echo "🚀 Starting deployment process..." echo " Target Server: alpha.mintel.me" echo " Deploy User: deploy (via sudo from root)" echo " Target Path: /home/deploy/sites/klz-cables.com" echo "" # Setup SSH with logging echo "🔐 Setting up SSH connection..." mkdir -p ~/.ssh echo "${{ secrets.ALPHA_SSH_KEY }}" > ~/.ssh/id_ed25519 chmod 600 ~/.ssh/id_ed25519 echo "🔑 Adding host to known_hosts..." ssh-keyscan -H alpha.mintel.me >> ~/.ssh/known_hosts 2>/dev/null if [ $? -eq 0 ]; then echo "✅ Host key added successfully" else echo "⚠️ Warning: Could not add host key" fi echo "" # Create .env file content echo "📝 Preparing environment configuration..." cat > /tmp/klz-cables.env << EOF # ============================================================================ # KLZ Cables - Production Environment Configuration # ============================================================================ # Auto-generated by CI/CD workflow # DO NOT EDIT MANUALLY - Changes will be overwritten on next deployment # ============================================================================ # Application NODE_ENV=production NEXT_PUBLIC_BASE_URL=${{ secrets.NEXT_PUBLIC_BASE_URL }} # Analytics (Umami) NEXT_PUBLIC_UMAMI_WEBSITE_ID=${{ secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID }} NEXT_PUBLIC_UMAMI_SCRIPT_URL=${{ secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL }} # Error Tracking (GlitchTip/Sentry) SENTRY_DSN=${{ secrets.SENTRY_DSN }} # Email Configuration (Mailgun) MAIL_HOST=${{ secrets.MAIL_HOST }} MAIL_PORT=${{ secrets.MAIL_PORT }} MAIL_USERNAME=${{ secrets.MAIL_USERNAME }} MAIL_PASSWORD=${{ secrets.MAIL_PASSWORD }} MAIL_FROM=${{ secrets.MAIL_FROM }} MAIL_RECIPIENTS=${{ secrets.MAIL_RECIPIENTS }} EOF echo "✅ Environment file prepared" echo "" # Execute deployment commands with detailed logging echo "📡 Connecting to server and executing deployment..." echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" # Copy .env file to server echo "📤 Uploading environment configuration..." scp -o StrictHostKeyChecking=accept-new \ -o ServerAliveInterval=30 \ -o ServerAliveCountMax=3 \ -o ConnectTimeout=10 \ /tmp/klz-cables.env \ root@alpha.mintel.me:/home/deploy/sites/klz-cables.com/.env if [ $? -eq 0 ]; then echo "✅ Environment file uploaded successfully" else echo "❌ Failed to upload environment file" exit 1 fi echo "" # SSH to server and run deployment echo "🚀 Executing deployment on server..." ssh -o StrictHostKeyChecking=accept-new \ -o ServerAliveInterval=30 \ -o ServerAliveCountMax=3 \ -o ConnectTimeout=10 \ root@alpha.mintel.me bash << EOF set -e PROJECT_DIR="/home/deploy/sites/klz-cables.com" cd "\$PROJECT_DIR" echo "🔒 Securing environment file..." chmod 600 .env chown deploy:deploy .env echo "🔐 Logging into Docker registry..." echo "${{ secrets.REGISTRY_PASS }}" | docker login registry.infra.mintel.me -u "${{ secrets.REGISTRY_USER }}" --password-stdin echo "🔄 Pulling latest image..." docker pull registry.infra.mintel.me/mintel/klz-cables.com:latest echo "🔄 Stopping existing containers..." docker-compose down echo "🚀 Starting new containers..." docker-compose up -d echo "⏳ Waiting for services to be healthy..." MAX_RETRIES=12 RETRY_COUNT=0 until curl -s -f http://localhost:3000/health > /dev/null || [ \$RETRY_COUNT -eq \$MAX_RETRIES ]; do echo " • Waiting for health check... (\$((RETRY_COUNT + 1))/\$MAX_RETRIES)" sleep 5 RETRY_COUNT=\$((RETRY_COUNT + 1)) done if [ \$RETRY_COUNT -eq \$MAX_RETRIES ]; then echo "❌ Health check failed after \$MAX_RETRIES retries" echo "🔍 Container logs:" docker-compose logs --tail=50 exit 1 fi echo "✅ Health check passed!" echo "🔍 Checking service status..." docker-compose ps echo "" echo "✅ Deployment complete!" EOF DEPLOY_EXIT_CODE=$? echo "" # Clean up temporary env file rm -f /tmp/klz-cables.env if [ $DEPLOY_EXIT_CODE -eq 0 ]; then echo "✅ Deployment completed successfully at: $(date -u +'%Y-%m-%d %H:%M:%S UTC')" else echo "❌ Deployment failed with exit code: $DEPLOY_EXIT_CODE" echo "" echo "🔍 Troubleshooting Tips:" echo " • Check server connectivity: ping alpha.mintel.me" echo " • Verify SSH key permissions on server" echo " • Check disk space on target server" echo " • Review docker compose configuration" echo " • Verify all required secrets are set in Gitea" exit $DEPLOY_EXIT_CODE fi echo "" # ═══════════════════════════════════════════════════════════════════════════════ # LOGGING: Workflow Summary # ═══════════════════════════════════════════════════════════════════════════════ - name: 📊 Workflow Summary if: always() run: | echo "╔══════════════════════════════════════════════════════════════════════════════╗" echo "║ Workflow Summary ║" echo "╚══════════════════════════════════════════════════════════════════════════════╝" echo "" echo "📊 Final Status:" echo " • Workflow: ${{ job.status }}" echo " • Completed: $(date -u +'%Y-%m-%d %H:%M:%S UTC')" echo "" echo "🎯 Deployment Target:" echo " • Image: registry.infra.mintel.me/mintel/klz-cables.com:latest" echo " • Server: alpha.mintel.me" echo " • Service: klz-cables.com" echo "" echo "🔐 Security Notes:" echo " • All secrets are masked (*** ) in logs" echo " • SSH keys are created with 600 permissions" echo " • Passwords are never displayed in plain text" echo " • .env file is auto-generated from Gitea secrets" echo " • .env file has 600 permissions on server" echo "" echo "╔══════════════════════════════════════════════════════════════════════════════╗" if [ "${{ job.status }}" == "success" ]; then echo "║ ✅ DEPLOYMENT SUCCESSFUL ║" else echo "║ ❌ DEPLOYMENT FAILED ║" fi echo "╚══════════════════════════════════════════════════════════════════════════════╝" # ═══════════════════════════════════════════════════════════════════════════════ # NOTIFICATION: Gotify # ═══════════════════════════════════════════════════════════════════════════════ - name: 🔔 Gotify Notification (Success) if: success() run: | echo "Sending success notification to Gotify..." RESPONSE=$(curl -k -s -w "\n%{http_code}" -X POST "${{ secrets.GOTIFY_URL }}/message?token=${{ secrets.GOTIFY_TOKEN }}" \ -F "title=✅ Deployment Success: ${{ github.repository }}" \ -F "message=The deployment of ${{ github.repository }} (branch: ${{ github.ref }}) was successful. Commit: ${{ github.sha }} Actor: ${{ github.actor }} Run ID: ${{ github.run_id }}" \ -F "priority=5") HTTP_CODE=$(echo "$RESPONSE" | tail -n1) BODY=$(echo "$RESPONSE" | sed '$d') echo "HTTP Status: $HTTP_CODE" echo "Response Body: $BODY" if [ "$HTTP_CODE" -lt 200 ] || [ "$HTTP_CODE" -ge 300 ]; then echo "Failed to send Gotify notification" exit 0 # Don't fail the workflow because of notification failure fi - name: 🔔 Gotify Notification (Failure) if: failure() run: | echo "Sending failure notification to Gotify..." RESPONSE=$(curl -k -s -w "\n%{http_code}" -X POST "${{ secrets.GOTIFY_URL }}/message?token=${{ secrets.GOTIFY_TOKEN }}" \ -F "title=❌ Deployment Failed: ${{ github.repository }}" \ -F "message=The deployment of ${{ github.repository }} (branch: ${{ github.ref }}) failed! Commit: ${{ github.sha }} Actor: ${{ github.actor }} Run ID: ${{ github.run_id }} Please check the logs for details." \ -F "priority=8") HTTP_CODE=$(echo "$RESPONSE" | tail -n1) BODY=$(echo "$RESPONSE" | sed '$d') echo "HTTP Status: $HTTP_CODE" echo "Response Body: $BODY" if [ "$HTTP_CODE" -lt 200 ] || [ "$HTTP_CODE" -ge 300 ]; then echo "Failed to send Gotify notification" exit 0 # Don't fail the workflow because of notification failure fi