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

nordisk/gunnar

crates/gunnar-front/build.rs

//! **What the front medium carries besides its own code**, embedded at build
//! time so the medium stays one binary (korp-start execs exactly one file):
//!
//! * the built console (gunnar-ui-web's `dist/`: `index.html`, the wasm, its
//!   loader, the stylesheet, the pictures) under the `console` feature — the
//!   community build (`dist/`), or the paid one (`dist-metered/`) under
//!   `metered`;
//!
//! Where from: `GUNNAR_FRONT_CONSOLE_DIST`, else the declared sibling beside
//! this checkout (`../gunnar-ui/crates/gunnar-ui-web/dist[-metered]`). An
//! absent console is NOT a build failure — tests build without it — but the
//! binary then knows it (`console::DIST_SOURCE` is `None`), says so by name
//! when it runs, and a `cargo:warning` says so here.
//!
//! * the SIGNED `gunnar` CLI the estate's own site offers under `/download/`
//!   (E4, 2026-10-11; `downloads` module): the Build Thing's `gunnar-musl`
//!   row's `gunnar` with the forge's `gunnar.p7s` + `gunnar.blake3`, served as
//!   `gunnar-x86_64-unknown-linux-musl[.p7s|.blake3]`, and the published trust
//!   root `korp-root-ca.der`. From `GUNNAR_FRONT_CLI_DIR`, else `<gunnar>/.cli-out/`
//!   (staged by `cargo xtask iso-installer`, `xtask/src/cli_stage.rs`); the root
//!   from `GUNNAR_FRONT_CLI_ROOT_CA`, else the staged one, else the sibling
//!   `dwarves/trust/`. An absent CLI is not a build failure either: the
//!   manifest says so by name.
//!
//! **No monetize plugin is embedded** (owner 2026-09-25): the front's monetize
//! is bare and RECEIVES its plugins in the feed (`monetize` module).

use std::fmt::Write as _;
use std::path::{Path, PathBuf};

fn walk(dir: &Path, base: &Path, out: &mut Vec<(String, PathBuf)>) {
    let Ok(rd) = std::fs::read_dir(dir) else {
        return;
    };
    let mut entries: Vec<_> = rd.flatten().collect();
    entries.sort_by_key(|e| e.file_name());
    for e in entries {
        let p = e.path();
        if p.is_dir() {
            walk(&p, base, out);
        } else if let Ok(rel) = p.strip_prefix(base) {
            out.push((
                rel.to_string_lossy().replace('\\', "/"),
                p.canonicalize().unwrap_or(p.clone()),
            ));
        }
    }
}

fn main() {
    let manifest =
        PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").expect("cargo sets CARGO_MANIFEST_DIR"));
    let out = PathBuf::from(std::env::var("OUT_DIR").expect("cargo sets OUT_DIR"));
    let console = std::env::var_os("CARGO_FEATURE_CONSOLE").is_some();
    let metered = std::env::var_os("CARGO_FEATURE_METERED").is_some();
    println!("cargo:rerun-if-env-changed=GUNNAR_FRONT_CONSOLE_DIST");
    let siblings = manifest.join("../../..");

    // ── the console ─────────────────────────────────────────────────────────
    let mut code = String::new();
    let dist = std::env::var_os("GUNNAR_FRONT_CONSOLE_DIST")
        .map(PathBuf::from)
        .unwrap_or_else(|| {
            siblings
                .join("gunnar-ui/crates/gunnar-ui-web")
                .join(if metered { "dist-metered" } else { "dist" })
        });
    let mut files = Vec::new();
    if console {
        // The PARENT too: a dist that does not exist yet has nothing for cargo
        // to watch, so a console built AFTER a first build never reached the
        // binary (MEASURED loki 2026-09-26, prod: the front medium was composed
        // twice with the same bytes and served 503 [console-not-on-medium]).
        if let Some(parent) = dist.parent() {
            println!("cargo:rerun-if-changed={}", parent.display());
        }
        println!("cargo:rerun-if-changed={}", dist.display());
        if dist.join("index.html").is_file() {
            walk(&dist, &dist, &mut files);
            for (_, abs) in &files {
                println!("cargo:rerun-if-changed={}", abs.display());
            }
        } else {
            println!("cargo:warning=gunnar-front: no built console at {} (index.html absent) — this binary serves a named refusal instead of the console", dist.display());
        }
    }
    let _ = writeln!(
        code,
        "/// The built console's files, `(path, bytes)`.\npub static FILES: &[(&str, &[u8])] = &["
    );
    for (rel, abs) in &files {
        let _ = writeln!(
            code,
            "    ({rel:?}, include_bytes!({:?})),",
            abs.display().to_string()
        );
    }
    let _ = writeln!(code, "];");
    let source = (!files.is_empty()).then(|| {
        dist.canonicalize()
            .unwrap_or(dist.clone())
            .display()
            .to_string()
    });
    let _ = writeln!(code, "/// Where they were read from at build time; `None` = not embedded.\npub const DIST_SOURCE: Option<&str> = {source:?};");
    std::fs::write(out.join("console_dist.rs"), code).expect("write console_dist.rs");

    // ── the signed gunnar CLI ───────────────────────────────────────────────
    println!("cargo:rerun-if-env-changed=GUNNAR_FRONT_CLI_DIR");
    println!("cargo:rerun-if-env-changed=GUNNAR_FRONT_CLI_ROOT_CA");
    const TRIPLE: &str = "x86_64-unknown-linux-musl";
    // Staged by `cargo xtask iso-installer` for a front medium (xtask/src/cli_stage.rs),
    // as `.plugin-out/` is for gunnar-server: the signed binary, its sidecars, the root.
    let cli_dir = std::env::var_os("GUNNAR_FRONT_CLI_DIR")
        .filter(|v| !v.is_empty())
        .map(PathBuf::from)
        .unwrap_or_else(|| manifest.join("../../.cli-out"));
    let root_ca = std::env::var_os("GUNNAR_FRONT_CLI_ROOT_CA")
        .filter(|v| !v.is_empty())
        .map(PathBuf::from)
        .unwrap_or_else(|| {
            let staged = cli_dir.join("korp-root-ca.der");
            if staged.is_file() {
                staged
            } else {
                siblings.join("dwarves/trust/korp-root-ca.der")
            }
        });
    let served = format!("gunnar-{TRIPLE}");
    let mut cli: Vec<(String, PathBuf)> = Vec::new();
    for (from, to) in [
        ("gunnar".to_string(), served.clone()),
        ("gunnar.p7s".to_string(), format!("{served}.p7s")),
        ("gunnar.blake3".to_string(), format!("{served}.blake3")),
    ] {
        let p = cli_dir.join(&from);
        if p.is_file() {
            println!("cargo:rerun-if-changed={}", p.display());
            cli.push((to, p));
        }
    }
    // The DIRECTORY too, as the console's parent is: a CLI staged AFTER a first
    // build must reach the binary. `.cli-out/` is in every checkout (its
    // `.gitkeep`), and a path watched while ABSENT would rerun this on every build.
    if cli_dir.is_dir() {
        println!("cargo:rerun-if-changed={}", cli_dir.display());
    }
    if root_ca.is_file() {
        println!("cargo:rerun-if-changed={}", root_ca.display());
        cli.push(("korp-root-ca.der".to_string(), root_ca.clone()));
    }
    if !cli.iter().any(|(n, _)| *n == served) {
        println!(
            "cargo:warning=gunnar-front: no gunnar CLI at {} — /download/ says [cli-not-on-medium]",
            cli_dir.display()
        );
    }
    let mut code = String::new();
    let _ = writeln!(
        code,
        "/// The CLI downloads, `(served name, bytes)`.\npub static FILES: &[(&str, &[u8])] = &["
    );
    for (name, abs) in &cli {
        let _ = writeln!(
            code,
            "    ({name:?}, include_bytes!({:?})),",
            abs.display().to_string()
        );
    }
    let _ = writeln!(code, "];");
    let source = cli
        .iter()
        .any(|(n, _)| *n == served)
        .then(|| cli_dir.display().to_string());
    let _ = writeln!(code, "/// Where the CLI was read from at build time; `None` = not embedded.\npub const CLI_SOURCE: Option<&str> = {source:?};");
    std::fs::write(out.join("cli_downloads.rs"), code).expect("write cli_downloads.rs");
}