textmachine/backend/internal/standdata/standdata.go

79 lines
3.2 KiB
Go

// 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
// <repo>/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("<repo-root-not-found>", filepath.Join(parts...))
}
return filepath.Join(append([]string{root}, parts...)...)
}
// StandFile resolves parts against the stand corpus root (<repo>/books).
func StandFile(parts ...string) string {
return RepoFile(append([]string{standRoot}, parts...)...)
}