Container Images¶
Images in use¶
Every reference in .github/workflows/ci.yml,
.github/workflows/docs-bundle.yml, .github/workflows/lint-workflows.yml,
.devcontainer/docker-compose.yml and ci/docker-compose.bdd.yml is pinned by
digest - <repo>@sha256:.... The tag
below is the readable handle; the digest in the files is what actually resolves.
The tag is kept alongside each reference: as a trailing comment on a container:
or Compose image: key, and in the comment above the step for a docker run
invocation, whose line continuation cannot carry one. Digests are deliberately
not repeated here, so there is one authoritative copy per reference and nothing
to drift.
For the same reason the table says what each image is for rather than listing what uses it. Those files already state that executably, and a prose copy of the answer can only fall behind them - which it twice did, once when the lwIP lanes arrived and once when the consumer-smoke ones did. Grep the image name to find its consumers.
| Image | Tag | What it is for |
|---|---|---|
ghcr.io/cososo-ltd/cpputest |
sha-6715942 |
The GCC base: CppUTest, CMake and the analysis tooling for host builds, tests and the static-analysis lanes. Ships include-what-you-use 0.23 (clang_19) matching the cpputest-clang build, so an iwyu run behaves the same in either |
ghcr.io/cososo-ltd/cpputest-clang |
sha-5905aea |
The Clang counterpart to the base, for the portability build and the IWYU lane |
ghcr.io/cososo-ltd/cpputest-freertos |
sha-1739f5b |
Host-TDD of the embedded adapters against fakes, and the integration suites that need a real upstream. Adds the FreeRTOS-Kernel, Plus-TCP, Plus-FAT, lwIP, FatFs, LittleFS, Mbed TLS, CMSIS_6 and CMSIS-FreeRTOS sources, and inherits IWYU from the cpputest base |
ghcr.io/cososo-ltd/cpputest-freertos-cross |
sha-1739f5b |
Cross-building the QEMU BDD targets and running them: everything the host image carries, plus gcc-arm-none-eabi, libnewlib-arm-none-eabi, gdb-multiarch (aliased as arm-none-eabi-gdb), qemu-system-arm, and python3 + behave so one image both builds a target and drives it. Upstream sources sit at /opt/freertos/kernel, /opt/freertos/plus-tcp, /opt/freertos/plus-fat, /opt/lwip, /opt/fatfs, /opt/littlefs, /opt/mbedtls, /opt/cmsis and /opt/cmsis-freertos |
balabit/syslog-ng |
4.8.2 |
The BDD test oracle, one instance per target so lanes cannot contend. Pinned to the 4.8 LTS line; 4.11.0 (latest as of 2026-02-24) regressed by aborting on STATS over the control socket, which crashed the oracle and cascaded to the dev-container network when a target shares the namespace |
ghcr.io/cososo-ltd/behave |
sha-be8da62 |
Driving BDD scenarios against a host-built example: Debian trixie, Python 3.12 and Behave. A QEMU target is driven from cpputest-freertos-cross instead, which carries Behave alongside the emulator |
davidanson/markdownlint-cli2 |
v0.22.1 |
Linting Markdown, in the analyze-markdown lane and in the pre-push command docs/local-checks.md gives. Upstream, so the tag is the release rather than a source commit |
rhysd/actionlint |
1.7.7 |
Validating the workflow files, in the lane that has to live outside ci.yml to survive a break in it. Upstream, same tag form |
ghcr.io/cososo-ltd/mkdocs-mkdoxy |
sha-34173c0 |
Building the documentation site: Doxygen 1.9.4, mkdocs 1.6.1, mkdocs-material 9.7.6, mkdoxy 1.2.8 and mkdocs-github-admonitions-plugin 0.1.1. mkdoxy renders Core/Interface/*.h as native Material API pages |
Docker Compose setup¶
The devcontainer uses Docker Compose (.devcontainer/docker-compose.yml).
VS Code connects to the gcc service (GCC). The clang service is on-demand only:
it starts when you explicitly run a command against it and stops when done.
BDD testing pairs each target with its own syslog-ng oracle so jobs running
in parallel (or developers switching containers) never interfere:
| Target | Behave runner | Oracle |
|---|---|---|
| Linux | behave-linux |
syslog-ng-linux |
| FreeRTOS | inside freertos-target (image carries both QEMU and Behave) |
syslog-ng-freertos |
The gcc service depends on syslog-ng-linux so it starts automatically with the
devcontainer; the freertos-target service depends on syslog-ng-freertos and shares
its network namespace via network_mode: service:syslog-ng-freertos, so QEMU's
slirp gateway 10.0.2.2 NATs to the pair's loopback where syslog-ng-freertos is
listening on 0.0.0.0:5514. Both oracles also alias as the bare hostname syslog-ng
on their network so the existing BDD target wiring (Bdd/Targets/Linux/BddTarget*Config.c,
the BDD step helpers) keeps resolving without per-target host overrides. The
pairs never run together, so the alias collision is academic.
As more cross-compilation targets are added, each gets its own oracle pair in the
same shape (syslog-ng-<target> + a runner service or in-container Behave).
FreeRTOS networking backend selection¶
cpputest-freertos and cpputest-freertos-cross both ship the
FreeRTOS-Plus-TCP and lwIP source trees side-by-side, and a default build
selects both - you can work on either networking stack, and run both
stacks' unit tests, without reconfiguring. The configure says so:
-- SolidSyslog tests: both FreeRTOS networking stacks selected — lwIP and Plus-TCP suites build together.
To work in one stack only, deselect the other with its own switch
(-DSOLIDSYSLOG_PLUSTCP=OFF, or -DSOLIDSYSLOG_LWIPRAW=OFF with
-DSOLIDSYSLOG_LWIPSOCKET=OFF), which drops that platform and its tests
together. The lwIP lint lanes do exactly this. The switch holds under the default
Auto selection; with a named list, leave the platform out of the list instead.
Which BDD ELF a cross build produces is separate, and maintainer-only -
SOLIDSYSLOG_BDD_TARGET=FREERTOS_PLUSTCP (default), FREERTOS_LWIP or
CMSIS_LWIP. It selects a test artefact, not a platform - platforms are named
in SOLIDSYSLOG_PLATFORMS like everything else.
CI runs each cross target in isolation, and all three of
build-freertos-target-plustcp, build-freertos-target-lwip and
build-cmsis-target-lwip are required checks.
Running the clang build locally¶
From a host terminal (not inside the devcontainer):
docker compose -f .devcontainer/docker-compose.yml run --rm clang \
cmake --preset clang-debug
docker compose -f .devcontainer/docker-compose.yml run --rm clang \
cmake --build --preset clang-debug --target junit
Updating an image¶
When a new image tag is available:
- Build and push the new image in the container image repo
- Resolve the new tag to its digest:
docker buildx imagetools inspect ghcr.io/cososo-ltd/<image>:sha-<new> \
--format '{{.Manifest.Digest}}'
Pin exactly what this command prints - the image index digest for a
multi-architecture image, or the single manifest digest for a
single-platform one. Do not substitute a per-platform digest dug out of
--raw: for an index that would nail the reference to one architecture.
Of the images here, only balabit/syslog-ng is genuinely multi-arch
(linux/amd64 and linux/arm64); the rest are linux/amd64 only.
- Update the digest and the accompanying tag comment in every file that
references the image (see table below), plus the tag in
docs/containers.md - Rebuild the devcontainer (
Ctrl+Shift+P→ "Dev Containers: Rebuild Container") and verify locally - Raise a PR: use
chore: bump container image to <sha>as the title
| Image | Files to update |
|---|---|
cpputest |
.devcontainer/docker-compose.yml, .github/workflows/ci.yml, docs/containers.md |
cpputest-clang |
.devcontainer/docker-compose.yml, .github/workflows/ci.yml, docs/containers.md |
cpputest-freertos |
.devcontainer/docker-compose.yml, .github/workflows/ci.yml, docs/containers.md |
cpputest-freertos-cross |
.devcontainer/docker-compose.yml, .github/workflows/ci.yml, ci/docker-compose.bdd.yml, docs/containers.md |
behave |
.devcontainer/docker-compose.yml, ci/docker-compose.bdd.yml, docs/bdd.md, docs/containers.md |
mkdocs-mkdoxy |
.github/workflows/ci.yml, .github/workflows/docs-bundle.yml, docs/containers.md |
markdownlint-cli2 |
.github/workflows/ci.yml, docs/local-checks.md, docs/containers.md |
actionlint |
.github/workflows/lint-workflows.yml, docs/containers.md |
syslog-ng |
.devcontainer/docker-compose.yml, ci/docker-compose.bdd.yml, docs/containers.md |
syslog-ng, markdownlint-cli2 and actionlint are upstream images rather than
ours, published by balabit,
DavidAnson and
rhysd. Step 1 does not apply to any
of them: the version is chosen from what upstream published and pinned, not
built. Read the 4.8 LTS pinning rationale in the first table before moving
syslog-ng.
The cpputest-freertos and cpputest-freertos-cross images both come from
CppUTestFreertosDocker.
A single push to that repo's main rebuilds and publishes both images at
the same sha-<short> tag; always update both rows together.
The mkdocs-mkdoxy image comes from
MkdocsMkdoxyDocker; a push to
that repo's main rebuilds and publishes it at a new sha-<short> tag.
mkdocs-mkdoxy is a CI-only image; it is not a devcontainer service, so the
generic "rebuild the devcontainer and verify" step above does not exercise it.
Verify a tag bump by running the same command the docs-build job runs, from the
repo root, before raising the bump PR:
docker run --rm -v "$PWD:/docs" \
ghcr.io/cososo-ltd/mkdocs-mkdoxy@sha256:<new> mkdocs build --strict
All references to a given image must use the same digest. Never update one without the others. To check that invariant across the digest-pinned files:
grep -rhno 'ghcr.io/cososo-ltd/[a-z-]*@sha256:[0-9a-f]*\|balabit/syslog-ng@sha256:[0-9a-f]*\|davidanson/markdownlint-cli2@sha256:[0-9a-f]*\|rhysd/actionlint@sha256:[0-9a-f]*' \
.github/workflows/ci.yml .github/workflows/docs-bundle.yml \
.github/workflows/lint-workflows.yml \
.devcontainer/docker-compose.yml ci/docker-compose.bdd.yml \
| sed 's/^[0-9]*://' | sort -u
One line per image means every reference agrees; two lines for the same image name means a bump was applied unevenly.
The audit covers digest-pinned references only. docs/local-checks.md invokes
markdownlint-cli2 by tag, deliberately: it is a command a developer types, where
a digest is unreadable and the engine version is the thing worth stating. That
line is out of this check by design, so leave it out when extending the pattern.
Switching to a different container as the devcontainer¶
The available services and the build preset each one drives:
| Service | Use case | BUILD_PRESET |
|---|---|---|
gcc |
Primary C/C++ development (default) | debug |
clang |
Clang-specific debugging / portability | clang-debug |
freertos-host |
TDD of FreeRTOS adapters against host-side fakes | debug |
freertos-target |
Cross builds, on-QEMU runs, GDB attach (Cortex-M3, mps2-an385), BDD against the QEMU target | freertos-cross |
behave-linux |
Linux BDD scenario development (Python + Behave) | (none - cmake skipped) |
To switch:
- In
.devcontainer/devcontainer.json, change"service": "gcc"to the target service name (e.g."freertos-target"). Ctrl+Shift+P→ "Dev Containers: Rebuild Container".- Work normally;
Ctrl+Shift+Band all other tasks pick up the right preset via$BUILD_PRESET.
When done, revert "service" back to "gcc" and rebuild again.
The same VS Code keys work across every service:
Ctrl+Shift+Bruns thebuild and testtask, which adapts to the activeBUILD_PRESET. Underfreertos-crossit builds theSolidSyslogBddTargetELF; underdebug/clang-debugit builds and runsSolidSyslogTests; withBUILD_PRESETempty (thebehave-linuxservice) it runsbehave.F5debugs:Debug SolidSyslogTests (host): works ingcc,clang, andfreertos-host(path resolves via${env:BUILD_PRESET}). Builds first via the samebuild and testtask and stops atmain.Debug FreeRTOS BDD Target (QEMU): works infreertos-target(cortex-debug + arm-none-eabi-gdb + qemu-system-arm). Stops atmainviarunToEntryPoint.- After switching the devcontainer service, pick the matching config from the Run-and-Debug dropdown once. VS Code remembers the last-picked config per workspace (not per container), so the previous choice survives a container rebuild; a stale selection will fail with the wrong debugger type.
Ctrl+Shift+P→ "Tasks: Run Task" →run on QEMU (FreeRTOS): one-shot QEMU run for sanity-checking the build, output to the integrated terminal. Use only in thefreertos-targetservice.
For the FreeRTOS BDD target, see Bdd/Targets/FreeRtos/README.md for build / run / GDB-attach instructions.