name: CD # Triggers: # - push: normal merges (manual gh pr merge) # - workflow_run: after automerge completes (GITHUB_TOKEN merges don't trigger push) # - workflow_dispatch: manual trigger via gh workflow run cd.yml on: push: branches: [main] workflow_run: workflows: ["Automerge"] branches: [main] types: [completed] workflow_dispatch: {} concurrency: group: deploy cancel-in-progress: false jobs: build-images: name: Build & Push Docker Images runs-on: ubuntu-latest if: github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' permissions: contents: read packages: write strategy: matrix: app: [journal, planner] 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: . file: apps/${{ matrix.app }}/Dockerfile push: true tags: | ghcr.io/trails-cool/${{ matrix.app }}:latest ghcr.io/trails-cool/${{ matrix.app }}:${{ github.sha }} build-args: | SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_RELEASE=${{ github.sha }} build-brouter: 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 to Hetzner needs: [build-images, build-brouter] runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Copy files to server uses: appleboy/scp-action@v1 with: host: ${{ secrets.DEPLOY_HOST }} username: root key: ${{ secrets.DEPLOY_SSH_KEY }} source: "infrastructure/docker-compose.yml,infrastructure/Caddyfile" target: /opt/trails-cool strip_components: 1 - name: Deploy via SSH uses: appleboy/ssh-action@v1 with: host: ${{ secrets.DEPLOY_HOST }} username: root key: ${{ secrets.DEPLOY_SSH_KEY }} script: | cd /opt/trails-cool # Login to ghcr.io to pull private images echo "${{ secrets.DEPLOY_GHCR_TOKEN }}" | docker login ghcr.io -u stigi --password-stdin # Download BRouter segments if missing if [ ! -d /opt/trails-cool/segments ] || [ -z "$(ls /opt/trails-cool/segments/*.rd5 2>/dev/null)" ]; then mkdir -p /opt/trails-cool/segments echo "Downloading Germany BRouter segments..." for tile in E5_N45 E5_N50 E10_N45 E10_N50; do [ -f "/opt/trails-cool/segments/${tile}.rd5" ] || \ wget -q "https://brouter.de/brouter/segments4/${tile}.rd5" -O "/opt/trails-cool/segments/${tile}.rd5" done fi # Pull latest images export SENTRY_RELEASE="${{ github.sha }}" docker compose pull # Push database schema (starts postgres, waits for healthy) docker compose run --rm journal npx drizzle-kit push --config /app/packages/db/drizzle.config.ts --force # Start all services docker compose up -d --remove-orphans # Verify services are running sleep 10 docker compose ps