From 86e0057adca1bd8d4407441a2aca57b83edff715 Mon Sep 17 00:00:00 2001 From: Yves Gugger Date: Sun, 21 Dec 2025 15:07:58 +0100 Subject: [PATCH] refactor: SSH-based deployment pipeline Changed from Docker-in-Docker to SSH-based deployment: - Uses rsync to sync code to server - Builds Docker images on host directly - More reliable for Coolify environments - Proper secret management via SSH --- .gitea/workflows/deploy.yml | 135 ++++++++++++++++++++---------------- 1 file changed, 77 insertions(+), 58 deletions(-) diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 967a7d9..44de9d6 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -5,38 +5,41 @@ on: branches: - main -env: - BACKEND_IMAGE: pounce-backend - FRONTEND_IMAGE: pounce-frontend - # Networks - SUPABASE_NETWORK: n0488s44osgoow4wgo04ogg0 - COOLIFY_NETWORK: coolify - jobs: - build-and-deploy: + deploy: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - - name: Build Backend Docker Image + - name: Setup SSH run: | - cd backend - docker build -t $BACKEND_IMAGE:${{ github.sha }} -t $BACKEND_IMAGE:latest . - echo "✅ Backend image built" + mkdir -p ~/.ssh + echo "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/deploy_key + chmod 600 ~/.ssh/deploy_key + ssh-keyscan -H 185.142.213.170 >> ~/.ssh/known_hosts 2>/dev/null - - name: Build Frontend Docker Image + - name: Sync Backend Code run: | - cd frontend - docker build \ - --build-arg NEXT_PUBLIC_API_URL=https://api.pounce.ch \ - --build-arg BACKEND_URL=http://pounce-backend:8000 \ - -t $FRONTEND_IMAGE:${{ github.sha }} \ - -t $FRONTEND_IMAGE:latest \ - . - echo "✅ Frontend image built" + rsync -avz --delete \ + -e "ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no" \ + --exclude '__pycache__' \ + --exclude '*.pyc' \ + --exclude '.git' \ + backend/ \ + administrator@185.142.213.170:/tmp/pounce-backend/ - - name: Deploy Backend + - name: Sync Frontend Code + run: | + rsync -avz --delete \ + -e "ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no" \ + --exclude 'node_modules' \ + --exclude '.next' \ + --exclude '.git' \ + frontend/ \ + administrator@185.142.213.170:/tmp/pounce-frontend/ + + - name: Build and Deploy env: DATABASE_URL: ${{ secrets.DATABASE_URL }} SECRET_KEY: ${{ secrets.SECRET_KEY }} @@ -48,18 +51,46 @@ jobs: CZDS_USERNAME: ${{ secrets.CZDS_USERNAME }} CZDS_PASSWORD: ${{ secrets.CZDS_PASSWORD }} run: | - # Stop existing container - docker stop pounce-backend 2>/dev/null || true - docker rm pounce-backend 2>/dev/null || true + ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no administrator@185.142.213.170 << 'DEPLOY_SCRIPT' - # Ensure directories exist - sudo mkdir -p /data/pounce/zones/czds /data/pounce/zones/switch /data/pounce/logs + # Build backend + cd /tmp/pounce-backend + sudo docker build -t pounce-backend:latest . || exit 1 + + # Build frontend + cd /tmp/pounce-frontend + sudo docker build \ + --build-arg NEXT_PUBLIC_API_URL=https://api.pounce.ch \ + --build-arg BACKEND_URL=http://pounce-backend:8000 \ + -t pounce-frontend:latest . || exit 1 + + echo "✅ Images built successfully" + DEPLOY_SCRIPT + + - name: Deploy Containers + env: + DATABASE_URL: ${{ secrets.DATABASE_URL }} + SECRET_KEY: ${{ secrets.SECRET_KEY }} + SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }} + STRIPE_SECRET_KEY: ${{ secrets.STRIPE_SECRET_KEY }} + STRIPE_WEBHOOK_SECRET: ${{ secrets.STRIPE_WEBHOOK_SECRET }} + GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }} + GH_OAUTH_SECRET: ${{ secrets.GH_OAUTH_SECRET }} + CZDS_USERNAME: ${{ secrets.CZDS_USERNAME }} + CZDS_PASSWORD: ${{ secrets.CZDS_PASSWORD }} + run: | + ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no administrator@185.142.213.170 << DEPLOY_CONTAINERS + + # Deploy Backend + sudo docker stop pounce-backend 2>/dev/null || true + sudo docker rm pounce-backend 2>/dev/null || true + + sudo mkdir -p /data/pounce/zones sudo chmod -R 777 /data/pounce/zones - # Deploy backend - docker run -d \ + sudo docker run -d \ --name pounce-backend \ - --network $SUPABASE_NETWORK \ + --network n0488s44osgoow4wgo04ogg0 \ --restart unless-stopped \ --shm-size=8g \ -v /data/pounce/zones:/data \ @@ -72,7 +103,6 @@ jobs: -e CORS_ORIGINS="https://pounce.ch,https://www.pounce.ch" \ -e COOKIE_SECURE="true" \ -e SITE_URL="https://pounce.ch" \ - -e FRONTEND_URL="https://pounce.ch" \ -e CZDS_DATA_DIR="/data/czds" \ -e CZDS_USERNAME="${CZDS_USERNAME}" \ -e CZDS_PASSWORD="${CZDS_PASSWORD}" \ @@ -81,7 +111,6 @@ jobs: -e SMTP_USER="hello@pounce.ch" \ -e SMTP_PASSWORD="${SMTP_PASSWORD}" \ -e SMTP_FROM_EMAIL="hello@pounce.ch" \ - -e SMTP_FROM_NAME="pounce" \ -e SMTP_USE_SSL="true" \ -e STRIPE_SECRET_KEY="${STRIPE_SECRET_KEY}" \ -e STRIPE_PUBLISHABLE_KEY="pk_live_51ScLbjCtFUamNRpNeFugrlTIYhszbo8GovSGiMnPwHpZX9p3SGtgG8iRHYRIlAtg9M9sl3mvT5r8pwXP3mOsPALG00Wk3j0wH4" \ @@ -95,55 +124,45 @@ jobs: -e GITHUB_CLIENT_SECRET="${GH_OAUTH_SECRET}" \ -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.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" \ - $BACKEND_IMAGE:latest + pounce-backend:latest - # Connect to Coolify network for Traefik - docker network connect $COOLIFY_NETWORK pounce-backend 2>/dev/null || true + sudo docker network connect coolify pounce-backend 2>/dev/null || true - echo "✅ Backend deployed" - - - name: Deploy Frontend - run: | - docker stop pounce-frontend 2>/dev/null || true - docker rm pounce-frontend 2>/dev/null || true + # Deploy Frontend + sudo docker stop pounce-frontend 2>/dev/null || true + sudo docker rm pounce-frontend 2>/dev/null || true - docker run -d \ + sudo docker run -d \ --name pounce-frontend \ - --network $COOLIFY_NETWORK \ + --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.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" \ - $FRONTEND_IMAGE:latest + pounce-frontend:latest - docker network connect $SUPABASE_NETWORK pounce-frontend 2>/dev/null || true + sudo docker network connect n0488s44osgoow4wgo04ogg0 pounce-frontend 2>/dev/null || true - echo "✅ Frontend deployed" + echo "✅ Containers deployed" + DEPLOY_CONTAINERS - name: Health Check run: | - echo "Waiting for services..." sleep 20 - - echo "=== Container Status ===" - docker ps --filter "name=pounce" --format "table {{.Names}}\t{{.Status}}" - - echo "" - echo "=== Backend Health ===" - docker exec pounce-backend curl -sf http://localhost:8000/health || echo "Starting..." + curl -sf https://api.pounce.ch/api/v1/health || echo "Backend starting..." + curl -sf https://pounce.ch || echo "Frontend starting..." - name: Cleanup run: | - docker image prune -f - docker container prune -f + ssh -i ~/.ssh/deploy_key administrator@185.142.213.170 "sudo docker image prune -f; sudo docker container prune -f" - name: Summary run: |