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

nordisk/holger

xtask/src/fast.rs

//! **`cargo xtask fast`**: Holger's estate on monetize's real code, its real box binaries in
//! rootless containers, in minutes (monetize v2.0 P1, A24 §8.2; CI lane 2026-10-23).
//!
//! This verb only prepares what is Holger's and hands the run to monetize's `monetize-fast`
//! library (through its binary; holger links no monetize-impl and names no unpublished crate):
//!
//! 1. Holger's two box rows, compiled in the toolchain container with their DECLARED argv
//!    (`estate-front-musl-metered`, `mvp-server-musl-metered`; the profile dropped, as
//!    suite-check drops it), static musl binaries any image runs. The front carries
//!    holger-ui's metered console (the sale check reads its `/login`):
//!    `../holger-ui/dist-metered`, built first when it is not there by holger-ui's own
//!    `cargo xtask build --variant mvp-ui-web-metered`
//!    (trunk on the host, its named exception), and refused as `estate-media` refuses it;
//! 2. Holger's CONTAINER MEDIA: what `holger-front-installer-metered` and
//!    `holger-installer-metered` boot (the program, its arguments, the row's environment, the
//!    estate's declaration from holger-recipe and the korp root it trusts, staged under
//!    `/data`), as JSON;
//! 3. Holger's monetize GUEST (`holger-monetize-guest`, wasm32, unsigned: the run signs it
//!    under its own CA) and the ONE box's kit, built by monetize's `monetize-fast kit`
//!    (monetize-admin, fast-edge, mock-upcloud, the fake Stripe, the UpCloud and Stripe
//!    guests from monetize-impl's build);
//! 4. `monetize-fast --product holger --kit …` on the host: it re-runs itself inside podman's
//!    rootless network namespace and writes the receipt (`target/fast/receipt.txt`, `.json`):
//!    the real ONE box lays, pairs, checks and captures the estate on its own code.
//!
//! `cargo xtask fast [--out DIR] [--budget-s N] [--twin-late-s N] [--no-build]` (the twin boots
//! [`DEFAULT_TWIN_LATE_S`] after the others unless said: A12's order, every run)

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

use anyhow::{bail, Context, Result};

/// The rows whose binaries the boxes run: (row, binary).
pub const ROWS: [(&str, &str); 2] = [
    ("estate-front-musl-metered", "holger-estate-front"),
    ("mvp-server-musl-metered", "holger-mvp-metered"),
];
/// The twin boots this many seconds after the others by default: A12's order (its key reaches
/// the installer after the front's verdict), every run.
pub const DEFAULT_TWIN_LATE_S: u64 = 20;
/// The target those rows declare.
pub const TARGET: &str = "x86_64-unknown-linux-musl";
/// holger-ui's console row (trunk, on the host): the METERED bundle the paid front embeds
/// (A28 M13; `mvp-ui-web` is the open one, with no sign-up, welcome or order page).
pub const CONSOLE_ROW: &str = "mvp-ui-web-metered";
/// Holger's monetize guest: the crate and its module.
pub const GUEST_CRATE: &str = "holger-monetize-guest";
pub const GUEST_WASM: &str = "wasm32-unknown-unknown/release/holger_monetize_guest.wasm";

/// Pure: **Holger's container media**, keyed by the hostname prefix of the machines that boot
/// them (monetize-server's Holger shape: `holger-web-ui-`, `holger-server-`, `holger-twin-`).
pub fn media() -> Vec<(&'static str, serde_json::Value)> {
    let ron = holger_recipe::estate_ron::render(&holger_recipe::estate_ron::EstateRon {
        release: "/data/release".into(),
        ..holger_recipe::estate_ron::EstateRon::on_a_box(None)
    });
    let server = serde_json::json!({
        "container_medium": 1,
        "program": "holger-mvp-metered",
        "args": ["start", "--config", "/data/holger.ron"],
        "env": {"HOLGER_APPLIANCE": "1", "HOLGER_CONSOLE_PATH": "/login", "HOLGER_DATA": "/data/holger", "RUST_LOG": "info"},
        "files": {"holger.ron": ron, "release/korp-root-ca.pem": holger_recipe::estate_ron::KORP_ROOT_PEM},
    });
    let front = serde_json::json!({
        "container_medium": 1,
        "program": "holger-estate-front",
        "args": [],
        "env": {"HOLGER_APPLIANCE_DATA": "/data", "RUST_LOG": "info"},
        "files": {},
    });
    vec![
        ("holger-web-ui-", front),
        ("holger-server-", server.clone()),
        ("holger-twin-", server),
    ]
}

/// **holger-ui's sibling patches, switched on** before its console is built: holger-ui's
/// xtask needs what only the estate's siblings carry (nornir-xtask-support from `../edda`),
/// so its `.cargo/siblings.toml` is written from its `.cargo/estate-siblings.toml`, as the CI
/// runner does for the repo it runs and as [`crate::siblings::refresh`] does for holger
/// (lane N3, T11's red). `None`: holger-ui commits no template (it builds from crates.io).
pub fn console_siblings(ui: &Path) -> Result<Option<crate::siblings::Refresh>> {
    if !ui.join(crate::siblings::TEMPLATE).is_file() {
        return Ok(None);
    }
    crate::siblings::refresh(ui)
        .map(Some)
        .with_context(|| format!("{}", ui.join(crate::siblings::COPY).display()))
}

fn value(args: &[String], flag: &str) -> Option<String> {
    args.iter()
        .position(|a| a == flag)
        .and_then(|i| args.get(i + 1))
        .cloned()
}

/// The verb.
pub fn run(root: &Path, args: &[String]) -> Result<()> {
    if nornir_xtask_support::ci::toolchain_is_here()
        && !std::env::var("NORNIR_CI_HOST").is_ok_and(|v| v == "1")
    {
        bail!("[fast-on-the-host] cargo xtask fast starts containers: run it on the host, not inside the toolchain container");
    }
    let out = value(args, "--out")
        .map(PathBuf::from)
        .unwrap_or_else(|| root.join("target/fast"));
    let budget = value(args, "--budget-s").unwrap_or_else(|| "180".into());
    let twin_late = value(args, "--twin-late-s").unwrap_or_else(|| DEFAULT_TWIN_LATE_S.to_string());
    std::fs::create_dir_all(&out).with_context(|| format!("{}", out.display()))?;
    let parent = root.parent().context("holger's parent")?;
    let monetize = parent.join("monetize");
    if !monetize.join("crates/monetize-fast/Cargo.toml").is_file() {
        bail!(
            "[fast-no-monetize] ../monetize/crates/monetize-fast is not checked out beside holger"
        );
    }

    // 1. the box binaries, from the declared rows, in the toolchain container.
    let container = nornir_xtask_support::ci::Container::from_env(root);
    std::fs::create_dir_all(&container.target)
        .with_context(|| format!("{}", container.target.display()))?;
    let bin_dir = container.target.join(TARGET).join("debug");
    if !args.iter().any(|a| a == "--no-build") {
        // The console the front row compiles in, read where its build.rs reads it inside the
        // container (the sibling beside this checkout, under the `/src` mount).
        let dist = crate::estate_media::front_console_dist(root, None);
        if crate::estate_media::refuse_front_without_console(
            &dist,
            crate::estate_media::holger_ui_head(root).as_deref(),
        )
        .is_err()
        {
            let ui = parent.join("holger-ui");
            match console_siblings(&ui).context("holger-ui's sibling patches")? {
                Some(crate::siblings::Refresh::Lone(missing)) => println!(
                    "fast: holger-ui's sibling patches stay OFF (../{missing} is not checked out)"
                ),
                Some(_) => println!(
                    "fast: holger-ui's sibling patches are ON ({})",
                    ui.join(crate::siblings::COPY).display()
                ),
                None => {}
            }
            println!(
                "fast: build holger-ui's console: cargo xtask build --variant {CONSOLE_ROW} in {}",
                ui.display()
            );
            let s = Command::new(std::env::var("CARGO").unwrap_or_else(|_| "cargo".into()))
                .current_dir(&ui)
                .args(["xtask", "build", "--variant", CONSOLE_ROW])
                .status()
                .with_context(|| format!("cargo xtask build in {}", ui.display()))?;
            if !s.success() {
                bail!("[fast-build] holger-ui's {CONSOLE_ROW}: cargo xtask build exited {s}");
            }
        }
        crate::estate_media::refuse_front_without_console(
            &dist,
            crate::estate_media::holger_ui_head(root).as_deref(),
        )?;
        let set = holger_build_space::build_variant_set()?;
        crate::siblings::refresh(root).context("the sibling patches")?;
        for (row, _) in ROWS {
            let v = set
                .variants
                .iter()
                .find(|v| v.name == row)
                .with_context(|| format!("[fast-row] {row} is not declared"))?;
            let mut a = crate::suite_check::row_args(v, "build");
            a.insert(1, "--locked".into());
            let argv = container.argv(None, &[], "cargo", &a);
            println!("fast: build {row}: cargo {}", a.join(" "));
            let s = Command::new(&argv[0])
                .args(&argv[1..])
                .status()
                .with_context(|| argv[0].clone())?;
            if !s.success() {
                bail!("[fast-build] {row}: cargo exited {s}");
            }
        }
    }
    for (_, bin) in ROWS {
        if !bin_dir.join(bin).is_file() {
            bail!("[fast-build] {} is not built", bin_dir.join(bin).display());
        }
    }
    // 3. Holger's guest, then the ONE box's kit (monetize's).
    let guest = container.target.join(GUEST_WASM);
    let kit_dir = out.join("kit");
    if !args.iter().any(|a| a == "--no-build") {
        let a: Vec<String> = [
            "build",
            "--locked",
            "--manifest-path",
            "estate/Cargo.toml",
            "-p",
            GUEST_CRATE,
            "--release",
            "--target",
            "wasm32-unknown-unknown",
        ]
        .iter()
        .map(|s| s.to_string())
        .collect();
        let argv = container.argv(None, &[], "cargo", &a);
        println!("fast: build {GUEST_CRATE}: cargo {}", a.join(" "));
        let s = Command::new(&argv[0])
            .args(&argv[1..])
            .status()
            .with_context(|| argv[0].clone())?;
        if !s.success() {
            bail!("[fast-build] {GUEST_CRATE}: cargo exited {s}");
        }
        if !guest.is_file() {
            bail!("[fast-build] {} is not built", guest.display());
        }
        let s = Command::new(std::env::var("CARGO").unwrap_or_else(|_| "cargo".into()))
            .current_dir(&monetize)
            .args([
                "run",
                "--quiet",
                "--manifest-path",
                "crates/monetize-fast/Cargo.toml",
                "--bin",
                "monetize-fast",
                "--",
                "kit",
                "--out",
            ])
            .arg(&kit_dir)
            .arg("--parent")
            .arg(parent)
            .arg("--guest")
            .arg(format!("holger={}", guest.display()))
            .status()
            .context("monetize-fast kit")?;
        if !s.success() {
            bail!("[fast-kit] monetize-fast kit exited {s}");
        }
    }

    // 2. Holger's container media (written below, beside the run's arguments).
    let media_dir = out.join("media");
    std::fs::create_dir_all(&media_dir)?;
    let mut run = Command::new(std::env::var("CARGO").unwrap_or_else(|_| "cargo".into()));
    run.current_dir(&monetize)
        .args([
            "run",
            "--quiet",
            "--manifest-path",
            "crates/monetize-fast/Cargo.toml",
            "--bin",
            "monetize-fast",
            "--",
        ])
        .args(["--product", "holger", "--kit"])
        .arg(&kit_dir)
        .arg("--bin-dir")
        .arg(&bin_dir)
        .arg("--out")
        .arg(&out)
        .args(["--budget-s", &budget, "--twin-late-s", &twin_late]);
    for (prefix, m) in media() {
        let f = media_dir.join(format!("{}json", prefix));
        std::fs::write(&f, serde_json::to_vec_pretty(&m)?)?;
        run.arg("--medium").arg(format!("{prefix}={}", f.display()));
    }
    for tree in ["holger", "monetize", "Skidbladnir", "edda"] {
        run.arg("--tree")
            .arg(format!("{tree}={}", parent.join(tree).display()));
    }
    // 4. the run, monetize's.
    println!(
        "fast: monetize-fast --product holger (the receipt goes to {})",
        out.display()
    );
    let s = run.status().context("cargo run monetize-fast")?;
    if !s.success() {
        bail!(
            "[fast-red] the fast test is RED; see {}",
            out.join("receipt.txt").display()
        );
    }
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn holgers_media_boot_what_its_installer_rows_boot() {
        let m = media();
        let prefixes: Vec<&str> = m.iter().map(|(p, _)| *p).collect();
        assert_eq!(
            prefixes,
            vec!["holger-web-ui-", "holger-server-", "holger-twin-"]
        );
        assert_eq!(
            m[1].1["args"],
            serde_json::json!(["start", "--config", "/data/holger.ron"]),
            "holger-installer-metered's cmd"
        );
        assert_eq!(m[1].1["env"]["HOLGER_DATA"], "/data/holger");
        assert!(
            m[1].1["files"]["holger.ron"]
                .as_str()
                .unwrap()
                .contains("ron_plugin_trust_roots: [\"/data/release/korp-root-ca.pem\"]"),
            "the declaration trusts the korp root staged beside it"
        );
        assert_eq!(
            m[0].1["env"]["HOLGER_APPLIANCE_DATA"], "/data",
            "holger-front-installer-metered's environment"
        );
    }

    /// A throwaway estate checkout: holger and holger-ui beside every sibling holger names.
    struct Tree(PathBuf);
    impl Drop for Tree {
        fn drop(&mut self) {
            let _ = std::fs::remove_dir_all(&self.0);
        }
    }

    fn estate(ui_template: Option<&str>) -> Tree {
        use std::sync::atomic::{AtomicU32, Ordering};
        static N: AtomicU32 = AtomicU32::new(0);
        let t = Tree(std::env::temp_dir().join(format!(
            "xtask-fast-ui-{}-{}",
            std::process::id(),
            N.fetch_add(1, Ordering::Relaxed)
        )));
        for s in crate::siblings::SIBLINGS
            .iter()
            .chain(&["holger", "holger-ui"])
        {
            std::fs::create_dir_all(t.0.join(s)).unwrap();
        }
        std::fs::create_dir_all(t.0.join("holger-ui/.cargo")).unwrap();
        if let Some(text) = ui_template {
            std::fs::write(t.0.join("holger-ui").join(crate::siblings::TEMPLATE), text).unwrap();
        }
        t
    }

    #[test]
    fn holger_uis_sibling_patches_are_switched_on_before_its_console_is_built() {
        let template = "[patch.crates-io]\nnornir-xtask-support = { path = \"../edda/crates/nornir-xtask-support\" }\n";
        let t = estate(Some(template));
        let ui = t.0.join("holger-ui");
        assert_eq!(
            console_siblings(&ui).unwrap(),
            Some(crate::siblings::Refresh::Written)
        );
        assert_eq!(
            std::fs::read_to_string(ui.join(crate::siblings::COPY)).unwrap(),
            template,
            "holger-ui's copy is its own template"
        );
        assert_eq!(
            console_siblings(&ui).unwrap(),
            Some(crate::siblings::Refresh::Current)
        );
    }

    #[test]
    fn a_holger_ui_with_no_template_is_left_as_it_is() {
        let t = estate(None);
        let ui = t.0.join("holger-ui");
        assert_eq!(console_siblings(&ui).unwrap(), None);
        assert!(!ui.join(crate::siblings::COPY).exists());
    }

    /// ★ A28 M13: the front's console is holger-ui's METERED bundle, the row that builds
    /// `dist-metered/`, where the front's build.rs and the media door read it.
    #[test]
    fn the_console_row_is_the_metered_bundle_the_front_embeds() {
        assert_eq!(CONSOLE_ROW, "mvp-ui-web-metered");
        let dist =
            crate::estate_media::front_console_dist(std::path::Path::new("/estate/holger"), None);
        assert_eq!(dist, std::path::Path::new("/estate/holger-ui/dist-metered"));
    }

    #[test]
    fn the_box_rows_are_declared() {
        let set = holger_build_space::build_variant_set().unwrap();
        for (row, bin) in ROWS {
            let v = set
                .variants
                .iter()
                .find(|v| v.name == row)
                .unwrap_or_else(|| panic!("{row}"));
            assert_eq!(v.bin.as_deref(), Some(bin));
            assert_eq!(v.target.as_deref(), Some(TARGET));
        }
    }
}