mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-23 15:10:34 +00:00
101 lines
6.6 KiB
Markdown
101 lines
6.6 KiB
Markdown
# Nix CI Docker images
|
|
|
|
This directory builds the Docker images used by xrpld's Linux CI. Each image
|
|
bundles the **exact same toolchain that the Nix development shell provides**
|
|
(see [`docs/build/nix.md`](../../docs/build/nix.md)), so what runs in CI matches
|
|
what developers get locally from `nix develop`.
|
|
|
|
The toolchain (CMake, Ninja, Conan, GCC, Clang, clang-tidy, the
|
|
sanitizer/coverage tools, …) is defined in [`nix/packages.nix`](../packages.nix)
|
|
and assembled for CI by [`nix/ci-env.nix`](../ci-env.nix). The Docker build
|
|
turns that Nix environment into an ordinary container image layered on top of a
|
|
conventional base image (Ubuntu, Debian, RHEL, or `nixos/nix`).
|
|
|
|
## Images
|
|
|
|
The images are built by the [`build-nix-images.yml`](../../.github/workflows/build-nix-images.yml)
|
|
workflow and pushed to `ghcr.io/xrplf/xrpld/nix-<distro>`. The `<distro>` is
|
|
selected through the `BASE_IMAGE` build argument; the base images are the
|
|
**oldest supported version** of each distribution we target:
|
|
|
|
| Image | `BASE_IMAGE` | Notes |
|
|
| ------------ | -------------------------------------------- | -------------------------------------------------- |
|
|
| `nix-nixos` | `nixos/nix:latest` | Build/lint only; binaries are not run (see below). |
|
|
| `nix-ubuntu` | `ubuntu:20.04` | Oldest supported Ubuntu (glibc 2.31). |
|
|
| `nix-debian` | `debian:bookworm` | |
|
|
| `nix-rhel` | `registry.access.redhat.com/ubi9/ubi:latest` | |
|
|
|
|
All images carry the full toolchain on `PATH` (via `/nix/ci-env/bin`) plus the
|
|
CA bundle shipped in the Nix environment, so HTTPS clients (git, curl, Conan)
|
|
work without `ca-certificates` being installed in the base image.
|
|
|
|
## Build stages
|
|
|
|
[`Dockerfile`](./Dockerfile) is a multi-stage build:
|
|
|
|
1. **`builder`** — On a `nixos/nix` builder, evaluate the flake and build the
|
|
CI environment (`nix/ci-env.nix`). The resulting Nix store closure (the
|
|
complete set of store paths the toolchain depends on) is copied into a
|
|
staging directory.
|
|
2. **`final`** — Start from `BASE_IMAGE`, copy in the Nix store closure and the
|
|
`ci-env` symlink tree, and wire up `PATH` and the CA bundle. It then:
|
|
- installs the dynamic linker if the base image lacks one (see
|
|
[How libc is handled](#how-libc-is-handled)),
|
|
- runs [`bin/check-tools.sh`](../../bin/check-tools.sh) to verify every
|
|
expected tool is present and runnable.
|
|
- compiles the C++ test programs in
|
|
[`test_files/cpp/sources/`](./test_files/cpp/sources) with both `g++` and
|
|
`clang++`, and sanitizers, and
|
|
- compiles the Rust test programs in
|
|
[`test_files/rust/sources/`](./test_files/rust/sources) with `rustc`, and
|
|
builds the [`test_files/rust/proc_macro/`](./test_files/rust/proc_macro)
|
|
workspace with `cargo` to exercise proc-macro dylib loading.
|
|
3. **`tester`** — Start again from a clean `BASE_IMAGE` (no Nix toolchain),
|
|
install only the sanitizer runtime libraries
|
|
([`install-sanitizer-libs.sh`](./install-sanitizer-libs.sh)), and run the
|
|
binaries compiled in `final`. This proves the binaries built with the Nix
|
|
toolchain actually run on a vanilla base image. On `nixos/nix` this step is
|
|
skipped (the binaries are patched for a conventional FHS loader).
|
|
4. **Output** — The final image is gated on the tester succeeding: it copies a
|
|
sentinel file out of `tester`, so a failed test run fails the whole build.
|
|
|
|
## How libc is handled
|
|
|
|
The goal is for binaries built in these images to run on the **oldest supported
|
|
base image** (Ubuntu 20.04, glibc 2.31) and newer — without the developer's Nix
|
|
toolchain being present at runtime. Two pieces make that work:
|
|
|
|
- **Compilers linked against an old glibc.** The Nix CI environment does not use
|
|
nixpkgs' current glibc. Instead it pins a 2020 nixpkgs snapshot whose primary
|
|
glibc is **2.31** (matching Ubuntu 20.04), via the `nixpkgs-custom-glibc`
|
|
flake input. GCC, Clang, binutils and compiler-rt are all rebuilt/wrapped
|
|
against this custom glibc (see [`nix/ci-env.nix`](../ci-env.nix)). As a result
|
|
the libraries they emit (`libstdc++`, `libgcc_s`, the sanitizer runtimes)
|
|
reference only symbols available in glibc 2.31.
|
|
|
|
- **An expected dynamic linker in the image.**
|
|
Binaries built in Nix environments reference a dynamic linker from Nix store paths, which won't be present in the base image. However,
|
|
[`loader-path.sh`](./loader-path.sh) reports the expected loader path for the
|
|
current architecture, so we can patch the binaries to use the correct loader.
|
|
|
|
The build then verifies all of this end to end, and the C++ and Rust programs
|
|
go through the same pipeline: each is compiled in `final`, has its `PT_INTERP`
|
|
patched to the target loader, and is then run in the clean `tester` stage to
|
|
confirm it emits the expected diagnostic on a stock base image. The C++ programs
|
|
are in `test_files/cpp/sources/` (a regular binary plus ASan/TSan/UBSan
|
|
variants); the Rust programs are in `test_files/rust/sources/` (a hello binary
|
|
plus panic and overflow-check variants), plus the `test_files/rust/proc_macro/`
|
|
workspace — a crate whose compilation additionally loads a proc-macro dylib, and
|
|
whose resulting binary is patched and run like the others.
|
|
|
|
## Files
|
|
|
|
| File | Purpose |
|
|
| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
| [`./Dockerfile`](./Dockerfile) | Multi-stage build described above. |
|
|
| [`./loader-path.sh`](./loader-path.sh) | Print the dynamic-linker (`PT_INTERP`) path for the current architecture. |
|
|
| [`./test_files/cpp/`](./test_files/cpp) | C++ sanitizer smoke test: sources + compile/run scripts. |
|
|
| [`./test_files/rust/`](./test_files/rust) | Rust smoke test: rustc sources + a cargo proc-macro workspace + compile/run scripts. |
|
|
| [`/bin/check-tools.sh`](../../bin/check-tools.sh) | Verify every expected tools are present and runnable. |
|
|
| [`/bin/install-sanitizer-libs.sh`](../../bin/install-sanitizer-libs.sh) | Install `libasan`/`libtsan`/`libubsan` runtimes on the supported base images. |
|