refs

package
v0.2026254.503 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 11, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Overview

Package refs is the spec fabric slice for the remote-repo GIT primitives — pure git-exec + string parsing + cache-path computation, host primitives a plugin composes but the kernel itself needs (reconcile's --remote tag query, the collection walk). RELOCATED from sdk/kit (#55 fabric-primitive extraction), so charly core reaches them via spec/refs directly and the remaining sdk/candy callers keep a thin kit re-export. The FETCH ORCHESTRATION (override → cache → clone → migrate) still lives in the refs plugin (candy/plugin-refs); these are the reusable mechanisms it composes. The only non-stdlib dependency is the advisory file lock (spec/lock) DownloadRepo serializes concurrent cache refresh with.

Index

Constants

View Source
const (
	DefaultRefsCacheTTL = time.Hour
	LatestTagTTL        = DefaultRefsCacheTTL
	DefaultBranchTTL    = DefaultRefsCacheTTL
	ResolveRefTTL       = DefaultRefsCacheTTL
)

Cache TTLs (see the freshness policy above). One default — the eval-batch reality: a 16-lane, multi-phase check run re-resolves the same refs hundreds of times, so a shorter TTL re-probes mid-batch (measured: 152 concurrent git ls-remote -> GitHub throttling -> the deploy-add stall). LatestTagTTL bounds ONLY the in-process batch-dedupe cache; the persisted latest_tags entries are the offline fallback (served on fetch failure only), never TTL-fresh data.

Variables

This section is empty.

Functions

func CompareSemver

func CompareSemver(a, b string) int

CompareSemver compares two semver-like version strings (e.g. "v1.2.3"). Returns -1 if a < b, 0 if equal, 1 if a > b. It delegates to spec/calver.CompareCalVer — the single dotted-version comparator (which strips the "v" prefix and falls back to lexical comparison for non-numeric parts) — so semver and CalVer strings share ONE implementation.

func DownloadRepo

func DownloadRepo(repoPath string, version string) (string, error)

DownloadRepo downloads a remote repo to the cache. Returns the cache path where the repo was stored.

Freshness contract: the cache is reused ONLY when its recorded provenance (the commit the export was cloned from) equals the ref's CURRENTLY resolved commit — so a branch that advanced upstream (main) is re-downloaded instead of silently serving stale content. The check costs the one ls-remote GitResolveRef already performs; for immutable tags the provenance always matches, so their cache-hit behavior is unchanged. A cache written before this contract has no provenance and is re-downloaded once, then self-heals.

func GitClone

func GitClone(repoURL string, ref string, commit string, targetDir string) error

GitClone clones a git repository at a specific ref into the target directory. Uses shallow clone for efficiency.

func GitDefaultBranch

func GitDefaultBranch(repoURL string) (string, error)

GitDefaultBranch detects the default branch of a remote repository. Uses git ls-remote --symref to find what HEAD points to. Returns the branch name (e.g., "main", "master").

This is the RAW primitive — it always hits the network. Caching is the GitClient's job (refs/git_client.go): every consumer that needs a cached answer goes through GitClient.DefaultBranch. The former per-primitive cache (latest-tags.json) is DELETED — one cache, one layer (R3/R5).

func GitLatestTag

func GitLatestTag(repoURL string) (string, error)

GitLatestTag queries a remote repo for tags and returns the highest semver tag. Looks for tags matching v* pattern, sorts by semver, returns the highest. Returns an error if no version tags are found.

This is the RAW primitive — it always hits the network. Caching is the GitClient's job (refs/git_client.go): every consumer that needs a cached answer goes through GitClient.LatestTag, which wraps this primitive with the persistent `cache:` section of the per-host charly.yml. The former per-primitive cache (latest-tags.json) is DELETED — one cache, one layer (R3/R5).

func GitResolveRef

func GitResolveRef(repoURL string, ref string) (string, error)

GitResolveRef resolves a git reference (tag, branch, or commit) to a full commit hash. Uses git ls-remote for tags/branches; for commit hashes, validates length and returns as-is.

NOTE: deliberately NOT cached. DownloadRepo's freshness contract resolves the ref to its CURRENT commit on every call, so a mutable branch (main) that moved upstream is re-downloaded instead of serving stale content (refs/git_test.go TestDownloadRepoFrom_RefreshesMovedBranch). A TTL cache here would break that contract — the freshness check must see the live commit.

func IsMutableRef

func IsMutableRef(version string) bool

IsMutableRef reports whether a repo ref version can ADVANCE upstream (a branch name such as main, or an empty version that resolves to the default branch). Immutable coordinates — CalVer/semver tags (v…) and full commit SHAs — never change what they point at; the project's tags are add-only. Mutable refs must re-resolve on every access: a cache hit on a branch otherwise freezes it at first-download content forever (the pre-#146 @main protocol skew — a stale main export served protocol-v1 plugin sources indefinitely).

func IsRepoCached

func IsRepoCached(repoPath, version string) (bool, error)

IsRepoCached reports whether a repo version is already in the cache AS A USABLE EXPORT — the directory exists AND every submodule it declares has content.

The completeness half is load-bearing for IMMUTABLE refs. EnsureRepoDownloaded short-circuits on `cached && !IsMutableRef(version)`, returning RepoCachePath directly and never entering DownloadRepo — so the repoCacheFresh completeness check cannot see a tag's cache at all. Directory-exists alone therefore pinned every incomplete TAG export permanently: a tag never moves, so nothing would ever re-fetch it. On the machine this was found, `charly@v2026.183.1359` held 0 of its 9 declared submodules and `charly@v2026.201.0706` held 1 of 10, both unrepairable for the life of the cache. Checking content here routes an incomplete export down the download branch instead, so tag and branch caches self-heal by the SAME predicate rather than one growing its own copy.

func RepoCacheDir

func RepoCacheDir() (string, error)

RepoCacheDir returns the cache directory for remote repos. Uses $CHARLY_REPO_CACHE env var if set, otherwise ~/.cache/charly/repos/.

func RepoCachePath

func RepoCachePath(repoPath, version string) (string, error)

RepoCachePath returns the cache path for a specific repo version. e.g. ~/.cache/charly/repos/github.com/org/repo@v1.0.0/

func RepoGitURL

func RepoGitURL(repoPath string) string

RepoGitURL converts a repo path to a git clone URL. e.g. "github.com/opencharly/ml-layers" -> "https://github.com/opencharly/ml-layers.git"

Types

type GitClient added in v0.2026241.711

type GitClient struct {
	// contains filtered or unexported fields
}

GitClient is the centralized git layer. Construct once per process (or per project) and share it — the cache is the point.

func NewGitClient added in v0.2026241.711

func NewGitClient(cacheFile string) *GitClient

NewGitClient returns a GitClient whose cache lives in the `cache:` section of the per-host charly.yml at cacheFile. When cacheFile is empty, the default deploy config path (~/.config/charly/charly.yml, honoring the CHARLY_DEPLOY_CONFIG override) is used.

func (*GitClient) BypassCache added in v0.2026247.2350

func (g *GitClient) BypassCache()

save persists the `cache:` section into the per-host charly.yml under the advisory lock (best-effort). It reads the CURRENT file, updates only the `cache:` key (preserving every other key — deploy:, provides:, ledger:, system:, …), and writes back atomically (tempfile + rename). BypassCache disables every cached lookup — the next resolutions are fresh (an operator on-demand truth: a new tag just released, a moved branch). Runtime-only: the flag does not survive the process (see SetBypass for the persisted form).

func (*GitClient) CacheStatus added in v0.2026247.2350

func (g *GitClient) CacheStatus() (string, int)

CacheStatus reports the cache file path and the number of cached git answers (the operator-facing `charly cache status` surface).

func (*GitClient) ClearCache added in v0.2026247.2350

func (g *GitClient) ClearCache() error

ClearCache drops every cached git answer (the in-memory entries and the persisted file), so the next resolutions are fresh — the `charly cache clear/refresh` surface.

func (*GitClient) DefaultBranch added in v0.2026241.711

func (g *GitClient) DefaultBranch(repoURL string) (string, error)

func (*GitClient) Download added in v0.2026241.711

func (g *GitClient) Download(repoPath, version string, download func(repoPath, version string) (string, error)) (string, error)

Download fetches repoPath@version into the repo cache and returns the cache path, CACHED with a short TTL (ResolveRefTTL — aliasing DefaultRefsCacheTTL today, the const block above documents the one-default reality and the future-divergence seam). A mutable ref (a branch or the default branch) can move, so the freshness contract requires re-resolving eventually — but not on every invocation: the TTL means a command run twice in quick succession (e.g. the status fan-out resolving the envelope multiple times) pays the download once. The cached PATH is additionally validated against its CONTENT (dirUsable): a wiped or evicted repo-cache dir must never be served for the TTL (the content-validity principle — the same class the materialized-tree cache fixed with component drift detection).

func (*GitClient) LatestTag added in v0.2026241.711

func (g *GitClient) LatestTag(repoURL string) (string, error)

LatestTag returns the highest semver tag of repoURL.

Freshness contract (the offline-fallback cutover): the IN-PROCESS cache is the only fresh-data cache (batch dedupe within one process — the 152-concurrent- ls-remote throttling storm was a within-one-process fanout, solved by this layer). The PERSISTED latest_tags entries are an OFFLINE FALLBACK, never fresh data: a fresh process with a reachable network re-probes, so a tag pushed minutes ago is always seen. (The former model — serving persisted entries as fresh for the 1h TTL — was the stale-latest-tag bug: a fresh run resolved plugin refs to a tag list up to an hour old.) The persisted entry is served ONLY when the network fetch fails, with a loud stderr line — resilience, not freshness.

func (*GitClient) ResolveRef added in v0.2026241.711

func (g *GitClient) ResolveRef(repoURL, ref string) (string, error)

func (*GitClient) SetBypass added in v0.2026248.719

func (g *GitClient) SetBypass(on bool) error

SetBypass persists the bypass flag in the cache: git: section of the per-host charly.yml — the `charly cache bypass` operator surface. When on, every cached lookup is disabled (fresh resolutions) until turned off, and a NEW process (a fresh GitClient) honors it at construction (load reads the flag). When off, the flag is cleared and the cache resumes.

func (*GitClient) WarmUp added in v0.2026241.711

func (g *GitClient) WarmUp(repoURLs []string, stderr *os.File)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL