docs: fix stale documentation across 12 files #633
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Telemetry System CI/CD | |
| on: | |
| workflow_dispatch: | |
| push: | |
| paths: | |
| - 'universal-telemetry-software/**' | |
| - 'pecan/**' | |
| - 'flight-recorder/**' | |
| - 'grafana-worker/**' | |
| - '.github/workflows/telemetry-ci.yml' | |
| pull_request: | |
| paths: | |
| - 'universal-telemetry-software/**' | |
| - 'pecan/**' | |
| - 'flight-recorder/**' | |
| - 'grafana-worker/**' | |
| - '.github/workflows/telemetry-ci.yml' | |
| jobs: | |
| detect-changes: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| telemetry: ${{ steps.filter.outputs.telemetry }} | |
| pecan: ${{ steps.filter.outputs.pecan }} | |
| fdr: ${{ steps.filter.outputs.fdr }} | |
| grafana_worker: ${{ steps.filter.outputs.grafana_worker }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dorny/paths-filter@v3 | |
| id: filter | |
| with: | |
| filters: | | |
| telemetry: | |
| - 'universal-telemetry-software/**' | |
| pecan: | |
| - 'pecan/**' | |
| fdr: | |
| - 'flight-recorder/**' | |
| grafana_worker: | |
| - 'grafana-worker/**' | |
| # ── Build images once, share via artifact ─────────────────────────────── | |
| build: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| # Build telemetry image with GHA layer cache (benefits publish jobs too) | |
| - name: Build telemetry image | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: ./universal-telemetry-software | |
| outputs: type=docker,dest=/tmp/telemetry.tar | |
| tags: universal-telemetry:test | |
| build-args: | | |
| GIT_HASH=${{ github.sha }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| - name: Build car-jitsi-client image | |
| run: | | |
| docker build -t car-jitsi-client:test ./universal-telemetry-software/car-jitsi-client | |
| docker save car-jitsi-client:test -o /tmp/jitsi.tar | |
| # Build pecan pointing at the local base station WebSocket for E2E tests | |
| - name: Build pecan image | |
| run: | | |
| docker build \ | |
| --build-arg VITE_WS_URL=ws://localhost:9080 \ | |
| -t pecan:test ./pecan | |
| docker save pecan:test -o /tmp/pecan.tar | |
| - name: Validate compose configurations | |
| working-directory: ./universal-telemetry-software | |
| run: | | |
| docker compose -f deploy/docker-compose.yml config --quiet | |
| docker compose -f deploy/docker-compose.test.yml config --quiet | |
| docker compose -f deploy/docker-compose.rpi-base.yml config --quiet | |
| docker compose -f deploy/docker-compose.macbook-base.yml --env-file deploy/.env.macbook config --quiet | |
| docker compose -f deploy/docker-compose.staging.yml config --quiet | |
| docker compose -f deploy/docker-compose.can-test.yml config --quiet | |
| echo "✓ All compose configs valid" | |
| - name: Upload Docker image artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: docker-images | |
| path: /tmp/*.tar | |
| retention-days: 1 | |
| # ── Unit tests: no containers, fast feedback ───────────────────────────── | |
| unit-tests: | |
| runs-on: ubuntu-latest | |
| needs: build | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Download Docker image artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: docker-images | |
| path: /tmp | |
| - name: Load Docker images | |
| run: | | |
| docker load -i /tmp/telemetry.tar | |
| docker load -i /tmp/jitsi.tar | |
| - name: Verify car-jitsi-client starts | |
| run: | | |
| docker run --rm --entrypoint node car-jitsi-client:test -e "require('./index.js')" & | |
| PID=$! | |
| sleep 5 | |
| kill $PID 2>/dev/null || true | |
| - name: Verify go2rtc config | |
| run: | | |
| docker run --rm -v ${{ github.workspace }}/universal-telemetry-software/go2rtc.yaml:/config/go2rtc.yaml:ro alexxit/go2rtc:latest -c /config/go2rtc.yaml & | |
| PID=$! | |
| sleep 3 | |
| kill $PID 2>/dev/null || true | |
| - name: Build and typecheck PECAN | |
| working-directory: ./pecan | |
| run: | | |
| npm ci | |
| npx tsc --noEmit | |
| - name: Build and typecheck FDR | |
| working-directory: ./flight-recorder | |
| run: | | |
| npm ci | |
| npx tsc --noEmit | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - name: Install Python dependencies | |
| run: | | |
| pip install \ | |
| pytest pytest-asyncio pytest-timeout \ | |
| cantools \ | |
| websockets redis requests | |
| - name: Run unit tests | |
| working-directory: ./universal-telemetry-software | |
| run: | | |
| python -m pytest \ | |
| tests/test_leds.py \ | |
| tests/test_helpers.py \ | |
| tests/test_car_mode.py \ | |
| tests/test_dbc_consistency.py \ | |
| tests/test_ws_relay.py \ | |
| tests/test_page_lock.py \ | |
| tests/test_stats_publisher.py \ | |
| tests/test_status_server.py \ | |
| tests/test_heartbeat.py \ | |
| -v | |
| # ── Integration tests: full docker-compose stack ───────────────────────── | |
| integration: | |
| runs-on: ubuntu-latest | |
| needs: build | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Download Docker image artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: docker-images | |
| path: /tmp | |
| # Loading the tars populates Docker's layer cache so compose --build is instant | |
| - name: Load Docker images | |
| run: | | |
| docker load -i /tmp/telemetry.tar | |
| docker load -i /tmp/pecan.tar | |
| # ── Pull heavy images in parallel ──────────────────────────────────── | |
| - name: Pull base images | |
| run: | | |
| docker pull redis:8.2 & | |
| wait | |
| echo "✓ Images pulled" | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - name: Install Python dependencies | |
| run: | | |
| pip install \ | |
| pytest pytest-asyncio pytest-timeout \ | |
| cantools \ | |
| websockets redis requests aiohttp \ | |
| playwright | |
| - name: Install Playwright browsers | |
| run: playwright install chromium --with-deps | |
| - name: Start integration test environment | |
| working-directory: ./universal-telemetry-software | |
| env: | |
| GIT_HASH: ${{ github.sha }} | |
| run: | | |
| docker compose -f deploy/docker-compose.test.yml down -v --remove-orphans || true | |
| docker compose -f deploy/docker-compose.test.yml up -d --build | |
| echo "Waiting for all services to become ready..." | |
| sleep 20 | |
| - name: Check container health | |
| working-directory: ./universal-telemetry-software | |
| run: | | |
| docker compose -f deploy/docker-compose.test.yml ps | |
| docker compose -f deploy/docker-compose.test.yml logs --tail 30 | |
| - name: Verify containers | |
| run: | | |
| for container in daq-car daq-base daq-car-redis daq-base-redis daq-pecan-test daq-test-timescaledb; do | |
| if docker ps --format '{{.Names}}' | grep -q "^${container}$"; then | |
| echo "✓ ${container} running" | |
| else | |
| echo "✗ ${container} NOT running" | |
| docker logs ${container} 2>&1 || true | |
| exit 1 | |
| fi | |
| done | |
| - name: Run integration tests | |
| working-directory: ./universal-telemetry-software | |
| run: | | |
| python -m pytest tests/test_integration.py tests/test_websocket_v2.py tests/test_ui_e2e.py -v -s --timeout=120 | |
| - name: Collect logs on failure | |
| if: failure() | |
| working-directory: ./universal-telemetry-software | |
| run: | | |
| mkdir -p test-logs | |
| docker compose -f deploy/docker-compose.test.yml logs --no-color > test-logs/docker-compose.log 2>&1 | |
| docker logs daq-car > test-logs/car.log 2>&1 || true | |
| docker logs daq-base > test-logs/base.log 2>&1 || true | |
| docker logs daq-car-redis > test-logs/car-redis.log 2>&1 || true | |
| docker logs daq-base-redis > test-logs/base-redis.log 2>&1 || true | |
| docker logs daq-pecan-test > test-logs/pecan.log 2>&1 || true | |
| docker logs daq-test-timescaledb > test-logs/timescaledb.log 2>&1 || true | |
| - name: Upload integration test logs | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: integration-test-logs | |
| path: universal-telemetry-software/test-logs/ | |
| retention-days: 7 | |
| - name: Cleanup integration test stack | |
| if: always() | |
| working-directory: ./universal-telemetry-software | |
| run: | | |
| docker compose -f deploy/docker-compose.test.yml down -v | |
| # ── vCAN pipeline tests: real CAN code path ────────────────────────────── | |
| vcan: | |
| runs-on: ubuntu-latest | |
| needs: build | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Download Docker image artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: docker-images | |
| path: /tmp | |
| - name: Load telemetry image | |
| run: docker load -i /tmp/telemetry.tar | |
| - name: Pull redis image | |
| run: docker pull redis:8.2 | |
| - name: Set up virtual CAN interface | |
| run: | | |
| sudo apt-get install -y linux-modules-extra-$(uname -r) | |
| sudo modprobe can | |
| sudo modprobe can_raw | |
| sudo modprobe vcan | |
| sudo ip link add dev can0 type vcan | |
| sudo ip link set up can0 | |
| ip link show can0 | |
| echo "✓ vcan interface can0 is UP" | |
| - name: Install can-utils | |
| run: sudo apt-get install -y can-utils | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - name: Install Python dependencies | |
| run: | | |
| pip install \ | |
| pytest pytest-asyncio pytest-timeout \ | |
| cantools \ | |
| websockets redis requests | |
| - name: Start vCAN test stack | |
| working-directory: ./universal-telemetry-software | |
| env: | |
| GIT_HASH: ${{ github.sha }} | |
| run: | | |
| docker compose -f deploy/docker-compose.can-test.yml up -d --build | |
| echo "Waiting for telemetry container to start CAN reader..." | |
| sleep 15 | |
| - name: Verify can0 visible inside container | |
| run: | | |
| if docker exec daq-can-test ip link show can0 2>&1 | grep -q "can0"; then | |
| echo "✓ can0 is visible inside container" | |
| else | |
| echo "✗ can0 NOT visible inside container" | |
| echo "--- Host interfaces ---" | |
| ip link show | |
| echo "--- Container interfaces ---" | |
| docker exec daq-can-test ip link show 2>&1 || true | |
| exit 1 | |
| fi | |
| - name: Verify real CAN mode (not simulation) | |
| run: | | |
| if docker logs daq-can-test 2>&1 | grep -q "CAN Reader started on can0"; then | |
| echo "✓ Container is reading real can0" | |
| else | |
| echo "✗ Container did NOT start real CAN reader" | |
| docker logs daq-can-test 2>&1 | |
| exit 1 | |
| fi | |
| - name: Run vCAN pipeline tests | |
| working-directory: ./universal-telemetry-software | |
| run: | | |
| python -m pytest tests/test_can_pipeline.py -v -s --timeout=60 | |
| - name: Run pecan UI vCAN tests | |
| working-directory: ./pecan | |
| run: | | |
| npm ci | |
| npm run test:ci | |
| - name: Collect vCAN test logs on failure | |
| if: failure() | |
| working-directory: ./universal-telemetry-software | |
| run: | | |
| mkdir -p test-logs | |
| docker logs daq-can-test > test-logs/can-test.log 2>&1 || true | |
| docker logs daq-can-test-redis > test-logs/can-test-redis.log 2>&1 || true | |
| - name: Upload vCAN test logs | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: vcan-test-logs | |
| path: universal-telemetry-software/test-logs/ | |
| retention-days: 7 | |
| - name: Cleanup vCAN test stack | |
| if: always() | |
| working-directory: ./universal-telemetry-software | |
| run: | | |
| docker compose -f deploy/docker-compose.can-test.yml down -v 2>/dev/null || true | |
| sudo ip link delete can0 2>/dev/null || true | |
| # ── LAN sender tests: base-only stack + lan_sender.py fake car ─────────────── | |
| lan-sender: | |
| runs-on: ubuntu-latest | |
| needs: build | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Download Docker image artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: docker-images | |
| path: /tmp | |
| - name: Load telemetry image | |
| run: docker load -i /tmp/telemetry.tar | |
| - name: Pull Redis and TimescaleDB images | |
| run: | | |
| docker pull redis:8.2 & | |
| docker pull timescale/timescaledb:2.17.1-pg16 & | |
| wait | |
| echo "✓ Images pulled" | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - name: Install Python test dependencies | |
| run: | | |
| pip install \ | |
| pytest pytest-asyncio pytest-timeout \ | |
| websockets redis requests aiohttp | |
| - name: Start base-only stack | |
| working-directory: ./universal-telemetry-software | |
| env: | |
| GIT_HASH: ${{ github.sha }} | |
| run: | | |
| docker compose -f deploy/docker-compose.lan-sender-test.yml down -v --remove-orphans || true | |
| docker compose -f deploy/docker-compose.lan-sender-test.yml up -d --build | |
| echo "Waiting for Redis and data.py to connect (80s for Redis boot + 60s for retry backoff)..." | |
| sleep 80 | |
| - name: Check container health | |
| working-directory: ./universal-telemetry-software | |
| run: | | |
| docker compose -f deploy/docker-compose.lan-sender-test.yml ps | |
| docker compose -f deploy/docker-compose.lan-sender-test.yml logs --tail 30 | |
| - name: Verify containers | |
| run: | | |
| for container in daq-lan-base daq-lan-base-redis daq-lan-timescaledb; do | |
| if docker ps --format '{{.Names}}' | grep -q "^${container}$"; then | |
| echo "✓ ${container} running" | |
| else | |
| echo "✗ ${container} NOT running" | |
| docker logs ${container} 2>&1 || true | |
| exit 1 | |
| fi | |
| done | |
| - name: Start lan_sender.py in background | |
| working-directory: ./universal-telemetry-software | |
| run: | | |
| # Run lan_sender inside the container so it shares the same network namespace. | |
| # Host-side UDP proxies can lose packets; exec avoids that entirely. | |
| docker exec -d daq-lan-base python3 /app/src/lan_sender.py 127.0.0.1 5005 | |
| echo "lan_sender started inside daq-lan-base" | |
| # No sleep needed — tests handle their own timing | |
| - name: Run lan_sender integration tests | |
| working-directory: ./universal-telemetry-software | |
| run: | | |
| python -m pytest tests/test_lan_sender.py -v -s --timeout=120 | |
| - name: Collect logs on failure | |
| if: failure() | |
| working-directory: ./universal-telemetry-software | |
| run: | | |
| mkdir -p test-logs | |
| docker compose -f deploy/docker-compose.lan-sender-test.yml logs --no-color > test-logs/lan-sender-compose.log 2>&1 | |
| docker logs daq-lan-base > test-logs/lan-base.log 2>&1 || true | |
| docker logs daq-lan-base-redis > test-logs/lan-base-redis.log 2>&1 || true | |
| docker logs daq-lan-timescaledb > test-logs/lan-timescaledb.log 2>&1 || true | |
| - name: Upload lan-sender test logs | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: lan-sender-test-logs | |
| path: universal-telemetry-software/test-logs/ | |
| retention-days: 7 | |
| - name: Cleanup lan-sender test stack | |
| if: always() | |
| working-directory: ./universal-telemetry-software | |
| run: | | |
| # lan_sender runs inside the container — docker compose down stops it automatically | |
| docker compose -f deploy/docker-compose.lan-sender-test.yml down -v | |
| # ── MacBook one-click stack smoke test with LAN sender fake car ───────────── | |
| macbook-lan-sender: | |
| runs-on: ubuntu-latest | |
| needs: build | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Download Docker image artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: docker-images | |
| path: /tmp | |
| - name: Load and tag MacBook stack images | |
| run: | | |
| docker load -i /tmp/telemetry.tar | |
| docker load -i /tmp/pecan.tar | |
| docker tag universal-telemetry:test ghcr.io/western-formula-racing/data-acquisition/universal-telemetry:latest | |
| docker tag pecan:test ghcr.io/western-formula-racing/data-acquisition/pecan:latest | |
| - name: Pull base images | |
| run: docker pull redis:8.2 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - name: Install Python test dependencies | |
| run: pip install pytest pytest-asyncio pytest-timeout websockets redis requests | |
| - name: Start MacBook base stack | |
| working-directory: ./universal-telemetry-software | |
| run: | | |
| docker compose -f deploy/docker-compose.macbook-base.yml --env-file deploy/.env.macbook down -v --remove-orphans || true | |
| docker compose -f deploy/docker-compose.macbook-base.yml --env-file deploy/.env.macbook up -d | |
| echo "Waiting for telemetry, WS relay, and PECAN to boot..." | |
| sleep 30 | |
| - name: Check MacBook stack health | |
| working-directory: ./universal-telemetry-software | |
| run: | | |
| docker compose -f deploy/docker-compose.macbook-base.yml --env-file deploy/.env.macbook ps | |
| docker compose -f deploy/docker-compose.macbook-base.yml --env-file deploy/.env.macbook logs --tail 40 | |
| - name: Start lan_sender.py inside telemetry container | |
| run: | | |
| docker exec -d daq-telemetry python3 /app/src/lan_sender.py 127.0.0.1 5005 | |
| echo "lan_sender started inside daq-telemetry" | |
| - name: Run MacBook stack LAN sender tests | |
| working-directory: ./universal-telemetry-software | |
| run: python -m pytest tests/test_macbook_base_lan_sender.py -v -s --timeout=120 | |
| - name: Collect MacBook stack logs on failure | |
| if: failure() | |
| working-directory: ./universal-telemetry-software | |
| run: | | |
| mkdir -p test-logs | |
| docker compose -f deploy/docker-compose.macbook-base.yml --env-file deploy/.env.macbook logs --no-color > test-logs/macbook-compose.log 2>&1 | |
| docker logs daq-telemetry > test-logs/macbook-telemetry.log 2>&1 || true | |
| docker logs daq-pecan > test-logs/macbook-pecan.log 2>&1 || true | |
| docker logs daq-redis > test-logs/macbook-redis.log 2>&1 || true | |
| - name: Upload MacBook stack logs | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: macbook-lan-sender-test-logs | |
| path: universal-telemetry-software/test-logs/ | |
| retention-days: 7 | |
| - name: Cleanup MacBook stack | |
| if: always() | |
| working-directory: ./universal-telemetry-software | |
| run: docker compose -f deploy/docker-compose.macbook-base.yml --env-file deploy/.env.macbook down -v | |
| publish-telemetry: | |
| needs: [unit-tests, integration, vcan, lan-sender, macbook-lan-sender] | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'workflow_dispatch' || github.event_name != 'pull_request' | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract metadata (tags, labels) for Telemetry | |
| id: meta-telemetry | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ghcr.io/${{ github.repository }}/universal-telemetry | |
| tags: | | |
| type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} | |
| type=raw,value=test-latest,enable=${{ github.ref != 'refs/heads/main' }} | |
| type=sha | |
| - name: Build and push Telemetry image | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: ./universal-telemetry-software | |
| file: ./universal-telemetry-software/Dockerfile | |
| platforms: linux/amd64,linux/arm64 | |
| push: true | |
| tags: ${{ steps.meta-telemetry.outputs.tags }} | |
| labels: ${{ steps.meta-telemetry.outputs.labels }} | |
| build-args: | | |
| GIT_HASH=${{ github.sha }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| - name: Extract metadata (tags, labels) for Car Jitsi Client | |
| id: meta-car-jitsi | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ghcr.io/${{ github.repository }}/car-jitsi-client | |
| tags: | | |
| type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} | |
| type=raw,value=test-latest,enable=${{ github.ref != 'refs/heads/main' }} | |
| type=sha | |
| - name: Build and push Car Jitsi Client image | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: ./universal-telemetry-software/car-jitsi-client | |
| file: ./universal-telemetry-software/car-jitsi-client/Dockerfile | |
| platforms: linux/amd64,linux/arm64 | |
| push: true | |
| tags: ${{ steps.meta-car-jitsi.outputs.tags }} | |
| labels: ${{ steps.meta-car-jitsi.outputs.labels }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| publish-pecan: | |
| needs: [unit-tests, integration, vcan] | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'workflow_dispatch' || github.event_name != 'pull_request' | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract metadata (tags, labels) for Pecan | |
| id: meta-pecan | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ghcr.io/${{ github.repository }}/pecan | |
| tags: | | |
| type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} | |
| type=raw,value=test-latest,enable=${{ github.ref != 'refs/heads/main' }} | |
| type=sha | |
| - name: Build and push Pecan image | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: ./pecan | |
| file: ./pecan/Dockerfile | |
| platforms: linux/amd64,linux/arm64 | |
| push: true | |
| tags: ${{ steps.meta-pecan.outputs.tags }} | |
| labels: ${{ steps.meta-pecan.outputs.labels }} | |
| build-args: | | |
| VITE_WS_PRESETS=[{"label":"Radio Link","url":"ws://10.71.1.20:9080"}] | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max,ignore-error=true | |
| deploy-pecan-pages: | |
| needs: [unit-tests, integration, vcan] | |
| runs-on: ubuntu-latest | |
| if: github.ref == 'refs/heads/main' | |
| permissions: | |
| contents: read | |
| pages: write | |
| id-token: write | |
| environment: | |
| name: github-pages | |
| url: ${{ steps.deployment.outputs.page_url }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| cache-dependency-path: pecan/package-lock.json | |
| - name: Install dependencies | |
| working-directory: ./pecan | |
| run: npm ci | |
| - name: Run tests | |
| working-directory: ./pecan | |
| run: npm run test:ci | |
| - name: Build | |
| working-directory: ./pecan | |
| run: npm run build | |
| env: | |
| VITE_FIREBASE_API_KEY: ${{ secrets.VITE_FIREBASE_API_KEY }} | |
| VITE_FIREBASE_AUTH_DOMAIN: ${{ secrets.VITE_FIREBASE_AUTH_DOMAIN }} | |
| VITE_FIREBASE_PROJECT_ID: ${{ secrets.VITE_FIREBASE_PROJECT_ID }} | |
| VITE_FIREBASE_STORAGE_BUCKET: ${{ secrets.VITE_FIREBASE_STORAGE_BUCKET }} | |
| VITE_FIREBASE_MESSAGING_SENDER_ID: ${{ secrets.VITE_FIREBASE_MESSAGING_SENDER_ID }} | |
| VITE_FIREBASE_APP_ID: ${{ secrets.VITE_FIREBASE_APP_ID }} | |
| VITE_GRAFANA_BRIDGE_URL: https://grafana-proxy.westernformularacing.workers.dev/grafana-bridge/api/grafana | |
| - name: Upload artifact | |
| uses: actions/upload-pages-artifact@v3 | |
| with: | |
| path: ./pecan/dist | |
| - name: Deploy to GitHub Pages | |
| id: deployment | |
| uses: actions/deploy-pages@v4 | |
| deploy-pecan-dev-cloudflare: | |
| needs: [unit-tests, integration, vcan] | |
| runs-on: ubuntu-latest | |
| if: github.ref == 'refs/heads/dev' | |
| permissions: | |
| contents: read | |
| deployments: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| cache-dependency-path: pecan/package-lock.json | |
| - name: Install dependencies | |
| working-directory: ./pecan | |
| run: npm ci | |
| - name: Run tests | |
| working-directory: ./pecan | |
| run: npm run test:ci | |
| - name: Build | |
| working-directory: ./pecan | |
| run: npm run build | |
| env: | |
| VITE_FIREBASE_API_KEY: ${{ secrets.VITE_FIREBASE_API_KEY_DEV }} | |
| VITE_FIREBASE_AUTH_DOMAIN: ${{ secrets.VITE_FIREBASE_AUTH_DOMAIN_DEV }} | |
| VITE_FIREBASE_PROJECT_ID: ${{ secrets.VITE_FIREBASE_PROJECT_ID_DEV }} | |
| VITE_FIREBASE_STORAGE_BUCKET: ${{ secrets.VITE_FIREBASE_STORAGE_BUCKET_DEV }} | |
| VITE_FIREBASE_MESSAGING_SENDER_ID: ${{ secrets.VITE_FIREBASE_MESSAGING_SENDER_ID_DEV }} | |
| VITE_FIREBASE_APP_ID: ${{ secrets.VITE_FIREBASE_APP_ID_DEV }} | |
| VITE_GRAFANA_BRIDGE_URL: https://grafana-proxy.westernformularacing.workers.dev/grafana-bridge/api/grafana | |
| - name: Deploy to Cloudflare Pages | |
| uses: cloudflare/pages-action@v1 | |
| with: | |
| apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| projectName: 'pecan-dev' | |
| directory: './pecan/dist' | |
| gitHubToken: ${{ secrets.GITHUB_TOKEN }} | |
| branch: dev | |
| deploy-fdr-cloudflare: | |
| needs: [unit-tests, integration, vcan] | |
| runs-on: ubuntu-latest | |
| if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/fdr' | |
| permissions: | |
| contents: read | |
| deployments: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| cache-dependency-path: flight-recorder/package-lock.json | |
| - name: Install dependencies | |
| working-directory: ./flight-recorder | |
| run: npm ci | |
| - name: Build | |
| working-directory: ./flight-recorder | |
| run: npm run build | |
| env: | |
| VITE_GITHUB_DBC_READONLY_TOKEN: ${{ secrets.VITE_GITHUB_DBC_READONLY_TOKEN }} | |
| - name: Deploy to Cloudflare Pages | |
| uses: cloudflare/pages-action@v1 | |
| with: | |
| apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| projectName: 'wfr-fdr' | |
| directory: './flight-recorder/dist' | |
| gitHubToken: ${{ secrets.GITHUB_TOKEN }} | |
| branch: ${{ github.ref_name }} | |
| # ── Deploy pecan-internal to Cloudflare Pages (main branch only) ────────── | |
| deploy-pecan-internal-cloudflare: | |
| needs: [detect-changes, unit-tests, integration, vcan] | |
| runs-on: ubuntu-latest | |
| if: | | |
| (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev') && | |
| needs.detect-changes.outputs.pecan == 'true' | |
| permissions: | |
| contents: read | |
| deployments: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| cache-dependency-path: pecan/package-lock.json | |
| - name: Install dependencies | |
| working-directory: ./pecan | |
| run: npm ci | |
| - name: Build | |
| working-directory: ./pecan | |
| run: npm run build | |
| env: | |
| VITE_INTERNAL: 'true' | |
| VITE_GITHUB_DBC_READONLY_TOKEN: ${{ secrets.VITE_GITHUB_DBC_READONLY_TOKEN }} | |
| VITE_RELAY_TOKEN: ${{ secrets.VITE_RELAY_TOKEN }} | |
| VITE_GRAFANA_BRIDGE_URL: https://grafana-proxy.westernformularacing.workers.dev/grafana-bridge/api/grafana | |
| VITE_FIREBASE_API_KEY: ${{ secrets.VITE_FIREBASE_API_KEY }} | |
| VITE_FIREBASE_AUTH_DOMAIN: ${{ secrets.VITE_FIREBASE_AUTH_DOMAIN }} | |
| VITE_FIREBASE_PROJECT_ID: ${{ secrets.VITE_FIREBASE_PROJECT_ID }} | |
| VITE_FIREBASE_STORAGE_BUCKET: ${{ secrets.VITE_FIREBASE_STORAGE_BUCKET }} | |
| VITE_FIREBASE_MESSAGING_SENDER_ID: ${{ secrets.VITE_FIREBASE_MESSAGING_SENDER_ID }} | |
| VITE_FIREBASE_APP_ID: ${{ secrets.VITE_FIREBASE_APP_ID }} | |
| - name: Deploy to Cloudflare Pages | |
| uses: cloudflare/pages-action@v1 | |
| with: | |
| apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| projectName: pecan-internal | |
| directory: ./pecan/dist | |
| gitHubToken: ${{ secrets.GITHUB_TOKEN }} | |
| branch: main | |
| # ── Deploy grafana-worker via Wrangler ─────────────────────────────────── | |
| deploy-worker: | |
| needs: [detect-changes] | |
| runs-on: ubuntu-latest | |
| if: | | |
| (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev') && | |
| needs.detect-changes.outputs.grafana_worker == 'true' | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| - name: Install Wrangler | |
| run: npm install -g wrangler | |
| - name: Deploy Worker | |
| working-directory: ./grafana-worker | |
| run: wrangler deploy | |
| env: | |
| CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| CF_ACCESS_CLIENT_ID: ${{ secrets.CF_ACCESS_CLIENT_ID }} | |
| CF_ACCESS_CLIENT_SECRET: ${{ secrets.CF_ACCESS_CLIENT_SECRET }} |