Skip to content

Add Redis heartbeat liveness probe so pubsub subscribers self-recover after car power-cycles #610

Add Redis heartbeat liveness probe so pubsub subscribers self-recover after car power-cycles

Add Redis heartbeat liveness probe so pubsub subscribers self-recover after car power-cycles #610

Workflow file for this run

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 \
-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 }}