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

nordisk/holger

crates/holger-monetize-guest/build.rs

//! **The build reference, stamped into the module** (the guest compiles no proto since it
//! opens no door, monetize v2.0 P2 T13) as the custom section
//! [`SECTION`] — the SAME short commit holger-server-lib stamps as
//! `HOLGER_GIT_HASH` (read the same way: the on-disk git plumbing with
//! `std::fs`, never a `git` subprocess). holger-server `metered` carries this
//! module and refuses to register a plugin whose stamp is not its own
//! `GIT_HASH`: the plugin and the server it speaks to are one commit, or the
//! plugin is not offered at all. A custom section is ignored by every wasm
//! engine and covered by the signature like every other byte.

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

/// The custom section the build reference is stamped into.
const SECTION: &str = "holger_build_ref";

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Read when the script RUNS, not when it was compiled: the target dir is
    // shared across checkouts.
    let manifest = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR")?);
    // The stamp: the same short hash as `HOLGER_GIT_HASH`.
    println!("cargo:rerun-if-env-changed=HOLGER_BUILD_REF");
    let r = std::env::var("HOLGER_BUILD_REF")
        .ok()
        .map(|s| s.trim().to_owned())
        .filter(|s| !s.is_empty())
        .or_else(|| git_short_hash(&manifest))
        .unwrap_or_else(|| {
            println!("cargo:warning=holger-monetize-guest: no build reference (no git metadata, HOLGER_BUILD_REF unset) — the module is stamped `unknown` and no holger-server will register it");
            "unknown".to_owned()
        });
    let bytes = r.as_bytes();
    let mut code = String::new();
    let _ = writeln!(
        code,
        "/// This module's build reference (holger-server-lib's `HOLGER_GIT_HASH`)."
    );
    let _ = writeln!(code, "pub const BUILD_REF: &str = {r:?};");
    let _ = writeln!(
        code,
        "/// The same bytes as the custom section `{SECTION}` — what holger-server reads back."
    );
    let _ = writeln!(code, "#[cfg(target_arch = \"wasm32\")]");
    let _ = writeln!(code, "#[used]");
    let _ = writeln!(code, "#[link_section = {SECTION:?}]");
    let _ = writeln!(
        code,
        "static STAMPED_BUILD_REF: [u8; {}] = {bytes:?};",
        bytes.len()
    );
    fs::write(
        PathBuf::from(std::env::var("OUT_DIR")?).join("build_ref.rs"),
        code,
    )?;
    Ok(())
}

// ── the short hash, exactly as `server/lib/build.rs` reads it ──────────────
// (A build script cannot link another's; the reader is small and the two must
// agree byte for byte, which `tests/holgers_own_proto.rs` checks on the stamp.)

fn git_short_hash(manifest_dir: &Path) -> Option<String> {
    let git_dir = find_git_dir(manifest_dir)?;
    let common_dir = common_dir(&git_dir);
    let head_path = git_dir.join("HEAD");
    rerun_if_changed(&head_path);
    let head = fs::read_to_string(&head_path).ok()?;
    let head = head.trim();
    let full = match head.strip_prefix("ref:") {
        Some(refname) => resolve_ref(refname.trim(), &git_dir, &common_dir)?,
        None => head.to_string(),
    };
    let full = full.trim();
    if full.is_empty() {
        return None;
    }
    Some(full.chars().take(7).collect())
}

fn find_git_dir(start: &Path) -> Option<PathBuf> {
    let mut dir = Some(start);
    while let Some(d) = dir {
        let dot_git = d.join(".git");
        if dot_git.is_dir() {
            return Some(dot_git);
        }
        if dot_git.is_file() {
            let contents = fs::read_to_string(&dot_git).ok()?;
            let target = contents.trim().strip_prefix("gitdir:")?.trim();
            return Some(normalize(&d.join(target)));
        }
        dir = d.parent();
    }
    None
}

fn common_dir(git_dir: &Path) -> PathBuf {
    match fs::read_to_string(git_dir.join("commondir")) {
        Ok(contents) => normalize(&git_dir.join(contents.trim())),
        Err(_) => git_dir.to_path_buf(),
    }
}

fn resolve_ref(refname: &str, git_dir: &Path, common_dir: &Path) -> Option<String> {
    for base in [git_dir, common_dir] {
        let loose = base.join(refname);
        if loose.is_file() {
            rerun_if_changed(&loose);
            if let Ok(s) = fs::read_to_string(&loose) {
                let s = s.trim();
                if !s.is_empty() {
                    return Some(s.to_string());
                }
            }
        }
    }
    let packed = common_dir.join("packed-refs");
    if packed.is_file() {
        rerun_if_changed(&packed);
        if let Ok(contents) = fs::read_to_string(&packed) {
            for line in contents.lines() {
                let line = line.trim();
                if line.is_empty() || line.starts_with('#') || line.starts_with('^') {
                    continue;
                }
                if let Some((sha, name)) = line.split_once(' ') {
                    if name.trim() == refname {
                        return Some(sha.trim().to_string());
                    }
                }
            }
        }
    }
    None
}

fn normalize(path: &Path) -> PathBuf {
    let mut out = PathBuf::new();
    for comp in path.components() {
        match comp {
            std::path::Component::ParentDir => {
                out.pop();
            }
            std::path::Component::CurDir => {}
            other => out.push(other.as_os_str()),
        }
    }
    out
}

fn rerun_if_changed(path: &Path) {
    println!("cargo:rerun-if-changed={}", path.display());
}