Skip to content
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
c5aa4de
Pulse health mosaic on advance
haoruizhou May 2, 2026
2291401
Add setting to toggle plot axis range source
haoruizhou May 6, 2026
01a62a8
Attempt to fix packet indicator; stream overlay update
haoruizhou May 8, 2026
4537ddf
Accurate speed calc, rpm alias, and loss recovery
haoruizhou May 8, 2026
7627fd3
Add healthy field to /api/health response for Upptime compatibility
haoruizhou May 8, 2026
c17c6b8
Add /api/timescale-health endpoint exposing TimescaleDB data lag for …
wfr-data-acquisition May 8, 2026
82e13c3
Updated docs to reflect the car-side move to systemd
haoruizhou May 19, 2026
279a226
Consolidate WebSocket protocol and add runtime notes
haoruizhou May 19, 2026
39d0972
Add agent orientation
haoruizhou May 19, 2026
3d420a0
Update docs
haoruizhou May 19, 2026
9868bf0
Add relay worker and update flight-recorder docs
haoruizhou May 19, 2026
91ab5e1
Add vitest script and LiveRelayService tests
haoruizhou May 19, 2026
b9ea2de
Mark lap-detector as tabled until GPS hardware
haoruizhou May 19, 2026
ac259dc
Route heartbeat frames and clean session URL
haoruizhou May 19, 2026
2dcd7f1
Merge pull request #69 from Western-Formula-Racing/recorder-upload
haoruizhou May 19, 2026
4328073
Move rag_viz.py and rag_vectors.png to server/installer/sandbox
haoruizhou May 20, 2026
7040477
add file-uploader, lap-detector, slackbot to package cleanup matrix
haoruizhou May 20, 2026
1e20682
offline bundle: fix image refs from daq-radio to data-acquisition nam…
haoruizhou May 20, 2026
a10e67e
Add /health endpoint and system health UI
haoruizhou May 21, 2026
70514b9
Update package references for data-acquisition repo
haoruizhou May 21, 2026
e270b6a
Merge health status and package reference updates
haoruizhou May 21, 2026
e678d38
Use .env.macbook and opt-in compose profiles
haoruizhou May 21, 2026
90e5255
Add auto TimescaleDB logging detection
haoruizhou May 21, 2026
3c521a8
Add lan_sender.py: simulated LAN-based car for stack testing without …
haoruizhou May 21, 2026
3f9be9b
Optimize DataStore versioning and reduce allocations. Fix increment _…
haoruizhou May 21, 2026
564adae
Add one-command macOS installer for base station
haoruizhou May 21, 2026
09327e7
Show internal vs external WebSocket client counts
haoruizhou May 21, 2026
7c267ff
Add MacBook LAN-sender CI job and tests
haoruizhou May 21, 2026
563b686
Update telemetry-ci.yml
haoruizhou May 21, 2026
6676428
Make installer cross-platform (Linux/macOS)
haoruizhou May 26, 2026
48bef94
Slackbot missing def, ci
haoruizhou May 29, 2026
4621ab3
Increase batch size and speed up row counting
haoruizhou May 29, 2026
a0f8bc6
Add daily DAQ stats report to Lappy
haoruizhou May 29, 2026
8e65bba
Add light/dark theme and toggle to data-downloadaer
haoruizhou May 31, 2026
9b16e6b
Revert "Increase batch size and speed up row counting"
wfr-data-acquisition May 31, 2026
a6c623c
Align sandbox prompt-guide.txt.example with the active guide and main…
wfr-data-acquisition Jun 4, 2026
5eed9f8
Wire slicks into sandbox via uv and bump default model to MiniMax-M3
wfr-data-acquisition Jun 4, 2026
cc080df
root: pin npm overrides to address quick-win Dependabot alerts
wfr-data-acquisition Jun 4, 2026
89e109a
pecan: bump react-router to ^7.15.0 and add npm overrides for quick-w…
wfr-data-acquisition Jun 4, 2026
ae20e82
flight-recorder: bump react-router-dom to ^7.15.0 and add npm overrides
wfr-data-acquisition Jun 4, 2026
57b0645
grafana-bridge: add npm overrides for quick-win Dependabot alerts
wfr-data-acquisition Jun 4, 2026
897a4e6
charging-dashboard: self-updating Slack TUI from Pecan accumulator
wfr-data-acquisition Jun 4, 2026
a3a6981
Merge origin/dev (Constellation) into charging-dashboard branch
wfr-data-acquisition Jun 4, 2026
58fbaa3
Address PR review: fix slackbot CI smoke-test + harden charge receiver
wfr-data-acquisition Jun 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 197 additions & 0 deletions .github/workflows/macbook-installer-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
name: Base Station Installer CI

on:
push:
branches: [ main, deploy ]
paths:
- 'universal-telemetry-software/deploy/install.sh'
- 'universal-telemetry-software/deploy/docker-compose.macbook-base.yml'
- 'universal-telemetry-software/deploy/.env.macbook'
- 'universal-telemetry-software/deploy/MACBOOK_DEPLOY.md'
- 'universal-telemetry-software/deploy/ci-validate-compose.py'
- 'universal-telemetry-software/deploy/wfr-hotspot.service'
- '.github/workflows/macbook-installer-ci.yml'
pull_request:
branches: [ main, deploy ]
paths:
- 'universal-telemetry-software/deploy/install.sh'
- 'universal-telemetry-software/deploy/docker-compose.macbook-base.yml'
- 'universal-telemetry-software/deploy/.env.macbook'
- 'universal-telemetry-software/deploy/MACBOOK_DEPLOY.md'
- 'universal-telemetry-software/deploy/ci-validate-compose.py'
- 'universal-telemetry-software/deploy/wfr-hotspot.service'
- '.github/workflows/macbook-installer-ci.yml'
workflow_dispatch:

jobs:

shellcheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Run ShellCheck
uses: ludeeus/action-shellcheck@master
with:
scandir: universal-telemetry-software/deploy
env:
SHELLCHECK_OPTS: -s bash -x

- name: Check script syntax
run: |
bash -n universal-telemetry-software/deploy/install.sh
echo "Bash syntax valid"

- name: Verify shebang and URL
run: |
head -1 universal-telemetry-software/deploy/install.sh | grep -q '^#!/.+/bash' && echo "Valid shebang"
grep -q 'https://raw.githubusercontent.com' universal-telemetry-software/deploy/install.sh && echo "GitHub raw URL present"

macos-installer-test:
runs-on: macos-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4

- name: Show Docker version
run: |
docker version || echo "Docker not available on this runner"

- name: Check Docker Desktop status
run: |
if docker info &>/dev/null; then
echo "Docker daemon running"
else
echo "Docker daemon not running - testing script logic only"
fi

- name: Validate compose file
run: |
if command -v docker &>/dev/null && docker info &>/dev/null; then
# Run from repo root so DBC_HOST_PATH (./universal-telemetry-software/deploy/example.dbc) resolves correctly.
docker compose \
--project-directory "${{ github.workspace }}" \
-f universal-telemetry-software/deploy/docker-compose.macbook-base.yml \
--env-file universal-telemetry-software/deploy/.env.macbook \
config --quiet
echo "Compose config valid"
else
echo "Skipping compose validation - Docker not running"
fi

- name: Run install.sh smoke test
run: |
chmod +x universal-telemetry-software/deploy/install.sh
output=$(bash universal-telemetry-software/deploy/install.sh 2>&1 || true)
echo "$output"
if echo "$output" | grep -q "Docker"; then
echo "Script correctly detected Docker state"
fi

linux-e2e:
runs-on: ubuntu-latest
# Docker Engine and git are pre-installed on ubuntu-latest runners.
# install_linux_deps() detects them and skips the apt-get paths.
# Images are pulled from ghcr.io (public) and stack runs in-band.
timeout-minutes: 20
steps:
- uses: actions/checkout@v4

- name: Syntax check
run: bash -n universal-telemetry-software/deploy/install.sh

- name: --help exits 0
run: bash universal-telemetry-software/deploy/install.sh --help

- name: Compose config validates against PR branch files
# Runs from repo root so DBC_HOST_PATH (./universal-telemetry-software/deploy/example.dbc) resolves correctly.
run: |
docker compose \
--project-directory "${{ github.workspace }}" \
-f universal-telemetry-software/deploy/docker-compose.macbook-base.yml \
--env-file universal-telemetry-software/deploy/.env.macbook \
config --quiet
echo "Compose config valid"

- name: Python compose structural check
run: python3 universal-telemetry-software/deploy/ci-validate-compose.py

- name: Run full installer end-to-end
# Clones ~/wfr-base-station from the main branch, pulls images, starts stack.
# Uses sudo docker compose (as install.sh does on Linux) — fine on GHA runners.
run: bash universal-telemetry-software/deploy/install.sh

- name: Assert all core containers are running
run: |
docker ps
docker ps --format '{{.Names}}' | grep -q daq-telemetry || { echo "daq-telemetry not running"; exit 1; }
docker ps --format '{{.Names}}' | grep -q daq-redis || { echo "daq-redis not running"; exit 1; }
docker ps --format '{{.Names}}' | grep -q daq-pecan || { echo "daq-pecan not running"; exit 1; }
echo "All 3 core containers are up"

- name: Wait for Pecan on port 3000
run: |
echo "Polling http://localhost:3000 ..."
for i in $(seq 1 30); do
if curl -sf http://localhost:3000 -o /dev/null; then
echo "Pecan is serving on port 3000"
exit 0
fi
echo " attempt $i/30 — retrying in 3 s"
sleep 3
done
echo "Pecan did not become ready within 90 s"
docker logs daq-pecan || true
exit 1

- name: Wait for telemetry status page on port 8080
run: |
echo "Polling http://localhost:8080 ..."
for i in $(seq 1 30); do
if curl -sf http://localhost:8080 -o /dev/null; then
echo "Status page is serving on port 8080"
exit 0
fi
echo " attempt $i/30 — retrying in 3 s"
sleep 3
done
echo "Telemetry status page did not become ready within 90 s"
docker logs daq-telemetry || true
exit 1

- name: Idempotency — re-run installer over existing clone
run: bash universal-telemetry-software/deploy/install.sh

- name: --hotspot skips gracefully in non-interactive CI
# stdin from /dev/null makes [[ ! -t 0 ]] true → prints skip message, returns 1.
# The outer script still reaches print_success and exits 0.
run: |
output=$(bash universal-telemetry-software/deploy/install.sh --hotspot </dev/null 2>&1) || true
echo "$output"
echo "$output" | grep -q "Not an interactive terminal" \
|| { echo "Expected hotspot-skip message was not found in output"; exit 1; }
echo "Non-interactive hotspot skip verified"

url-accessibility:
runs-on: ubuntu-latest
steps:
- name: Check install.sh raw URL
run: |
HTTP_CODE=$(curl -fsSL -o /dev/null -w "%{http_code}" \
https://raw.githubusercontent.com/Western-Formula-Racing/data-acquisition/main/universal-telemetry-software/deploy/install.sh)
echo "install.sh HTTP $HTTP_CODE"
test "$HTTP_CODE" = "200" || exit 1

- name: Check compose file raw URL
run: |
HTTP_CODE=$(curl -fsSL -o /dev/null -w "%{http_code}" \
https://raw.githubusercontent.com/Western-Formula-Racing/data-acquisition/main/universal-telemetry-software/deploy/docker-compose.macbook-base.yml)
echo "docker-compose.macbook-base.yml HTTP $HTTP_CODE"
test "$HTTP_CODE" = "200" || exit 1

- name: Check .env.macbook raw URL
run: |
HTTP_CODE=$(curl -fsSL -o /dev/null -w "%{http_code}" \
https://raw.githubusercontent.com/Western-Formula-Racing/data-acquisition/main/universal-telemetry-software/deploy/.env.macbook)
echo ".env.macbook HTTP $HTTP_CODE"
test "$HTTP_CODE" = "200" || exit 1
8 changes: 4 additions & 4 deletions .github/workflows/offline-bundle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ jobs:

- name: Pull all required images
run: |
docker pull ghcr.io/western-formula-racing/daq-radio/universal-telemetry:latest
docker pull ghcr.io/western-formula-racing/daq-radio/pecan:latest
docker pull ghcr.io/western-formula-racing/data-acquisition/universal-telemetry:latest
docker pull ghcr.io/western-formula-racing/data-acquisition/pecan:latest
docker pull timescale/timescaledb:latest-pg16
docker pull redis:8.2
docker pull bluenviron/mediamtx:latest
Expand All @@ -54,8 +54,8 @@ jobs:
run: |
cd universal-telemetry-software/deploy
docker save \
ghcr.io/western-formula-racing/daq-radio/universal-telemetry:latest \
ghcr.io/western-formula-racing/daq-radio/pecan:latest \
ghcr.io/western-formula-racing/data-acquisition/universal-telemetry:latest \
ghcr.io/western-formula-racing/data-acquisition/pecan:latest \
timescale/timescaledb:latest-pg16 \
redis:8.2 \
bluenviron/mediamtx:latest \
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/package-cleanup.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:

strategy:
matrix:
package: [universal-telemetry, car-jitsi-client, pecan]
package: [universal-telemetry, car-jitsi-client, pecan, file-uploader, lap-detector, slackbot]

steps:
- name: Delete old package versions
Expand Down
90 changes: 90 additions & 0 deletions .github/workflows/slackbot-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: Slackbot CI

on:
push:
paths:
- 'server/installer/slackbot/**'
- '.github/workflows/slackbot-ci.yml'
pull_request:
paths:
- 'server/installer/slackbot/**'
- '.github/workflows/slackbot-ci.yml'
workflow_dispatch:

jobs:
lint-and-import:
runs-on: ubuntu-latest
defaults:
run:
working-directory: server/installer/slackbot

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: pip
cache-dependency-path: server/installer/slackbot/requirements.txt

- name: Install dependencies
run: pip install -r requirements.txt ruff

- name: Lint
run: ruff check slack_bot.py

- name: Import smoke-test (catches missing def, bad module-level code)
env:
SLACK_APP_TOKEN: xapp-test-000000000000
SLACK_BOT_TOKEN: xoxb-test-000000000000
run: |
python - <<'EOF'
import importlib, sys, types

# Stub out slack_sdk and requests so the import doesn't need real network/tokens
for mod in ["slack_sdk", "slack_sdk.web", "slack_sdk.socket_mode",
"slack_sdk.socket_mode.request", "slack_sdk.socket_mode.response"]:
sys.modules.setdefault(mod, types.ModuleType(mod))

# Minimal stubs the module actually uses at import time
web_mod = sys.modules["slack_sdk.web"]
web_mod.WebClient = lambda **_: None # type: ignore[attr-defined]

sm_mod = sys.modules["slack_sdk.socket_mode"]
class _FakeSMC:
def __init__(self, **_): pass
socket_mode_request_listeners = []
sm_mod.SocketModeClient = _FakeSMC # type: ignore[attr-defined]

import requests as _req
_orig_get = _req.get
def _no_net(*a, **kw): raise RuntimeError("network disabled in CI")
_req.get = _no_net # type: ignore[attr-defined]

bot = importlib.import_module("slack_bot")

# Verify all command handlers are callable
required = ["handle_help", "handle_wx", "handle_agent", "handle_approve",
"handle_aistats", "handle_testimage", "process_events"]
missing = [fn for fn in required if not callable(getattr(bot, fn, None))]
if missing:
print(f"FAIL: missing callables: {missing}")
sys.exit(1)
print("OK: all command handlers are defined and callable")
EOF

docker-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: docker/setup-buildx-action@v3

- name: Build Docker image
uses: docker/build-push-action@v5
with:
context: server/installer/slackbot
push: false
tags: lappy-slackbot:ci
cache-from: type=gha
cache-to: type=gha,mode=max
Loading
Loading