gunnar-server 3.0.0

The gunnar git server: SSH and smart-HTTP git transports and the gRPC control plane. The appliance half, extracted from gunnar-cli so the server code lives in its own crate.
Documentation
//! Writes the build-thing RECEIPT for the appliance binary.
//!
//! The receipt says which **recipe** built this artifact — `gunnar-build-thing`,
//! its version, its content hash, and every resolved setting of the variant this
//! compilation is. It goes into the binary's `.nordisk.buildthing` section via
//! the `include!` in `src/main.rs`, and `dwarves` reads it back out with an
//! object parser without ever running the artifact.
//!
//! Until 2026-08-10 this crate had no `build.rs` and the `gunnar-server` variant
//! still named package `gunnar-cli`, where the bin lived before the extraction
//! The variant follows the code now, and so does the writer.
//!
//! This is the RECEIPT half only. The `GUNNAR_BUILD_REF`/`GUNNAR_BUILD_DATE`
//! version stamps come from `gunnar-common`'s own `build.rs` — two copies of
//! the receipt static in one artifact would concatenate and be reported as
//! "more than one receipt", which is why each binary crate writes exactly its
//! own. The long story of what a receipt is and why a refused one PANICS lives
//! in `crates/gunnar-cli/build.rs`.

use std::process::Command;

fn main() {
    // ONE `git describe`, as the receipt's `source-commit` input. NOT the build
    // date: two identical builds would differ by it and by nothing else, and
    // `nornir_build_thing::receipt::audit_receipt` refuses a timestamp outright.
    // The four `-c` overrides are the SAME ones `gunnar-cli/build.rs` carries and
    // for the same measured reason: a git configured for git-lfs
    // (`filter.lfs.required = true`, as this box's /etc/gitconfig is) runs the
    // lfs filter for `--dirty`'s status walk, and inside the pinned cargo
    // toolchain image — where `git-lfs` is not on PATH — `describe` then dies
    // with `git-lfs: not found`. `gunnar-cli` and `gunnar-common` were fixed on
    // 2026-09-05; THIS script was left behind, and it runs the same `--dirty`
    // walk. The cost here is quieter than a bare SSH banner and no less real:
    // `source-commit` simply drops out of the build receipt, so a
    // container-built gunnar-server ships a receipt that does not name the
    // source it was built from. Describing a commit needs no filter.
    // MEASURED 2026-09-15 and recorded in `gunnar-common/build.rs`, which carries
    // both of these and which this script did not: inside the pinned toolchain
    // container the checkout is bind-mounted and the building uid does not own
    // it, so git answers `fatal: detected dubious ownership` and the stamp falls
    // to nothing — there, `source-commit` simply leaves the receipt.
    //
    // `core.checkStat=minimal` is the one that bit THIS script, MEASURED
    // 2026-09-16 on the medium composed from a clean clone at b39da6c: the
    // binary carried BOTH `v1.4.0-279-gb39da6c` (the banner, from
    // gunnar-common, which has this override) and
    // `v1.4.0-279-gb39da6c-modified` (this receipt, which did not). The tree was
    // clean on both sides of the mount. Rootless podman remaps uid/gid and git's
    // default stat check compares uid, gid, inode and ctime against the index,
    // so every tracked file reads as changed — and `--dirty=-modified`, which
    // only this script passes, turns that into a receipt claiming a modified
    // source that never was. `minimal` compares size and mtime, which a bind
    // mount preserves.
    //
    // Reading a version is not a trust decision: nothing here runs the
    // repository's hooks or config.
    let describe = Command::new("git")
        .args([
            "-c",
            "safe.directory=*",
            "-c",
            "core.checkStat=minimal",
            "-c",
            "filter.lfs.required=false",
            "-c",
            "filter.lfs.process=",
            "-c",
            "filter.lfs.clean=",
            "-c",
            "filter.lfs.smudge=",
            "describe",
            "--tags",
            "--always",
            "--dirty=-modified",
            "--abbrev=7",
        ])
        .output()
        .ok()
        .filter(|o| o.status.success())
        .map(|o| String::from_utf8_lossy(&o.stdout).trim().to_owned())
        .filter(|s| !s.is_empty());

    let mut inputs: Vec<(&str, String)> = Vec::new();
    if let Some(d) = &describe {
        inputs.push(("source-commit", d.clone()));
    }

    match gunnar_recipe::emit::write_receipt(&inputs) {
        Ok(emitted) => {
            if let Some(variant) = emitted.attribution.variant() {
                println!("cargo:rustc-env=GUNNAR_BUILD_VARIANT={}", variant.name);
            }
        }
        Err(e) => panic!(
            "the build-thing receipt was refused: {e:#}\n\
             This is a defect in gunnar-build-thing's declaration, not in this build. \
             A binary that ships with a receipt its own audit would reject is worse than \
             one with none, so the build stops here."
        ),
    }

    // Re-run when HEAD moves, so the receipt's source-commit cannot go stale in
    // an incremental build. `--git-path` rather than `../../.git/…` because in a
    // git WORKTREE `.git` is a file and naming paths under it triggers cargo's
    // always-dirty behaviour — see gunnar-cli/build.rs for the full account.
    for what in ["HEAD", "refs"] {
        let path = Command::new("git")
            .args(["rev-parse", "--git-path", what])
            .output()
            .ok()
            .filter(|o| o.status.success())
            .map(|o| String::from_utf8_lossy(&o.stdout).trim().to_owned())
            .filter(|s| !s.is_empty());
        if let Some(path) = path {
            if std::path::Path::new(&path).exists() {
                println!("cargo:rerun-if-changed={path}");
            }
        }
    }
}