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

nordisk/gunnar

installtest/src/lane_dir.rs

//! One derivation of "a cargo target directory that belongs to *this* worktree".
//!
//! Two callers need it and they are in different crates:
//!
//!  * [`crate::subject`] — where the **subject binary** is built. Sharing that
//!    directory means one worktree installs and asserts another worktree's `gunnar`.
//!    MEASURED 2026-08-05: one worktree's `gunnar-install/release/gunnar` was
//!    rewritten at 08:59 stamped `v1.1-8-g6c02185` while another worktree's run was
//!    still reading `v1.1-6-g95354c2` out of the same path, and two arms were
//!    green *because* of the sharing. Fixed in `81c5ae5`.
//!  * `xtask`'s `install-test` / `bare` tasks — where the **harness itself** is
//!    built. A constant there is a different failure with the same cause: five
//!    lanes running `cargo xtask install-test` block on each other's package
//!    lock instead of testing the wrong binary.
//!
//! Both must never be the same function written twice, so it is written
//! here, once, taking its root explicitly. `xtask` has **zero dependencies** on
//! purpose and cannot depend on this crate, so it reaches this file with
//! `#[path = "../../installtest/src/lane_dir.rs"]`: a source include adds no
//! edge to the dependency graph, and a second copy of nine lines of FNV is
//! exactly what that rule is not meant to buy.

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

/// `<target_root>/<worktree basename>-<8 hex of FNV-1a over the canonical path>`.
///
/// Taking `root` explicitly, and not off some ambient `workspace_root()`, so the
/// property that matters can actually be asserted: **two different worktrees
/// must get two different directories.** With a constant those two calls are
/// equal, which is the bug, and no test that only ever passes one root can see
/// it.
///
/// The hash is what carries the guarantee — worktree basenames collide
/// constantly here (`~/scratch/wt/redci/gunnar` and
/// `~/git/gunnar` are both `gunnar`) — and the basename rides along
/// only so a human reading `ls` can tell whose directory is whose.
pub fn lane_dir(target_root: &str, root: &Path) -> PathBuf {
    let root = root.canonicalize().unwrap_or_else(|_| root.to_path_buf());
    let name = root
        .file_name()
        .and_then(|s| s.to_str())
        .unwrap_or("worktree");
    let name: String = name
        .chars()
        .map(|c| {
            if c.is_ascii_alphanumeric() || c == '-' || c == '_' {
                c
            } else {
                '-'
            }
        })
        .collect();
    home_expanded(target_root).join(format!("{name}-{:08x}", path_hash(&root)))
}

/// **`~/…` expanded against `$HOME`.**
///
/// The scratch roots are written as `~/scratch/…` rather than as one
/// developer's absolute home: a path with a username baked into it is wrong for
/// everybody else and does not belong in a public tree. `~` is a shell
/// convention and nothing expands it inside a Rust string, so it is expanded
/// HERE — the one function every root passes through — rather than at each of
/// the callers, where the day somebody forgets is the day a literal `~` directory
/// appears in the working directory.
///
/// Restated rather than reused (the twin is `installtest::expand_home`, and the
/// canonical keep since 2026-09-10 is `nornir_xtask_support::expand_home` in
/// edda) because this file is also `#[path]`-included by `xtask`, which
/// deliberately has no dependencies and cannot reach either crate. The
/// zero-dependency rule is `xtask/Cargo.toml`'s and it is argued there: a
/// workspace member's dependency takes part in resolver-2 feature unification,
/// and the guard must not be the thing it guards against.
///
/// So this is the ONE copy the sweep could not remove even in principle —
/// `nornir mimir analyze duplicates` ranked the cluster first, `bench/` and
/// `deploy/upcloud/` became `pub use`s of the leaf, and this file and its
/// `#[path]` includer are the two that stay. Behaviour is pinned in the leaf by
/// `expand_home_expands_only_a_leading_tilde_slash`.
pub fn home_expanded(p: &str) -> PathBuf {
    match p.strip_prefix("~/") {
        Some(rest) => {
            PathBuf::from(std::env::var("HOME").unwrap_or_else(|_| "/root".to_string())).join(rest)
        }
        None => PathBuf::from(p),
    }
}

/// FNV-1a over the canonical path, folded to 32 bits. Hand-rolled because
/// `installtest` reaches this from a crate with one path dependency and `xtask`
/// from a crate with none; nothing cryptographic is being asked of it, only that
/// two distinct worktrees on one box do not land on one directory.
fn path_hash(path: &Path) -> u32 {
    let mut h: u64 = 0xcbf2_9ce4_8422_2325;
    for b in path.as_os_str().as_encoded_bytes() {
        h ^= u64::from(*b);
        h = h.wrapping_mul(0x0000_0100_0000_01b3);
    }
    (h ^ (h >> 32)) as u32
}

#[cfg(test)]
mod tests {
    use super::*;

    /// Two worktrees on this box, deliberately not existing, so this says
    /// something about the derivation rather than about the desktop's filesystem.
    const LANE_A: &str = "~/scratch/wt/__lane_a__/gunnar";
    const LANE_B: &str = "~/scratch/wt/__lane_b__/gunnar";
    const ROOT: &str = "~/scratch/cargo/__probe__";

    /// The defect, in the derivation itself: both lanes are called `gunnar`, so
    /// a basename-only slug — or a constant — makes these equal.
    #[test]
    fn two_worktrees_get_two_directories() {
        assert_ne!(
            lane_dir(ROOT, Path::new(LANE_A)),
            lane_dir(ROOT, Path::new(LANE_B)),
            "two worktrees resolved to one directory"
        );
    }

    /// The identity-value trap in the other direction: a pid, a timestamp or a
    /// random would satisfy the test above perfectly and throw away every
    /// incremental build every lane has.
    #[test]
    fn one_worktree_gets_one_directory_every_time() {
        assert_eq!(
            lane_dir(ROOT, Path::new(LANE_A)),
            lane_dir(ROOT, Path::new(LANE_A)),
            "one worktree got two different directories, so nothing is ever incremental"
        );
    }

    /// It stays under the root it was given, and it never collapses back onto
    /// the root itself — which is the shared directory this exists to end.
    #[test]
    fn the_directory_is_under_the_root_and_is_not_the_root() {
        let d = lane_dir(ROOT, Path::new(LANE_A));
        assert!(
            d.starts_with(home_expanded(ROOT)),
            "{} left its root",
            d.display()
        );
        assert_ne!(
            d,
            home_expanded(ROOT),
            "the derivation collapsed back onto the shared root itself"
        );
    }

    /// Two different roots must not be conflated either: `installtest`'s subject
    /// directory and `xtask`'s harness directory are two different things for
    /// the same worktree, and putting them in one place is the contention this
    /// was split apart to end.
    #[test]
    fn two_roots_give_two_directories_for_one_worktree() {
        assert_ne!(
            lane_dir("~/scratch/cargo/gunnar-install", Path::new(LANE_A)),
            lane_dir("~/scratch/cargo/gunnar-install-test", Path::new(LANE_A)),
            "the subject directory and the harness directory are the same path"
        );
    }
}