Tool Chain Guidelines¶
Applies to all projects, independent of programming language or artifact type.
Local Development¶
- The project tool chain is managed by Devbox (
devbox.jsonchecked into the repository). - Run
devbox shellto enter an isolated shell with all tools available at the pinned versions. - Alternatively, use the CI build image locally via
docker runto achieve full parity with CI builds.
CI Build Images¶
Reproducible CI builds require a self-contained build environment. The project uses pre-built,
language-specific Docker images hosted in the GitLab Container Registry at
registry.gitlab.com/hzahnlei/build-images.
The Dockerfiles and CI pipeline for these images are maintained in the dedicated
build-images repository. This project does not embed
build image Dockerfiles.
Core Invariants¶
- No installation during CI runs. Commands such as
apt install,go install,pip install,npm install -g, or equivalent are forbidden inside pipeline jobs. All tools must already be present in the build image. - Self-contained and hermetic. Builds run without outbound internet access. All dependencies are either in the image or in a vendored/cached artifact layer.
- Pinned versions. Every tool is version-pinned in the image's
Dockerfile. Never uselatesttags in pipeline job definitions; pin by image digest in critical stages.
Images¶
| Image | Base | Purpose |
|---|---|---|
cpp-build-glibc |
Debian (glibc) | C++ build, tests, coverage, and static analysis |
cpp-build-musl |
Alpine (musl) | C++ release build of statically linked binaries |
cpp-build-arm-embedded |
Debian | Cross-compilation of ARM Cortex-M firmware |
go-build |
Alpine | Go toolchain and linters |
java-build |
Alpine (Temurin JDK) | JDK and Maven |
typescript-build |
Debian (Node.js) | Node.js and the Playwright browsers |
e2e-test-web-ui |
Debian (Node.js) | End-to-end tests of a web UI |
kicad-build |
Official KiCad image | KiCad CLI and the tools of the CI jobs |
mkdocs |
Debian (Python) | The docs site |
shell-tools |
Alpine | ShellCheck and the command-line tools of the lint jobs |
C++ — Dual-Platform Build¶
C++ code must be compiled and tested on both musl and glibc to catch ABI and portability issues early:
cpp-build-musl(musl libc) — detects musl-specific incompatibilitiescpp-build-glibc(glibc) — matches most production runtime environments
The runtime images of services and tools are distroless; see the Containerization Guidelines.
Image Versioning¶
- Tag images with a date-based version (
YYYY.MM.DD) or a semantic version (v<major>.<minor>). - Rebuild and republish images whenever tool versions change; automate rebuilds via a scheduled or
trigger-based pipeline in the
build-imagesrepository.
Versions¶
- Use the most recent stable versions of tools and libraries when creating a new project.
- Update tool and library versions when revisiting an existing project.
- Track versions explicitly in
devbox.jsonand the build imageDockerfiles — never rely on implicitlatestresolution.
Generated and Third-Party Files¶
- Linters, format checks, and the coverage gate cover the project's own code only. Exclude generated code (e.g. CubeMX output, Protobuf stubs), third-party code, and tool output such as test reports and caches.
- Add the exclusion in the same change that introduces whatever produces the files; added later, it usually follows a failed pipeline.
Directives¶
- Project tool chain managed by Devbox (
devbox.jsonchecked in); usedevbox shellfor local development - Never install tools in pipeline jobs — all tools must be pre-installed in the CI build image
- CI builds are hermetic: no outbound internet access; all dependencies in the image or vendored
- Pin all tool versions in
devbox.jsonand build image Dockerfiles; never uselatesttags - Use the most recent stable tool/library versions for a new project; update them when revisiting an existing one
- Tag build images with a date (
YYYY.MM.DD) or semver; rebuild and republish whenever pinned tool versions change - C++ must be built and tested on both musl (Alpine) and glibc (Debian) images
- Exclude generated code, third-party code, and tool output (reports, caches) from linting, format checks, and the coverage gate; add the exclusion in the same change that introduces whatever produces the files