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

nordisk/holger

xtask/src/lone_clone.rs

//! **`cargo xtask lone-clone [--stage]`: a clone of holger ALONE builds, installs and
//! serves** (owner order 2026-10-22; the CI step `lone_clone`, H1 of docs/HOLGER-V2.md).
//!
//! `git clone` of this commit into an empty directory (no sibling beside it, no
//! `.cargo/siblings.toml`, no patch), then exactly what a user types:
//!
//! 1. `cargo build --locked` (the default member, `server/cli`);
//! 2. `cargo install --locked --path server/cli --root <tmp>`, and the `cargo install`
//!    line of every other product of this repository (`holger-downstream`, `holger-cli`),
//!    each bin run once;
//! 3. the installed `holger` is started on free loopback ports and `GET /healthz` must
//!    answer `ok`.
//!
//! The root workspace is the open core (the estate crates are members of `estate/`), so
//! the clone resolves from crates.io alone once every crate of [`ROOT_NEEDS`] is there at
//! the version this workspace locks. Until then the plain run is RED by its reason.
//!
//! `--stage` stands in for that publish: every crate of [`ROOT_NEEDS`] that crates.io does
//! not serve yet is packaged from its sibling checkout (`cargo package`, which rewrites its
//! path dependencies to registry ones exactly as a publish does), unpacked into a local
//! overlay and patched into the clone's build with `--config patch.crates-io.<name>.path=…`.
//! Everything else comes from crates.io, so a sibling whose version crates.io already
//! serves with OLDER content shows up as a compile error here, as it would after the
//! publish. The staged run relocks once (`cargo metadata`: the lock's sibling entries are
//! path entries, see below), then runs steps 1 to 3 with `--locked`.
//!
//! The committed root lock is the crates.io lock (B7, after the owner's publish of
//! 2026-10-26): the root takes no sibling patch in any checkout (only `estate/` and
//! `holger-estate-front/` carry them, in their manifests), so the plain run resolves with
//! `--locked`. A sibling crate whose tree is newer than crates.io reaches the root only
//! after it is published; until then `--stage` stands in for that publish.

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

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

/// **The sibling crates the root resolution holds**, in the order they must be published
/// (a crate after every sibling crate it depends on): name, the version the root locks,
/// and the checkout it is packaged from (relative to the parent of the repository).
/// cargo resolves EVERY root member's dependencies, optional and dev included, so this is
/// the whole set, not only what the default build links. `xtask/tests/lone_clone.rs`
/// (`the_publish_list_is_exactly_the_sibling_crates_of_the_root_resolution`) holds it
/// equal to `cargo metadata --all-features` of the root. Open crates only: nothing from
/// `monetize-impl`.
pub const ROOT_NEEDS: &[(&str, &str, &str)] = &[
    ("nornir-hash", "0.1.1", "edda/crates/nornir-hash"),
    ("nornir-service", "0.1.14", "edda/crates/nornir-service"),
    ("jera", "0.2.1", "edda/crates/jera"),
    ("nornir-escape", "0.1.1", "edda/crates/nornir-escape"),
    (
        "skidbladnir-systemd",
        "0.1.0",
        "Skidbladnir/crates/skidbladnir-systemd",
    ),
    ("skidbladnir", "0.2.3", "Skidbladnir"),
    (
        "nornir-build-thing",
        "0.2.6",
        "edda/crates/nornir-build-thing",
    ),
    (
        "wg-appliance-core",
        "0.1.8",
        "edda/crates/wg-appliance-core",
    ),
    ("korp-installer", "0.2.11", "tunnr/crates/korp-installer"),
    (
        "monetize-product",
        "0.3.0",
        "monetize/crates/monetize-product",
    ),
    ("monetize-embed", "0.3.0", "monetize/crates/monetize-embed"),
    ("appliance-grow", "0.1.1", "edda/crates/appliance-grow"),
    ("appliance-pid1", "0.1.8", "edda/crates/appliance-pid1"),
    ("disk-snapshot", "0.1.1", "edda/crates/disk-snapshot"),
    ("edda-admit", "0.1.0", "edda/crates/admit"),
    ("estate-clock", "0.1.1", "edda/crates/estate-clock"),
    ("facett-core", "0.2.0", "facett/facett-core"),
    ("facett-app", "0.2.0", "facett/facett-app"),
    ("facett-syschart", "0.2.0", "facett/facett-syschart"),
    ("facett-table", "0.2.0", "facett/facett-table"),
    ("guest-host", "0.2.1", "edda/crates/guest-host"),
    ("modgunn", "0.0.8", "modgunn"),
    ("monetize-proto", "0.3.3", "monetize/crates/monetize-proto"),
    (
        "nordisk-pairing",
        "0.5.28",
        "Skidbladnir/crates/installer/nordisk-pairing",
    ),
    (
        "monetize-estate-side",
        "0.3.0",
        "monetize/crates/monetize-estate-side",
    ),
    ("monetize-mail", "0.3.0", "monetize/crates/monetize-mail"),
    (
        "nordisk-narrate",
        "0.1.2",
        "Skidbladnir/crates/installer/nordisk-narrate",
    ),
    (
        "nordisk-twin",
        "0.1.2",
        "Skidbladnir/crates/installer/nordisk-twin",
    ),
    ("nornir-auth-oidc", "0.2.0", "edda/crates/nornir-auth-oidc"),
    (
        "nornir-auth-webauthn",
        "0.2.2",
        "edda/crates/nornir-auth-webauthn",
    ),
    ("nornir-identity", "0.2.2", "edda/crates/nornir-identity"),
    (
        "nornir-identity-session",
        "0.1.1",
        "edda/crates/nornir-identity-session",
    ),
    (
        "nornir-auth-policy",
        "0.1.6",
        "edda/crates/nornir-auth-policy",
    ),
    ("nornir-gate", "0.1.0", "edda/crates/nornir-gate"),
    ("nornir-vault", "0.2.0", "edda/crates/nornir-vault"),
    (
        "nornir-testmatrix",
        "0.2.14",
        "edda/crates/nornir-testmatrix",
    ),
    (
        "plugin-door-wire",
        "0.1.1",
        "Skidbladnir/crates/installer/plugin-door-wire",
    ),
];

/// The list, in publish order.
pub fn publish_first() -> impl Iterator<Item = &'static (&'static str, &'static str, &'static str)>
{
    ROOT_NEEDS.iter()
}

/// What the verb did.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LoneClone {
    Passed,
    Failed(String),
}

/// Clone, build, install and boot; see the module doc.
pub fn lone_clone(root: &Path, stage: bool) -> LoneClone {
    let work = std::env::temp_dir().join(format!("holger-lone-clone-{}", std::process::id()));
    let _ = std::fs::remove_dir_all(&work);
    if let Err(e) = std::fs::create_dir_all(&work) {
        return LoneClone::Failed(format!("create {}: {e}", work.display()));
    }
    let verdict = (|| -> Result<LoneClone> {
        let patches = if stage {
            stage_overlay(root, &work.join("stage"))?
        } else {
            Vec::new()
        };
        let clone = work.join("holger");
        // LFS objects (docs, pictures) are not fetched: the build reads none of them,
        // and a local clone has no LFS remote to fetch them from.
        let ok = Command::new("git")
            .env("GIT_LFS_SKIP_SMUDGE", "1")
            .args(["clone", "--quiet", "--no-local"])
            .arg(root)
            .arg(&clone)
            .status()
            .map(|s| s.success())
            .unwrap_or(false);
        if !ok {
            return Ok(LoneClone::Failed(format!(
                "git clone {} {}: failed",
                root.display(),
                clone.display()
            )));
        }
        if clone.join(".cargo/siblings.toml").exists() {
            return Ok(LoneClone::Failed(
                "the clone carries .cargo/siblings.toml: it must never be committed".into(),
            ));
        }
        Ok(build_install_and_boot(&clone, &patches, stage, &work))
    })();
    let _ = std::fs::remove_dir_all(&work);
    verdict.unwrap_or_else(|e| LoneClone::Failed(format!("{e:#}")))
}

/// Package every crate of [`publish_first`] that crates.io does not serve yet from its
/// sibling checkout, unpack it under `stage`, and return the `--config
/// patch.crates-io.<name>.path=…` arguments. A crate crates.io already serves at that
/// version is taken from crates.io, so the overlay shrinks as the publish goes on.
pub fn stage_overlay(root: &Path, stage: &Path) -> Result<Vec<String>> {
    let parent = root.parent().context("the repository has a parent")?;
    let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
    let target = stage.join("target");
    std::fs::create_dir_all(stage)?;
    let mut todo: Vec<(&str, &str, &str)> = Vec::new();
    for &(name, version, dir) in publish_first() {
        if on_crates_io(name, version) {
            println!("lone-clone: {name} {version} is on crates.io, not staged");
            continue;
        }
        let manifest = parent.join(dir).join("Cargo.toml");
        if !manifest.is_file() {
            bail!(
                "--stage packages {name} from {}, which is not checked out",
                manifest.display()
            );
        }
        todo.push((name, version, dir));
    }
    let mut out = Vec::new();
    let mut earlier: Vec<(String, PathBuf)> = Vec::new();
    for g in package_groups(&todo) {
        // Only what this group's crates name: a patch for anything else would move
        // the sibling's own resolution and break its --locked.
        let mut needs = std::collections::BTreeSet::new();
        for &(_, _, dir) in &g.crates {
            needs.extend(dependency_names(
                &std::fs::read_to_string(parent.join(dir).join("Cargo.toml")).unwrap_or_default(),
            ));
        }
        let patches: Vec<String> = earlier
            .iter()
            .filter(|(n, _)| needs.contains(n))
            .flat_map(|(n, d)| patch_args(n, d))
            .collect();
        let names: Vec<&str> = g.crates.iter().map(|c| c.0).collect();
        let o = package_group_command(&cargo, parent, &g, &target, &patches)
            .output()
            .with_context(|| format!("cargo package {}", names.join(" ")))?;
        if !o.status.success() {
            bail!(
                "cargo package {} (in {}): {}",
                names.join(" "),
                parent.join(g.repo).display(),
                String::from_utf8_lossy(&o.stderr).trim()
            );
        }
        for &(name, version, _) in &g.crates {
            let file = target
                .join("package")
                .join(format!("{name}-{version}.crate"));
            if !file.is_file() {
                bail!("cargo package {name} left no {} — the publish list names {version}; is the sibling at another version?", file.display());
            }
            let ok = Command::new("tar")
                .arg("-xzf")
                .arg(&file)
                .arg("-C")
                .arg(stage)
                .status()?
                .success();
            if !ok {
                bail!("unpack {}", file.display());
            }
            out.extend(patch_args(name, &stage.join(format!("{name}-{version}"))));
        }
        for &(name, _, dir) in &g.crates {
            earlier.push((name.to_string(), parent.join(dir)));
        }
    }
    Ok(out)
}

/// One sibling workspace's share of the stage: its checkout (`repo`, the first segment of
/// [`ROOT_NEEDS`]'s path) and every crate of it the stage packages, in publish order.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PackageGroup<'a> {
    pub repo: &'a str,
    pub crates: Vec<(&'a str, &'a str, &'a str)>,
}

/// Pure: the staged crates grouped by their sibling workspace (each sibling's workspace is
/// its checkout's root), the workspaces in the order their first crate is published. One
/// `cargo package -p … -p …` per workspace lets cargo resolve a crate against another
/// crate it packages in the same run (A3's 0.3.0 train: monetize-embed needs
/// monetize-product 0.3.0 before crates.io serves it).
pub fn package_groups<'a>(staged: &[(&'a str, &'a str, &'a str)]) -> Vec<PackageGroup<'a>> {
    let mut groups: Vec<PackageGroup<'a>> = Vec::new();
    for &(name, version, dir) in staged {
        let repo = dir.split('/').next().unwrap_or(dir);
        match groups.iter_mut().find(|g| g.repo == repo) {
            Some(g) => g.crates.push((name, version, dir)),
            None => groups.push(PackageGroup {
                repo,
                crates: vec![(name, version, dir)],
            }),
        }
    }
    groups
}

/// Every dependency name a manifest names, of every kind and target.
pub fn dependency_names(manifest: &str) -> std::collections::BTreeSet<String> {
    let mut out = std::collections::BTreeSet::new();
    let Ok(v) = manifest.parse::<toml::Table>() else {
        return out;
    };
    let mut tables: Vec<&toml::Table> = Vec::new();
    for k in ["dependencies", "dev-dependencies", "build-dependencies"] {
        if let Some(t) = v.get(k).and_then(|t| t.as_table()) {
            tables.push(t);
        }
    }
    if let Some(targets) = v.get("target").and_then(|t| t.as_table()) {
        for t in targets.values().filter_map(|t| t.as_table()) {
            for k in ["dependencies", "dev-dependencies", "build-dependencies"] {
                if let Some(d) = t.get(k).and_then(|d| d.as_table()) {
                    tables.push(d);
                }
            }
        }
    }
    for t in tables {
        for (key, val) in t {
            let name = val.get("package").and_then(|p| p.as_str()).unwrap_or(key);
            out.insert(name.to_string());
        }
    }
    out
}

/// The `cargo package` of one [`PackageGroup`]: in the sibling's own checkout, over its
/// workspace manifest, `--locked`, one `-p` per crate; the `.crate`s into `target`.
/// `earlier`: the `--config patch…` arguments of every crate staged before this group
/// (other sibling workspaces), pointing at its sibling checkout (the path the
/// sibling's own lock records, so `--locked` holds), so a crate that needs an
/// unpublished crate of another workspace resolves it.
pub fn package_group_command(
    cargo: &str,
    parent: &Path,
    g: &PackageGroup<'_>,
    target: &Path,
    earlier: &[String],
) -> Command {
    let mut c = Command::new(cargo);
    c.current_dir(parent.join(g.repo))
        .args([
            "package",
            "--quiet",
            "--locked",
            "--no-verify",
            "--allow-dirty",
            "--manifest-path",
        ])
        .arg(parent.join(g.repo).join("Cargo.toml"))
        .arg("--target-dir")
        .arg(target);
    for (name, _, _) in &g.crates {
        c.args(["-p", name]);
    }
    c.args(earlier);
    c
}

/// Does crates.io serve `name` at `version`? The sparse index through `curl` (xtask
/// has no HTTP client to spend), or `cargo info` where there is no `curl` (the
/// toolchain container). Unreachable counts as no: the crate is then staged.
fn on_crates_io(name: &str, version: &str) -> bool {
    match Command::new("curl")
        .args(["-sf", "-A", "nordisk-dep-audit"])
        .arg(index_url(name))
        .output()
    {
        Ok(o) if o.status.success() => {
            String::from_utf8_lossy(&o.stdout).contains(&format!("\"vers\":\"{version}\""))
        }
        Ok(_) => false,
        Err(_) => {
            let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
            Command::new(cargo)
                .current_dir(std::env::temp_dir())
                .args(["info", "--quiet", "--registry", "crates-io"])
                .arg(format!("{name}@{version}"))
                .stdout(std::process::Stdio::null())
                .stderr(std::process::Stdio::null())
                .status()
                .map(|s| s.success())
                .unwrap_or(false)
        }
    }
}

/// Pure: the sparse-index URL of a crate.
pub fn index_url(name: &str) -> String {
    let n = name.to_lowercase();
    let p = match n.len() {
        1 => format!("1/{n}"),
        2 => format!("2/{n}"),
        3 => format!("3/{}/{n}", &n[..1]),
        _ => format!("{}/{}/{n}", &n[..2], &n[2..4]),
    };
    format!("https://index.crates.io/{p}")
}

/// Pure: the two arguments that patch `name` to the unpacked crate at `dir`.
pub fn patch_args(name: &str, dir: &Path) -> [String; 2] {
    [
        "--config".to_string(),
        format!(
            "patch.crates-io.{name}.path={:?}",
            dir.display().to_string()
        ),
    ]
}

/// **The products a clone of holger alone installs** (HOL-PRODUCTS): each product row of this
/// repository, its `cargo install` line (`holger_recipe::twins::PRODUCTS`) and the bin it
/// leaves. The console (`holger-ui`) is the holger-ui repository's, so it is not here.
pub fn open_rows() -> Vec<(String, &'static str, String)> {
    let declared = holger_recipe::variants::variants();
    holger_recipe::twins::PRODUCTS
        .iter()
        .filter(|r| r.name != holger_recipe::variants::HOLGER_UI)
        .map(|r| {
            let v = declared
                .iter()
                .find(|v| v.name == r.name)
                .unwrap_or_else(|| panic!("product `{}` has no row", r.name));
            (
                r.name.to_string(),
                r.install,
                v.bin.clone().unwrap_or_default(),
            )
        })
        .collect()
}

/// The argv of the cargo steps (pure, for the tests): the relock of a staged run,
/// `cargo build --locked`, then every product's `cargo install` line ([`open_rows`]),
/// each into `install_root`.
pub fn cargo_steps(
    target: &Path,
    install_root: &Path,
    patches: &[String],
    stage: bool,
) -> Vec<Vec<String>> {
    let t = target.display().to_string();
    let mut steps = Vec::new();
    if stage {
        let mut relock = vec![
            "metadata".to_string(),
            "--format-version".into(),
            "1".into(),
        ];
        relock.extend(patches.iter().cloned());
        steps.push(relock);
    }
    let mut build = vec![
        "build".to_string(),
        "--locked".into(),
        "--target-dir".into(),
        t.clone(),
    ];
    build.extend(patches.iter().cloned());
    steps.push(build);
    for (_, line, _) in open_rows() {
        // `cargo install …` as the user types it, minus `cargo`.
        let mut install: Vec<String> = line.split_whitespace().skip(1).map(String::from).collect();
        install.extend([
            "--root".to_string(),
            install_root.display().to_string(),
            "--target-dir".into(),
            t.clone(),
        ]);
        install.extend(patches.iter().cloned());
        steps.push(install);
    }
    steps
}

fn build_install_and_boot(clone: &Path, patches: &[String], stage: bool, work: &Path) -> LoneClone {
    let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
    // The clone's own target dir unless the caller names one: a shared target dir only
    // saves compile time, it changes nothing about what resolves.
    let target: PathBuf = std::env::var_os("HOLGER_LONE_TARGET_DIR")
        .map(PathBuf::from)
        .unwrap_or_else(|| clone.join("target"));
    let installed = work.join("installed");
    println!(
        "lone-clone: {} ({} staged crate(s))",
        clone.display(),
        patches.len() / 2
    );
    for args in cargo_steps(&target, &installed, patches, stage) {
        // The overlay's `--config patch…` pairs are counted above, not printed.
        let shown: Vec<&str> = args
            .iter()
            .map(String::as_str)
            .take_while(|a| *a != "--config")
            .collect();
        println!("lone-clone: cargo {}", shown.join(" "));
        let out = Command::new(&cargo)
            .current_dir(clone)
            .env_remove("CARGO_TARGET_DIR")
            .args(&args)
            .stdout(if args[0] == "metadata" {
                std::process::Stdio::null()
            } else {
                std::process::Stdio::inherit()
            })
            .status();
        match out {
            Ok(s) if s.success() => {}
            Ok(s) => {
                return LoneClone::Failed(format!("`cargo {}` in a lone clone exited {s}", args[0]))
            }
            Err(e) => return LoneClone::Failed(format!("{cargo} {}: {e}", args[0])),
        }
    }
    // Every product's bin is installed; each one runs, and the server answers /healthz.
    for (row, line, bin) in open_rows() {
        let path = installed.join("bin").join(&bin);
        if !path.is_file() {
            return LoneClone::Failed(format!("`{line}` (row `{row}`) left no {}", path.display()));
        }
        match Command::new(&path)
            .arg("--help")
            .stdout(std::process::Stdio::null())
            .status()
        {
            Ok(s) if s.success() => println!("lone-clone: row `{row}`: {} runs", path.display()),
            other => {
                return LoneClone::Failed(format!(
                    "{} --help (row `{row}`): {other:?}",
                    path.display()
                ))
            }
        }
    }
    boot(&installed.join("bin/holger"), &work.join("data"))
}

/// Start the built server on free loopback ports and ask `GET /healthz`.
fn boot(bin: &Path, data: &Path) -> LoneClone {
    use std::io::{Read, Write};
    let free = || {
        std::net::TcpListener::bind("127.0.0.1:0")
            .and_then(|l| l.local_addr())
            .map(|a| a.port())
    };
    let (Ok(grpc), Ok(http)) = (free(), free()) else {
        return LoneClone::Failed("no free loopback port".into());
    };
    let child = Command::new(bin)
        .args(["dev-pair", "--data"])
        .arg(data)
        .args([
            "--grpc",
            &format!("127.0.0.1:{grpc}"),
            "--http",
            &format!("127.0.0.1:{http}"),
        ])
        .stdout(std::process::Stdio::null())
        .stderr(std::process::Stdio::null())
        .spawn();
    let mut child = match child {
        Ok(c) => c,
        Err(e) => return LoneClone::Failed(format!("start {}: {e}", bin.display())),
    };
    let mut answer = String::new();
    for _ in 0..120 {
        std::thread::sleep(std::time::Duration::from_millis(500));
        if let Ok(mut s) = std::net::TcpStream::connect(("127.0.0.1", http)) {
            let _ = s.write_all(
                b"GET /healthz HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n",
            );
            let _ = s.read_to_string(&mut answer);
            if !answer.is_empty() {
                break;
            }
        }
    }
    // Our own child only: never a pattern kill.
    let _ = child.kill();
    let _ = child.wait();
    if healthy(&answer) {
        println!(
            "lone-clone: the installed {} answered /healthz on 127.0.0.1:{http}: ok",
            bin.display()
        );
        LoneClone::Passed
    } else {
        LoneClone::Failed(format!("/healthz on 127.0.0.1:{http} answered {answer:?}"))
    }
}

/// Pure: a `200` whose body is `ok`.
pub fn healthy(answer: &str) -> bool {
    answer.starts_with("HTTP/1.1 200")
        && answer.split("\r\n\r\n").nth(1).map(str::trim) == Some("ok")
}

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

    #[test]
    fn the_steps_build_and_install_locked_and_a_staged_run_relocks_first() {
        let t = Path::new("/t");
        let r = Path::new("/r");
        let plain = cargo_steps(t, r, &[], false);
        assert_eq!(plain.len(), 4, "no relock in the plain run: {plain:?}");
        assert_eq!(plain[0], ["build", "--locked", "--target-dir", "/t"]);
        assert_eq!(
            plain[1],
            [
                "install",
                "--locked",
                "--path",
                "server/cli",
                "--root",
                "/r",
                "--target-dir",
                "/t"
            ]
        );
        // Every product of this repository is installed by its own `cargo install` line.
        assert_eq!(
            plain[2],
            [
                "install",
                "--locked",
                "--path",
                "holger-downstream",
                "--root",
                "/r",
                "--target-dir",
                "/t"
            ]
        );
        assert_eq!(
            plain[3],
            [
                "install",
                "--locked",
                "--path",
                "holger-cli",
                "--root",
                "/r",
                "--target-dir",
                "/t"
            ]
        );
        let p = patch_args("nordisk-twin", Path::new("/s/nordisk-twin-0.1.2")).to_vec();
        let staged = cargo_steps(t, r, &p, true);
        assert_eq!(staged.len(), 5);
        assert_eq!(staged[0][0], "metadata");
        for step in &staged {
            assert!(
                step.ends_with(&p),
                "every staged step carries the overlay: {step:?}"
            );
        }
    }

    #[test]
    fn the_lone_clone_installs_every_product_of_this_repository_and_nothing_else() {
        let rows = open_rows();
        assert_eq!(
            rows.iter()
                .map(|(row, _, _)| row.as_str())
                .collect::<Vec<_>>(),
            ["holger", "holger-downstream", "holger-cli"]
        );
        assert_eq!(
            rows[0].2, "holger",
            "the server line installs the default bin"
        );
        assert_eq!(rows[1].2, "holger-downstream");
        assert_eq!(rows[2].2, "holger-cli");
    }

    #[test]
    fn a_staged_crate_is_patched_in_by_name() {
        let [flag, value] = patch_args("nordisk-twin", Path::new("/s/nordisk-twin-0.1.2"));
        assert_eq!(flag, "--config");
        assert_eq!(
            value,
            "patch.crates-io.nordisk-twin.path=\"/s/nordisk-twin-0.1.2\""
        );
    }

    #[test]
    fn healthz_is_a_200_saying_ok() {
        assert!(healthy(
            "HTTP/1.1 200 OK\r\ncontent-type: text/plain\r\ncontent-length: 2\r\n\r\nok"
        ));
        assert!(!healthy("HTTP/1.1 503 Service Unavailable\r\n\r\nok"));
        assert!(!healthy("HTTP/1.1 200 OK\r\n\r\nnot yet"));
        assert!(!healthy(""));
    }

    #[test]
    fn the_publish_list_names_each_crate_once() {
        let all: std::collections::BTreeSet<&str> = publish_first().map(|c| c.0).collect();
        assert_eq!(all.len(), ROOT_NEEDS.len(), "a crate is named twice");
    }

    #[test]
    fn nothing_from_monetize_impl_is_ever_staged_for_publish() {
        for (name, _, dir) in publish_first() {
            assert!(
                !dir.starts_with("monetize-impl/"),
                "{name} is from monetize-impl, which is never published"
            );
        }
    }

    /// T14-FIX, the red as it happened, in a tree laid out like the estate's: `holger` with
    /// its sibling patches ON beside a sibling workspace `mon` with a committed lock. The
    /// stage's `cargo package` of a crate of `mon` leaves `mon`'s lock byte for byte.
    #[test]
    fn the_stage_leaves_a_sibling_workspaces_lock_as_committed() {
        let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
        let t = tempfile::tempdir().unwrap();
        let e = t.path();
        let write = |p: &str, text: &str| {
            let p = e.join(p);
            std::fs::create_dir_all(p.parent().unwrap()).unwrap();
            std::fs::write(p, text).unwrap();
        };
        // holger's sibling patches, switched on (`.cargo/siblings.toml`, as `cargo xtask siblings` writes it).
        write(
            "holger/.cargo/config.toml",
            "include = [{ path = \"siblings.toml\", optional = true }]\n",
        );
        write(
            "holger/.cargo/siblings.toml",
            "[patch.crates-io]\nlone-patched = { path = \"../lone-patched\" }\n",
        );
        write(
            "lone-patched/Cargo.toml",
            "[package]\nname = \"lone-patched\"\nversion = \"0.1.0\"\nedition = \"2021\"\n",
        );
        write("lone-patched/src/lib.rs", "");
        // The sibling workspace (monetize's root), its lock committed without holger's patches.
        write(
            "mon/Cargo.toml",
            "[workspace]\nmembers = [\"crates/proto\"]\nresolver = \"2\"\n",
        );
        write("mon/crates/proto/Cargo.toml", "[package]\nname = \"proto\"\nversion = \"0.1.0\"\nedition = \"2021\"\nlicense = \"MIT\"\ndescription = \"x\"\n");
        write("mon/crates/proto/src/lib.rs", "");
        let ok = Command::new(&cargo)
            .current_dir(e.join("mon"))
            .env("CARGO_NET_OFFLINE", "true")
            .args(["generate-lockfile", "--offline"])
            .status()
            .unwrap();
        assert!(ok.success());
        let committed = std::fs::read(e.join("mon/Cargo.lock")).unwrap();
        let group = PackageGroup {
            repo: "mon",
            crates: vec![("proto", "0.1.0", "mon/crates/proto")],
        };
        let out = package_group_command(&cargo, e, &group, &e.join("stage/target"), &[])
            .env("CARGO_NET_OFFLINE", "true")
            .env_remove("CARGO_TARGET_DIR")
            .output()
            .unwrap();
        assert!(
            out.status.success(),
            "{}",
            String::from_utf8_lossy(&out.stderr)
        );
        assert!(e.join("stage/target/package/proto-0.1.0.crate").is_file());
        let after = std::fs::read_to_string(e.join("mon/Cargo.lock")).unwrap();
        assert_eq!(
            after,
            String::from_utf8(committed).unwrap(),
            "the stage rewrote the sibling's lock with holger's patches"
        );
    }

    #[test]
    fn the_dependency_names_of_a_manifest_cover_every_table_and_rename() {
        let m = "[package]\nname = \"a\"\n[dependencies]\njera = { path = \"x\", version = \"0.2.1\" }\n[dev-dependencies]\ngatling = { package = \"rotaryengine\", version = \"0.1\" }\n[target.'cfg(unix)'.dependencies]\nnix = \"0.31\"\n";
        let n: Vec<String> = dependency_names(m).into_iter().collect();
        assert_eq!(n, ["jera", "nix", "rotaryengine"]);
    }

    /// ★ B4's second publish: skidbladnir 0.2.3 needs jera 0.2.1, and crates.io serves
    /// neither yet. jera is in ANOTHER sibling workspace (edda), so the overlay of
    /// skidbladnir's own `cargo package` cannot see it: a later workspace packages with
    /// the patches of every crate staged before it.
    #[test]
    fn a_later_workspace_packages_against_the_crates_staged_before_it() {
        let staged = [
            ("jera", "0.2.1", "edda/crates/jera"),
            ("skidbladnir", "0.2.3", "Skidbladnir"),
        ];
        let groups = package_groups(&staged);
        // The sibling checkout itself: the path the sibling's own lock records, so
        // `--locked` holds (a patch to the unpacked stage moves the source).
        let earlier: Vec<String> = patch_args("jera", &Path::new("/e").join(staged[0].2)).to_vec();
        let c = package_group_command(
            "cargo",
            Path::new("/e"),
            &groups[1],
            Path::new("/s/target"),
            &earlier,
        );
        let args: Vec<String> = c
            .get_args()
            .map(|a| a.to_string_lossy().into_owned())
            .collect();
        assert!(
            args.windows(2).any(|w| w[0] == "--config"
                && w[1] == "patch.crates-io.jera.path=\"/e/edda/crates/jera\""),
            "skidbladnir's package resolves jera from the stage: {args:?}"
        );
        let first = package_group_command(
            "cargo",
            Path::new("/e"),
            &groups[0],
            Path::new("/s/target"),
            &[],
        );
        assert!(
            !first.get_args().any(|a| a == "--config"),
            "the first workspace has nothing staged before it"
        );
    }

    /// ★ A3's 0.3.0 train: monetize-embed 0.3.0 needs monetize-product 0.3.0, and crates.io
    /// serves neither yet. The stage packages each sibling WORKSPACE once, with every crate
    /// of it that it stages, so cargo resolves the later ones against the earlier ones it
    /// packages in the same run (its local overlay), still `--locked` on the sibling's lock.
    #[test]
    fn the_stage_packages_each_sibling_workspace_once_with_all_its_crates() {
        let staged = [
            (
                "monetize-product",
                "0.3.0",
                "monetize/crates/monetize-product",
            ),
            ("jera", "0.2.1", "edda/crates/jera"),
            ("monetize-embed", "0.3.0", "monetize/crates/monetize-embed"),
            ("draupnir", "0.2.0", "draupnir"),
        ];
        let groups = package_groups(&staged);
        let names: Vec<(&str, Vec<&str>)> = groups
            .iter()
            .map(|g| (g.repo, g.crates.iter().map(|c| c.0).collect()))
            .collect();
        assert_eq!(
            names,
            [
                ("monetize", vec!["monetize-product", "monetize-embed"]),
                ("edda", vec!["jera"]),
                ("draupnir", vec!["draupnir"])
            ]
        );
        let c = package_group_command(
            "cargo",
            Path::new("/e"),
            &groups[0],
            Path::new("/s/target"),
            &[],
        );
        assert_eq!(c.get_current_dir(), Some(Path::new("/e/monetize")));
        let args: Vec<String> = c
            .get_args()
            .map(|a| a.to_string_lossy().into_owned())
            .collect();
        assert!(args.iter().any(|a| a == "--locked"), "{args:?}");
        assert!(
            args.windows(2)
                .any(|w| w[0] == "--manifest-path" && w[1] == "/e/monetize/Cargo.toml"),
            "{args:?}"
        );
        for n in ["monetize-product", "monetize-embed"] {
            assert!(
                args.windows(2).any(|w| w[0] == "-p" && w[1] == n),
                "{n}: {args:?}"
            );
        }
    }

    /// ★ The same, for real: a sibling workspace whose `emb` 0.3.0 needs its `prod` 0.3.0,
    /// neither on any registry (offline). Packaged alone, `emb` cannot be prepared; packaged
    /// with `prod` in one run, both `.crate`s come out and the sibling's lock is untouched.
    #[test]
    fn a_staged_crate_whose_own_dependency_is_staged_too_is_packaged() {
        let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
        let t = tempfile::tempdir().unwrap();
        let e = t.path();
        let write = |p: &str, text: &str| {
            let p = e.join(p);
            std::fs::create_dir_all(p.parent().unwrap()).unwrap();
            std::fs::write(p, text).unwrap();
        };
        write(
            "mon/Cargo.toml",
            "[workspace]\nmembers = [\"crates/prod\", \"crates/emb\"]\nresolver = \"2\"\n",
        );
        write("mon/crates/prod/Cargo.toml", "[package]\nname = \"prod\"\nversion = \"0.3.0\"\nedition = \"2021\"\nlicense = \"MIT\"\ndescription = \"x\"\n");
        write("mon/crates/prod/src/lib.rs", "pub fn p() {}\n");
        write(
            "mon/crates/emb/Cargo.toml",
            "[package]\nname = \"emb\"\nversion = \"0.3.0\"\nedition = \"2021\"\nlicense = \"MIT\"\ndescription = \"x\"\n[dependencies]\nprod = { version = \"0.3.0\", path = \"../prod\" }\n",
        );
        write("mon/crates/emb/src/lib.rs", "pub fn e() { prod::p() }\n");
        let ok = Command::new(&cargo)
            .current_dir(e.join("mon"))
            .env("CARGO_NET_OFFLINE", "true")
            .args(["generate-lockfile", "--offline"])
            .status()
            .unwrap();
        assert!(ok.success());
        let committed = std::fs::read_to_string(e.join("mon/Cargo.lock")).unwrap();
        let staged = [
            ("prod", "0.3.0", "mon/crates/prod"),
            ("emb", "0.3.0", "mon/crates/emb"),
        ];
        let groups = package_groups(&staged);
        assert_eq!(groups.len(), 1);
        let out = package_group_command(&cargo, e, &groups[0], &e.join("stage/target"), &[])
            .env("CARGO_NET_OFFLINE", "true")
            .env_remove("CARGO_TARGET_DIR")
            .output()
            .unwrap();
        assert!(
            out.status.success(),
            "{}",
            String::from_utf8_lossy(&out.stderr)
        );
        for f in ["prod-0.3.0.crate", "emb-0.3.0.crate"] {
            assert!(e.join("stage/target/package").join(f).is_file(), "{f}");
        }
        assert_eq!(
            std::fs::read_to_string(e.join("mon/Cargo.lock")).unwrap(),
            committed,
            "the stage rewrote the sibling's lock"
        );
    }

    #[test]
    fn the_index_url_follows_the_sparse_layout() {
        assert_eq!(index_url("jera"), "https://index.crates.io/je/ra/jera");
        assert_eq!(
            index_url("nordisk-twin"),
            "https://index.crates.io/no/rd/nordisk-twin"
        );
        assert_eq!(index_url("syn"), "https://index.crates.io/3/s/syn");
    }
}