Files
wehub-resource-sync a06f331eb8
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
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

159 lines
5.4 KiB
Go

package indexer
import (
"encoding/json"
"path/filepath"
"sort"
"strconv"
"strings"
)
// extractorVersions records the logic version of each language's
// extractor. Bump a language's entry when its extraction logic changes
// in a way that should re-extract already-indexed files whose content
// did not change (a new edge kind, a corrected node shape, a fixed
// parser bug). The version is mixed into the Merkle leaf salt (see
// merkleSaltFor), so a bump re-flags exactly that language's files as
// stale on the next reconcile — without re-reading unchanged content
// and without disturbing other languages.
//
// A language absent here, or pinned at 1, carries no salt and therefore
// behaves exactly as before: the registry is dormant until a version is
// deliberately raised. This is the surgical alternative to the
// binary-wide snapshot invalidation (which restages the whole repo on
// any binary change): a Go-extractor fix re-extracts only `.go` files.
var extractorVersions = map[string]int{
// Languages default to version 1 (no salt). Raise an entry here in
// the same change that alters a language's extraction logic, e.g.
// "go": 2,
}
// extractorSaltExtLang maps a lower-case file extension to the language
// key used in extractorVersions. It need not be exhaustive: an unmapped
// extension simply carries no extractor-version salt (content-only
// staleness, the pre-existing behaviour). Extensions are grouped to the
// extractor that owns them.
var extractorSaltExtLang = map[string]string{
".go": "go",
".py": "python",
".pyi": "python",
".js": "javascript",
".jsx": "javascript",
".mjs": "javascript",
".cjs": "javascript",
".ts": "typescript",
".tsx": "typescript",
".mts": "typescript",
".cts": "typescript",
".java": "java",
".rb": "ruby",
".rs": "rust",
".c": "c",
".h": "c",
".cc": "cpp",
".cpp": "cpp",
".cxx": "cpp",
".hpp": "cpp",
".hh": "cpp",
".cs": "csharp",
".php": "php",
".swift": "swift",
".kt": "kotlin",
".kts": "kotlin",
".scala": "scala",
".m": "objc",
".mm": "objcpp",
".lua": "lua",
".dart": "dart",
".ex": "elixir",
".exs": "elixir",
".sh": "bash",
".bash": "bash",
}
// ExtractorLangForFile returns the extractor-staleness language key for a
// repo-relative path (by file extension), or "" when the extension carries no
// extractor-version tracking. Used to tell whether a touched file belongs to a
// language whose extractor is stale.
func ExtractorLangForFile(rel string) string {
return extractorSaltExtLang[strings.ToLower(filepath.Ext(rel))]
}
// extractorVersionForLang returns the registered extractor version for a
// language, defaulting to 1.
func extractorVersionForLang(lang string) int {
if v, ok := extractorVersions[lang]; ok && v > 0 {
return v
}
return 1
}
// merkleSaltFor returns the Merkle leaf salt for a repo-relative path:
// "" when the file's language extractor is at the baseline version 1
// (so the leaf equals the content hash and nothing changes), or
// "lang@N" once a language's extractor version is bumped, so its files
// re-extract on the next reconcile even when their content is unchanged.
func merkleSaltFor(rel string) string {
lang := extractorSaltExtLang[strings.ToLower(filepath.Ext(rel))]
if lang == "" {
return ""
}
v := extractorVersionForLang(lang)
if v <= 1 {
return ""
}
return lang + "@" + strconv.Itoa(v)
}
// ExtractorVersionStaleLangs reports which languages' extractors have been
// bumped SINCE the graph was last indexed — comparing the per-language
// versions persisted on RepoIndexState (a JSON object lang->version) against
// the running binary's current versions. A language is stale when its stored
// version is behind the current one: its already-indexed files would
// re-extract on the next reconcile. Returns the stale languages, sorted.
//
// This is the per-LANGUAGE precision that turns "your index is from an older
// binary" into "reindex only Go + Python" — a scoped reindex instead of a full
// cold rebuild. An empty/absent stored map (no baseline) reports nothing.
func ExtractorVersionStaleLangs(storedJSON string) []string {
storedJSON = strings.TrimSpace(storedJSON)
if storedJSON == "" {
return nil
}
var stored map[string]int
if err := json.Unmarshal([]byte(storedJSON), &stored); err != nil || len(stored) == 0 {
return nil
}
return staleLangsBetween(stored, extractorVersionsSnapshot())
}
// staleLangsBetween returns the languages whose stored version is behind the
// current version — only languages present in BOTH maps are compared, so a
// language the stored snapshot never recorded is not spuriously flagged.
func staleLangsBetween(stored, current map[string]int) []string {
var stale []string
for lang, storedV := range stored {
if cur, ok := current[lang]; ok && storedV < cur {
stale = append(stale, lang)
}
}
sort.Strings(stale)
return stale
}
// extractorVersionsSnapshot returns a copy of the current per-language
// extractor versions for persistence in repo_index_state, so a future
// reconcile can tell which extractor produced the stored graph.
func extractorVersionsSnapshot() map[string]int {
out := make(map[string]int, len(extractorSaltExtLang))
seen := map[string]bool{}
for _, lang := range extractorSaltExtLang {
if seen[lang] {
continue
}
seen[lang] = true
out[lang] = extractorVersionForLang(lang)
}
return out
}