Skip to content

Add Docker build environment and centralize dependency versions#6

Merged
f1yaw4y merged 12 commits into
f1yaw4y:mainfrom
dougborg:pr/04-docker-and-deps
Mar 8, 2026
Merged

Add Docker build environment and centralize dependency versions#6
f1yaw4y merged 12 commits into
f1yaw4y:mainfrom
dougborg:pr/04-docker-and-deps

Conversation

@dougborg

@dougborg dougborg commented Feb 2, 2026

Copy link
Copy Markdown
Contributor

Stack order: 4/8 — merge after #5 (PR3: common headers)

Summary

  • Add Docker build environment with all ESP32 dependencies pre-baked
  • Add docker-compose.yml for easy container builds
  • Centralize all version pins in versions.env as single source of truth
  • Update Makefile and Dockerfile to source versions from versions.env

Test plan

  • Run docker compose build and verify image builds successfully
  • Run docker compose run build VARIANT=m5stick to verify in-container compilation
  • Verify versions.env values match those used in Dockerfile and Makefile

🤖 Generated with Claude Code

dougborg and others added 12 commits February 2, 2026 00:48
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>
Copilot AI review requested due to automatic review settings February 2, 2026 08:06

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 root Makefile, Dockerfile, and docker-compose.yml to support reproducible builds/tests and centralized dependency pinning.
  • Introduced a shared detector-based threat analysis stack in common/ (new Detectors, ThreatAnalyzer, updated ThreatEvent shape, 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.

Comment thread docker-compose.yml
Comment on lines +1 to +6
services:
build-all:
image: flocksquawk-build:latest
volumes:
- .:/workspace
command: make all

Copilot AI Feb 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +34
// 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;

Copilot AI Feb 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment thread CLAUDE.md

## 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.

Copilot AI Feb 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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.

Copilot uses AI. Check for mistakes.
Comment on lines +16 to +20
#include "EventBus.h"
#include "DeviceSignatures.h"
#include "src/RadioScanner.h"
#include "src/ThreatAnalyzer.h"
#include "src/TelemetryReporter.h"
#include "ThreatAnalyzer.h"
#include "TelemetryReporter.h"

Copilot AI Feb 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment thread Makefile
build-$(1):
arduino-cli compile \
--fqbn $($(1)_FQBN) \
--build-property "build.defines=-I$(COMMON_DIR)" \

Copilot AI Feb 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Suggested change
--build-property "build.defines=-I$(COMMON_DIR)" \
--build-property "build.extra_flags=-I$(COMMON_DIR)" \

Copilot uses AI. Check for mistakes.
Comment thread Makefile
Comment on lines +1 to +8
# FlockSquawk — arduino-cli build system
# Usage: make help

# ──────────────────────────────────────────────
# Dependency versions (shared with Dockerfile)
# ──────────────────────────────────────────────
include versions.env

Copilot AI Feb 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment thread entrypoint.sh
Comment on lines +9 to +11
if [ -d "test" ] && [ ! -f "${DOCTEST_DST}" ] && [ -f "${DOCTEST_SRC}" ]; then
cp "${DOCTEST_SRC}" "${DOCTEST_DST}"
fi

Copilot AI Feb 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment thread Makefile
Comment on lines +144 to +152
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)

Copilot AI Feb 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment thread Dockerfile
Comment on lines +36 to +40
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

Copilot AI Feb 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment thread Dockerfile
Comment on lines +62 to +64
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

Copilot AI Feb 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
@f1yaw4y f1yaw4y merged commit b197e5f into f1yaw4y:main Mar 8, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants