The Launcher shows the Apps as a 4 x 3 grid of 32 x 32 icons, the selected one's name under it (issue #9). The icons are the website's own, doubled, and two drawn the same way for SSH and the Shell; they are PNGs in assets/icons, turned into arrays by scripts/make_icons.py, which CI checks. The selected tile is the website's: dark blue on cyan, with notched corners. - GridModel (lib/ui, host-tested) holds the selection and the scrolling. - App::badge(): a dot on the tile for unread IRC messages, a Track recording, a Capture running, an SSH session open. - Settings > Launcher: Grid or List. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
2.7 KiB
+++ title = "Build, test and release" description = "Docker is the only tool you need. How the firmware is built, how the host tests run, and what CI does on a pull request and on a tag." weight = 1
[extra] docs = true source = "README.md" tag = "Build" +++
Requirements
Only Docker is needed. PlatformIO and the ESP32 toolchain run inside a container, and are cached in the roro9stack-pio Docker volume. The first build downloads about 1 GB and takes a few minutes.
Build and test (local CI)
scripts/ci.sh
This runs the host-side unit tests (test/, native environment), then builds the firmware. The output is .pio/build/cardputer-adv/firmware.factory.bin.
scripts/make_icons.py turns the Launcher's icons (assets/icons/<App id>.png, 32 × 32, one colour) into the arrays in src/ui/icons.cpp; run it after changing a picture. CI fails when the two don't match.
scripts/coverage.sh runs the same tests with coverage counters and writes a line-by-line report to .pio/coverage/index.html. The badge above is its figure for main: the share of the lines of lib/ that the host tests run. lib/ is the logic that compiles on a PC; lib/SD (the card's driver) and src/ (the Apps, the Services, everything that needs the device) have no host tests and aren't in that figure.
The framework is rebuilt with the TLS settings in platformio.ini (custom_sdkconfig, ADR 0006), so the first build after a fresh checkout takes about 6 minutes; later builds take about 15 seconds when little has changed. The platform knows the framework is already rebuilt by sdkconfig.defaults in the project folder, which it writes and git ignores: delete it and the next build rebuilds the framework. CI keeps that file, a build cache and ccache in its volume (docs/milestones/R1.md).
CI and releases
Gitea Actions (.gitea/workflows/ci.yml, docs/milestones/R1.md) runs the host tests on every push to main, and on a pull request also builds the firmware: changes reach main through pull requests. Pushing a tag v* runs all of it and publishes a release on Gitea with:
roro9stack-<version>.ota, the signed Update File;roro9stack-<version>-factory.bin, the whole flash image for a first install over USB;roro9stack-<version>.elf.gz, to decode crash reports from that build;SHA256SUMS.
CI signs with the project's key, held as a repository secret (ADR 0008). There is one firmware: the Debug Console is in every build, switched off until its owner switches it on (ADR 0010).
scripts/ota_verify.py <file.ota> checks an Update File on a PC the way a device does. scripts/release_build.sh and scripts/release_publish.py are what the workflow runs; they work the same by hand.