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