Skip to content

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.json checked into the repository).
  • Run devbox shell to enter an isolated shell with all tools available at the pinned versions.
  • Alternatively, use the CI build image locally via docker run to 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 use latest tags 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 incompatibilities
  • cpp-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-images repository.

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.json and the build image Dockerfiles — never rely on implicit latest resolution.

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.json checked in); use devbox shell for 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.json and build image Dockerfiles; never use latest tags
  • 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