name: Deploy backend on: push: branches: [develop] paths: - 'Club12-Backend/**' - 'docker-compose.yml' - '.github/workflows/deploy-backend.yml' workflow_dispatch: concurrency: group: deploy-backend-${{ github.ref }} cancel-in-progress: false env: IMAGE: ghcr.io/francoru/club12-backend SERVICE: backend DEPLOY_DIR: /home/docker-compose/Club12 jobs: build: runs-on: ubuntu-latest timeout-minutes: 30 permissions: contents: read packages: write steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Buildx uses: docker/setup-buildx-action@v3 - name: Log in to GHCR uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push uses: docker/build-push-action@v6 with: context: ./Club12-Backend push: true provenance: false tags: ${{ env.IMAGE }}:latest cache-from: type=gha cache-to: type=gha,mode=max deploy: needs: build runs-on: [self-hosted, Club-12-back-runner] timeout-minutes: 15 permissions: contents: read packages: read concurrency: group: club12-deploy-${{ github.ref }} cancel-in-progress: false steps: - name: Checkout uses: actions/checkout@v4 with: persist-credentials: false - name: Log in to GHCR uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Sync compose file to the server project directory run: | set -euo pipefail if [ ! -d "$DEPLOY_DIR" ]; then echo "::error::$DEPLOY_DIR does not exist on this runner host. Create it before deploying." exit 1 fi if [ ! -f "$DEPLOY_DIR/.env" ]; then echo "::error::$DEPLOY_DIR/.env is missing. Create it once by hand from .env.example (see DEPLOYMENT.md)." exit 1 fi install -m 0644 docker-compose.yml "$DEPLOY_DIR/docker-compose.yml" echo "Synced docker-compose.yml -> $DEPLOY_DIR/docker-compose.yml" - name: Archive the currently running image as :previous working-directory: /home/docker-compose/Club12 run: | set -euo pipefail if docker image inspect "$IMAGE:latest" >/dev/null 2>&1; then docker tag "$IMAGE:latest" "$IMAGE:previous" echo "Archived $IMAGE:latest -> $IMAGE:previous" else echo "No local $IMAGE:latest yet (first deploy) - nothing to archive." fi - name: Pull the new image working-directory: /home/docker-compose/Club12 run: | set -euo pipefail docker compose pull "$SERVICE" - name: Restart the service working-directory: /home/docker-compose/Club12 run: | set -euo pipefail docker compose up -d --no-deps --no-build "$SERVICE" docker compose ps "$SERVICE" - name: Re-point the frontend proxy at the new backend working-directory: /home/docker-compose/Club12 run: | set -euo pipefail # The frontend's nginx resolves `backend` once at start-up. Recreating # the backend gives it a new container IP, so without this restart # every /api/* request 502s until the frontend is bounced. if docker compose ps --status running --services | grep -qx frontend; then docker compose restart frontend docker compose ps frontend else echo "frontend not running on this host - skipping proxy refresh" fi - name: Prune dangling images run: | set -euo pipefail docker image prune -f