nornir 0.5.4

Companion to cargo: dependency tracking, release gating, deploy, benchmarks, and documentation assembly. Project-agnostic.
//! Declarative **build variants** (task #19) + the **build-thing** UI surface,
//! **build-provenance** attestation, and warehouse sink (task #36).
//!
//! EXTRACTED (task #36) into the reusable, dependency-light
//! [`nornir_build_thing`] leaf crate — mirroring how the test-matrix engine lives
//! in `nornir-testmatrix`. This module is now a thin **re-export** so every
//! existing path keeps working:
//!
//! * `nornir::build::variant::{BuildVariant, BuildVariantSet, Profile, …}`
//! * `nornir::build::surface::BuildSurface` — the data-driven build-thing UI model
//! * `nornir::build::provenance::{BuildProvenance, Attestation, build_variant, …}`
//! * `nornir::build::sink::BuildProvenanceSink` — the warehouse seam
//! * `nornir::build::elm::BuildThing` — the facett-Elm UI component (pure core)
//!
//! A repo like holger ships **three split executables from one tree**
//! (`embedded`, `minimal`, `full`); it declares them as data and nornir drives
//! `cargo` from the descriptor. Building one (with `--attest`) captures a
//! content-addressed provenance record → writes it to the warehouse
//! ([`crate::warehouse::build_provenance`]) → then dumps a znippy archive beside
//! the artifact ([`seal`], feature `build-provenance`).

// Re-export the leaf crate's modules verbatim so `nornir::build::variant::*`,
// `::surface::*`, `::provenance::*`, `::sink::*`, `::elm::*`, `::container::*`
// all resolve.
pub use nornir_build_thing::{
    container, elm, image_build, provenance, sink, surface, variant, vm,
};

// Flat re-exports of the headline API (mirrors the crate's own prelude).
pub use nornir_build_thing::{
    build_variant, Attestation, BuildOutcome, BuildProvenance, BuildProvenanceSink, BuildSurface,
    BuildThing, ContainerBuild, ImageFormat, ImageLayer, ProvenanceSealer,
};

// Flat re-exports of the IMAGE/VM build-target API — the peer of `build_variant`
// for `ContainerBuild` images and `VmBuild` appliances. The heavy real container
// builder (`BollardImageBuilder`) is forwarded behind the `build-image` feature.
pub use nornir_build_thing::{
    build_container, build_vm, BuiltImage, ImageBuilder, PrebuiltVmBuilder, TargetAttestation,
    TargetBuildOutcome, TargetKind, TargetProvenance, VmBuild, VmFormat, VmImageBuilder,
};

/// The real bollard-backed container image builder — only linked when the main
/// crate is built with `--features build-image` (which forwards
/// `nornir-build-thing/image-build`).
#[cfg(feature = "build-image")]
pub use nornir_build_thing::image_build::BollardImageBuilder;

/// The real **znippy-sealed** attestation path (feature `build-provenance`): a
/// detached CMS signature over the attestation digest, and the znippy-archive
/// dump placed beside the released artifact.
#[cfg(feature = "build-provenance")]
pub mod seal;

/// **f5 — a build/pipeline as a root jera job that OWNS a `PlanDag` of sub-jobs.**
/// Every pipeline step is a tracked sub-job (its own `job_id` + `queued → running →
/// done|failed` lifecycle on the nornir-jobs ledger); edges are `needs`
/// dependencies. Reuses funnel-core's [`crate::funnel::PlanDag`] for the DAG
/// vocabulary and the ledger for the sub-job hierarchy — the existing single-job
/// path is unchanged (a one-step "DAG").
pub mod pipeline;

/// **Slice 5 — the Flight-backed [`BuildInfoSink`](nornir_build_thing::build_info::BuildInfoSink).**
/// Serializes build-info rows to the §f3 Arrow `RecordBatch` and ships them over
/// `edda/nornir-flight`'s `do_put` to a remote endpoint (with a `do_get`/`subscribe`
/// consumer), so the same build-info stream is in-process (default) OR Flight
/// (remote) behind the one sink trait. Gated behind `build-info-flight` — the
/// in-process default pays nothing (no second arrow, no Flight deps).
#[cfg(feature = "build-info-flight")]
pub mod flight_sink;

#[cfg(test)]
mod reexport_tests {
    //! Compile-level proof that the IMAGE/VM build-target API is reachable through
    //! the `nornir::build::` re-export (not only the leaf crate).

    /// The `vm` + `image_build` modules and the flat `build_vm`/`PrebuiltVmBuilder`
    /// re-exports resolve, and the dep-light VM path seals a produced-artifact
    /// attestation whose digest is 64 hex chars.
    #[test]
    fn build_vm_reachable_via_reexport_and_seals_a_digest() {
        use crate::build::image_build::build_vm;
        use crate::build::vm::VmBuild;
        use crate::build::PrebuiltVmBuilder;

        let dir = tempfile::tempdir().unwrap();
        let root = dir.path();
        std::fs::create_dir_all(root.join("artifacts")).unwrap();
        std::fs::write(root.join("artifacts/bzImage"), b"KERNEL bytes").unwrap();
        std::fs::write(root.join("artifacts/rootfs.cpio.gz"), b"ROOTFS bytes").unwrap();

        let vm = VmBuild {
            name: "appliance".into(),
            kernel: "artifacts/bzImage".into(),
            rootfs: "artifacts/rootfs.cpio.gz".into(),
            ..Default::default()
        };
        let out = build_vm(&PrebuiltVmBuilder, &vm, "repo", root).unwrap();

        // A TargetAttestation over the produced kernel+rootfs hashes.
        assert_eq!(out.kind, crate::build::TargetKind::Vm);
        let hex = out.attestation.digest_hex();
        assert_eq!(hex.len(), 64, "sha-256 digest is 64 hex chars: {hex}");
        assert!(hex.chars().all(|c| c.is_ascii_hexdigit()));
        assert!(out.attestation.digest_matches_record().unwrap());
    }
}