name: CD BRouter

on:
  push:
    branches: [main]
    paths:
      - "docker/brouter/**"
      - "infrastructure/brouter-host/**"
  workflow_dispatch: {}

concurrency:
  group: deploy-brouter
  cancel-in-progress: true

jobs:
  build:
    name: Build & Push BRouter Image
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v7

      - uses: docker/login-action@v4
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - uses: docker/build-push-action@v7
        with:
          context: docker/brouter
          push: true
          tags: |
            ghcr.io/trails-cool/brouter:latest
            ghcr.io/trails-cool/brouter:${{ github.sha }}

  deploy:
    name: Deploy BRouter to dedicated host
    needs: [build]
    runs-on: ubuntu-latest
    environment: infra
    steps:
      - uses: actions/checkout@v7

      - name: Decrypt shared secret
        id: decrypt
        run: |
          curl -sLO https://github.com/getsops/sops/releases/download/v3.9.4/sops-v3.9.4.linux.amd64
          chmod +x sops-v3.9.4.linux.amd64
          # Extract ONLY BROUTER_AUTH_TOKEN from secrets.app.env — the
          # rest of that file is app-only and has no business reaching
          # the BRouter host.
          SOPS_AGE_KEY="${{ secrets.AGE_SECRET_KEY }}" ./sops-v3.9.4.linux.amd64 -d infrastructure/secrets.app.env \
            | grep '^BROUTER_AUTH_TOKEN=' > infrastructure/brouter-host/.env
          chmod 0600 infrastructure/brouter-host/.env

          # GHCR pull credential — not shipped to the host's filesystem;
          # passed to the SSH step as an env var so it only lives in
          # memory during `docker login`.
          GHCR_TOKEN=$(SOPS_AGE_KEY="${{ secrets.AGE_SECRET_KEY }}" ./sops-v3.9.4.linux.amd64 -d infrastructure/secrets.app.env \
            | grep '^DEPLOY_GHCR_TOKEN=' | cut -d= -f2-)
          echo "::add-mask::$GHCR_TOKEN"
          echo "GHCR_TOKEN=$GHCR_TOKEN" >> $GITHUB_ENV

      - name: Deploy to dedicated host (single SSH connection)
        # Consolidated into one SSH session on purpose: UFW on the
        # dedicated host rate-limits port 2232 via `LIMIT` (~6 conns /
        # 30s). `appleboy/scp-action` burns that budget across
        # scp+mkdir+untar+cleanup conns and the final cleanup times
        # out. One SSH here sidesteps the limit entirely. The tarball
        # is piped in as a base64 env var so we don't fight over
        # stdin (reserved for the heredoc script).
        env:
          SSH_KEY: ${{ secrets.BROUTER_DEPLOY_SSH_KEY }}
          HOST: ${{ secrets.BROUTER_DEPLOY_HOST }}
          PORT: ${{ secrets.BROUTER_DEPLOY_SSH_PORT }}
        run: |
          set -euo pipefail

          # SSH key to a locked-down file (env-var keys break openssh)
          install -m 0600 /dev/null ~/id_deploy
          printf '%s\n' "$SSH_KEY" > ~/id_deploy

          # Bundle the compose project
          TARBALL_B64=$(tar -C infrastructure/brouter-host -czf - \
            docker-compose.yml Caddyfile promtail-config.yml \
            download-segments.sh .env \
            poi-extract \
            | base64 -w0)

          # One SSH session: untar, (re)start containers, report status
          ssh -i ~/id_deploy -p "$PORT" \
            -o StrictHostKeyChecking=accept-new \
            -o UserKnownHostsFile=/tmp/known_hosts \
            -o ConnectTimeout=30 \
            trails@"$HOST" \
            "TARBALL_B64='$TARBALL_B64' GHCR_TOKEN='$GHCR_TOKEN' bash -s" <<'REMOTE'
          set -euo pipefail
          mkdir -p ~/brouter && cd ~/brouter
          echo "$TARBALL_B64" | base64 -d | tar -xzf -
          chmod +x download-segments.sh poi-extract/poi-extract.sh poi-extract/to-ndjson.py

          # Segment seeding is a one-shot operator task (~10 GB, a few
          # minutes). CD must not rerun it on every deploy.
          if [ ! -d segments ] || [ -z "$(ls -A segments 2>/dev/null)" ]; then
            echo "WARNING: segments/ is empty. BRouter will start but return 404 until segments are seeded."
            mkdir -p segments
          fi

          # GHCR brouter image is private; log in so pull works.
          echo "$GHCR_TOKEN" | docker login ghcr.io -u stigi --password-stdin

          docker compose pull
          docker compose up -d --remove-orphans
          docker compose ps
          REMOTE

      - name: Annotate deploy in flagship Grafana
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.DEPLOY_HOST }}
          username: root
          key: ${{ secrets.DEPLOY_SSH_KEY }}
          script: |
            cd /opt/trails-cool
            GRAFANA_TOKEN=$(grep GRAFANA_SERVICE_TOKEN .env | cut -d= -f2-)
            if [ -n "$GRAFANA_TOKEN" ]; then
              docker compose exec -T grafana curl -sf -X POST \
                -H "Authorization: Bearer $GRAFANA_TOKEN" \
                -H "Content-Type: application/json" \
                -d '{"text":"Deploy brouter ${{ github.sha }}","tags":["deploy","brouter"]}' \
                http://localhost:3000/api/annotations || true
            fi
