// Package standdata resolves the paths of the measurement data the corpus-gated tests read. // // Two kinds of input, and the difference is the whole reason this package exists: // // - IN-REPO paths that git does not carry. The jieba contrast artifact sits at eval/exp16/data but // falls under the polygon's blanket raw-data rule (eval/.gitignore), so no clone ever receives it; // it is regenerated from jieba 0.42.1 (SHA in docs/experiments/16-bank-mining.md). // - The STAND CORPUS — book derivatives that are versioned by their own git repository under // /books (D39.157 п.2, moved there on 24.08; the previous address $HOME/books is dead). // // Both are derived from the repository root found by MARKER, never counted in ".." and never taken from // $HOME: a default that reads a home directory is wrong for every machine but the one it was written on, // and counting parents breaks under -trimpath. Every caller keeps its env override (TM_*), which stays // senior to these defaults — the defaults are what a bare clone gets, not a policy. package standdata import ( "os" "path/filepath" "runtime" ) // standRoot is the corpus directory inside the repository. Its own git repository, its own origin, and // the outer repo holds it in .gitignore — so a path under it is present on a stand and absent in CI. const standRoot = "books" // EnvOr returns the environment override for key, or def when it is unset/empty. func EnvOr(key, def string) string { if v := os.Getenv(key); v != "" { return v } return def } // Root finds the repository root by MARKER (backend/go.mod) rather than by counting "..", so the // derivation cannot quietly go wrong. It starts from this file's own directory, and falls back to the // working directory when that path is not absolute — which is what -trimpath does: it rewrites // runtime.Caller to a module-relative path, and a naive "../../.." then yields a RELATIVE root that can // only ever miss, turning a corpus test into a permanent silent skip on every machine including the // stand. `go test` always runs in the package directory, so the fallback is sound. func Root() string { start := "" if _, self, _, ok := runtime.Caller(0); ok && filepath.IsAbs(self) { start = filepath.Dir(self) } if start == "" { wd, err := os.Getwd() if err != nil { return "" } start = wd } for dir := start; ; { if _, err := os.Stat(filepath.Join(dir, "backend", "go.mod")); err == nil { return dir } parent := filepath.Dir(dir) if parent == dir { return "" } dir = parent } } // RepoFile resolves parts against the repository root so an in-repo path is found wherever the clone // lives. When the root cannot be established the returned path is deliberately un-plausible: a missing // marker must read as "my derivation is broken", never as "the data is simply absent". func RepoFile(parts ...string) string { root := Root() if root == "" { return filepath.Join("", filepath.Join(parts...)) } return filepath.Join(append([]string{root}, parts...)...) } // StandFile resolves parts against the stand corpus root (/books). func StandFile(parts ...string) string { return RepoFile(append([]string{standRoot}, parts...)...) }