Documentation
¶
Overview ¶
Package mirror converges a mirror forge toward a desired release set, granularly and provenance-bounded: it only ever creates/updates/deletes releases IT placed there (marked), never foreign or human-authored ones.
The desired set is computed upstream (typically primary's releases filtered by the retention engine); this package merely converges reality to it. Cadence is a caller concern — ReconcileReleases is idempotent, so any trigger (publish, cron, serve, manual) is equivalent.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DestroyRelease ¶
DestroyRelease removes a release by tag across the given forges (primary + mirrors). Deliberate and explicit — the `sf release destroy` engine. No tombstone: once it's gone from the source, a later mirror reconcile has nothing to resurrect (the desired set no longer contains it). Errors are collected per forge, not fatal, so one unreachable mirror doesn't block the rest.
Types ¶
type DesiredAsset ¶
type DesiredAsset struct {
Name string
Digest string // content digest, "" if unknown
Size int64
Source forge.ReleaseAsset // remote source (mirror re-host)
Local string // local file path (author upsert)
}
DesiredAsset is a file the release should carry. For MIRRORING, Source names how to fetch it from the source forge (re-host). For AUTHORING (upsert), Local is a path to the freshly-built file; Local takes precedence over Source.
type DesiredLink ¶ added in v0.9.0
DesiredLink is an EXTERNAL reference (a registry/image link) the mirror release should carry. Unlike a DesiredAsset it is NEVER downloaded — it is re-created on the mirror via AddReleaseLink (on GitHub, appended to the release body).
type DesiredRelease ¶
type DesiredRelease struct {
Tag string
Ref string
Name string
Body string
Prerelease bool
Assets []DesiredAsset // downloadable files: download from source → upload to mirror
Links []DesiredLink // external references (registry/image): re-created as links
Diagnostics []string // explicit notes about assets that could not be classified
Fingerprint string
}
DesiredRelease is one release the mirror should hold. Body is verbatim from the source; Assets are the file assets to re-host. Fingerprint, if set, is the source's content fingerprint (else it's computed).
func BuildDesiredReleases ¶
func BuildDesiredReleases(ctx context.Context, src releaseSource, templates []string, policy config.RetentionPolicy) ([]DesiredRelease, error)
BuildDesiredReleases computes a mirror's desired release set: the source's releases matching `templates`, filtered by the retention `policy`, each with its file assets resolved for re-hosting.
This is the retention-as-desired-set layering — retention decides what SHOULD exist; the reconciler (ReconcileReleases) converges the mirror to it. The keep decision is read out of the shipped retention engine via a recording store (no refactor, no real deletes).
func DesiredFromReleases ¶
func DesiredFromReleases(ctx context.Context, src releaseSource, rels []forge.ReleaseInfo) ([]DesiredRelease, error)
DesiredFromReleases builds the desired set from an already-selected list of source releases (e.g. scoped by a sync facet), resolving each release's file assets for re-hosting. Use this when the caller has already decided WHICH releases to mirror; BuildDesiredReleases is the template+retention front door.
type Destroyer ¶
Destroyer is the delete side of a forge — anything that can remove a release by tag. *forge.Forge implementations satisfy it.
type Options ¶
type Options struct {
Prune bool
InScope func(tag string) bool
// PreserveAdopted is called with the mirror's CURRENT release, once, immediately
// before an unmarked in-scope release is overwritten by adoption. Adoption is the
// only step that replaces content SF did not write, so it is also the only one that
// can lose something — this is the hook that makes it recoverable. Returning an
// error ABORTS that adoption: what cannot be preserved is not destroyed. Nil skips
// preservation entirely (the caller accepts the loss).
PreserveAdopted func(tag string, prior forge.ReleaseInfo) error
}
Options tunes convergence. Prune enables removal of OUR mirror releases no longer in the desired set (never foreign). InScope, when set, further limits pruning to tags it returns true for (the ownership boundary).
type RefOptions ¶
type RefOptions struct {
Prune bool
Force bool
// ForceRef force-updates a SPECIFIC diverged ref even when Force is off — for a
// ref that is mutable BY DESIGN (a rolling tag alias like "latest" that advances
// each release and would otherwise perpetually diverge). Immutable refs are left to
// the default keep-divergent path. nil means "no per-ref force". Force (the blanket
// flag) still wins for every ref when set.
ForceRef func(ref string) bool
// IsAncestor reports whether the first commit is an ancestor of the second. It
// distinguishes a fast-forward (the mirror ref is behind the source) from a true
// divergence: a differing ref whose mirror commit is an ancestor of the source
// commit fast-forwards (a non-force Update), everything else keeps divergent. It
// must return false whenever either commit is not resolvable in local history — a
// mirror commit absent locally IS the divergence signal. nil ⇒ every differing ref
// is treated as diverged (the ancestry-blind behavior).
IsAncestor func(ancestor, descendant string) bool
InScope func(ref string) bool
}
RefOptions tunes ref reconciliation. InScope is the ownership boundary; nil means "mirror all" for create/update, but leaves prune a no-op (nothing is attributable, so nothing is deleted).
type RefPlan ¶
type RefPlan struct {
Create []RefUpdate // refs missing on the mirror
Update []RefUpdate // fast-forward, or forced when diverged+Force
Diverged []string // diverged, kept (Force off) — surfaced, never clobbered
Prune []RefUpdate // OUR refs (in scope) gone from source — deletes
Foreign []string // mirror-only refs outside scope — left untouched
InSync int // already matching
}
RefPlan is the computed, side-effect-free reconciliation plan.
type RefUpdate ¶
type RefUpdate struct {
Ref string // e.g. "refs/heads/main" or a bare branch/tag name (caller's convention)
SHA string // target commit (empty for a delete)
Force bool // overwrite a diverged ref (only set when Force is enabled)
Delete bool // remove the ref on the mirror
// Verified marks an update PROVEN to be a fast-forward by ancestry over peeled
// commits. The executor may assert it instead of asking the transport to
// re-derive the same answer from unpeeled ref hashes — which it cannot do for
// an annotated tag, whose ref hash names a tag object rather than a commit.
Verified bool
}
RefUpdate is one push/delete the plan calls for.
type Result ¶
type Result struct {
Created []string
Updated []string
Pruned []string
InSync int // skipped via fingerprint fast-path
Adopted []string // in-scope tag existed unmarked — converged and marked as ours
Errors []error
// Diagnostics are non-fatal notes surfaced separately from Errors — e.g. a source
// asset that is neither a downloadable file nor an external link, disclosed instead of
// silently 404-ing or being uploaded as a pretend-file.
Diagnostics []string
}
Result reports what the reconcile did (all counts/lists are mirror-side).
func ReconcileReleases ¶
func ReconcileReleases(ctx context.Context, src, dst releaseForge, desired []DesiredRelease, opts Options) (*Result, error)
ReconcileReleases converges dst toward desired, re-hosting assets from src. Idempotent; provenance-bounded; granular. Errors on individual releases are collected, not fatal — the pass continues.
func UpsertReleases ¶
func UpsertReleases(ctx context.Context, dst releaseForge, desired []DesiredRelease) (*Result, error)
UpsertReleases authors releases onto dst — create-or-update, content-aware, from local build files (DesiredAsset.Local). A release that already exists is converged (notes drift + changed assets replaced), never duplicated; an unchanged one is a fingerprint no-op. There is no prune (authoring adds; it does not manage a set), and a foreign release with the same tag is left untouched (never clobbered) — exactly like the mirror reconciler.
This is the `sf release create` engine: idempotent by construction, so a re-run on the same commit changes nothing, and fixing a bad binary is just a re-run with the new file.