Skip to content

Commit 726ac36

Browse files
Charging dashboard (Slack TUI) + reconcile main→dev keeping Constellation (#71)
* Pulse health mosaic on advance Update status page to visually pulse the newest column when the health mosaic advances and add a unit test for the behavior. * Add setting to toggle plot axis range source DBC and Dynamic y axis selection * Attempt to fix packet indicator; stream overlay update * Accurate speed calc, rpm alias, and loss recovery Overlay speed calculation updated to derive vehicle speed from wheel RPM using gear ratio and wheel radius (replaces naive rpm/500). Telemetry-store rpm/motor_speed signal aliases switched to INV_Fast_Motor_Speed. universal-telemetry-software: add missing_seq_seconds to track when gaps occurred, clear on sequence reset, cap removal of oldest missing entries, record recovery timestamps and use them to mark per-second status correctly, and use a robust TCP read loop that accumulates all chunks. * Add healthy field to /api/health response for Upptime compatibility * Add /api/timescale-health endpoint exposing TimescaleDB data lag for newest season table * Updated docs to reflect the car-side move to systemd * Consolidate WebSocket protocol and add runtime notes * Add agent orientation Update docs * Update docs * Add relay worker and update flight-recorder docs Add a Cloudflare Wrangler/Durable-Object relay backend under flight-recorder/relay-worker (package.json, src/index.ts, tsconfig.json, wrangler.toml) and a README describing deployment and usage as an optional public live-relay for remote viewers. Update flight-recorder documentation and top-level README to mark the app as internal (protected by Cloudflare Zero Trust), clarify store-and-forward behavior, relationship to the old lte-relay approach, optional live-relay semantics (public/tokenless worker, 1 Hz synthetic heartbeat, demo-source behavior), and DBC access/build notes. Also add a LiveRelayService and update WebSocketService and App.tsx to expose guarded controls for WS Relay vs DB forward so live relay can be used independently of database sync. * Add vitest script and LiveRelayService tests * Mark lap-detector as tabled until GPS hardware * Route heartbeat frames and clean session URL TelemetryHandler: treat both UTS (1999) and flight-relay (0x7FD) CAN IDs as diagnostic heartbeats and ingest them under DIAG_MSG_IDS.HEARTBEAT. RadioStatChips now uses the DIAG_MSG_IDS constant instead of a hardcoded '1999' string. Added TelemetryHandler tests to assert UTS and relay heartbeat frames are routed to the heartbeat diagnostic message. LiveRelayService: strip search and hash when building the /session URL to avoid stale query/hash data; updated its test to stub global fetch and ensure sessions can be created from older ingest URLs. Misc: test cleanup unstubs globals after each run. * Move rag_viz.py and rag_vectors.png to server/installer/sandbox * add file-uploader, lap-detector, slackbot to package cleanup matrix * offline bundle: fix image refs from daq-radio to data-acquisition namespace * Add /health endpoint and system health UI Introduce a unified system health endpoint and UI backed by an atomic health snapshot. - README and MACBOOK_DEPLOY: document the new /health endpoint and troubleshooting steps. - src/data.py: add HEALTH_FILE env var; compute detailed health snapshot (components, timescale, redis, UDP listener, car state, version, clock, stats); perform Redis ping and timescale parsing; record uptime and UDP listener bound state; write snapshot atomically to HEALTH_FILE. - src/status_server.py: add /health handler, helpers to load, finalize and synthesize unknown/stale health payloads, and mark stale snapshots when producer_ts is old; introduce HEALTH_STALE_SECONDS config. - status/index.html: replace pipeline WebSocket-driven status with polling /health, add system health UI (CSS/JS) and rendering logic, update metrics/status text and polling cadence. - tests/test_status_server.py: add tests for loading valid, missing, malformed and stale health snapshots and retain 404 test for unknown endpoints. This change enables a simple JSON health probe for monitoring and makes the status page reflect aggregated infrastructure state even when the car is powered off. * Update package references for data-acquisition repo * Use .env.macbook and opt-in compose profiles Switch MacBook deployment to use the committed deploy/.env.macbook via --env-file and make TimescaleDB, media, and cloudflared optional Docker Compose profiles. Add ENABLE_TIMESCALE_LOGGING (default false) to the env file and update docker-compose.macbook-base.yml to honor the env file and include profiles for timescale/media/tunnel. Update docs (top-level README, universal-telemetry-software/README.md, MACBOOK_DEPLOY.md, WHICH_ONE.md, and offline README) to describe the new minimal default stack, show example profile commands, and note which images are optional for offline saves. * Add auto TimescaleDB logging detection Introduce an "auto" mode for ENABLE_TIMESCALE_LOGGING so the service will probe the configured POSTGRES_DSN and enable the Timescale writer only when the DB is reachable. * Add lan_sender.py: simulated LAN-based car for stack testing without hardware (#70) * Optimize DataStore versioning and reduce allocations. Fix increment _version before notifyAll so subscribers see correct version. (#68) * Add one-command macOS installer for base station * Show internal vs external WebSocket client counts Track WebSocket bridge client counts through Redis as total, internal, and external clients. Treat loopback connections such as the local relay upstream as internal so the status page does not count them as dashboard viewers. Refresh the count periodically while the bridge is running, preventing the health snapshot from falling back to "Client count unavailable" after the Redis key expires. Update the status UI to show external/internal counts and add focused regression coverage. * Add MacBook LAN-sender CI job and tests Add a new GitHub Actions job (macbook-lan-sender) to smoke-test the MacBook one-click stack using deploy/docker-compose.macbook-base.yml. The job loads built Docker images, pulls base images, sets up Python, brings up the MacBook stack, starts lan_sender inside the telemetry container, runs pytest against a new test file, and collects/logs artifacts on failure. Also add tests/universal-telemetry-software/tests/test_macbook_base_lan_sender.py which exercise container health, status page, PECAN, telemetry logs, WebSocket CAN messages, and health metrics. Update CI compose validation to include the macbook compose file and add the new job as a dependency for publish-telemetry. * Update telemetry-ci.yml * Make installer cross-platform (Linux/macOS) Add full Linux support and an interactive --hotspot mode to the one-line base-station installer; keep macOS support. The installer now bootstraps git and Docker on Debian/Ubuntu/Raspberry Pi OS, can enable a NetworkManager-based Wi‑Fi AP (wfr-hotspot) and installs a systemd unit for it. CI workflow updated to validate compose from the repo root and run a linux e2e job that syntax-checks the script, validates compose, runs the installer, asserts core containers, checks service ports, verifies idempotency, and ensures non-interactive hotspot behaviour. Documentation (README and MACBOOK_DEPLOY.md) and WHICH_ONE.md updated with Linux/RPi instructions and hotspot notes. Fix .env macbook DBC_HOST_PATH to be repo-root relative and add wfr-hotspot.service file. * Slackbot missing def, ci * Increase batch size and speed up row counting * Add daily DAQ stats report to Lappy Adds !stats command and a 9 AM ET daily auto-post showing rows logged, CAN message count, and testing duration for yesterday and the past 7 days. Pulls from TimescaleDB wfr26 and monitoring tables directly. Also adds psycopg2-binary to slackbot requirements. * Add light/dark theme and toggle to data-downloadaer * Revert "Increase batch size and speed up row counting" This reverts commit 4621ab3. * Align sandbox prompt-guide.txt.example with the active guide and main-branch slicks API - Use generic placeholders (<season_table>, <signal_1>, etc.) instead of team-specific table and signal names so the example is shareable. - Drop the 'monitoring table holds infra metrics' note (team-internal). - Drop the verified Grafana query patterns section (team-internal sensor lists and table names). - Update env-var references: TIMESCALE_TABLE (with TIMESCALE_SEASON fallback) replaces POSTGRES_TABLE; remove INFLUX_* references. - Update slicks examples to match the merged main API: * discover_sensors() now requires start_time and end_time * connect_timescaledb() now has (dsn, schema, table) signature * env-var auto-connect note uses POSTGRES_DSN + TIMESCALE_TABLE - Same overall structure as the active (gitignored) prompt-guide.txt, but generic. * Wire slicks into sandbox via uv and bump default model to MiniMax-M3 Sandbox slicks wiring (matches the slicks v0.3.0 TimescaleDB backend on the timescaledb-migration branch that was merged to main): - server/installer/sandbox/Dockerfile.sandbox: install uv, then use it for both requirements-docker.txt and the editable slicks install from /slicks_src (a docker compose additional_context pointing at the sibling slicks checkout). uv keeps the image layer smaller than pip and the install deterministic. Added a build-time smoke test that imports slicks to catch future regressions early. - server/installer/docker-compose.yml: * sandbox service: pass slicks source as a build-time additional_context and bind-mount the same path at runtime so live source edits are picked up on the next container recreate without an image rebuild (controlled by SLICKS_HOST_PATH env var, default points at /home/ubuntu/projects/slicks). * sandbox service: export POSTGRES_DSN, TIMESCALE_TABLE, TIMESCALE_SEASON, and POSTGRES_TABLE so slicks auto-connects on import. The published PyPI slicks (0.2.3) is the InfluxDB backend and would not be importable as TimescaleDB-aware code; install from local source instead. - server/installer/sandbox/requirements-docker.txt: drop the 'slicks>=0.2.0' PyPI pin. Slicks is now installed editable from the local source above; the pin was pulling the InfluxDB-only release. - server/installer/sandbox/requirements.txt: add psycopg2-binary (slicks depends on it; listing explicitly so the layer order is stable across slicks extras changes). Code-generator Dockerfile fix: - server/installer/sandbox/Dockerfile: the previous version only COPY'd prompt-guide.txt.example, so the active (gitignored) prompt-guide.txt was silently being ignored at every build. Switched to COPY prompt-guide.txt* ./ (with the existing fallback to copy the example when the active file is missing). This restores the team's customization workflow where the active prompt-guide lives in the build context and gets shipped to the container. Model bump: - server/installer/.env.example: ANTHROPIC_MODEL default MiniMax-M2.7 -> MiniMax-M3. - server/installer/sandbox/code_generator.py: same bump in the os.getenv default so a missing env var falls back to M3. Tested end-to-end: code-generator /api/generate-code returns result.status=success with a PNG, slicks.fetch_telemetry() returns 407k rows from a real TimescaleDB window, and the slackbot -> code-gen -> sandbox chain still works. * root: pin npm overrides to address quick-win Dependabot alerts Adds an npm 'overrides' block to the root package.json to force patched versions of five transitive dependencies that had Dependabot alerts: lodash -> ^4.18.0 (CVE-2026-4800, CVE-2026-2950) qs -> ^6.15.2 (CVE-2026-8723) picomatch -> ^2.3.2 (CVE-2026-32233) fast-uri -> ^3.1.2 (CVE-2026-6321, CVE-2026-6322) serialize-javascript -> ^7.0.5 (CVE-2026-34043) Regenerated package-lock.json with 'npm install --package-lock-only' (no node_modules write, no network risk beyond what 'install' needs to resolve). Verified the lock now pins: picomatch 2.3.2 (remaining 4 packages aren't pulled into this lock). * pecan: bump react-router to ^7.15.0 and add npm overrides for quick-win CVEs react-router was a direct dep pinned at ^7.9.3; multiple CVEs (DoS via path expansion, RCE via turbo-stream, XSS via redirects) require >= 7.15.0. Bumping the direct dep range is cleaner than an override (npm errors out on an override that conflicts with a direct dep). Also adds an npm 'overrides' block to force patched transitive deps: lodash -> ^4.18.0 qs -> ^6.15.2 picomatch -> ^2.3.2 fast-uri -> ^3.1.2 serialize-javascript -> ^7.0.5 Regenerated package-lock.json. Verified the lock now pins: lodash 4.18.1 qs 6.15.2 picomatch 2.3.2 fast-uri 3.1.2 serialize-javascript 7.0.5 react-router 7.16.0 * flight-recorder: bump react-router-dom to ^7.15.0 and add npm overrides react-router-dom was a direct dep pinned at ^7.9.3; same CVE class as pecan's react-router (DoS, RCE, XSS via redirects) requires >= 7.15.0. Bumped the direct dep range. Also adds an npm 'overrides' block to force patched transitive deps: lodash -> ^4.18.0 qs -> ^6.15.2 picomatch -> ^2.3.2 fast-uri -> ^3.1.2 serialize-javascript -> ^7.0.5 Regenerated package-lock.json. Verified the lock now pins: lodash 4.18.1 picomatch 2.3.2 fast-uri 3.1.2 serialize-javascript 7.0.5 react-router-dom 7.16.0 * grafana-bridge: add npm overrides for quick-win Dependabot alerts grafana-bridge is a minimal Express app (single dep) but still pulls in the same vulnerable transitive packages. Adds an 'overrides' block to force patched versions: lodash -> ^4.18.0 qs -> ^6.15.2 picomatch -> ^2.3.2 fast-uri -> ^3.1.2 serialize-javascript -> ^7.0.5 Regenerated package-lock.json. Verified qs pinned at 6.15.2. * charging-dashboard: self-updating Slack TUI from Pecan accumulator Adds a charging dashboard that posts one self-updating Slack message (post once, chat.update every 5s, like the file-uploader) while charging the accumulator via the Kvaser bridge on the internal (pecan-dev) build. Bot (server/installer/slackbot): - charge_dashboard.py: TUI renderer (SoC bar, pack/cell/temp/alert lines, per-module sparkline + stats), per-session manager, stdlib HTTP receiver (POST /charging/state behind CF Zero Trust + shared-token auth), heartbeat staleness. - soc_model.py: data-derived SOC/ETA from the wfr26 audit. PackCurrent/BMS SOC are dead sentinels and PackVoltage is absent, so SOC is an OCV->SOC lookup on the limiting cell and ETA integrates a CV taper; phase is from the voltage trend. - Wired into slack_bot.py __main__; compose port/env; Dockerfile. - unittests: 17 (renderer, formatters, session updates, HTTP auth, model). Pecan: - chargingSnapshot.ts: pure, mockable snapshot builder mirroring the accumulator page; sentinel-guards the dead signals; derives pack voltage from the series-cell sum. - useChargingDashboard.ts: 5s poster, gated on VITE_INTERNAL + live Kvaser source (:9081). Never broadcasts the demo relay or replay data. - vitest: 15 (aggregation, alert chips, dead-signal guards, broadcast gate). Co-authored-by: Haorui Zhou <haorui2002@gmail.com> * Address PR review: fix slackbot CI smoke-test + harden charge receiver - slackbot-ci: stub SocketModeRequest/Response on the slack_sdk stub modules (import was failing with 'cannot import name SocketModeRequest') and provision /app/logs so the module-level mkdir doesn't fail on the runner. - slack_bot: require CHARGE_RELAY_TOKEN before starting the charge-dashboard receiver; the port is host-published and must not accept unauthenticated POSTs. - docker-compose.lan-sender-test: set POSTGRES_DSN to the timescaledb service (default DSN points at localhost, which is the base container here). - websocket_bridge: aclose the Redis client via try/finally so a failing set doesn't leak the connection. - redis_utils: correct get_sync_client docstring (default retries=5, ~8s). --------- Co-authored-by: Haorui Zhou <haorui2002@gmail.com>
1 parent 91b9db2 commit 726ac36

112 files changed

Lines changed: 8176 additions & 1159 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
name: Base Station Installer CI
2+
3+
on:
4+
push:
5+
branches: [ main, deploy ]
6+
paths:
7+
- 'universal-telemetry-software/deploy/install.sh'
8+
- 'universal-telemetry-software/deploy/docker-compose.macbook-base.yml'
9+
- 'universal-telemetry-software/deploy/.env.macbook'
10+
- 'universal-telemetry-software/deploy/MACBOOK_DEPLOY.md'
11+
- 'universal-telemetry-software/deploy/ci-validate-compose.py'
12+
- 'universal-telemetry-software/deploy/wfr-hotspot.service'
13+
- '.github/workflows/macbook-installer-ci.yml'
14+
pull_request:
15+
branches: [ main, deploy ]
16+
paths:
17+
- 'universal-telemetry-software/deploy/install.sh'
18+
- 'universal-telemetry-software/deploy/docker-compose.macbook-base.yml'
19+
- 'universal-telemetry-software/deploy/.env.macbook'
20+
- 'universal-telemetry-software/deploy/MACBOOK_DEPLOY.md'
21+
- 'universal-telemetry-software/deploy/ci-validate-compose.py'
22+
- 'universal-telemetry-software/deploy/wfr-hotspot.service'
23+
- '.github/workflows/macbook-installer-ci.yml'
24+
workflow_dispatch:
25+
26+
jobs:
27+
28+
shellcheck:
29+
runs-on: ubuntu-latest
30+
steps:
31+
- uses: actions/checkout@v4
32+
33+
- name: Run ShellCheck
34+
uses: ludeeus/action-shellcheck@master
35+
with:
36+
scandir: universal-telemetry-software/deploy
37+
env:
38+
SHELLCHECK_OPTS: -s bash -x
39+
40+
- name: Check script syntax
41+
run: |
42+
bash -n universal-telemetry-software/deploy/install.sh
43+
echo "Bash syntax valid"
44+
45+
- name: Verify shebang and URL
46+
run: |
47+
head -1 universal-telemetry-software/deploy/install.sh | grep -q '^#!/.+/bash' && echo "Valid shebang"
48+
grep -q 'https://raw.githubusercontent.com' universal-telemetry-software/deploy/install.sh && echo "GitHub raw URL present"
49+
50+
macos-installer-test:
51+
runs-on: macos-latest
52+
timeout-minutes: 30
53+
steps:
54+
- uses: actions/checkout@v4
55+
56+
- name: Show Docker version
57+
run: |
58+
docker version || echo "Docker not available on this runner"
59+
60+
- name: Check Docker Desktop status
61+
run: |
62+
if docker info &>/dev/null; then
63+
echo "Docker daemon running"
64+
else
65+
echo "Docker daemon not running - testing script logic only"
66+
fi
67+
68+
- name: Validate compose file
69+
run: |
70+
if command -v docker &>/dev/null && docker info &>/dev/null; then
71+
# Run from repo root so DBC_HOST_PATH (./universal-telemetry-software/deploy/example.dbc) resolves correctly.
72+
docker compose \
73+
--project-directory "${{ github.workspace }}" \
74+
-f universal-telemetry-software/deploy/docker-compose.macbook-base.yml \
75+
--env-file universal-telemetry-software/deploy/.env.macbook \
76+
config --quiet
77+
echo "Compose config valid"
78+
else
79+
echo "Skipping compose validation - Docker not running"
80+
fi
81+
82+
- name: Run install.sh smoke test
83+
run: |
84+
chmod +x universal-telemetry-software/deploy/install.sh
85+
output=$(bash universal-telemetry-software/deploy/install.sh 2>&1 || true)
86+
echo "$output"
87+
if echo "$output" | grep -q "Docker"; then
88+
echo "Script correctly detected Docker state"
89+
fi
90+
91+
linux-e2e:
92+
runs-on: ubuntu-latest
93+
# Docker Engine and git are pre-installed on ubuntu-latest runners.
94+
# install_linux_deps() detects them and skips the apt-get paths.
95+
# Images are pulled from ghcr.io (public) and stack runs in-band.
96+
timeout-minutes: 20
97+
steps:
98+
- uses: actions/checkout@v4
99+
100+
- name: Syntax check
101+
run: bash -n universal-telemetry-software/deploy/install.sh
102+
103+
- name: --help exits 0
104+
run: bash universal-telemetry-software/deploy/install.sh --help
105+
106+
- name: Compose config validates against PR branch files
107+
# Runs from repo root so DBC_HOST_PATH (./universal-telemetry-software/deploy/example.dbc) resolves correctly.
108+
run: |
109+
docker compose \
110+
--project-directory "${{ github.workspace }}" \
111+
-f universal-telemetry-software/deploy/docker-compose.macbook-base.yml \
112+
--env-file universal-telemetry-software/deploy/.env.macbook \
113+
config --quiet
114+
echo "Compose config valid"
115+
116+
- name: Python compose structural check
117+
run: python3 universal-telemetry-software/deploy/ci-validate-compose.py
118+
119+
- name: Run full installer end-to-end
120+
# Clones ~/wfr-base-station from the main branch, pulls images, starts stack.
121+
# Uses sudo docker compose (as install.sh does on Linux) — fine on GHA runners.
122+
run: bash universal-telemetry-software/deploy/install.sh
123+
124+
- name: Assert all core containers are running
125+
run: |
126+
docker ps
127+
docker ps --format '{{.Names}}' | grep -q daq-telemetry || { echo "daq-telemetry not running"; exit 1; }
128+
docker ps --format '{{.Names}}' | grep -q daq-redis || { echo "daq-redis not running"; exit 1; }
129+
docker ps --format '{{.Names}}' | grep -q daq-pecan || { echo "daq-pecan not running"; exit 1; }
130+
echo "All 3 core containers are up"
131+
132+
- name: Wait for Pecan on port 3000
133+
run: |
134+
echo "Polling http://localhost:3000 ..."
135+
for i in $(seq 1 30); do
136+
if curl -sf http://localhost:3000 -o /dev/null; then
137+
echo "Pecan is serving on port 3000"
138+
exit 0
139+
fi
140+
echo " attempt $i/30 — retrying in 3 s"
141+
sleep 3
142+
done
143+
echo "Pecan did not become ready within 90 s"
144+
docker logs daq-pecan || true
145+
exit 1
146+
147+
- name: Wait for telemetry status page on port 8080
148+
run: |
149+
echo "Polling http://localhost:8080 ..."
150+
for i in $(seq 1 30); do
151+
if curl -sf http://localhost:8080 -o /dev/null; then
152+
echo "Status page is serving on port 8080"
153+
exit 0
154+
fi
155+
echo " attempt $i/30 — retrying in 3 s"
156+
sleep 3
157+
done
158+
echo "Telemetry status page did not become ready within 90 s"
159+
docker logs daq-telemetry || true
160+
exit 1
161+
162+
- name: Idempotency — re-run installer over existing clone
163+
run: bash universal-telemetry-software/deploy/install.sh
164+
165+
- name: --hotspot skips gracefully in non-interactive CI
166+
# stdin from /dev/null makes [[ ! -t 0 ]] true → prints skip message, returns 1.
167+
# The outer script still reaches print_success and exits 0.
168+
run: |
169+
output=$(bash universal-telemetry-software/deploy/install.sh --hotspot </dev/null 2>&1) || true
170+
echo "$output"
171+
echo "$output" | grep -q "Not an interactive terminal" \
172+
|| { echo "Expected hotspot-skip message was not found in output"; exit 1; }
173+
echo "Non-interactive hotspot skip verified"
174+
175+
url-accessibility:
176+
runs-on: ubuntu-latest
177+
steps:
178+
- name: Check install.sh raw URL
179+
run: |
180+
HTTP_CODE=$(curl -fsSL -o /dev/null -w "%{http_code}" \
181+
https://raw.githubusercontent.com/Western-Formula-Racing/data-acquisition/main/universal-telemetry-software/deploy/install.sh)
182+
echo "install.sh HTTP $HTTP_CODE"
183+
test "$HTTP_CODE" = "200" || exit 1
184+
185+
- name: Check compose file raw URL
186+
run: |
187+
HTTP_CODE=$(curl -fsSL -o /dev/null -w "%{http_code}" \
188+
https://raw.githubusercontent.com/Western-Formula-Racing/data-acquisition/main/universal-telemetry-software/deploy/docker-compose.macbook-base.yml)
189+
echo "docker-compose.macbook-base.yml HTTP $HTTP_CODE"
190+
test "$HTTP_CODE" = "200" || exit 1
191+
192+
- name: Check .env.macbook raw URL
193+
run: |
194+
HTTP_CODE=$(curl -fsSL -o /dev/null -w "%{http_code}" \
195+
https://raw.githubusercontent.com/Western-Formula-Racing/data-acquisition/main/universal-telemetry-software/deploy/.env.macbook)
196+
echo ".env.macbook HTTP $HTTP_CODE"
197+
test "$HTTP_CODE" = "200" || exit 1

.github/workflows/offline-bundle.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ jobs:
4343

4444
- name: Pull all required images
4545
run: |
46-
docker pull ghcr.io/western-formula-racing/daq-radio/universal-telemetry:latest
47-
docker pull ghcr.io/western-formula-racing/daq-radio/pecan:latest
46+
docker pull ghcr.io/western-formula-racing/data-acquisition/universal-telemetry:latest
47+
docker pull ghcr.io/western-formula-racing/data-acquisition/pecan:latest
4848
docker pull timescale/timescaledb:latest-pg16
4949
docker pull redis:8.2
5050
docker pull bluenviron/mediamtx:latest
@@ -54,8 +54,8 @@ jobs:
5454
run: |
5555
cd universal-telemetry-software/deploy
5656
docker save \
57-
ghcr.io/western-formula-racing/daq-radio/universal-telemetry:latest \
58-
ghcr.io/western-formula-racing/daq-radio/pecan:latest \
57+
ghcr.io/western-formula-racing/data-acquisition/universal-telemetry:latest \
58+
ghcr.io/western-formula-racing/data-acquisition/pecan:latest \
5959
timescale/timescaledb:latest-pg16 \
6060
redis:8.2 \
6161
bluenviron/mediamtx:latest \

.github/workflows/package-cleanup.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313

1414
strategy:
1515
matrix:
16-
package: [universal-telemetry, car-jitsi-client, pecan]
16+
package: [universal-telemetry, car-jitsi-client, pecan, file-uploader, lap-detector, slackbot]
1717

1818
steps:
1919
- name: Delete old package versions

.github/workflows/slackbot-ci.yml

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
name: Slackbot CI
2+
3+
on:
4+
push:
5+
paths:
6+
- 'server/installer/slackbot/**'
7+
- '.github/workflows/slackbot-ci.yml'
8+
pull_request:
9+
paths:
10+
- 'server/installer/slackbot/**'
11+
- '.github/workflows/slackbot-ci.yml'
12+
workflow_dispatch:
13+
14+
jobs:
15+
lint-and-import:
16+
runs-on: ubuntu-latest
17+
defaults:
18+
run:
19+
working-directory: server/installer/slackbot
20+
21+
steps:
22+
- uses: actions/checkout@v4
23+
24+
- uses: actions/setup-python@v5
25+
with:
26+
python-version: '3.12'
27+
cache: pip
28+
cache-dependency-path: server/installer/slackbot/requirements.txt
29+
30+
- name: Install dependencies
31+
run: pip install -r requirements.txt ruff
32+
33+
- name: Lint
34+
run: ruff check slack_bot.py
35+
36+
- name: Import smoke-test (catches missing def, bad module-level code)
37+
env:
38+
SLACK_APP_TOKEN: xapp-test-000000000000
39+
SLACK_BOT_TOKEN: xoxb-test-000000000000
40+
run: |
41+
# slack_bot.py creates /app/logs at module import; the CI user can't
42+
# write under / by default, so provision it before the smoke-test.
43+
sudo mkdir -p /app/logs && sudo chmod 777 /app/logs
44+
python - <<'EOF'
45+
import importlib, sys, types
46+
47+
# Stub out slack_sdk and requests so the import doesn't need real network/tokens
48+
for mod in ["slack_sdk", "slack_sdk.web", "slack_sdk.socket_mode",
49+
"slack_sdk.socket_mode.request", "slack_sdk.socket_mode.response"]:
50+
sys.modules.setdefault(mod, types.ModuleType(mod))
51+
52+
# Minimal stubs the module actually uses at import time
53+
web_mod = sys.modules["slack_sdk.web"]
54+
web_mod.WebClient = lambda **_: None # type: ignore[attr-defined]
55+
56+
sm_mod = sys.modules["slack_sdk.socket_mode"]
57+
class _FakeSMC:
58+
def __init__(self, **_): pass
59+
socket_mode_request_listeners = []
60+
sm_mod.SocketModeClient = _FakeSMC # type: ignore[attr-defined]
61+
62+
# slack_bot imports SocketModeRequest/Response from these submodules at
63+
# module load; the stubs need the attributes or the import raises.
64+
sys.modules["slack_sdk.socket_mode.request"].SocketModeRequest = type( # type: ignore[attr-defined]
65+
"SocketModeRequest", (), {})
66+
sys.modules["slack_sdk.socket_mode.response"].SocketModeResponse = type( # type: ignore[attr-defined]
67+
"SocketModeResponse", (), {})
68+
69+
import requests as _req
70+
_orig_get = _req.get
71+
def _no_net(*a, **kw): raise RuntimeError("network disabled in CI")
72+
_req.get = _no_net # type: ignore[attr-defined]
73+
74+
bot = importlib.import_module("slack_bot")
75+
76+
# Verify all command handlers are callable
77+
required = ["handle_help", "handle_wx", "handle_agent", "handle_approve",
78+
"handle_aistats", "handle_testimage", "process_events"]
79+
missing = [fn for fn in required if not callable(getattr(bot, fn, None))]
80+
if missing:
81+
print(f"FAIL: missing callables: {missing}")
82+
sys.exit(1)
83+
print("OK: all command handlers are defined and callable")
84+
EOF
85+
86+
docker-build:
87+
runs-on: ubuntu-latest
88+
steps:
89+
- uses: actions/checkout@v4
90+
91+
- uses: docker/setup-buildx-action@v3
92+
93+
- name: Build Docker image
94+
uses: docker/build-push-action@v5
95+
with:
96+
context: server/installer/slackbot
97+
push: false
98+
tags: lappy-slackbot:ci
99+
cache-from: type=gha
100+
cache-to: type=gha,mode=max

0 commit comments

Comments
 (0)