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

nordisk/gunnar

xtask/src/medium.rs

//! **What a composed installer medium CAN DO, written beside it:
//! `.iso-out/<variant>.medium.json`.**
//!
//! # Why this file exists
//!
//! Until korp-installer 0.1.14 (edda `f4b38bb`) the installer could not read
//! the CD it booted from: the kernel donor had no ATA/SCSI at all (`nuked`
//! vetoes them), and korp-installer probed virtio alone. So every UpCloud
//! re-image uploaded the same ~43 MB medium TWICE — once as the cdrom the
//! firmware boots, once as a virtio disk the installer reads its payload from —
//! and waited for two syncs (mock behaviour 25, ~4 min per re-image).
//!
//! 0.1.14 asks optical drives first, and tunnr `939dbe3` gives the donor
//! ATA + PIIX (i440fx IDE, UpCloud), AHCI (q35) and SR. A medium built from
//! BOTH can be uploaded once. A medium built from either half alone cannot —
//! T16's negative control: a no-ATA medium uploaded only as a cdrom sits in
//! `maintenance` for ever.
//!
//! The re-image (`deploy/upcloud`) must therefore know which kind of medium it
//! holds, and it must learn it from THE MEDIUM, not from a flag an operator has
//! to remember. This is where the medium says so: written at compose time, from
//! the facts that decide it, bound to the ISO by its sha256.
//!
//! # What is judged, and from what
//!
//! * `korp_installer` — the version the APPLIANCE's lock resolved
//!   (`installer_pin::PLACES[0]`), the installer this medium carries.
//! * the kernel — `--build kernel` compiles the donor from the sibling `tunnr`
//!   checkout; its `variants/kernel/tunnr-hardening.config` is read for the CD
//!   block `939dbe3` added. A kernel NAMED from elsewhere, or carried off this
//!   box, has no config this verb can read: it is recorded as UNKNOWN, and an
//!   unknown kernel never reads its own CD.
//!
//! `cd_driver` is true only when both halves are proven. Everything else —
//! including a missing file — means "upload it twice", which is slower and
//! always safe.

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

/// The first korp-installer that asks optical drives first.
pub const FIRST_CD_READING_INSTALLER: (u64, u64, u64) = (0, 1, 14);

/// The kconfig lines tunnr `939dbe3` added to the donor closure. ALL of them,
/// because the two machines this medium meets differ: PIIX is UpCloud's IDE
/// (i440fx), AHCI is the mock's q35, and SR is the cdrom driver on both.
pub const CD_BLOCK: &[&str] = &[
    "CONFIG_ATA=y",
    "CONFIG_ATA_PIIX=y",
    "CONFIG_SATA_AHCI=y",
    "CONFIG_SCSI=y",
    "CONFIG_BLK_DEV_SR=y",
];

/// Where the donor's config fragment lives in a tunnr checkout.
pub const DONOR_CONFIG: &str = "variants/kernel/tunnr-hardening.config";

/// Where the medium's record lives, beside the ISO.
pub fn record_path(iso: &Path) -> PathBuf {
    iso.with_extension("medium.json")
}

/// `0.1.14` → `(0, 1, 14)`; anything else → `None`.
pub fn semver(v: &str) -> Option<(u64, u64, u64)> {
    let mut it = v.trim().split('.').map(|p| p.parse::<u64>().ok());
    Some((it.next()??, it.next()??, it.next()??))
}

/// The kconfig lines of [`CD_BLOCK`] a fragment does NOT enable. Empty = the
/// donor has the CD block. A commented `# CONFIG_ATA is not set` is not a yes.
pub fn missing_cd_lines(fragment: &str) -> Vec<&'static str> {
    CD_BLOCK
        .iter()
        .copied()
        .filter(|want| !fragment.lines().any(|l| l.trim() == *want))
        .collect()
}

/// **The decision, pure.** `Ok(why)` when the medium reads its own CD; `Err(why)`
/// when it must be uploaded twice. Both carry the sentence the record keeps.
pub fn cd_driver(
    korp_installer: Option<&str>,
    donor_fragment: Option<&str>,
) -> Result<String, String> {
    let v =
        korp_installer.ok_or("the installer's version could not be read off the appliance lock")?;
    let parsed = semver(v).ok_or_else(|| format!("korp-installer {v:?} is not a version"))?;
    if parsed < FIRST_CD_READING_INSTALLER {
        return Err(format!(
            "korp-installer {v} predates 0.1.14 and probes virtio alone"
        ));
    }
    let frag = donor_fragment.ok_or(
        "the kernel was not compiled from the tunnr donor here (named, or carried off this box), \
         so its config cannot be read — an unknown kernel is assumed to have no CD driver",
    )?;
    let missing = missing_cd_lines(frag);
    if !missing.is_empty() {
        return Err(format!(
            "the tunnr donor lacks the CD block (tunnr 939dbe3): {} not enabled",
            missing.join(", ")
        ));
    }
    Ok(format!(
        "korp-installer {v} reads optical drives first, and the donor kernel carries {}",
        CD_BLOCK.join(" ")
    ))
}

fn esc(s: &str) -> String {
    let mut o = String::with_capacity(s.len());
    for c in s.chars() {
        match c {
            '"' => o.push_str("\\\""),
            '\\' => o.push_str("\\\\"),
            '\n' => o.push_str("\\n"),
            c if (c as u32) < 0x20 => o.push_str(&format!("\\u{:04x}", c as u32)),
            c => o.push(c),
        }
    }
    o
}

/// The record's JSON.
pub fn record_json(
    iso_sha256: &str,
    korp_installer: Option<&str>,
    kernel: &str,
    tunnr_head: Option<&str>,
    verdict: &Result<String, String>,
) -> String {
    let opt = |v: Option<&str>| {
        v.map(|s| format!("\"{}\"", esc(s)))
            .unwrap_or_else(|| "null".into())
    };
    let (cd, why) = match verdict {
        Ok(w) => (true, w.as_str()),
        Err(w) => (false, w.as_str()),
    };
    format!(
        "{{\n  \"iso_sha256\": \"{}\",\n  \"korp_installer\": {},\n  \"kernel\": \"{}\",\n  \"tunnr_head\": {},\n  \
         \"cd_driver\": {cd},\n  \"why\": \"{}\",\n  \"note\": \"Written by `cargo xtask iso-installer` beside the ISO whose sha256 it names. \
         deploy/upcloud reads it to decide whether the medium is uploaded once (cdrom only) or twice (cdrom + virtio).\"\n}}\n",
        esc(iso_sha256),
        opt(korp_installer),
        esc(kernel),
        opt(tunnr_head),
        esc(why)
    )
}

/// `HEAD` of a checkout, read off `.git` without a git binary. `None` when it
/// cannot be read — it is recorded for the reader, not decided on.
pub fn head_of(checkout: &Path) -> Option<String> {
    let git = checkout.join(".git");
    let gitdir = if git.is_file() {
        let t = std::fs::read_to_string(&git).ok()?;
        PathBuf::from(t.trim().strip_prefix("gitdir:")?.trim())
    } else {
        git
    };
    let head = std::fs::read_to_string(gitdir.join("HEAD")).ok()?;
    let head = head.trim();
    match head.strip_prefix("ref:") {
        None => Some(head.to_string()),
        Some(r) => {
            let r = r.trim();
            if let Ok(s) = std::fs::read_to_string(gitdir.join(r)) {
                return Some(s.trim().to_string());
            }
            let common = std::fs::read_to_string(gitdir.join("commondir"))
                .ok()
                .map(|c| gitdir.join(c.trim()))
                .unwrap_or(gitdir.clone());
            if let Ok(s) = std::fs::read_to_string(common.join(r)) {
                return Some(s.trim().to_string());
            }
            let packed = std::fs::read_to_string(common.join("packed-refs")).ok()?;
            packed
                .lines()
                .find_map(|l| l.strip_suffix(r).map(|h| h.trim().to_string()))
        }
    }
}

// ── the installer the composer ACTUALLY builds (T15 TD8/TD11, 2026-09-22) ──
//
// The medium's installer is not the one gunnar's lock pins. The composer is
// dwarves, and dwarves links korp-installer by PATH (feature `appliance-iso`,
// `crates/dwarves/Cargo.toml`), so the PID-1 on the medium is built from THAT
// checkout. MEASURED 2026-09-22: pin 0.1.15, record 0.1.15, sibling at f4b38bb
// = 0.1.14, and the twin fell into the 0.1.14 disk election and hung on
// SeaBIOS. So the record names what was built, and a mismatch with the pin is
// refused before the compose starts.

/// The declared exception, spelled as korp-installer's composer spells it
/// (`korp_installer::installer_source::UNPINNED_ENV`): compose from a source
/// that is not the pin, for a proof of an unreleased installer. Recorded.
pub const UNPINNED_ENV: &str = "KORP_INSTALLER_UNPINNED";

/// Where dwarves declares its korp-installer, relative to the dwarves root.
pub const DWARVES_MANIFEST: &str = "crates/dwarves/Cargo.toml";

/// The korp-installer the composer builds: its version, and where it comes from.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Built {
    pub version: String,
    /// The source directory for a path-dep, `None` for a registry dep.
    pub dir: Option<PathBuf>,
    /// HEAD of the checkout holding `dir`, when it could be read.
    pub revision: Option<String>,
}

/// The `path = "…"` of dwarves' `korp-installer = { … }` line, if any.
pub fn path_dep(dwarves_manifest: &str) -> Option<String> {
    dwarves_manifest.lines().find_map(|l| {
        let l = l.trim();
        if !l.starts_with("korp-installer") || l.starts_with('#') {
            return None;
        }
        let rest = &l[l.find("path")? + 4..];
        let rest = &rest[rest.find('"')? + 1..];
        Some(rest[..rest.find('"')?].to_string())
    })
}

/// The `[package] version` of a manifest.
pub fn package_version(manifest: &str) -> Option<String> {
    let mut in_package = false;
    for l in manifest.lines().map(str::trim) {
        if l.starts_with('[') {
            in_package = l == "[package]";
            continue;
        }
        if in_package {
            if let Some(v) = l.strip_prefix("version") {
                let v = v.trim_start().strip_prefix('=')?.trim();
                return Some(v.trim_matches('"').to_string());
            }
        }
    }
    None
}

/// The nearest enclosing git checkout's HEAD.
fn head_above(dir: &Path) -> Option<String> {
    let mut d = dir.to_path_buf();
    loop {
        if d.join(".git").exists() {
            return head_of(&d);
        }
        if !d.pop() {
            return None;
        }
    }
}

/// **Which korp-installer dwarves at `dwarves_root` builds into a medium.** A
/// path-dep is read off the sibling's own manifest; a registry dep off
/// dwarves' lock.
pub fn built_installer(dwarves_root: &Path) -> Result<Built, String> {
    let manifest_path = dwarves_root.join(DWARVES_MANIFEST);
    let manifest = std::fs::read_to_string(&manifest_path)
        .map_err(|e| format!("{}: {e}", manifest_path.display()))?;
    match path_dep(&manifest) {
        Some(rel) => {
            let dir = manifest_path.parent().unwrap_or(dwarves_root).join(rel);
            let dir = dir.canonicalize().unwrap_or(dir);
            let m = dir.join("Cargo.toml");
            let text = std::fs::read_to_string(&m).map_err(|e| {
                format!(
                    "{} declares korp-installer at {}, and {e}",
                    manifest_path.display(),
                    m.display()
                )
            })?;
            let version = package_version(&text)
                .ok_or_else(|| format!("{}: no [package] version", m.display()))?;
            Ok(Built {
                version,
                revision: head_above(&dir),
                dir: Some(dir),
            })
        }
        None => {
            let lock = dwarves_root.join("Cargo.lock");
            let text =
                std::fs::read_to_string(&lock).map_err(|e| format!("{}: {e}", lock.display()))?;
            let version = crate::installer_pin::resolved_version(&text)
                .ok_or_else(|| format!("{} resolves no korp-installer", lock.display()))?;
            Ok(Built {
                version,
                dir: None,
                revision: None,
            })
        }
    }
}

/// **Hold the built installer to the appliance's pin.** `Ok(verdict word)`:
/// `match`, or `unpinned` under the exception. `Err` is the refusal.
pub fn check_built_against_pin(
    built: &Built,
    pin: Option<&str>,
    lock: &str,
    unpinned_granted: bool,
) -> Result<&'static str, String> {
    let Some(pin) = pin else {
        return Err(format!("REFUSED [installer-source-not-the-pin] {lock} resolves no korp-installer, so the medium's installer cannot be held to a pin"));
    };
    if pin == built.version {
        return Ok("match");
    }
    if unpinned_granted {
        return Ok("unpinned");
    }
    Err(format!(
        "REFUSED [installer-source-not-the-pin] the composer would build the medium's installer from \
         korp-installer {} at {} [{}], but {lock} pins korp-installer {pin}. The record would name the pin \
         while the medium carries the other (T15 TD8, 2026-09-22: pin 0.1.15, sibling f4b38bb = 0.1.14, the \
         twin booted the wrong disk). Point the edda sibling at the pinned release, bump the pin, or set \
         {UNPINNED_ENV}=1 for a proof of an unreleased installer (recorded as unpinned).",
        built.version,
        built.dir.as_ref().map(|d| d.display().to_string()).unwrap_or_else(|| "the registry (dwarves' lock)".into()),
        built.revision.as_deref().unwrap_or("revision unknown"),
    ))
}

/// The check `iso-installer` runs BEFORE it spawns the compose.
pub fn preflight(gunnar_root: &Path, dwarves_root: &Path) -> Result<(Built, &'static str), String> {
    let (_, lock) = crate::installer_pin::PLACES[0];
    let pin = std::fs::read_to_string(gunnar_root.join(lock))
        .ok()
        .and_then(|l| crate::installer_pin::resolved_version(&l));
    let built = built_installer(dwarves_root)?;
    let granted = std::env::var(UNPINNED_ENV).is_ok_and(|v| !v.trim().is_empty());
    let verdict = check_built_against_pin(&built, pin.as_deref(), lock, granted)?;
    Ok((built, verdict))
}

/// **Write the record for a freshly composed ISO.** Impure: reads the lock,
/// the donor checkout and the ISO's bytes. `korp_installer` is the version
/// the composer BUILT (see [`preflight`]), never the pin.
pub fn write(
    gunnar_root: &Path,
    iso: &Path,
    kernel_from_source: bool,
    kernel_describe: &str,
    built: &Built,
    verdict: &str,
) -> Result<PathBuf, String> {
    let sha = nornir_build_thing::kernel::sha256_file_hex(iso)
        .map_err(|e| format!("{}: {e}", iso.display()))?;
    let (_, lock) = crate::installer_pin::PLACES[0];
    let pin = std::fs::read_to_string(gunnar_root.join(lock))
        .ok()
        .and_then(|l| crate::installer_pin::resolved_version(&l));
    let korp = Some(built.version.clone());
    let tunnr = gunnar_root.parent().map(|p| p.join("tunnr"));
    let fragment = if kernel_from_source {
        tunnr
            .as_ref()
            .and_then(|t| std::fs::read_to_string(t.join(DONOR_CONFIG)).ok())
    } else {
        None
    };
    let head = if kernel_from_source {
        tunnr.as_ref().and_then(|t| head_of(t))
    } else {
        None
    };
    let cd = cd_driver(korp.as_deref(), fragment.as_deref());
    // The record is written FIRST — it is evidence, and evidence of a medium
    // that cannot install is exactly what the next reader needs — and then the
    // compose REFUSES. See `refuse_unless_cd`.
    let path = record_path(iso);
    let json = with_installer_source(
        record_json(&sha, korp.as_deref(), kernel_describe, head.as_deref(), &cd),
        built,
        pin.as_deref(),
        verdict,
    );
    std::fs::write(&path, json).map_err(|e| format!("{}: {e}", path.display()))?;
    refuse_unless_cd(&cd, iso)?;
    Ok(path)
}

/// **A medium that cannot read its own CD is not composed.**
///
/// OWNER'S ORDER 2026-09-23: "refuse when cd_driver is false". Until then the
/// verdict was a FIELD in the record — `cd_driver: false` with the reason —
/// and the compose went on. The ladder uploads a medium ONCE and installs it
/// from the tray; a kernel without the CD block boots, finds no ISO-9660
/// volume, prints FAILED and parks. MEASURED 2026-09-23 22:53: a medium whose
/// record said `cd_driver: false — the tunnr donor lacks the CD block` was
/// composed, uploaded, attached and booted, and the ladder waited 1200 s on a
/// box that had failed at second ten. The record knew. Nothing refused.
///
/// Pure over the verdict, so the refusal is testable without an ISO.
pub fn refuse_unless_cd(cd: &Result<String, String>, iso: &Path) -> Result<(), String> {
    match cd {
        Ok(_) => Ok(()),
        Err(why) => Err(format!(
            "REFUSED [medium-no-cd-driver] {} cannot read its own CD: {why}. The ladder uploads a medium \
             once and installs it from the tray, so this medium would boot, find no ISO-9660 volume and \
             park on FAILED. The record beside it says so (cd_driver: false). Rebuild the donor kernel \
             with the CD block (tunnr 939dbe3 or later: CONFIG_ATA=y CONFIG_ATA_PIIX=y CONFIG_SATA_AHCI=y \
             CONFIG_SCSI=y CONFIG_BLK_DEV_SR=y) and compose again; the stale kernel in .kernel-out/ is \
             what a pin check that looks for EXISTENCE hands back.",
            iso.display()
        )),
    }
}

/// The record plus where its installer came from: the source, its revision,
/// the pin it was held to and the verdict. Appended as the record's last
/// fields, so every existing reader keeps its fields where they were.
pub fn with_installer_source(
    record: String,
    built: &Built,
    pin: Option<&str>,
    verdict: &str,
) -> String {
    let opt = |v: Option<&str>| {
        v.map(|s| format!("\"{}\"", esc(s)))
            .unwrap_or_else(|| "null".into())
    };
    let dir = built.dir.as_ref().map(|d| d.display().to_string());
    let tail = format!(
        ",\n  \"korp_installer_source\": {},\n  \"korp_installer_revision\": {},\n  \"korp_installer_pin\": {},\n  \"korp_installer_verdict\": \"{}\"\n}}\n",
        opt(dir.as_deref().or(Some("registry (dwarves' Cargo.lock)"))),
        opt(built.revision.as_deref()),
        opt(pin),
        esc(verdict)
    );
    let body = record
        .trim_end()
        .strip_suffix('}')
        .unwrap_or(&record)
        .trim_end()
        .to_string();
    body + &tail
}

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

    const WITH_BLOCK: &str = "# the CD block\nCONFIG_ATA=y\nCONFIG_ATA_SFF=y\nCONFIG_ATA_BMDMA=y\nCONFIG_ATA_PIIX=y\nCONFIG_SATA_AHCI=y\nCONFIG_SCSI=y\nCONFIG_BLK_DEV_SR=y\n";
    const WITHOUT: &str =
        "# CONFIG_ATA is not set\n# CONFIG_SCSI is not set\nCONFIG_VIRTIO_BLK=y\n";

    #[test]
    fn both_halves_proven_reads_its_own_cd() {
        let v = cd_driver(Some("0.1.14"), Some(WITH_BLOCK)).unwrap();
        assert!(
            v.contains("0.1.14") && v.contains("CONFIG_BLK_DEV_SR=y"),
            "{v}"
        );
        // OWNER 2026-09-23: a false verdict REFUSES the compose, by name, and
        // names the kernel options; a true one lets it through.
        let iso = std::path::Path::new("/x/gunnar-installer-metered.iso");
        assert!(refuse_unless_cd(&Ok(v.clone()), iso).is_ok());
        let e =
            refuse_unless_cd(&cd_driver(Some("0.1.19"), Some("CONFIG_ATA=y\n")), iso).unwrap_err();
        assert!(e.starts_with("REFUSED [medium-no-cd-driver]"), "{e}");
        assert!(
            e.contains("CONFIG_BLK_DEV_SR=y")
                && e.contains("939dbe3")
                && e.contains("gunnar-installer-metered.iso"),
            "{e}"
        );
        let e = refuse_unless_cd(&cd_driver(Some("0.1.19"), None), iso).unwrap_err();
        assert!(
            e.contains("unknown kernel is assumed to have no CD driver"),
            "an unproven kernel refuses too: {e}"
        );
        assert!(cd_driver(Some("0.2.0"), Some(WITH_BLOCK)).is_ok());
    }

    /// Each half alone is not enough — T16's negative control is the second.
    #[test]
    fn either_half_missing_means_two_uploads() {
        assert!(cd_driver(Some("0.1.13"), Some(WITH_BLOCK))
            .unwrap_err()
            .contains("predates 0.1.14"));
        let e = cd_driver(Some("0.1.14"), Some(WITHOUT)).unwrap_err();
        assert!(
            e.contains("939dbe3") && e.contains("CONFIG_ATA_PIIX=y"),
            "{e}"
        );
        assert!(cd_driver(Some("0.1.14"), None)
            .unwrap_err()
            .contains("unknown kernel"));
        assert!(cd_driver(None, Some(WITH_BLOCK)).is_err());
        assert!(cd_driver(Some("zero"), Some(WITH_BLOCK)).is_err());
        // One line of the block missing is the whole block missing.
        let partial = WITH_BLOCK.replace("CONFIG_SATA_AHCI=y\n", "");
        assert_eq!(missing_cd_lines(&partial), vec!["CONFIG_SATA_AHCI=y"]);
    }

    fn built(v: &str) -> Built {
        Built {
            version: v.into(),
            dir: Some("/t/edda/crates/korp-installer".into()),
            revision: Some("f4b38bb738320afde".into()),
        }
    }

    /// The TD8 shape: pin 0.1.15, sibling 0.1.14 at f4b38bb. Refused, naming both.
    #[test]
    fn a_sibling_that_is_not_the_pin_is_refused_before_the_compose() {
        let e = check_built_against_pin(&built("0.1.14"), Some("0.1.15"), "Cargo.lock", false)
            .unwrap_err();
        assert!(e.contains("REFUSED [installer-source-not-the-pin]"), "{e}");
        assert!(
            e.contains("korp-installer 0.1.14") && e.contains("pins korp-installer 0.1.15"),
            "{e}"
        );
        assert!(
            e.contains("/t/edda/crates/korp-installer") && e.contains("f4b38bb"),
            "{e}"
        );
        assert!(e.contains(UNPINNED_ENV), "{e}");
        assert_eq!(
            check_built_against_pin(&built("0.1.15"), Some("0.1.15"), "Cargo.lock", false),
            Ok("match")
        );
        assert_eq!(
            check_built_against_pin(&built("0.1.18"), Some("0.1.17"), "Cargo.lock", true),
            Ok("unpinned")
        );
        assert!(
            check_built_against_pin(&built("0.1.17"), None, "Cargo.lock", true).is_err(),
            "no pin is never a pass"
        );
    }

    /// dwarves' own declaration is what is read, and the sibling's manifest
    /// is what is judged — the two ends TD11 compares.
    #[test]
    fn the_built_installer_is_read_off_dwarves_path_dep_and_the_siblings_manifest() {
        struct D(std::path::PathBuf);
        impl D {
            fn path(&self) -> &Path {
                &self.0
            }
        }
        impl Drop for D {
            fn drop(&mut self) {
                let _ = std::fs::remove_dir_all(&self.0);
            }
        }
        let d = D(std::env::temp_dir().join(format!("gunnar-medium-built-{}", std::process::id())));
        let dw = d.path().join("dwarves/crates/dwarves");
        let ki = d.path().join("edda/crates/korp-installer");
        std::fs::create_dir_all(&dw).unwrap();
        std::fs::create_dir_all(&ki).unwrap();
        std::fs::write(
            dw.join("Cargo.toml"),
            "[features]\nappliance-iso = [\"dep:korp-installer\"]\n[dependencies]\n# korp-installer = { path = \"nowhere\" }\nkorp-installer = { version = \"0.1.0\", path = \"../../../edda/crates/korp-installer\", default-features = false, optional = true }\n",
        )
        .unwrap();
        std::fs::write(ki.join("Cargo.toml"), "[package]\nname = \"korp-installer\"\nversion = \"0.1.14\"\n[dependencies]\nfoo = { version = \"9.9.9\" }\n").unwrap();
        let b = built_installer(&d.path().join("dwarves")).unwrap();
        assert_eq!(b.version, "0.1.14");
        assert_eq!(b.dir.as_deref(), Some(ki.canonicalize().unwrap().as_path()));
        assert_eq!(
            package_version(
                "[workspace]\nversion = \"1\"\n[package]\nname = \"x\"\nversion = \"0.2.0\"\n"
            )
            .as_deref(),
            Some("0.2.0")
        );
    }

    /// The record names what was built, and says where it came from.
    #[test]
    fn the_record_names_the_built_installer_not_the_pin() {
        let base = record_json(
            "ab",
            Some("0.1.14"),
            "from source",
            None,
            &cd_driver(Some("0.1.14"), Some(WITH_BLOCK)),
        );
        let j = with_installer_source(base, &built("0.1.14"), Some("0.1.15"), "unpinned");
        let v = crate::json::Json::parse(&j).expect("valid JSON");
        assert_eq!(
            v.get("korp_installer").and_then(|x| x.str()),
            Some("0.1.14")
        );
        assert_eq!(
            v.get("korp_installer_pin").and_then(|x| x.str()),
            Some("0.1.15")
        );
        assert_eq!(
            v.get("korp_installer_source").and_then(|x| x.str()),
            Some("/t/edda/crates/korp-installer")
        );
        assert_eq!(
            v.get("korp_installer_revision").and_then(|x| x.str()),
            Some("f4b38bb738320afde")
        );
        assert_eq!(
            v.get("korp_installer_verdict").and_then(|x| x.str()),
            Some("unpinned")
        );
        // TD11 reads `"korp_installer": ` off a line; it still finds the built version.
        assert!(
            j.lines()
                .any(|l| l.trim() == "\"korp_installer\": \"0.1.14\","),
            "{j}"
        );
    }

    #[test]
    fn the_record_is_json_beside_the_iso() {
        assert_eq!(
            record_path(Path::new(".iso-out/gunnar-installer-metered.iso")),
            PathBuf::from(".iso-out/gunnar-installer-metered.medium.json")
        );
        let j = record_json(
            "ab",
            Some("0.1.14"),
            "from source",
            Some("3f7c2ff"),
            &cd_driver(Some("0.1.14"), Some(WITH_BLOCK)),
        );
        let v = crate::json::Json::parse(&j).expect("valid JSON");
        assert_eq!(v.get("iso_sha256").and_then(|x| x.str()), Some("ab"));
        assert_eq!(v.get("cd_driver"), Some(&crate::json::Json::Bool(true)));
        let j = record_json("ab", None, "named \"x\"", None, &cd_driver(None, None));
        assert_eq!(
            crate::json::Json::parse(&j).unwrap().get("cd_driver"),
            Some(&crate::json::Json::Bool(false))
        );
    }
}