Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/dwarves

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");
}