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

lockfile

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Oct 23, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Overview

Package lockfile manages CLI lockfile state: loading, saving, and converting the on-disk format.

Index

Constants

This section is empty.

Variables

View Source
var ErrCorruptLockfile = errors.New("invalid lockfile")

ErrCorruptLockfile reports that a lockfile exists on disk but its contents can't be used: malformed YAML, unknown fields, a dependency entry missing a required key, or an entry ref the Actions backend wouldn't accept. It is distinct from a missing lockfile (legitimately empty) and from a future-version lockfile (ErrFutureVersion). Loading must never silently discard such a lockfile and overwrite it. Match it with errors.Is; the error text names the actual problem instead.

Functions

func CanonicalRef added in v0.3.0

func CanonicalRef(ref string) string

CanonicalRef spells a full commit SHA in lowercase and returns any other ref unchanged. The Actions backend matches a `uses:` ref to its lockfile key exactly, so a full SHA has one spelling in both.

func EntryKey added in v0.3.0

func EntryKey(p parserlock.Pin) string

EntryKey is a pin's on-disk key.

func RecordsOwnSHA added in v0.3.0

func RecordsOwnSHA(pin parserlock.Pin, ref string) bool

RecordsOwnSHA reports whether a full-SHA key's entry records that same SHA as its ref. The Actions backend rejects it, but a run can re-record the tag or branch the commit came from, so such an entry loads instead of failing the lockfile. It stays recorded and tracked; only ref seeding (Seedable) and parity's ref lookup skip it.

func RootUse added in v0.3.0

func RootUse(refs []parserlock.ActionRef) func(pin parserlock.Pin, commit string) (parserlock.ActionRef, bool)

RootUse returns the lookup of the use in refs, a workflow's current `uses:`, that still uses a recorded root: the use naming its key, else one naming its commit as a full SHA, since rewriting `uses:` to the commit keeps the tag-keyed entry. SHAs compare case-insensitively; other refs are case-sensitive. commit is the entry's algo-hex commit.

func Seedable added in v0.3.0

func Seedable(d dep.Dependency) bool

Seedable reports whether a recorded dependency (from Get or AllDeps) may seed ref resolution or branch hints. One whose entry records its own SHA names no tag or branch, so it is resolved afresh; its host and repository identity still bind.

func UsedRoot added in v0.3.0

func UsedRoot(refs []parserlock.ActionRef) func(pin parserlock.Pin, commit string) bool

UsedRoot returns the test for whether a workflow's recorded root is still used by refs, the workflow's current `uses:` (see RootUse).

func UsesCycle added in v0.3.0

func UsesCycle(f parserlock.File, workflowKey string) []string

UsesCycle returns a `uses:` cycle in workflowKey's recorded closure, as the keys along it with the first repeated last, or nil. The lockfile parser rejects any cycle, so a write must check for one first.

func ValidEntryRef added in v0.3.0

func ValidEntryRef(pin parserlock.Pin, ref string) bool

ValidEntryRef reports whether ref is a lock entry ref the Actions backend resolver accepts for pin: a short tag or branch name it accepts (never a SHA, a refs/ name, revision syntax like main~1, or a `git describe` name), and the key's own ref unless the key is a full SHA. The resolver looks it up as refs/tags/<ref>, then refs/heads/<ref>, and requires the locked commit in its history.

Types

type DirectTracker

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

DirectTracker captures, by dep index, which resolved deps correspond to a workflow's direct uses: refs. Build it from the freshly resolved deps (before any narrowing/normalization mutates dep.Ref), then read the final NWO@Ref direct-key set back with Keys after mutation.

Keys takes the (possibly reassigned) deps slice so callers that swap the slice for an index-aligned copy — e.g. PreserveRefs — still resolve to the post-mutation refs. The index alignment is the contract: every transform between NewDirectTracker and Keys must preserve dep order and length.

Directness is determined by the workflow file's own uses: refs, not by the resolver parent map. A dep that is both a direct use and a composite transitive dep (e.g. actions/setup-go used directly and also pulled in by a composite) has a parent in the parent map but must still be recorded as a workflow-direct pin — otherwise it is perpetually re-flagged as not-pinned.

func NewDirectTracker

func NewDirectTracker(refs []parserlock.ActionRef, deps []dep.Dependency) DirectTracker

NewDirectTracker records which entries of deps match one of the workflow's direct refs, by NWO@Ref, at the deps' current (pre-mutation) refs.

func (DirectTracker) IsDirect added in v0.0.14

func (t DirectTracker) IsDirect(i int) bool

IsDirect reports whether the dep at index i is a workflow-direct use. Out-of-range indices are treated as transitive (false) so callers can gate ref-rewriting on directness without bounds-checking.

func (DirectTracker) Keys

func (t DirectTracker) Keys(deps []dep.Dependency) map[string]bool

Keys returns the set of workflow-direct NWO@Ref keys, reading each dep's current (post-mutation) Key() from the supplied index-aligned slice.

type GraphFault added in v0.3.0

type GraphFault struct {
	Parent string
	Child  string
}

GraphFault is a recorded edge whose Child has no dependencies entry. Parent is empty for an edge from the workflow itself. The lockfile parser rejects cycles, so a loaded graph has none to report.

func GraphFaults added in v0.3.0

func GraphFaults(f parserlock.File, workflowKey string, live func(parserlock.Pin, parserlock.Action) bool) []GraphFault

GraphFaults reports the edges of workflowKey's recorded closure, rooted at the pins live accepts, whose child has no entry.

func (GraphFault) String added in v0.3.0

func (g GraphFault) String() string

type LockedPin added in v0.2.0

type LockedPin struct {
	Pin    parserlock.Pin
	Action parserlock.Action
	// Parent is the canonical key of the composite action that pulls this
	// pin in; empty for the workflow's direct pins.
	Parent string
}

LockedPin is one entry in a workflow's recorded closure.

type MetadataResolver

type MetadataResolver interface {
	RepoIDs(ctx context.Context, hostname, owner, repo string) (ownerID, repoID int64, err error)
}

MetadataResolver fetches the owner/repo numeric IDs for a NWO. The store needs these to populate the dependencies: section on every write.

type OwnSHAEntry added in v0.3.0

type OwnSHAEntry struct {
	Key       string
	Workflows []string
	Parents   []string
}

OwnSHAEntry is an entry that records its own SHA as its ref, with the workflows that list it directly and the entries whose `uses:` reach it, both sorted. An entry with neither is an orphan the next Save drops.

type State

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

State wraps the on-disk dependency lockfile for the current invocation. Save garbage-collects orphan dependencies: entries — entries still referenced by an out-of-scope workflow are preserved, so partial invocations (e.g. `upgrade workflows/foo.yml`) stay noop on unrelated workflows.

State is safe for concurrent use: Set/Save/Get/File/AllDeps/lookupIDs all take the same mutex, allowing parallel pin and upgrade workers to share a single store instance without external synchronization.

func LoadState

func LoadState(repoRoot string, meta MetadataResolver) (*State, error)

LoadState reads the lockfile at repoRoot, returning an empty in-memory file when none exists on disk. A lockfile that exists but cannot be parsed is surfaced as ErrCorruptLockfile rather than being silently treated as empty.

func LoadStateAt

func LoadStateAt(lockfilePath string, meta MetadataResolver) (*State, error)

LoadStateAt reads the lockfile at the given path, returning an empty in-memory file when none exists on disk. Use this when the lockfile lives outside the standard .github/workflows/ location.

func (*State) AllDeps

func (s *State) AllDeps() []dep.Dependency

AllDeps returns every action entry in the lockfile as a Dependency, populated with Tag and Branch inferred from the action's ref field. Order is undefined. Intended for callers that need the union of recorded pins across all workflows (e.g. seeding resolver caches on startup); filter ref seeding with Seedable.

func (*State) Closure added in v0.2.0

func (s *State) Closure(workflowKey string, live func(parserlock.Pin, parserlock.Action) bool) []LockedPin

Closure returns the workflow's recorded pins, direct and transitive, by walking the `uses:` list of each direct pin that live accepts. Stale roots are skipped so their children are not reported. Each pin appears once; a pin the workflow uses directly is always reported as direct (no Parent), even when a composite also uses it.

func (*State) Descendants added in v0.3.0

func (s *State) Descendants(key string) []LockedPin

Descendants returns the recorded entries below the entry stored under key, each with the entry whose `uses:` reached it as Parent. A write that keeps the entry keeps them too.

func (*State) Entry added in v0.3.0

func (s *State) Entry(key string) (parserlock.Action, bool)

Entry returns the action recorded under an on-disk key.

func (*State) File

func (s *State) File() parserlock.File

File returns the in-memory parser-level lockfile snapshot. Intended for consumers that drive the workflow-parser diagnostics engine directly and need the whole file (workflow keys + actions metadata) in one shot.

func (*State) GC added in v0.3.0

func (s *State) GC()

GC drops entries no workflow reaches, as Save does.

func (*State) Get

func (s *State) Get(workflowKey string) ([]dep.Dependency, error)

Get returns the dependencies recorded for workflowKey (e.g. ".github/workflows/ci.yml"). Returns nil when the workflow has no entry.

func (*State) HasWorkflow

func (s *State) HasWorkflow(workflowKey string) bool

HasWorkflow reports whether the lockfile's workflows{} map already contains an entry for workflowKey. Used by `upgrade --no-onboard` to refuse silently onboarding a previously-untracked workflow during a Dependabot run.

func (*State) KeepEdges added in v0.3.0

func (s *State) KeepEdges(workflowKeys []string)

KeepEdges makes Set keep the recorded edges of every entry the recorded closures of workflowKeys reach. Call it with the workflows a run doesn't write: a manifest read covers only the sub-actions this run uses, and those workflows may use others of the same entry.

func (*State) OriginalVersion added in v0.1.0

func (s *State) OriginalVersion() string

OriginalVersion returns the version string that was on disk before the lockfile was loaded and migrated. Empty when the file did not exist.

func (*State) OwnSHAEntries added in v0.3.0

func (s *State) OwnSHAEntries() []OwnSHAEntry

OwnSHAEntries returns every entry that records its own SHA as its ref, sorted by key.

func (*State) Preview added in v0.3.0

func (s *State) Preview() *State

Preview returns an in-memory copy of s for computing what a write would leave on disk. It never reaches the network or the file: an owner/repo ID missing from s's cache gets a placeholder in the preview's own cache, and Save refuses. So a preview matches the real write in its keys, refs, commits, edges and workflow lists, which no ID decides, but not in its IDs. Writes to the preview leave s alone.

func (*State) PruneWorkflows added in v0.1.6

func (s *State) PruneWorkflows(keep map[string]bool, refs ...parserlock.ActionRef) []string

PruneWorkflows removes every workflow entry whose key is not in keep and returns the removed keys in sorted order. Orphaned dependencies are removed from memory too, so they cannot seed caches or block identity verification. refs preserves pins still used by newly added or renamed workflows.

Callers must only pass a keep set derived from a full-directory scan. A partial invocation (an explicit subset of workflow paths) has no authority to decide a workflow is deleted and must not call this.

func (*State) RecordedRepoID added in v0.2.0

func (s *State) RecordedRepoID(owner, repo string) int64

RecordedRepoID returns the repo ID recorded for owner/repo, or 0. It never hits the network.

func (*State) RecordsOwnSHA added in v0.3.0

func (s *State) RecordsOwnSHA(owner, repo, sha string) bool

RecordsOwnSHA reports whether the lockfile's entry for owner/repo@sha records that SHA as its ref (see the RecordsOwnSHA function).

func (*State) Save

func (s *State) Save() error

Save persists the lockfile to disk, garbage-collecting orphan action entries (pins referenced by no workflow). When the in-memory file is empty after GC, the on-disk file is removed.

func (*State) Set

func (s *State) Set(ctx context.Context, workflowKey string, deps []dep.Dependency, parentMap map[string][]string, directKeys map[string]bool) error

Set replaces the workflow's direct-dependency entry and upserts the per-action metadata for every pin in the resolved closure. The workflow entry holds only the action refs that appear in the workflow file itself (its direct uses); per-action `uses:` lists encode the transitive graph, so a reader walks from each direct pin to reconstruct the full closure — the npm/cargo lockfile model.

parentMap is the resolver's child → []parent map (Dependency.Key() form: NWO@Ref, without the SHA suffix), as returned by Resolver.ParentMap. Set uses it to build per-action `uses:` lists by inversion. Both sides are translated into canonical pin keys (NWO@Ref:algo-hex). A dep whose manifest this run read gets the edges it found; one taken from the lockfile unread keeps its recorded edges (see admitUses).

directKeys is the set of Dependency.Key() values (NWO@Ref) that the workflow file uses directly. It is authoritative for the workflow's direct-pin list: a dep is recorded as workflow-direct iff its key is in directKeys. This correctly handles deps that are both a direct use and a composite-transitive dep (e.g. actions/setup-go used directly and also pulled in by a composite) — such a dep has a parent in parentMap but must still appear in the workflow's direct list. When directKeys is nil, Set falls back to treating any dep with no parent in parentMap as direct.

Resolution of owner/repo numeric IDs happens lazily per NWO and is cached for the lifetime of the store.

func (*State) SetEntryRef added in v0.3.0

func (s *State) SetEntryRef(key, ref string) error

SetEntryRef records ref for the existing entry key, which must be one the Actions backend accepts.

func (*State) SetHostname added in v0.2.0

func (s *State) SetHostname(hostname string) error

SetHostname binds omitted v0.0.3 hostnames to the invocation's home host. Legacy pins retain the SDK's explicit github.com binding.

func (*State) SetMetadataResolver added in v0.1.0

func (s *State) SetMetadataResolver(meta MetadataResolver)

SetMetadataResolver sets the resolver used by lookupIDs to fetch owner/repo numeric IDs. This allows loading the lockfile before auth is available, then wiring in the resolver once the API client is ready.

func (*State) UsedBy added in v0.3.0

func (s *State) UsedBy(key string) []string

UsedBy returns the keys of the entries whose recorded `uses:` lists key, sorted.

func (*State) VerifyHosts added in v0.2.0

func (s *State) VerifyHosts(ctx context.Context, workflowKeys []string) error

VerifyHosts confirms that the pins reachable from workflowKeys can be looked up on their recorded host before they are trusted on Proxima. Older producers used omission for dotcom, not the home tenant. Only inconclusive lookups fail here; a missing repository or a changed repo ID is left to the parity pass, which reports repo-unavailable or repo-replaced.

func (*State) WorkflowKeys added in v0.1.6

func (s *State) WorkflowKeys() []string

WorkflowKeys returns the workflow paths currently recorded in the lockfile's workflows{} map. Order is undefined. Intended for callers that need to reconcile the recorded set against the workflows present on disk.

Jump to

Keyboard shortcuts

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