Help us shape the future of Go! Take the 2026 Go Developer Survey

mirror

package
v0.12.0 Latest Latest
Warning

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

Go to latest
Published: Sep 12, 2026 License: AGPL-3.0, AGPL-3.0-only Imports: 11 Imported by: 0

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

func DestroyRelease(ctx context.Context, tag string, forges ...Destroyer) []error

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 struct {
	Name     string
	URL      string
	LinkType string
}

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

type Destroyer interface {
	DeleteRelease(ctx context.Context, tag string) error
}

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
}

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.

func PlanRefs

func PlanRefs(srcRefs, mirrorRefs map[string]string, opts RefOptions) RefPlan

PlanRefs computes the convergence plan from source and mirror ref sets (name → commit SHA). Pure and deterministic; ApplyRefPlan executes it.

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
}

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
	SkippedForeign []string // in-scope tag exists but is not ours — left untouched
	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.

Jump to

Keyboard shortcuts

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