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

nordisk/gunnar

xtask/tests/no_kit.rs

//! **gunnar compiles and starts as a server WITHOUT the kit** (the owner, 2026-10-11 05:10Z).
//!
//! The kit — `private-gunnar-ops` (its xtask, units, sitetest, cutovertest, idp-certs and its
//! `terraform/` declaration) and `private-monetize-ops` — drives gunnar from the outside; gunnar
//! never reaches back into it. A cheap gate, no `cargo` run: every manifest, every lockfile and
//! every Rust source of the tree (the detached workspaces included) is read as text, and any
//! dependency on the kit, any path into it, or any file compiled in from it fails here.

use std::path::{Path, PathBuf};

use xtask::one_clone::{lockfiles, manifests};

/// The kit's own checkouts: a path or a git source naming one is a dependency on the kit.
const KIT_CHECKOUTS: &[&str] = &["private-gunnar-ops", "private-monetize-ops"];

/// The kit's crates (`private-gunnar-ops`; its `xtask` shares the name of gunnar's own, so it is
/// caught by its path, not its name).
const KIT_CRATES: &[&str] = &[
    "gunnar-units",
    "gunnar-sitetest",
    "cutovertest",
    "idp-certs",
];

fn root() -> PathBuf {
    Path::new(env!("CARGO_MANIFEST_DIR"))
        .parent()
        .expect("xtask has a parent")
        .to_path_buf()
}

/// A text line that is code, not prose: `#` (TOML) and `//` (Rust) comments are not dependencies.
fn code_lines<'a>(text: &'a str, comment: &'a str) -> impl Iterator<Item = &'a str> + 'a {
    text.lines()
        .map(str::trim)
        .filter(move |l| !l.starts_with(comment))
        .map(move |l| l.split(&format!(" {comment}")).next().unwrap_or(l))
}

/// A path or a source into the kit: one of its checkouts, or its `terraform/` declaration.
fn into_kit(line: &str) -> bool {
    KIT_CHECKOUTS.iter().any(|k| line.contains(k)) || line.contains("terraform/")
}

/// Pure: **what a manifest's text takes from the kit** — a kit crate as a `<name> =` key or a
/// `[dependencies.<name>]` table, or a `path`/`git` into the kit.
fn kit_in_manifest(text: &str) -> Vec<String> {
    let mut out = Vec::new();
    for line in code_lines(text, "#") {
        let key = line
            .split('=')
            .next()
            .unwrap_or("")
            .trim()
            .trim_matches('"');
        let table = line.trim_start_matches('[').trim_end_matches(']');
        for name in KIT_CRATES {
            if key == *name || table.ends_with(&format!(".{name}")) {
                out.push(format!("the kit's crate {name}"));
            }
        }
        if (line.contains("path") || line.contains("git")) && into_kit(line) {
            out.push(format!("a path into the kit: {line}"));
        }
    }
    out
}

/// Pure: **the kit's packages a lockfile holds** — by name, or by a source into the kit.
fn kit_in_lock(text: &str) -> Vec<String> {
    let mut out: Vec<String> = KIT_CRATES
        .iter()
        .filter(|n| text.contains(&format!("name = \"{n}\"")))
        .map(|n| format!("the kit's crate {n}"))
        .collect();
    out.extend(
        text.lines()
            .map(str::trim)
            .filter(|l| l.starts_with("source =") && into_kit(l))
            .map(|l| format!("a source in the kit: {l}")),
    );
    out
}

/// Pure: **what a Rust source compiles in from the kit** — an `include_str!`/`include_bytes!`
/// or a `#[path]` reaching into it.
fn kit_in_source(text: &str) -> Vec<String> {
    code_lines(text, "//")
        .filter(|l| {
            (l.contains("include_str!") || l.contains("include_bytes!") || l.contains("#[path"))
                && into_kit(l)
        })
        .map(|l| format!("compiled in from the kit: {l}"))
        .collect()
}

fn rust_sources(root: &Path, dir: &Path, out: &mut Vec<String>) {
    let Ok(entries) = std::fs::read_dir(dir) else {
        return;
    };
    for entry in entries.flatten() {
        let path = entry.path();
        let name = entry.file_name().to_string_lossy().into_owned();
        if path.is_dir() {
            if name == "target" || name == ".git" || name.starts_with(".iso-out") {
                continue;
            }
            rust_sources(root, &path, out);
        } else if name.ends_with(".rs") {
            if let Ok(rel) = path.strip_prefix(root) {
                out.push(rel.to_string_lossy().replace('\\', "/"));
            }
        }
    }
}

#[test]
fn gunnar_takes_nothing_from_the_kit() {
    let root = root();
    let (ms, ls) = (manifests(&root), lockfiles(&root));
    let mut rs = Vec::new();
    rust_sources(&root, &root, &mut rs);
    // This gate's own red half spells the shapes out.
    rs.retain(|r| r != "xtask/tests/no_kit.rs");
    assert!(
        ms.len() > 20 && ms.iter().any(|m| m == "crates/gunnar-server/Cargo.toml"),
        "the walk sees the tree: {} manifests",
        ms.len()
    );
    assert!(
        ls.iter().any(|l| l == "Cargo.lock") && rs.len() > 100,
        "the walk sees the lock and the sources: {ls:?}, {} sources",
        rs.len()
    );
    let read = |f: &String| std::fs::read_to_string(root.join(f)).unwrap_or_default();
    let mut found = Vec::new();
    found.extend(ms.iter().flat_map(|m| {
        kit_in_manifest(&read(m))
            .into_iter()
            .map(move |p| format!("{m}: {p}"))
    }));
    found.extend(ls.iter().flat_map(|l| {
        kit_in_lock(&read(l))
            .into_iter()
            .map(move |p| format!("{l}: {p}"))
    }));
    found.extend(rs.iter().flat_map(|r| {
        kit_in_source(&read(r))
            .into_iter()
            .map(move |p| format!("{r}: {p}"))
    }));
    assert!(
        found.is_empty(),
        "gunnar takes from the kit — it must compile and start as a server without it:\n  {}",
        found.join("\n  ")
    );
}

/// The red half: each shape is SEEN, and prose about the kit is not mistaken for it.
#[test]
fn a_dependency_on_the_kit_is_reported_in_each_shape() {
    assert_eq!(
        kit_in_manifest("gunnar-units = { path = \"../../private-gunnar-ops/units\" }").len(),
        2
    );
    assert_eq!(
        kit_in_manifest("[dependencies.idp-certs]\nversion = \"0.1\""),
        vec!["the kit's crate idp-certs".to_string()]
    );
    assert_eq!(
        kit_in_manifest("xtask = { path = \"../private-gunnar-ops/xtask\" }").len(),
        1,
        "the kit's xtask, by its path"
    );
    assert_eq!(
        kit_in_manifest("ops = { git = \"ssh://git@github.com/x/private-monetize-ops\" }").len(),
        1
    );
    assert_eq!(
        kit_in_manifest("tf = { path = \"../ops/terraform/gen\" }").len(),
        1
    );
    assert!(
        kit_in_manifest("# the kit (`private-gunnar-ops/terraform/`) declares the boxes")
            .is_empty(),
        "prose is not a dependency"
    );
    assert!(kit_in_manifest("gunnar-server = { path = \"crates/gunnar-server\" }").is_empty());
    assert_eq!(
        kit_in_lock("[[package]]\nname = \"gunnar-sitetest\"\n").len(),
        1
    );
    assert_eq!(kit_in_lock("[[package]]\nname = \"ops\"\nsource = \"git+ssh://git@github.com/x/private-gunnar-ops#abc\"\n").len(), 1);
    assert!(
        kit_in_lock("[[package]]\nname = \"xtask\"\nversion = \"0.1.0\"\n").is_empty(),
        "gunnar's own xtask"
    );
    assert_eq!(kit_in_source("const T: &str = include_str!(\"../../../private-gunnar-ops/terraform/estate.auto.tfvars.json\");").len(), 1);
    assert!(
        kit_in_source("// include_str!(\"../private-gunnar-ops/x\") would be wrong").is_empty()
    );
}