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@v6 - 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@v6 - name: Decrypt shared secret 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 - name: Copy compose project to dedicated host uses: appleboy/scp-action@v1 with: host: ${{ secrets.BROUTER_DEPLOY_HOST }} username: trails port: ${{ secrets.BROUTER_DEPLOY_SSH_PORT }} key: ${{ secrets.BROUTER_DEPLOY_SSH_KEY }} source: "infrastructure/brouter-host/docker-compose.yml,infrastructure/brouter-host/Caddyfile,infrastructure/brouter-host/download-segments.sh,infrastructure/brouter-host/.env" target: /home/trails/brouter strip_components: 2 - name: Pull image and restart containers uses: appleboy/ssh-action@v1 with: host: ${{ secrets.BROUTER_DEPLOY_HOST }} username: trails port: ${{ secrets.BROUTER_DEPLOY_SSH_PORT }} key: ${{ secrets.BROUTER_DEPLOY_SSH_KEY }} script: | set -euo pipefail cd /home/trails/brouter chmod +x download-segments.sh # Segment download is explicitly NOT run here — it's # multi-hour and idempotent. Seeding the segments directory # is a one-shot operator task (see brouter-host/README.md). # Run `~/brouter/download-segments.sh` by hand (or via cron) # to refresh. 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 docker compose pull docker compose up -d --remove-orphans docker compose ps - 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