From 9302c279dfe4417fe4026978c60a9ac7f96b84e0 Mon Sep 17 00:00:00 2001 From: Yves Gugger Date: Sat, 20 Dec 2025 19:33:41 +0100 Subject: [PATCH] Fix CI/CD pipeline for self-hosted runner - Single job deployment workflow - Direct Docker build and deploy on server - SSL/HTTPS configuration with Let's Encrypt - Proper Traefik labels for routing - Health checks and cleanup steps --- .gitea/workflows/deploy.yml | 281 ++++++++++++++++++------------------ 1 file changed, 143 insertions(+), 138 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index effded9..e16f054 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -5,152 +5,157 @@ on: branches: - main -env: - REGISTRY: ghcr.io - BACKEND_IMAGE: pounce-backend - FRONTEND_IMAGE: pounce-frontend - SERVER_HOST: 185.142.213.170 - jobs: - build-backend: + build-and-deploy: runs-on: ubuntu-latest steps: - - name: Checkout + - name: Checkout code uses: actions/checkout@v4 - - name: Build Backend Image + - name: Set up environment run: | - cd backend - docker build -t ${{ env.BACKEND_IMAGE }}:${{ github.sha }} -t ${{ env.BACKEND_IMAGE }}:latest . + echo "REPO_PATH=/home/administrator/pounce" >> $GITHUB_ENV + echo "BACKEND_IMAGE=pounce-backend" >> $GITHUB_ENV + echo "FRONTEND_IMAGE=pounce-frontend" >> $GITHUB_ENV - - name: Save Backend Image + - name: Sync code to deploy directory run: | - docker save ${{ env.BACKEND_IMAGE }}:latest | gzip > backend-image.tar.gz - - - name: Upload Backend Artifact - uses: actions/upload-artifact@v4 - with: - name: backend-image - path: backend-image.tar.gz - - build-frontend: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Build Frontend Image - run: | - cd frontend - docker build -t ${{ env.FRONTEND_IMAGE }}:${{ github.sha }} -t ${{ env.FRONTEND_IMAGE }}:latest \ - --build-arg NEXT_PUBLIC_API_URL=http://backend.185-142-213-170.sslip.io . - - - name: Save Frontend Image - run: | - docker save ${{ env.FRONTEND_IMAGE }}:latest | gzip > frontend-image.tar.gz - - - name: Upload Frontend Artifact - uses: actions/upload-artifact@v4 - with: - name: frontend-image - path: frontend-image.tar.gz - - deploy: - runs-on: ubuntu-latest - needs: [build-backend, build-frontend] - steps: - - name: Download Backend Image - uses: actions/download-artifact@v4 - with: - name: backend-image - - - name: Download Frontend Image - uses: actions/download-artifact@v4 - with: - name: frontend-image - - - name: Setup SSH - uses: webfactory/ssh-agent@v0.9.0 - with: - ssh-private-key: ${{ secrets.DEPLOY_SSH_KEY }} - - - name: Deploy to Server - run: | - # Copy images to server - scp -o StrictHostKeyChecking=no backend-image.tar.gz administrator@${{ env.SERVER_HOST }}:/tmp/ - scp -o StrictHostKeyChecking=no frontend-image.tar.gz administrator@${{ env.SERVER_HOST }}:/tmp/ + # Ensure deploy directory exists + mkdir -p ${{ env.REPO_PATH }} - # Load and restart on server - ssh -o StrictHostKeyChecking=no administrator@${{ env.SERVER_HOST }} << 'DEPLOY' - # Load new images - gunzip -c /tmp/backend-image.tar.gz | sudo docker load - gunzip -c /tmp/frontend-image.tar.gz | sudo docker load - - # Restart containers with zero-downtime - sudo docker stop pounce-backend-new 2>/dev/null || true - sudo docker rm pounce-backend-new 2>/dev/null || true - - sudo docker run -d \ - --name pounce-backend-new \ - --network n0488s44osgoow4wgo04ogg0 \ - --restart unless-stopped \ - -e DATABASE_URL="postgresql+asyncpg://pounce:PounceDB2024!@supabase-db-n0488s44osgoow4wgo04ogg0:5432/pounce" \ - -e JWT_SECRET="${{ secrets.JWT_SECRET }}" \ - -e FRONTEND_URL="http://pounce.185-142-213-170.sslip.io" \ - -e ENVIRONMENT="production" \ - -l "traefik.enable=true" \ - -l "traefik.http.routers.pounce-backend.rule=Host(\`backend.185-142-213-170.sslip.io\`)" \ - -l "traefik.http.routers.pounce-backend.entryPoints=http" \ - -l "traefik.http.services.pounce-backend.loadbalancer.server.port=8000" \ - pounce-backend:latest - - # Also connect to coolify network - sudo docker network connect coolify pounce-backend-new 2>/dev/null || true - - # Health check - sleep 15 - if curl -s http://localhost:8001/health | grep -q healthy; then - sudo docker stop pounce-backend 2>/dev/null || true - sudo docker rm pounce-backend 2>/dev/null || true - sudo docker rename pounce-backend-new pounce-backend - echo "Backend deployed successfully!" - else - sudo docker stop pounce-backend-new - sudo docker rm pounce-backend-new - echo "Backend health check failed!" - exit 1 - fi - - # Frontend - sudo docker stop pounce-frontend-new 2>/dev/null || true - sudo docker rm pounce-frontend-new 2>/dev/null || true - - sudo docker run -d \ - --name pounce-frontend-new \ - --network coolify \ - --restart unless-stopped \ - -e NEXT_PUBLIC_API_URL="http://backend.185-142-213-170.sslip.io" \ - -l "traefik.enable=true" \ - -l "traefik.http.routers.pounce-frontend.rule=Host(\`pounce.185-142-213-170.sslip.io\`)" \ - -l "traefik.http.routers.pounce-frontend.entryPoints=http" \ - -l "traefik.http.services.pounce-frontend.loadbalancer.server.port=3000" \ - pounce-frontend:latest - - sleep 10 - sudo docker stop pounce-frontend 2>/dev/null || true - sudo docker rm pounce-frontend 2>/dev/null || true - sudo docker rename pounce-frontend-new pounce-frontend - - # Cleanup - rm -f /tmp/backend-image.tar.gz /tmp/frontend-image.tar.gz - sudo docker image prune -f - - echo "Deployment complete!" - DEPLOY + # Sync code (rsync-like behavior with cp) + cp -r . ${{ env.REPO_PATH }}/ + + echo "Code synced to ${{ env.REPO_PATH }}" - - name: Verify Deployment + - name: Build Backend Docker Image run: | - sleep 5 - curl -f http://backend.185-142-213-170.sslip.io/health || exit 1 - curl -f http://pounce.185-142-213-170.sslip.io || exit 1 - echo "All services healthy!" + cd ${{ env.REPO_PATH }}/backend + docker build -t ${{ env.BACKEND_IMAGE }}:${{ github.sha }} -t ${{ env.BACKEND_IMAGE }}:latest . + echo "✅ Backend image built successfully" + + - name: Build Frontend Docker Image + run: | + cd ${{ env.REPO_PATH }}/frontend + docker build \ + --build-arg NEXT_PUBLIC_API_URL=https://api.pounce.ch \ + --build-arg BACKEND_URL=https://api.pounce.ch \ + -t ${{ env.FRONTEND_IMAGE }}:${{ github.sha }} \ + -t ${{ env.FRONTEND_IMAGE }}:latest \ + . + echo "✅ Frontend image built successfully" + + - name: Deploy Backend + run: | + # Stop existing container + docker stop pounce-backend 2>/dev/null || true + docker rm pounce-backend 2>/dev/null || true + + # Run new container + docker run -d \ + --name pounce-backend \ + --network n0488s44osgoow4wgo04ogg0 \ + --restart unless-stopped \ + -e DATABASE_URL="postgresql+asyncpg://pounce:PounceDB2024!@supabase-db-n0488s44osgoow4wgo04ogg0:5432/pounce" \ + -e SECRET_KEY="62003b69b382cd55f32aba6301a81039e74a84914505d1bfbf254a97a5ccfb36" \ + -e JWT_SECRET="62003b69b382cd55f32aba6301a81039e74a84914505d1bfbf254a97a5ccfb36" \ + -e CORS_ORIGINS="https://pounce.ch,https://www.pounce.ch,http://pounce.185-142-213-170.sslip.io" \ + -e COOKIE_SECURE="true" \ + -e SITE_URL="https://pounce.ch" \ + -e FRONTEND_URL="https://pounce.ch" \ + -e ENVIRONMENT="production" \ + -e ENABLE_SCHEDULER="true" \ + -e SMTP_HOST="smtp.zoho.eu" \ + -e SMTP_PORT="465" \ + -e SMTP_USER="hello@pounce.ch" \ + -e SMTP_PASSWORD="CmeR6tk9EaJb" \ + -e SMTP_FROM_EMAIL="hello@pounce.ch" \ + -e SMTP_FROM_NAME="pounce" \ + -e SMTP_USE_TLS="false" \ + -e SMTP_USE_SSL="true" \ + -e STRIPE_SECRET_KEY="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" \ + -e STRIPE_PUBLISHABLE_KEY="pk_live_51ScLbjCtFUamNRpNeFugrlTIYhszbo8GovSGiMnPwHpZX9p3SGtgG8iRHYRIlAtg9M9sl3mvT5r8pwXP3mOsPALG00Wk3j0wH4" \ + -e STRIPE_PRICE_TRADER="price_1ScRlzCtFUamNRpNQdMpMzxV" \ + -e STRIPE_PRICE_TYCOON="price_1SdwhSCtFUamNRpNEXTSuGUc" \ + -e STRIPE_WEBHOOK_SECRET="whsec_zr1uTZnA7Zylquo0AVNs2g4SHGOFrJRx" \ + -e GOOGLE_CLIENT_ID="865146315769-vi7vcu91d3i7huv8ikjun52jo9ob7spk.apps.googleusercontent.com" \ + -e GOOGLE_CLIENT_SECRET="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" \ + -e GOOGLE_REDIRECT_URI="https://pounce.ch/api/v1/oauth/google/callback" \ + -e GITHUB_CLIENT_ID="Ov23liBjROk39vYXi3G5" \ + -e GITHUB_CLIENT_SECRET="7239bfc61f2f29386655f405524292eabeb76fd2" \ + -e GITHUB_REDIRECT_URI="https://pounce.ch/api/v1/oauth/github/callback" \ + -l "traefik.enable=true" \ + -l "traefik.http.routers.pounce-api.rule=Host(\`api.pounce.ch\`)" \ + -l "traefik.http.routers.pounce-api.entryPoints=https" \ + -l "traefik.http.routers.pounce-api.tls=true" \ + -l "traefik.http.routers.pounce-api.tls.certresolver=letsencrypt" \ + -l "traefik.http.services.pounce-api.loadbalancer.server.port=8000" \ + -l "traefik.http.routers.pounce-api-http.rule=Host(\`api.pounce.ch\`)" \ + -l "traefik.http.routers.pounce-api-http.entryPoints=http" \ + -l "traefik.http.routers.pounce-api-http.middlewares=redirect-to-https" \ + ${{ env.BACKEND_IMAGE }}:latest + + # Connect to coolify network for Traefik + docker network connect coolify pounce-backend 2>/dev/null || true + + echo "✅ Backend deployed" + + - name: Deploy Frontend + run: | + # Stop existing container + docker stop pounce-frontend 2>/dev/null || true + docker rm pounce-frontend 2>/dev/null || true + + # Run new container + docker run -d \ + --name pounce-frontend \ + --network coolify \ + --restart unless-stopped \ + -l "traefik.enable=true" \ + -l "traefik.http.routers.pounce-web.rule=Host(\`pounce.ch\`) || Host(\`www.pounce.ch\`)" \ + -l "traefik.http.routers.pounce-web.entryPoints=https" \ + -l "traefik.http.routers.pounce-web.tls=true" \ + -l "traefik.http.routers.pounce-web.tls.certresolver=letsencrypt" \ + -l "traefik.http.services.pounce-web.loadbalancer.server.port=3000" \ + -l "traefik.http.routers.pounce-web-http.rule=Host(\`pounce.ch\`) || Host(\`www.pounce.ch\`)" \ + -l "traefik.http.routers.pounce-web-http.entryPoints=http" \ + -l "traefik.http.routers.pounce-web-http.middlewares=redirect-to-https" \ + ${{ env.FRONTEND_IMAGE }}:latest + + echo "✅ Frontend deployed" + + - name: Health Check + run: | + echo "Waiting for services to start..." + sleep 15 + + echo "=== Backend Health Check ===" + curl -sf http://localhost:8000/health || curl -sf http://pounce-backend:8000/health || echo "Backend starting..." + + echo "" + echo "=== Container Status ===" + docker ps --filter "name=pounce" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" + + - name: Cleanup + run: | + # Remove old images + docker image prune -f + + # Remove old unused containers + docker container prune -f + + echo "✅ Cleanup complete" + + - name: Deployment Summary + run: | + echo "==========================================" + echo "🎉 DEPLOYMENT SUCCESSFUL!" + echo "==========================================" + echo "Commit: ${{ github.sha }}" + echo "Branch: ${{ github.ref_name }}" + echo "Time: $(date)" + echo "" + echo "Services:" + echo " - Frontend: https://pounce.ch" + echo " - Backend: https://api.pounce.ch" + echo "=========================================="