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

nordisk/skade

serve/build.rs

// Apache-2.0 licensed. See LICENSE-APACHE.

//! Pure-filesystem git-commit stamp for the `skade-katalog` serve binary.
//!
//! Reads the commit id straight out of `.git/` — **no `git` subprocess** — so it
//! honors the nordisk zero-shell law (no shelling out, ever). Emits
//! `cargo:rustc-env=SKADE_GIT_HASH=<short-sha>` for the binary to `env!()`, plus
//! `rerun-if-changed` on every file it reads so the stamp refreshes on commit.
//!
//! Handles the cases that actually occur:
//!   * a normal `.git/` directory with a loose ref;
//!   * a ref that lives only in `packed-refs`;
//!   * a detached HEAD (raw commit id in `.git/HEAD`);
//!   * a linked worktree / submodule where `.git` is a *file* pointing at a
//!     `gitdir:` and refs resolve against the `commondir`.
//!
//! When no repository is present (e.g. a `cargo publish` tarball build), it
//! falls back to `unknown` rather than failing the build.

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

fn main() {
    let hash = git_hash().unwrap_or_else(|| "unknown".to_string());
    println!("cargo:rustc-env=SKADE_GIT_HASH={hash}");
}

/// Locate the `.git` store for this crate: walk up from the manifest dir, and
/// follow a `gitdir:` pointer file when `.git` is a file (worktree/submodule).
fn git_dir() -> Option<PathBuf> {
    let manifest = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").ok()?);
    let mut cur: Option<&Path> = Some(manifest.as_path());
    while let Some(dir) = cur {
        let dot = dir.join(".git");
        if dot.is_dir() {
            return Some(dot);
        }
        if dot.is_file() {
            let content = fs::read_to_string(&dot).ok()?;
            let rest = content.strip_prefix("gitdir:")?.trim();
            let gitdir = dir.join(rest);
            return Some(fs::canonicalize(&gitdir).unwrap_or(gitdir));
        }
        cur = dir.parent();
    }
    None
}

/// The shared object/ref store. For a linked worktree, `.git/commondir` points
/// at the main `.git` where `refs/` and `packed-refs` live; otherwise it is the
/// git dir itself.
fn common_dir(git: &Path) -> PathBuf {
    if let Ok(s) = fs::read_to_string(git.join("commondir")) {
        let p = PathBuf::from(s.trim());
        let joined = if p.is_absolute() { p } else { git.join(p) };
        return fs::canonicalize(&joined).unwrap_or(joined);
    }
    git.to_path_buf()
}

fn git_hash() -> Option<String> {
    let git = git_dir()?;
    let common = common_dir(&git);

    let head_path = git.join("HEAD");
    println!("cargo:rerun-if-changed={}", head_path.display());
    let head = fs::read_to_string(&head_path).ok()?;
    let head = head.trim();

    let full = match head.strip_prefix("ref:") {
        // Symbolic HEAD, e.g. `ref: refs/heads/main`.
        Some(ref_path) => resolve_ref(&common, ref_path.trim())?,
        // Detached HEAD: the raw commit id is in HEAD.
        None => head.to_string(),
    };
    Some(full.chars().take(12).collect())
}

/// Resolve a ref name (`refs/heads/main`) to a commit id: a loose ref file
/// first, then `packed-refs`.
fn resolve_ref(common: &Path, ref_path: &str) -> Option<String> {
    let loose = common.join(ref_path);
    println!("cargo:rerun-if-changed={}", loose.display());
    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.join("packed-refs");
    println!("cargo:rerun-if-changed={}", packed.display());
    let content = fs::read_to_string(&packed).ok()?;
    for line in content.lines() {
        let line = line.trim();
        // Skip comments and `^`-prefixed peeled-tag lines.
        if line.is_empty() || line.starts_with('#') || line.starts_with('^') {
            continue;
        }
        if let Some((sha, name)) = line.split_once(' ') {
            if name == ref_path {
                return Some(sha.to_string());
            }
        }
    }
    None
}