state

package
v0.2.1 Latest Latest
Warning

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

Go to latest
Published: Sep 30, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package state stores ar2's state in a container registry (GHCR).

The state doesn't belong in the aqua-registry-g2 repository: it changes on every run and would add a commit each time. GHCR is used the same way aqua-registry-updater uses it, as a small object store keyed by a tag.

Index

Constants

View Source
const FileName = "state.json"

FileName is the name of the state file inside the OCI artifact.

View Source
const SchemaVersion = "0.1.0"

SchemaVersion is the current schema version of the state.

View Source
const Tag = "latest"

Tag is the tag the state artifact is pushed to.

Variables

View Source
var ErrNotFound = errors.New("the container registry holds no state")

ErrNotFound is returned by Fetch when the container registry holds no state yet, which is what a repository looks like before 'ar2 init' has ever run.

Functions

func Fingerprint

func Fingerprint(versions []string) string

Fingerprint reduces a sweep's versions to something worth storing for every package in the registry.

The versions themselves would be a few hundred kilobytes of state pushed on every run; what a sweep asks of them is only whether they are the same as last time.

func NewRepository

func NewRepository(reg *Registry, token string) (*remote.Repository, error)

NewRepository creates a client for the remote OCI repository.

func Pull

func Pull(ctx context.Context, repo *remote.Repository, dir, tag string) error

Pull downloads the state file at tag into dir.

func Push

func Push(ctx context.Context, repo *remote.Repository, dir, tag string) error

Push uploads the state file in dir to the remote repository under tag.

func Store

func Store(ctx context.Context, reg *Registry, token string, s *State) error

Store writes the state to the container registry.

func Write

func Write(path string, s *State) error

Write writes the state to path.

Types

type Flags

type Flags struct {
	Registry   string
	Repository string
	Username   string
}

Flags are the settings locating the state in a container registry. Both 'ar2 init', which writes it, and 'ar2 run', which reads it, take these.

func (*Flags) Resolve

func (f *Flags) Resolve() (*Registry, error)

Resolve turns the flags into a Registry, filling in what GitHub Actions provides.

type Package

type Package struct {
	RepoOwner string `json:"repo_owner,omitempty"`
	RepoName  string `json:"repo_name,omitempty"`
	// Stars orders the work: packages with more stars are processed first.
	// It is not omitempty: a repository with 0 stars must stay distinguishable from
	// one whose star count could not be read, so that the latter can be retried.
	Stars int `json:"stars"`

	// Versions fingerprints the newest versions the last sweep saw upstream.
	//
	// It is what an ETag would have been, over the one thing a sweep cares about.
	// GitHub's own ETag for the release listing changes whenever an asset is
	// downloaded, because the listing carries the count, so it says "something
	// changed" for packages where nothing did — and does it most for the popular
	// ones, which is exactly backwards.
	Versions string `json:"versions,omitempty"`
	// CaughtUp says the registry held every version of that sweep.
	//
	// It is recorded from asking the registry, never from having opened a pull
	// request. A pull request that fails CI is never merged, and a package marked
	// done for work that didn't land would never be looked at again. Versions are
	// not removed from the registry, so what was true when it was written stays
	// true.
	CaughtUp bool `json:"caught_up,omitempty"`
	// Round counts the runs that have reached this package.
	//
	// A run works through the packages in order and stops when its budget is spent,
	// so the ones it never reached keep the lower count and come first next time.
	// Without it the order is the same every run, and a package that takes a share
	// and gets nowhere -- one whose asset can't be downloaded, say -- takes the same
	// share of every run and holds up everything behind it for as long as it is
	// broken.
	//
	// It counts turns rather than naming a round the whole registry is in, so that
	// nothing has to decide when a round ended: a package that has had fewer turns
	// than another is behind, and that is the whole rule. A package the registry has
	// just gained starts from the largest count there is, which is the back of the
	// order; starting from none would put it ahead of everything until it caught up.
	Round int `json:"round,omitempty"`
	// LastDeepCheck is when the package's whole history was last walked. Zero means
	// never, which is what a package that hasn't been backfilled looks like.
	//
	// A sweep sees the newest versions, and anything published after the history is
	// complete appears among them. A release dated in the past doesn't, so the
	// history is walked again from time to time to find what the sweep structurally
	// cannot.
	LastDeepCheck time.Time `json:"last_deep_check,omitzero"`
}

Package is the state of a single package.

type Progress added in v0.0.26

type Progress struct {
	// Packages is how many packages are in the order.
	Packages int
	// CaughtUp is how many hold every version their last sweep saw upstream.
	CaughtUp int
	// Untouched is how many no run has reached yet.
	Untouched int
	// Lap is the fewest turns any package has had, which is the lap the order is on.
	Lap int
	// Waiting is how many are still on that lap, so a run reaches them before anything
	// gets another turn.
	Waiting int
	// Unwalked is how many have never had their whole history walked. A sweep sees the
	// newest versions, so a release dated in the past is only found by walking.
	Unwalked int
	// Renamed is how many names the registry has stopped holding, each pointing at the
	// one it holds now.
	Renamed int
}

Progress is how far the registry has got, as the state sees it.

What the repository holds is the registry; this is what the order knows about getting there, which nothing else can answer. Counting branches says how many packages have anything at all, and says nothing about the ones still waiting for a turn or the ones whose history hasn't been walked.

type Registry

type Registry struct {
	// Registry is the container registry host, e.g. "ghcr.io".
	Registry string
	// Repository is the OCI repository, e.g. "aquaproj/ar2".
	Repository string
	// Username is the user the token belongs to.
	Username string
}

Registry describes where the state artifact lives.

type State

type State struct {
	// SchemaVersion allows the format to change without breaking an older ar2 that
	// reads the state.
	SchemaVersion string    `json:"schema_version"`
	UpdatedAt     time.Time `json:"updated_at"`
	// Packages is keyed by package name.
	Packages map[string]*Package `json:"packages"`
	// Renamed maps a name the registry has stopped holding to the one it holds now.
	//
	// A repository that is renamed makes the registry's name for the package the wrong
	// one, and the package is moved: its branch is carried over and the catalogue is
	// brought to the new name. What is left is aqua-registry, which still lists the old
	// name until its own updater notices, and a run reading that would add the package
	// back under the name it was just moved off.
	//
	// It is also what stops the same rename being done twice: the sweep asks about the
	// repository a package records, so once that is the new one GitHub says nothing has
	// changed.
	Renamed map[string]string `json:"renamed,omitempty"`
}

State is ar2's state.

It holds only what is safe to cache. Which versions have been generated is not here: that is read from aqua-registry-g2 itself, because a version only counts once its pull request is merged, and a record of "already generated" would stop a version whose CI failed from ever being retried.

func Fetch

func Fetch(ctx context.Context, reg *Registry, token string) (*State, error)

Fetch pulls the state from the container registry and reads it. It returns ErrNotFound when there is none.

func New

func New() *State

New creates an empty state.

func Read

func Read(r io.Reader) (*State, error)

Read reads a state.

func (*State) Front added in v0.0.23

func (s *State) Front() int

Front is the fewest turns anything in the order has had: the packages waiting for this lap's turn.

It is where a package the registry has just gained joins. Starting from none instead would put it ahead of the whole registry until it caught up -- a turn in every run for as long as that took, which is what counting turns is there to stop -- and starting from the most would cost it a lap for no reason but having arrived late. A package nothing has been generated for is the last thing to hold back, and it still takes one turn a lap like everything else.

The counts in the order are never more than one apart, so this is a choice between this lap and the next. In a registry that is caught up there is no difference at all: a run reaches every package, so every count is the same.

func (*State) Progress added in v0.0.26

func (s *State) Progress() *Progress

Progress reports how far the registry has got.

func (*State) Rename added in v0.0.20

func (s *State) Rename(from, to string)

Rename records that a package is held under another name now, and moves what is known about it.

The turn count comes along, so a renamed package neither loses its place in the order nor jumps to the front of it: it is the same package and has had the same turns.

Jump to

Keyboard shortcuts

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