Add Docker build environment and centralize dependency versions#6
Conversation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Provides per-variant build/upload/flash/monitor targets for all 6 hardware variants, plus LittleFS data upload for variants with audio assets. Uses GNU Make define/eval/call to generate targets from a single template. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests compile natively with clang++/g++ — no ESP32 hardware needed. Covers detectors, device tracker state machine, and threat analyzer scoring pipeline (37 cases, 126 assertions) targeting the M5Stick variant's pure-logic headers. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move DetectorTypes.h, Detectors.h, DeviceSignatures.h, EventBus.h, ThreatAnalyzer.h, and TelemetryReporter.h from the M5Stick variant's src/ into a new top-level common/ directory. Update Makefile to pass -I common via build.extra_flags for all variants, update test includes, and fix the M5Stick FQBN (m5stick_c_plus2 -> m5stack_stickc_plus2). Merge AudioEvent (from M5Fire's EventBus.h) into the shared header so other variants can adopt it when migrated. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Delete local copies of EventBus.h, DeviceSignatures.h, ThreatAnalyzer.h, and TelemetryReporter.h from m5fire/src/. Replace legacy ThreatAnalyzer (simple boolean matching) and TelemetryReporter (DynamicJsonDocument with nested objects) implementations with the shared detector-based system. Add ISR-safe deferred event processing with portMUX spinlocks for WiFi, BLE, and threat events. Add ThreatAnalyzer::tick() heartbeat in loop() and shouldAlert gate on triggerAlert(). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Delete local copies of EventBus.h, DeviceSignatures.h, ThreatAnalyzer.h, and TelemetryReporter.h from mini12864/src/. Replace legacy ThreatAnalyzer and TelemetryReporter implementations with shared detector-based system. Add ISR-safe deferred event processing with portMUX spinlocks. Move display notifications (Mini12864DisplayNotifyWifiFrame, ShowAlert) and audio playback to the main loop's deferred handlers. Add tick() heartbeat and shouldAlert gate. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Also fix Makefile build.extra_flags override that was clobbering ESP32 core defines (-DESP32=ESP32 etc). Use build.defines instead, which is included within build.extra_flags and starts empty. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move variant-specific src/ files into sketch directory to follow Arduino convention. Keep Flipper's own TelemetryReporter (line-based protocol for Flipper app). Fix radioType null-check for char array. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Dockerfile builds a debian:bookworm-slim image containing arduino-cli, ESP32 core v3.0.7, all Arduino libraries (version-pinned), doctest.h, and pre-warmed core caches for all 4 FQBNs. Source is bind-mounted at runtime so the image is reusable across branches. Also adds docker-compose.yml (build-all, test, shell, build-variant services), entrypoint.sh (seeds doctest.h into bind-mount), .dockerignore, and Makefile docker-* targets. Fixes a portability bug in test/mocks/Arduino.h: adds <cstdio> for snprintf, which macOS clang resolves transitively but Debian clang-14 does not. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Audit all dependency versions against latest available in the Arduino library index, update where possible, and centralize pins in a single versions.env file consumed by both Makefile (include) and Dockerfile (--build-arg). Version changes: - Base image: debian:bookworm-slim → debian:trixie-slim (Debian 13) - arduino-cli: unpinned → 1.4.1 - ArduinoJson: 7.3.0 → 7.4.2 - NimBLE-Arduino: 2.2.1 → 2.3.7 - M5Unified: 0.2.2 → 0.2.11 - Adafruit SSD1306: 2.5.13 → 2.5.16 - doctest: 2.4.11 → 2.4.12 - ESP32 core: 3.0.7 (unchanged — newer causes IRAM overflow) - U8g2: 2.35.30 (unchanged — already latest in Arduino index) - Adafruit GFX: 1.12.4 (unchanged — already latest) Makefile install-deps now pins library versions from versions.env, matching what the Dockerfile installs. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a containerized arduino-cli build environment with centralized version pinning, while also refactoring the detection/telemetry core into shared common/ headers and introducing host-side unit tests.
Changes:
- Added
versions.env, a new rootMakefile,Dockerfile, anddocker-compose.ymlto support reproducible builds/tests and centralized dependency pinning. - Introduced a shared detector-based threat analysis stack in
common/(newDetectors,ThreatAnalyzer, updatedThreatEventshape, new telemetry JSON format). - Added doctest-based host unit tests plus Arduino mocks to exercise detectors, device tracking, and threat analysis logic.
Reviewed changes
Copilot reviewed 48 out of 52 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| versions.env | Centralized version pins used by Makefile/Docker build args. |
| Dockerfile | New “pre-baked” toolchain image for arduino-cli + ESP32 deps + doctest. |
| docker-compose.yml | New compose helpers to run builds/tests/shell in the container. |
| entrypoint.sh | Entrypoint to seed doctest header into workspace before running commands. |
| Makefile | New arduino-cli build/test orchestration + docker helper targets. |
| .dockerignore | Excludes build artifacts and repo metadata from Docker context. |
| .gitignore | Ignores build output and fetched doctest header. |
| CLAUDE.md | New repository guidance doc (needs alignment with new build/shared-code reality). |
| common/EventBus.h | Extends ThreatEvent (radio/category strings, detector metadata, shouldAlert). |
| common/DetectorTypes.h | New detector flags/types + device tracking constants. |
| common/Detectors.h | New detector functions (SSID patterns, OUI match, BLE UUID/name matching, RSSI modifier). |
| common/DeviceSignatures.h | Shared MAC OUI prefix list used by detectors. |
| common/ThreatAnalyzer.h | New detector-registry-based analyzer + device presence tracking + heartbeat tick. |
| common/TelemetryReporter.h | New JSON telemetry serializer including detector breakdown. |
| test/test_main.cpp | Doctest main entrypoint. |
| test/mocks/Arduino.h | Minimal Arduino mock for host testing (needs _GNU_SOURCE fix). |
| test/eventbus_impl.cpp | Host-side definitions for EventBus static members + mock millis(). |
| test/test_detectors.cpp | Unit tests for detector helpers and detector functions. |
| test/test_device_tracker.cpp | Unit tests for DeviceTracker behavior (states, timeout, eviction). |
| test/test_threat_analyzer.cpp | Unit tests for scoring, subsumption, clamping, alert gating, heartbeat. |
| m5stack/flocksquawk_m5stick/flocksquawk_m5stick.ino | Switches to shared headers; adds deferred BLE processing and gates alerts via shouldAlert. |
| m5stack/flocksquawk_m5stick/src/RadioScanner.h | Adjusts channel hop interval and makes currentWifiChannel volatile. |
| m5stack/flocksquawk_m5stick/src/EventBus.h | Removed variant-local header (now expected from shared/common). |
| m5stack/flocksquawk_m5stick/src/DeviceSignatures.h | Removed variant-local header (now expected from shared/common). |
| m5stack/flocksquawk_m5stick/src/ThreatAnalyzer.h | Removed variant-local header (now expected from shared/common). |
| m5stack/flocksquawk_m5stick/src/TelemetryReporter.h | Removed variant-local header (now expected from shared/common). |
| m5stack/flocksquawk_m5fire/flocksquawk_m5fire.ino | Switches to shared headers; adds deferred event processing and shouldAlert gating. |
| m5stack/flocksquawk_m5fire/src/EventBus.h | Removed variant-local header (now expected from shared/common). |
| m5stack/flocksquawk_m5fire/src/DeviceSignatures.h | Removed variant-local header (now expected from shared/common). |
| m5stack/flocksquawk_m5fire/src/ThreatAnalyzer.h | Removed variant-local header (now expected from shared/common). |
| m5stack/flocksquawk_m5fire/src/TelemetryReporter.h | Removed variant-local header (now expected from shared/common). |
| Mini12864/flocksquawk_mini12864/flocksquawk_mini12864.ino | Switches to shared headers; adds deferred event processing and shouldAlert gating. |
| Mini12864/flocksquawk_mini12864/src/DeviceSignatures.h | Removed variant-local header (now expected from shared/common). |
| Mini12864/flocksquawk_mini12864/src/ThreatAnalyzer.h | Removed variant-local header (now expected from shared/common). |
| Mini12864/flocksquawk_mini12864/src/TelemetryReporter.h | Removed variant-local header (now expected from shared/common). |
| 128x32_OLED/flocksquawk_128x32/flocksquawk_128x32.ino | Switches to shared headers; adds deferred event processing and shouldAlert gating. |
| 128x32_OLED/flocksquawk_128x32/src/EventBus.h | Removed variant-local header (now expected from shared/common). |
| 128x32_OLED/flocksquawk_128x32/src/DeviceSignatures.h | Removed variant-local header (now expected from shared/common). |
| 128x32_OLED/flocksquawk_128x32/src/ThreatAnalyzer.h | Removed variant-local header (now expected from shared/common). |
| 128x32_OLED/flocksquawk_128x32/src/TelemetryReporter.h | Removed variant-local header (now expected from shared/common). |
| 128x32_OLED/flocksquawk_128x32_portable/flocksquawk_128x32_portable.ino | Switches to shared headers; adds deferred event processing, heartbeat beep, shouldAlert gating. |
| 128x32_OLED/flocksquawk_128x32_portable/src/EventBus.h | Removed variant-local header (now expected from shared/common). |
| 128x32_OLED/flocksquawk_128x32_portable/src/DeviceSignatures.h | Removed variant-local header (now expected from shared/common). |
| 128x32_OLED/flocksquawk_128x32_portable/src/ThreatAnalyzer.h | Removed variant-local header (now expected from shared/common). |
| 128x32_OLED/flocksquawk_128x32_portable/src/TelemetryReporter.h | Removed variant-local header (now expected from shared/common). |
| flipper-zero/dev-board-firmware/flocksquawk-flipper/flocksquawk-flipper.ino | Switches to shared headers; defers event processing; gates alerts via shouldAlert. |
| flipper-zero/dev-board-firmware/flocksquawk-flipper/src/RadioScanner.h | New Flipper-specific scanner header with ESP32-S2 BLE gating. |
| flipper-zero/dev-board-firmware/flocksquawk-flipper/src/TelemetryReporter.h | New Flipper telemetry reporter interface (UART protocol). |
| flipper-zero/dev-board-firmware/flocksquawk-flipper/src/SoundEngine.h | New Flipper sound engine header (I2S). |
| flipper-zero/dev-board-firmware/src/EventBus.h | Removed variant-local header (now expected from shared/common). |
| flipper-zero/dev-board-firmware/src/DeviceSignatures.h | Removed variant-local header (now expected from shared/common). |
| flipper-zero/dev-board-firmware/src/ThreatAnalyzer.h | Removed variant-local header (now expected from shared/common). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| services: | ||
| build-all: | ||
| image: flocksquawk-build:latest | ||
| volumes: | ||
| - .:/workspace | ||
| command: make all |
There was a problem hiding this comment.
docker-compose.yml declares only image: flocksquawk-build:latest for all services and has no build: section. As a result, docker compose build (in the PR test plan) will not build this repo’s Dockerfile, and will instead do nothing / attempt to pull the image. Consider adding build: . (and optionally image:) to one or all services, or adjusting the documented/tested workflow to use make docker-build-image / docker build explicitly.
| // Use a unique-ish MAC based on first char so each test gets distinct devices | ||
| f.mac[0] = 0xDE; f.mac[1] = 0xAD; | ||
| f.mac[2] = (uint8_t)ssid[0]; f.mac[3] = (uint8_t)ssid[1]; | ||
| f.mac[4] = 0x00; f.mac[5] = 0x01; |
There was a problem hiding this comment.
makeWiFiFrame reads ssid[1] when constructing a pseudo-unique MAC. Several tests call makeWiFiFrame(""), where ssid[1] is out-of-bounds for the empty string literal (UB). Guard the second character access (e.g., use ssid[0] ? ssid[1] : 0) or base the MAC on strlen(ssid) / a hash.
|
|
||
| ## Build System | ||
|
|
||
| This is a pure **Arduino IDE** project (no CMake, PlatformIO, or Makefile). Each hardware variant is a self-contained Arduino sketch that opens directly in the IDE. |
There was a problem hiding this comment.
This documentation claims the repo has no Makefile and is “pure Arduino IDE”, but this PR adds a Makefile and Docker-based build workflow. Please update this section to reflect the new supported build paths (and any impact on Arduino IDE builds) so contributors don’t follow stale instructions.
| This is a pure **Arduino IDE** project (no CMake, PlatformIO, or Makefile). Each hardware variant is a self-contained Arduino sketch that opens directly in the IDE. | |
| This project supports multiple build paths: the **Arduino IDE** / `arduino-cli` for interactive development, a repository **Makefile** for scripted builds, and a **Docker-based** workflow for fully containerized, reproducible builds. Each hardware variant remains a self-contained Arduino sketch that opens directly in the IDE, and the Makefile/Docker workflows do not change Arduino IDE compatibility. |
| #include "EventBus.h" | ||
| #include "DeviceSignatures.h" | ||
| #include "src/RadioScanner.h" | ||
| #include "src/ThreatAnalyzer.h" | ||
| #include "src/TelemetryReporter.h" | ||
| #include "ThreatAnalyzer.h" | ||
| #include "TelemetryReporter.h" |
There was a problem hiding this comment.
These includes now rely on EventBus.h / ThreatAnalyzer.h / etc being discoverable via the include path, but this sketch directory no longer contains those headers (they’re in the repo-level common/ directory). This will break Arduino IDE builds (which won’t add common/ to the include path by default). Either add wrapper headers in the sketch folder, switch to relative includes (e.g. ../../common/...), or explicitly document that builds must go through make/arduino-cli with -I common.
| build-$(1): | ||
| arduino-cli compile \ | ||
| --fqbn $($(1)_FQBN) \ | ||
| --build-property "build.defines=-I$(COMMON_DIR)" \ |
There was a problem hiding this comment.
arduino-cli compile is being passed --build-property "build.defines=-I$(COMMON_DIR)" to add an include path. This relies on platform-specific behavior (and build.defines is typically intended for -D defines). To make builds more robust across cores/platform.txt changes, consider using the dedicated extra-flags properties (e.g. compiler.cpp.extra_flags / compiler.c.extra_flags / build.extra_flags) for -I$(COMMON_DIR).
| --build-property "build.defines=-I$(COMMON_DIR)" \ | |
| --build-property "build.extra_flags=-I$(COMMON_DIR)" \ |
| # FlockSquawk — arduino-cli build system | ||
| # Usage: make help | ||
|
|
||
| # ────────────────────────────────────────────── | ||
| # Dependency versions (shared with Dockerfile) | ||
| # ────────────────────────────────────────────── | ||
| include versions.env | ||
|
|
There was a problem hiding this comment.
PR description/title focus on Docker + version pin centralization, but this PR also introduces a shared common/ code architecture, new host-side unit tests, and changes runtime alerting/concurrency behavior across multiple sketches. Please update the PR description (and test plan) to reflect these broader functional changes so reviewers know what’s in scope.
| if [ -d "test" ] && [ ! -f "${DOCTEST_DST}" ] && [ -f "${DOCTEST_SRC}" ]; then | ||
| cp "${DOCTEST_SRC}" "${DOCTEST_DST}" | ||
| fi |
There was a problem hiding this comment.
This copies doctest.h into the bind-mounted working tree. When the container runs as root (default), this will create a root-owned test/doctest.h on the host, which can be annoying to clean up/edit. Consider avoiding writes to the host mount (e.g., keep doctest in a container-only path and adjust test include path), or run the container with the host UID/GID in the Makefile/compose configuration.
| DOCTEST_URL := https://raw.githubusercontent.com/doctest/doctest/v$(DOCTEST_VERSION)/doctest/doctest.h | ||
|
|
||
| .PHONY: test test-verbose fetch-doctest | ||
|
|
||
| fetch-doctest: test/doctest.h | ||
|
|
||
| test/doctest.h: | ||
| @echo "Fetching doctest.h …" | ||
| curl -sL -o $@ $(DOCTEST_URL) |
There was a problem hiding this comment.
The fetch-doctest Makefile target downloads doctest.h from GitHub at test time without any checksum or signature verification and compiles it into a native test binary. If the remote file or its delivery path is compromised, running make test would execute attacker-controlled code on the developer or CI machine. To reduce this supply-chain risk, vendor a fixed version of doctest.h into the repository or enforce integrity checking (e.g., by validating a known-good hash) before compiling it.
| RUN mkdir -p /tmp/acli \ | ||
| && cd /tmp/acli \ | ||
| && curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh -s -- ${ARDUINO_CLI_VERSION} \ | ||
| && mv bin/arduino-cli /usr/local/bin/ \ | ||
| && rm -rf /tmp/acli |
There was a problem hiding this comment.
The Dockerfile downloads and executes the arduino-cli installer script via curl ... | sh (install.sh) without any checksum or signature verification. If the GitHub repository or network path is compromised, an attacker could run arbitrary code inside your build container and inject a backdoored toolchain or firmware during image build. To harden the supply chain, download a specific release artifact and verify its checksum or signature before executing, or vendor a trusted arduino-cli binary into the image instead of piping a remote script to sh.
| RUN mkdir -p /opt/flocksquawk-deps \ | ||
| && curl -sL -o /opt/flocksquawk-deps/doctest.h \ | ||
| https://raw.githubusercontent.com/doctest/doctest/v${DOCTEST_VERSION}/doctest/doctest.h |
There was a problem hiding this comment.
The Dockerfile also fetches doctest.h directly from GitHub at build time without integrity verification and then uses it in compiled tests. If that remote file is tampered with, malicious C++ code would be compiled and executed in your build environment, allowing an attacker to compromise the host running container builds. Consider vendoring a known-good copy of doctest.h in the repo or verifying a published checksum/signature before using the downloaded header in compilation.
Summary
versions.envas single source of truthversions.envTest plan
docker compose buildand verify image builds successfullydocker compose run build VARIANT=m5stickto verify in-container compilationversions.envvalues match those used in Dockerfile and Makefile🤖 Generated with Claude Code