chore: import upstream snapshot with attribution
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:33:42 +08:00
commit a06f331eb8
3186 changed files with 689843 additions and 0 deletions
+185
View File
@@ -0,0 +1,185 @@
package pathkey
import (
"os"
"path/filepath"
"runtime"
"strings"
)
// CaseInsensitivePaths reports whether path identity comparisons should
// ignore letter case. The default is chosen for the host filesystem:
// true on Windows and macOS (whose default NTFS / APFS volumes are
// case-insensitive), false on Linux.
//
// The environment override GORTEX_CASE_SENSITIVE_PATHS lets an operator
// correct the default for an unusual mount:
//
// - =1 (or true/yes) forces case-SENSITIVE comparisons — for a
// case-sensitive APFS or NTFS volume on macOS / Windows.
// - =0 (or false/no) forces case-INSENSITIVE comparisons — for a
// case-insensitive mount (e.g. an exFAT / SMB share) on Linux.
//
// It is a settable package variable rather than a constant so that
// higher-layer tests can flip it deterministically on any CI platform.
// A test that flips it MUST restore it via t.Cleanup and MUST NOT call
// t.Parallel — the variable is process-global.
var CaseInsensitivePaths bool
func init() {
CaseInsensitivePaths = defaultCaseInsensitive()
}
// defaultCaseInsensitive resolves the initial value of CaseInsensitivePaths
// from the environment override, falling back to the host GOOS default.
func defaultCaseInsensitive() bool {
switch strings.ToLower(strings.TrimSpace(os.Getenv("GORTEX_CASE_SENSITIVE_PATHS"))) {
case "1", "true", "yes", "on":
return false
case "0", "false", "no", "off":
return true
}
return runtime.GOOS == "windows" || runtime.GOOS == "darwin"
}
// foldPath reduces p to a canonical identity key: filepath.Clean removes
// redundant separators and dot segments, Normalize folds Unicode to NFC,
// the Windows-style volume component (drive letter or UNC root) is
// upper-cased so "c:" and "C:" agree, and — when ci is true — the
// remainder is lower-cased so a case-insensitive filesystem treats
// "Documents" and "documents" as one path.
//
// It is a pure function of (string, bool): the volume is detected with
// host-independent semantics (see volumeNameLen) so Windows and macOS
// folding can be table-tested on a Linux CI runner. It never touches the
// filesystem and never resolves symlinks — identity is lexical.
func foldPath(p string, ci bool) string {
p = filepath.Clean(p)
p = Normalize(p)
vl := volumeNameLen(p)
vol := strings.ToUpper(p[:vl])
rest := p[vl:]
if ci {
rest = strings.ToLower(rest)
}
return vol + rest
}
// EqualPaths reports whether two absolute paths identify the same
// location once folded. A byte-equal fast path mirrors Equal so the
// common case allocates nothing.
func EqualPaths(a, b string) bool {
if a == b {
return true
}
return foldPath(a, CaseInsensitivePaths) == foldPath(b, CaseInsensitivePaths)
}
// HasPathPrefix reports whether path is root itself or lies beneath it,
// comparing on folded identity and respecting path-component boundaries:
// "/Users/me/Doc" is not under "/Users/me/Documents". The volume root
// ("/" on POSIX, "C:\" on Windows) already ends in a separator after
// filepath.Clean, which the trailing-separator branch handles.
func HasPathPrefix(path, root string) bool {
fp := foldPath(path, CaseInsensitivePaths)
fr := foldPath(root, CaseInsensitivePaths)
if fp == fr {
return true
}
sep := string(filepath.Separator)
if strings.HasSuffix(fr, sep) {
return strings.HasPrefix(fp, fr)
}
return strings.HasPrefix(fp, fr+sep)
}
// NormalizeVolume returns p with its Windows-style volume component
// (drive letter or UNC root) upper-cased, e.g. "c:\x" -> "C:\x". It is a
// no-op when there is no volume component (every POSIX path) and when the
// volume is already upper-cased, so it never allocates on the common path.
//
// It is applied at store time to NEW tracked-repo entries only, to
// converge cosmetically with os.Getwd's convention (which upper-cases the
// drive letter on Windows). The volume is never part of a repo basename,
// so re-casing it cannot rotate a repo prefix or node IDs. It must never
// be applied to an already-stored path.
func NormalizeVolume(p string) string {
vl := volumeNameLen(p)
if vl == 0 {
return p
}
up := strings.ToUpper(p[:vl])
if up == p[:vl] {
return p
}
return up + p[vl:]
}
// SamePathIdentity is the store-time dedup predicate. It treats a and b
// as the same repo when they fold equal AND, when they differ byte-wise
// but both stat cleanly, os.SameFile confirms they resolve to the same
// directory — so two genuinely distinct directories on a case-sensitive
// volume are never merged. If either path fails to stat, the fold is
// trusted: a path that cannot be stat'd cannot be a distinct live repo.
//
// It stats the filesystem, so it must never be called on a per-request
// hot path — only when adding, removing, or deduping a tracked entry.
func SamePathIdentity(a, b string) bool {
if a == b {
return true
}
if !EqualPaths(a, b) {
return false
}
ai, aerr := os.Stat(a)
bi, berr := os.Stat(b)
if aerr != nil || berr != nil {
return true
}
return os.SameFile(ai, bi)
}
// volumeNameLen returns the length of the leading Windows-style volume
// component of p — a drive letter ("c:") or a UNC root ("\\host\share").
// It uses Windows semantics regardless of the host OS so that path
// folding is deterministic and testable on every platform; a POSIX path
// (which begins with "/") has no volume and yields 0. Both '\\' and '/'
// are accepted as separators because a Windows path may arrive with
// either.
func volumeNameLen(p string) int {
if len(p) < 2 {
return 0
}
// Drive letter, e.g. "c:".
if p[1] == ':' && isDriveLetter(p[0]) {
return 2
}
// UNC root, e.g. "\\host\share" or "//host/share".
if isAnySeparator(p[0]) && isAnySeparator(p[1]) {
n := len(p)
i := 2
host := i
for i < n && !isAnySeparator(p[i]) {
i++
}
if i == host {
return 0 // "\\" with no host is not a UNC volume
}
if i < n {
i++ // consume the separator before the share name
}
for i < n && !isAnySeparator(p[i]) {
i++
}
return i
}
return 0
}
func isDriveLetter(c byte) bool {
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
}
func isAnySeparator(c byte) bool {
return c == '\\' || c == '/'
}
+268
View File
@@ -0,0 +1,268 @@
package pathkey
import (
"os"
"path/filepath"
"testing"
)
// withCaseInsensitive flips the process-global CaseInsensitivePaths for
// the duration of a test and restores it on cleanup. Tests that call it
// must not run in parallel.
func withCaseInsensitive(t *testing.T, v bool) {
t.Helper()
prev := CaseInsensitivePaths
CaseInsensitivePaths = v
t.Cleanup(func() { CaseInsensitivePaths = prev })
}
func TestFoldPath_CaseInsensitiveLowersRemainder(t *testing.T) {
// With ci=true, two casings of the same path fold to one key.
a := foldPath("/Users/me/Documents/Project", true)
b := foldPath("/Users/me/documents/project", true)
if a != b {
t.Fatalf("foldPath ci=true did not converge: %q vs %q", a, b)
}
}
func TestFoldPath_CaseSensitiveKeepsRemainder(t *testing.T) {
// With ci=false, differing casings stay distinct.
a := foldPath("/Users/me/Documents", false)
b := foldPath("/Users/me/documents", false)
if a == b {
t.Fatalf("foldPath ci=false collapsed distinct casings: both %q", a)
}
}
func TestFoldPath_UppercasesDriveLetter(t *testing.T) {
// Windows drive letters fold identically regardless of case, even
// when the rest of the path is compared case-sensitively.
for _, ci := range []bool{false, true} {
a := foldPath(`c:\work\git\myrepo`, ci)
b := foldPath(`C:\work\git\myrepo`, ci)
if a != b {
t.Fatalf("foldPath ci=%v did not fold drive-letter case: %q vs %q", ci, a, b)
}
}
}
func TestFoldPath_CleansRedundantSegments(t *testing.T) {
got := foldPath("/Users/me/./project/../project", false)
want := "/Users/me/project"
if got != want {
t.Fatalf("foldPath did not clean: got %q want %q", got, want)
}
}
func TestFoldPath_FoldsNFDBeforeCasing(t *testing.T) {
// The NFC fold runs before case folding, so an NFD macOS spelling
// and an NFC git spelling of the same accented path converge.
a := foldPath("/x/"+cafeNFD, true)
b := foldPath("/x/"+cafeNFC, true)
if a != b {
t.Fatalf("foldPath did not reconcile NFD vs NFC: %q vs %q", a, b)
}
}
func TestEqualPaths_CaseVariants(t *testing.T) {
withCaseInsensitive(t, true)
if !EqualPaths("/Users/me/Documents/Project", "/Users/me/documents/project") {
t.Fatal("EqualPaths should treat case variants as equal when case-insensitive")
}
}
func TestEqualPaths_CaseSensitiveRejects(t *testing.T) {
withCaseInsensitive(t, false)
if EqualPaths("/Users/me/Documents", "/Users/me/documents") {
t.Fatal("EqualPaths must keep case variants distinct when case-sensitive")
}
}
func TestEqualPaths_ByteEqualFastPath(t *testing.T) {
withCaseInsensitive(t, false)
if !EqualPaths("/a/b/c", "/a/b/c") {
t.Fatal("EqualPaths byte-equal fast path failed")
}
}
func TestEqualPaths_DistinctPaths(t *testing.T) {
withCaseInsensitive(t, true)
if EqualPaths("/Users/me/alpha", "/Users/me/beta") {
t.Fatal("EqualPaths reported genuinely distinct paths as equal")
}
}
func TestHasPathPrefix_RootItself(t *testing.T) {
withCaseInsensitive(t, true)
if !HasPathPrefix("/Users/me/Repo", "/Users/me/repo") {
t.Fatal("a path is a prefix of itself (case-insensitive)")
}
}
func TestHasPathPrefix_ChildUnderRoot(t *testing.T) {
withCaseInsensitive(t, true)
if !HasPathPrefix("/Users/me/Repo/pkg/file.go", "/Users/me/repo") {
t.Fatal("child path should be under root regardless of case")
}
}
func TestHasPathPrefix_ComponentBoundary(t *testing.T) {
withCaseInsensitive(t, false)
// A shared textual prefix that is not a path-component prefix must
// not match: "/Users/me/Doc" is NOT under "/Users/me/Documents".
if HasPathPrefix("/Users/me/Documents", "/Users/me/Doc") {
t.Fatal("prefix-but-not-component must not match")
}
if HasPathPrefix("/Users/me/Doc", "/Users/me/Documents") {
t.Fatal("shorter sibling must not be under longer sibling")
}
}
func TestHasPathPrefix_PosixRoot(t *testing.T) {
withCaseInsensitive(t, false)
if !HasPathPrefix("/anything/here", "/") {
t.Fatal("everything is under the POSIX root")
}
if !HasPathPrefix("/", "/") {
t.Fatal("root is a prefix of itself")
}
}
func TestHasPathPrefix_TrailingSeparatorRoot(t *testing.T) {
withCaseInsensitive(t, true)
// A root passed with a trailing separator is cleaned; the child
// still matches.
if !HasPathPrefix("/Users/me/Repo/x", "/Users/me/repo/") {
t.Fatal("trailing-separator root should still match its children")
}
}
func TestHasPathPrefix_WindowsDriveRoot(t *testing.T) {
withCaseInsensitive(t, true)
// The drive-letter fold — the #277 fix — is exercised by the
// root-equals-cwd path (VS Code passes "c:\..." for a config-stored
// "C:\..."). This holds with either separator on any host OS.
if !HasPathPrefix(`C:\work\git\myrepo`, `c:\work\git\myrepo`) {
t.Fatal("Windows drive-letter case must not defeat the coverage check")
}
// The child-under-root branch relies on filepath.Separator, so use a
// forward-slash Windows path (also accepted by Windows) so the
// component boundary is verifiable on a POSIX CI runner too.
if !HasPathPrefix(`c:/work/git/myrepo/pkg`, `C:/work/git/myrepo`) {
t.Fatal("child under a drive-letter root must match despite case")
}
}
func TestNormalizeVolume(t *testing.T) {
cases := []struct {
in string
want string
}{
{`c:\x`, `C:\x`},
{`C:\x`, `C:\x`},
{`\\srv\share`, `\\SRV\SHARE`},
{`\\Srv\Share\dir`, `\\SRV\SHARE\dir`},
{"/Users/me/project", "/Users/me/project"}, // no volume: no-op
{"relative/path", "relative/path"}, // no volume: no-op
{"", ""},
}
for _, c := range cases {
if got := NormalizeVolume(c.in); got != c.want {
t.Errorf("NormalizeVolume(%q) = %q, want %q", c.in, got, c.want)
}
}
}
// TestNormalizeVolume_Idempotent proves store-time volume normalization is
// stable: applying it in the CLI (runTrack / install) and again in the
// daemon's TrackRepoCtx / ReconcileRepoCtx never drifts the stored path.
func TestNormalizeVolume_Idempotent(t *testing.T) {
for _, in := range []string{`c:\x`, `C:\x`, `\\srv\share\dir`, "/Users/me/p", "relative/p", ""} {
once := NormalizeVolume(in)
if twice := NormalizeVolume(once); twice != once {
t.Errorf("NormalizeVolume not idempotent for %q: once=%q twice=%q", in, once, twice)
}
}
}
func TestVolumeNameLen(t *testing.T) {
cases := []struct {
in string
want int
}{
{"/Users/me", 0},
{"relative", 0},
{`c:\x`, 2},
{`C:`, 2},
{`\\srv\share`, len(`\\srv\share`)},
{`\\srv\share\dir`, len(`\\srv\share`)},
{`\\`, 0},
{"", 0},
{"a", 0},
}
for _, c := range cases {
if got := volumeNameLen(c.in); got != c.want {
t.Errorf("volumeNameLen(%q) = %d, want %d", c.in, got, c.want)
}
}
}
func TestSamePathIdentity_DistinctDirsNotMerged(t *testing.T) {
// Two genuinely distinct directories must never be merged, even
// when the fold is forced case-insensitive.
withCaseInsensitive(t, true)
a := t.TempDir()
b := t.TempDir()
if SamePathIdentity(a, b) {
t.Fatalf("distinct dirs %q and %q must not be identified", a, b)
}
}
func TestSamePathIdentity_CaseVariantOfSameDir(t *testing.T) {
// On this host's default FS, a case variant of a real directory
// resolves to the same inode. Only assert the merge when the host
// filesystem is actually case-insensitive (so the variant exists).
withCaseInsensitive(t, true)
dir := t.TempDir()
base := filepath.Base(dir)
variant := filepath.Join(filepath.Dir(dir), swapCase(base))
if _, err := os.Stat(variant); err != nil {
t.Skipf("host filesystem is case-sensitive; %q does not resolve", variant)
}
if !SamePathIdentity(dir, variant) {
t.Fatalf("case variant %q of %q should identify as the same dir", variant, dir)
}
}
func TestSamePathIdentity_MissingPathTrustsFold(t *testing.T) {
// When a path cannot be stat'd, the fold is trusted rather than
// treating the pair as distinct.
withCaseInsensitive(t, true)
a := "/nonexistent/gortex/Repo"
b := "/nonexistent/gortex/repo"
if !SamePathIdentity(a, b) {
t.Fatal("fold-equal missing paths should be identified")
}
}
func TestSamePathIdentity_ByteEqualFastPath(t *testing.T) {
withCaseInsensitive(t, false)
if !SamePathIdentity("/x/y", "/x/y") {
t.Fatal("byte-equal fast path failed")
}
}
// swapCase toggles the case of ASCII letters so a case-variant directory
// name can be constructed deterministically.
func swapCase(s string) string {
b := []byte(s)
for i := range b {
switch {
case b[i] >= 'a' && b[i] <= 'z':
b[i] -= 32
case b[i] >= 'A' && b[i] <= 'Z':
b[i] += 32
}
}
return string(b)
}
+72
View File
@@ -0,0 +1,72 @@
// Package pathkey canonicalises filesystem paths to a single Unicode
// form so that every subsystem that keys, compares, or deduplicates a
// path agrees on its byte representation regardless of where the path
// came from.
//
// The problem this solves is platform-dependent Unicode normalisation.
// A file named "café.go" or "日本語.go" has no single byte encoding:
//
// - macOS APFS / HFS+ decompose filenames to NFD ("e" + combining
// acute) — so filepath.WalkDir and the FSEvents watcher hand back
// decomposed bytes.
// - Linux filesystems preserve the bytes as written, which for a
// file created from git or a Linux editor is almost always NFC
// (precomposed "é").
// - git itself never normalises: `git diff` emits paths exactly as
// the bytes were committed, i.e. typically NFC, even on macOS.
//
// So the *same* file can present as two different byte sequences
// within one process: the filesystem walk stores one form, the git
// watcher reports another. A graph node keyed under one form is then
// invisible to a lookup made with the other — lost symbols, missed
// watcher events, snapshot-key misses that trigger a full re-index.
//
// The fix is to fold every path to one canonical form at every keying
// boundary. NFC is chosen as the target: it is what git stores, what
// the W3C and IETF recommend for interchange, and the form Linux
// repositories already carry, so normalising costs nothing on the
// common path and only repairs macOS's decomposed form.
package pathkey
import "golang.org/x/text/unicode/norm"
// Normalize returns p folded to Unicode NFC. An all-ASCII path — the
// overwhelmingly common case — is returned unchanged without invoking
// the normaliser, so the helper is free on hot indexing paths. A path
// already in NFC is likewise returned as-is by norm.NFC.String, which
// allocates nothing when no rune needs recomposing.
//
// Normalize only touches Unicode composition; it does not clean,
// resolve, or change the separator style of the path. Callers that
// also want filepath.Clean / ToSlash semantics apply those separately
// — the two concerns are deliberately kept independent.
func Normalize(p string) string {
if isASCII(p) {
return p
}
return norm.NFC.String(p)
}
// Equal reports whether two paths denote the same path once both are
// folded to NFC. Use this instead of a raw == when either operand may
// have come from a different platform or from git rather than from a
// filesystem walk.
func Equal(a, b string) bool {
if a == b {
return true
}
return Normalize(a) == Normalize(b)
}
// isASCII reports whether s contains only bytes < 0x80. Such a string
// is identical in every Unicode normal form, so it can skip the
// normaliser entirely. Inlined as a tight byte loop rather than
// ranging runes to avoid UTF-8 decoding on the hot path.
func isASCII(s string) bool {
for i := 0; i < len(s); i++ {
if s[i] >= 0x80 {
return false
}
}
return true
}
+145
View File
@@ -0,0 +1,145 @@
package pathkey
import (
"testing"
"golang.org/x/text/unicode/norm"
)
// All non-ASCII fixtures are spelled with explicit \u / \U escapes so
// this source file stays pure-ASCII and the byte form of each fixture
// is deterministic regardless of the editor encoding used to save it.
//
// - cafeNFC / cafeNFD: "cafe.go" with an accented e. NFC precomposes
// it to U+00E9; NFD spells it as base "e" (U+0065) + U+0301
// COMBINING ACUTE ACCENT. Same filename, different bytes — the
// exact macOS-NFD vs git/Linux-NFC split this package reconciles.
// - cjkName / cyrName: CJK ideographs and Cyrillic letters have no
// canonical decomposition, so they are identical in every normal
// form; included to prove non-Latin scripts pass through cleanly.
// - emoji: an astral-plane code point, to prove rune-correctness.
const (
cafeNFC = "café.go" // precomposed e-acute
cafeNFD = "café.go" // "e" + combining acute accent
cjkName = "日本語.go" // CJK: "Japanese language"
cyrName = "кириллица.go" // Cyrillic
emoji = "\U0001f600.go" // grinning face
)
func TestNormalize_FoldsNFDToNFC(t *testing.T) {
// The decomposed input must come back as the precomposed form, so
// a path observed in NFD (macOS) keys identically to the same
// path observed in NFC (git / Linux).
if cafeNFD == cafeNFC {
t.Fatal("test fixture invalid: NFD and NFC forms are byte-identical")
}
got := Normalize(cafeNFD)
if got != cafeNFC {
t.Fatalf("Normalize(NFD) = %q, want NFC %q", got, cafeNFC)
}
}
func TestNormalize_NFCIsStable(t *testing.T) {
// An already-NFC path is returned unchanged — normalisation is
// idempotent, so re-keying an NFC path never drifts.
if got := Normalize(cafeNFC); got != cafeNFC {
t.Fatalf("Normalize(NFC) = %q, want unchanged %q", got, cafeNFC)
}
}
func TestNormalize_Idempotent(t *testing.T) {
for _, in := range []string{cafeNFD, cafeNFC, cjkName, cyrName, emoji, "plain/ascii/path.go"} {
once := Normalize(in)
twice := Normalize(once)
if once != twice {
t.Errorf("Normalize not idempotent for %q: once=%q twice=%q", in, once, twice)
}
}
}
func TestNormalize_ASCIIUnchanged(t *testing.T) {
// Pure-ASCII paths — the overwhelmingly common case — must pass
// through byte-identical (and via the isASCII fast path).
for _, in := range []string{
"main.go",
"internal/indexer/watcher.go",
"./pkg/util.go",
"a/b/c/d/e.go",
"",
} {
if got := Normalize(in); got != in {
t.Errorf("Normalize(%q) = %q, want unchanged", in, got)
}
}
}
func TestNormalize_AlwaysReturnsNFC(t *testing.T) {
// Whatever Normalize returns must itself be in NFC — that is the
// contract every keying call site relies on.
for _, in := range []string{cafeNFD, cafeNFC, cjkName, cyrName, emoji} {
got := Normalize(in)
if !norm.NFC.IsNormalString(got) {
t.Errorf("Normalize(%q) = %q is not NFC-normalised", in, got)
}
}
}
func TestNormalize_PreservesSeparatorsAndContent(t *testing.T) {
// Normalisation must only touch Unicode composition — it must not
// drop, reorder, or rewrite path separators or other content.
cjkDir := "src/" + cjkName // already NFC
if got := Normalize(cjkDir); got != cjkDir {
t.Fatalf("Normalize altered an already-NFC CJK path: got %q want %q", got, cjkDir)
}
// A decomposed component nested in a multi-segment path folds in
// place without disturbing the surrounding segments.
nested := "a/" + cafeNFD + "/b.go"
want := "a/" + cafeNFC + "/b.go"
if got := Normalize(nested); got != want {
t.Fatalf("Normalize(%q) = %q, want %q", nested, got, want)
}
}
func TestEqual_NFCvsNFD(t *testing.T) {
// The whole point: the two byte forms of one filename compare
// equal once folded.
if !Equal(cafeNFC, cafeNFD) {
t.Fatalf("Equal(%q, %q) = false, want true", cafeNFC, cafeNFD)
}
}
func TestEqual_IdenticalShortCircuit(t *testing.T) {
if !Equal(cjkName, cjkName) {
t.Fatalf("Equal of identical strings returned false")
}
}
func TestEqual_DistinctPathsNotEqual(t *testing.T) {
if Equal(cjkName, cyrName) {
t.Fatalf("Equal reported two genuinely different paths as equal")
}
if Equal("a.go", "b.go") {
t.Fatalf("Equal reported a.go == b.go")
}
}
func TestIsASCII(t *testing.T) {
cases := []struct {
in string
want bool
}{
{"", true},
{"plain.go", true},
{"internal/indexer/watcher.go", true},
{cafeNFC, false},
{cafeNFD, false},
{cjkName, false},
{cyrName, false},
{emoji, false},
}
for _, c := range cases {
if got := isASCII(c.in); got != c.want {
t.Errorf("isASCII(%q) = %v, want %v", c.in, got, c.want)
}
}
}