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_clippy_and_rustfmt.rs

//! Guard: the CI test image carries clippy AND rustfmt for the PINNED toolchain (gate lane,
//! 2026-10-25: some lanes' `nordisk-cargo-toolchain:ci2-53fec13a5c1c7f2c` had no clippy for
//! 1.97.1, and none has rustfmt: `cargo fmt` answers "'cargo-fmt' is not installed for the
//! toolchain '1.97.1-x86_64-unknown-linux-gnu'").
//!
//! A bare `rustup component add clippy` lands on whatever toolchain is active while the image
//! builds; the Containerfile names the pinned one, and proves both tools answer before the
//! image is tagged. The runner builds its image only when the tag is missing, so a change to
//! what the image carries moves the tag too.

use std::path::Path;

/// The toolchain dwarves pins (`[build.toolchain] rust_version`).
const PINNED: &str = "1.97.1";

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

#[test]
fn the_pin_this_guard_names_is_the_declared_one() {
    let v = read(".nornir/build-variants.toml");
    let table = v.split("\n[build.toolchain]\n").nth(1).expect("dwarves declares [build.toolchain]");
    assert!(table.contains(&format!("rust_version = \"{PINNED}\"")), "the declared toolchain is {PINNED}");
}

#[test]
fn the_ci_image_adds_clippy_and_rustfmt_for_the_pinned_toolchain() {
    let c = containerfile();
    let add = c
        .split("rustup component add")
        .skip(1)
        .find(|rest| rest.split("&&").next().unwrap_or("").contains(&format!("--toolchain {PINNED}")))
        .unwrap_or_else(|| panic!("a `rustup component add --toolchain {PINNED} …`:\n{c}"));
    let add = add.split("&&").next().unwrap();
    assert!(add.contains("clippy"), "clippy for {PINNED}: {add}");
    assert!(add.contains("rustfmt"), "rustfmt for {PINNED}: {add}");
    assert!(c.contains(&format!("cargo +{PINNED} clippy --version")), "the build proves clippy answers");
    assert!(c.contains(&format!("cargo +{PINNED} fmt --version")), "the build proves rustfmt answers");
}

#[test]
fn the_runner_builds_the_image_under_a_tag_that_carries_the_tools() {
    let r = read("ci/runner.sh");
    let line = r.lines().find(|l| l.starts_with("CI_IMAGE=")).expect("runner.sh sets CI_IMAGE");
    assert!(
        !line.contains(":ci2-") && !line.contains(":ci-"),
        "ci and ci2 were built without rustfmt (and in some lanes without clippy); the runner \
         builds only a missing tag, so it must name a new one: {line}"
    );
    assert!(r.contains("ci/ci-toolchain.Containerfile"), "the runner builds it from the Containerfile");
}