ci/verbs/tests/ci_image_has_the_test_clients.rs
//! Guard: the CI test image carries the clients a workspace TEST starts, and the runner names
//! it by DIGEST (lane OX-RED, 2026-10-27).
//!
//! ci3 (`nordisk-cargo-toolchain:ci3-53fec13a5c1c7f2c`) had no ssh client, no strace and no
//! git-lfs: about 42 gunnar test targets (gunnar-cli's ssh-keygen and push tests,
//! gunnar-lfs, gunnar-server's `audit_read_io` under `strace -f`) were red there and green in
//! a lane-local image that added the three. A tag names another image on each box and after
//! each re-mint, so the runner pins the image by its manifest digest; the tag is only what a
//! box that lacks the digest builds from this Containerfile.
use std::path::Path;
fn read(rel: &str) -> String {
let p = Path::new(env!("CARGO_MANIFEST_DIR")).join("../..").join(rel);
std::fs::read_to_string(&p).unwrap_or_else(|e| panic!("{}: {e}", p.display()))
}
/// The Containerfile's non-comment text, with its line continuations joined.
fn containerfile() -> String {
let s = read("ci/ci-toolchain.Containerfile");
let code: Vec<&str> = s.lines().filter(|l| !l.trim_start().starts_with('#')).collect();
code.join("\n").replace("\\\n", " ")
}
/// The value of the runner's `NAME=` line.
fn runner_var(name: &str) -> String {
let r = read("ci/runner.sh");
let line = r
.lines()
.find(|l| l.starts_with(&format!("{name}=")))
.unwrap_or_else(|| panic!("runner.sh sets {name}"));
line[name.len() + 1..].trim().trim_matches('"').to_string()
}
#[test]
fn the_ci_image_installs_ssh_strace_and_git_lfs_and_proves_they_answer() {
let c = containerfile();
let install = c
.split("apt-get install")
.nth(1)
.and_then(|rest| rest.split("&&").next())
.unwrap_or_else(|| panic!("an `apt-get install …`:\n{c}"));
for pkg in ["openssh-client", "strace", "git-lfs", "libssl-dev"] {
assert!(install.split_whitespace().any(|w| w == pkg), "{pkg} in the install: {install}");
}
for proof in ["ssh -V", "ssh-keygen", "strace -V", "git lfs version"] {
assert!(c.contains(proof), "the build proves `{proof}` answers:\n{c}");
}
}
#[test]
fn the_runner_pins_the_ci_image_by_digest_and_builds_a_new_tag_only_when_it_is_missing() {
let image = runner_var("CI_IMAGE");
let (name, digest) = image.split_once("@sha256:").unwrap_or_else(|| panic!("CI_IMAGE by digest: {image}"));
assert_eq!(name, "localhost/nordisk-cargo-toolchain", "{image}");
assert_eq!(digest.len(), 64, "a full sha256: {digest}");
assert!(digest.bytes().all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase()), "lower hex: {digest}");
let tag = runner_var("CI_TAG");
assert!(tag.starts_with("localhost/nordisk-cargo-toolchain:ci4-"), "ci3 had no ssh/strace/git-lfs, so a new tag: {tag}");
let r = read("ci/runner.sh");
assert!(r.contains("podman image exists \"$CI_IMAGE\""), "the runner asks for the DIGEST first");
assert!(r.contains("-t \"$CI_TAG\""), "and builds the tag only when the digest is missing");
}