Advertisement
nuclearsmilz

CI-CD-Pipeline

Jul 4th, 2025
220
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
YAML 9.86 KB | None | 0 0
  1. name: Backend CI/CD
  2.  
  3. on:
  4.   push:
  5.     paths:
  6.      - 'backend/**'
  7.       - 'backend/infra/traefik/**'
  8.       - '.github/workflows/ci-cd-pipeline.yml'
  9.     branches: [main]
  10.   pull_request:
  11.     paths:
  12.      - 'backend/**'
  13.       - 'backend/infra/traefik/**'
  14.       - '.github/workflows/ci-cd-pipeline.yml'
  15.  
  16. jobs:
  17.   build-and-push:
  18.     name: Build and Push Docker Image
  19.     runs-on: ubuntu-latest
  20.     outputs:
  21.       image_tag: ${{ steps.get_version.outputs.image_tag }}
  22.     steps:
  23.       - name: Checkout code
  24.         uses: actions/checkout@v3
  25.  
  26.       - name: Set up Docker Buildx
  27.         uses: docker/setup-buildx-action@v3
  28.  
  29.       - name: Log in to DigitalOcean Container Registry
  30.         uses: docker/login-action@v3
  31.         with:
  32.           registry: registry.digitalocean.com
  33.           username: do
  34.           password: ${{ secrets.DOCR_TOKEN }}
  35.  
  36.       - name: Install Poetry
  37.         run: pip install poetry
  38.  
  39.       - name: Get backend version
  40.         id: get_version
  41.         working-directory: ./backend
  42.         run: |
  43.          VERSION=$(poetry version -s)
  44.           echo "VERSION=$VERSION" >> $GITHUB_ENV
  45.           echo "image_tag=$VERSION" >> $GITHUB_OUTPUT
  46.  
  47.       - name: Print VERSION
  48.         run: echo "VERSION is ${{ env.VERSION }}"
  49.         env:
  50.           VERSION: ${{ env.VERSION }}
  51.  
  52.       - name: Build and push Docker image
  53.         id: build_push
  54.         uses: docker/build-push-action@v5
  55.         env:
  56.             VERSION: ${{ env.VERSION }}
  57.         with:
  58.           context: ./backend
  59.           file: ./backend/Dockerfile
  60.           push: true
  61.           tags: |
  62.            registry.digitalocean.com/hackalyst-backend/hackalyst-backend:latest
  63.             registry.digitalocean.com/hackalyst-backend/hackalyst-backend:${{ env.VERSION }}
  64.             registry.digitalocean.com/hackalyst-backend/hackalyst-backend:${{ github.sha }}
  65.  
  66.   deploy-traefik:
  67.     name: Deploy Traefik Configuration
  68.     needs: build-and-push
  69.     runs-on: ubuntu-latest
  70.     if: github.ref == 'refs/heads/main'
  71.     steps:
  72.       - name: Checkout code
  73.         uses: actions/checkout@v3
  74.  
  75.       - name: Copy Traefik files to remote server
  76.         uses: appleboy/scp-action@master
  77.         with:
  78.           host: ${{ secrets.DROPLET_IP }}
  79.           username: ${{ secrets.DROPLET_USER }}
  80.           key: ${{ secrets.DROPLET_SSH_KEY }}
  81.           source: "backend/infra/traefik/*,backend/infra/traefik/dynamic/*"
  82.           target: "/opt/hackalyst/"
  83.           strip_components: 2
  84.  
  85.       - name: Deploy Traefik Configuration
  86.         uses: appleboy/[email protected]
  87.         with:
  88.           host: ${{ secrets.DROPLET_IP }}
  89.           username: ${{ secrets.DROPLET_USER }}
  90.           key: ${{ secrets.DROPLET_SSH_KEY }}
  91.           script_stop: true
  92.           script: |
  93.            # Create traefik directory if it doesn't exist
  94.             mkdir -p /opt/hackalyst/traefik
  95.             mkdir -p /opt/hackalyst/traefik/dynamic
  96.            
  97.             # Navigate to Traefik directory
  98.             cd /opt/hackalyst/traefik
  99.            
  100.             # Fix acme.json permissions
  101.             touch acme/acme.json
  102.             chmod 600 acme/acme.json
  103.            
  104.             # Remove the version line from docker-compose.yml
  105.             grep -v "^version:" docker-compose.yml > docker-compose.tmp && mv docker-compose.tmp docker-compose.yml
  106.            
  107.             # Update network configuration to use external network
  108.             sed -i 's/external: false/external: true/' docker-compose.yml
  109.            
  110.             # Fix network issue by handling active containers before recreating the network
  111.             # First stop traefik container if it exists
  112.             docker stop traefik || true
  113.             docker rm traefik || true
  114.            
  115.             # Handle active endpoints on the proxy network
  116.             if docker network inspect proxy 2>/dev/null; then
  117.               # Get list of containers connected to the proxy network
  118.               CONNECTED_CONTAINERS=$(docker network inspect proxy -f '{{range .Containers}}{{.Name}} {{end}}')
  119.              
  120.               if [ -n "$CONNECTED_CONTAINERS" ]; then
  121.                 echo "Disconnecting containers from proxy network: $CONNECTED_CONTAINERS"
  122.                 for CONTAINER in $CONNECTED_CONTAINERS; do
  123.                   echo "Disconnecting $CONTAINER from proxy network"
  124.                   docker network disconnect -f proxy "$CONTAINER" || true
  125.                 done
  126.               fi
  127.              
  128.               # Now it's safe to remove the network
  129.               docker network rm proxy || true
  130.             fi
  131.            
  132.             # Create the network with proper subnet and labels
  133.             docker network create --subnet=137.184.161.0/24 --gateway=137.184.161.1 --label="com.docker.compose.network=proxy" proxy
  134.            
  135.             # Pull latest changes and restart Traefik
  136.             # Ensure Traefik dashboard port is available
  137.             if netstat -tuln | grep -q ':8081'; then
  138.               echo "Warning: Port 8081 is already in use. Checking what's using it..."
  139.               fuser -n tcp 8081 || true
  140.               lsof -i:8081 || true
  141.               echo "Attempting to stop any process using port 8081..."
  142.               fuser -k 8081/tcp || true
  143.             fi
  144.            
  145.             # Pass the Traefik dashboard credentials from GitHub secrets
  146.             export TRAEFIK_DASHBOARD_CRED="${{ secrets.TRAEFIK_DASHBOARD_CRED }}"
  147.            
  148.             docker compose -f docker-compose.yml pull
  149.             docker compose -f docker-compose.yml up -d
  150.  
  151.   deploy-production:
  152.     name: Deploy to Production Droplet
  153.     needs: deploy-traefik
  154.     runs-on: ubuntu-latest
  155.     if: github.ref == 'refs/heads/main'
  156.     steps:
  157.       - name: Checkout code
  158.         uses: actions/checkout@v3
  159.  
  160.       - name: Install jq
  161.         run: sudo apt-get update && sudo apt-get install -y jq
  162.  
  163.       - name: Install Infisical CLI
  164.         run: |
  165.          curl -1sLf 'https://artifacts-cli.infisical.com/setup.deb.sh' | sudo -E bash
  166.           sudo apt-get update && sudo apt-get install -y infisical
  167.  
  168.       - name: Generate Infisical Access Token
  169.         run: |
  170.          chmod +x backend/scripts/generate_infisical_token.sh
  171.           source backend/scripts/generate_infisical_token.sh
  172.           echo "INFISICAL_ACCESS_TOKEN=$INFISICAL_ACCESS_TOKEN" >> $GITHUB_ENV
  173.         env:
  174.           INFISICAL_MACHINE_IDENTITY_ID: ${{ secrets.INFISICAL_MACHINE_IDENTITY_ID }}
  175.           INFISICAL_MACHINE_IDENTITY_CLIENT_SECRET: ${{ secrets.INFISICAL_MACHINE_IDENTITY_CLIENT_SECRET }}
  176.           INFISICAL_PROJECT_ID: ${{ secrets.INFISICAL_PROJECT_ID }}
  177.  
  178.       - name: Deploy to Production Droplet
  179.         uses: appleboy/[email protected]
  180.         with:
  181.           envs: INFISICAL_MACHINE_IDENTITY_ID,INFISICAL_MACHINE_IDENTITY_CLIENT_SECRET,INFISICAL_PROJECT_ID
  182.           host: ${{ secrets.DROPLET_IP }}
  183.           username: ${{ secrets.DROPLET_USER }}
  184.           key: ${{ secrets.DROPLET_SSH_KEY }}
  185.           script: |
  186.            # Install Docker if needed
  187.             if ! command -v docker &> /dev/null; then
  188.               curl -fsSL https://get.docker.com -o get-docker.sh
  189.               sudo sh get-docker.sh
  190.               sudo usermod -aG docker $USER
  191.               newgrp docker
  192.             fi
  193.  
  194.             # Login to DigitalOcean Container Registry
  195.             echo "${{ secrets.DOCR_TOKEN }}" | docker login \
  196.               --username do \
  197.               --password-stdin \
  198.               registry.digitalocean.com
  199.  
  200.             # Pull the latest image
  201.             docker pull registry.digitalocean.com/hackalyst-backend/hackalyst-backend:latest
  202.            
  203.             # Stop and remove existing container if it exists
  204.             docker stop hackalyst-backend || true
  205.             docker rm hackalyst-backend || true
  206.  
  207.             # Ensure proxy network exists with the correct labels and subnet
  208.             # Remove existing network if it exists
  209.             docker network inspect proxy >/dev/null 2>&1 && docker network rm proxy
  210.            
  211.             # Create network with specific subnet
  212.             docker network create --subnet=137.184.161.0/24 --gateway=137.184.161.1 proxy --label="com.docker.compose.network=proxy"
  213.            
  214.             # Run the new container with Infisical environment variables and Traefik labels
  215.             docker run -d --name hackalyst-backend --network proxy \
  216.               --ip 137.184.161.2 \
  217.               --network-alias hackalyst-backend \
  218.               -p 127.0.0.1:8000:8000 \
  219.               -e INFISICAL_MACHINE_IDENTITY_ID="${{ secrets.INFISICAL_MACHINE_IDENTITY_ID }}" \
  220.               -e INFISICAL_MACHINE_IDENTITY_CLIENT_SECRET="${{ secrets.INFISICAL_MACHINE_IDENTITY_CLIENT_SECRET }}" \
  221.               -e INFISICAL_PROJECT_ID="${{ secrets.INFISICAL_PROJECT_ID }}" \
  222.               -l "traefik.enable=true" \
  223.               -l "traefik.http.routers.backend.rule=Host(\`backend.hackalyst.com\`)" \
  224.               -l "traefik.http.routers.backend.entrypoints=websecure" \
  225.               -l "traefik.http.routers.backend.tls.certresolver=letsencrypt" \
  226.               -l "traefik.http.services.backend.loadbalancer.server.port=3000" \
  227.               --restart unless-stopped \
  228.               registry.digitalocean.com/hackalyst-backend/hackalyst-backend:latest
  229.              
  230.             # Verify DNS resolution is working correctly
  231.             echo "Verifying DNS resolution from traefik to hackalyst-backend..."
  232.             docker exec traefik ping -c 4 hackalyst-backend
  233.            
  234.             # Verify IP address assignment
  235.             echo "Verifying IP address assignment..."
  236.             docker inspect hackalyst-backend | grep "IPv4Address"
  237.  
  238.         env:
  239.           INFISICAL_MACHINE_IDENTITY_ID: ${{ secrets.INFISICAL_MACHINE_IDENTITY_ID }}
  240.           INFISICAL_MACHINE_IDENTITY_CLIENT_SECRET: ${{ secrets.INFISICAL_MACHINE_IDENTITY_CLIENT_SECRET }}
  241.           INFISICAL_ACCESS_TOKEN: ${{ env.INFISICAL_ACCESS_TOKEN }}
  242.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement