Documentation
¶
Overview ¶
Package g2 talks to the aqua-registry-g2 repository.
Index ¶
- Constants
- func BranchName(pkgName string) string
- func EncodePackageName(name string) string
- func ErrNoTemplate(err error) bool
- func HeadBranchName(pkgName string) string
- type Breadth
- type Client
- func (c *Client) BranchSHA(ctx context.Context, branch string) (string, error)
- func (c *Client) Commit(ctx context.Context, branch, parent, message string, files []*File) error
- func (c *Client) Config(ctx context.Context, pkgName string) (*aquag2.Config, error)
- func (c *Client) CreateIndexPullRequest(ctx context.Context, base, title, body string) (*gogithub.PullRequest, error)
- func (c *Client) CreatePullRequest(ctx context.Context, pkgName, title, body string) (*gogithub.PullRequest, error)
- func (c *Client) EnsurePackageBranch(ctx context.Context, pkgName string) (string, error)
- func (c *Client) File(ctx context.Context, ref, path string) (string, error)
- func (c *Client) Index(ctx context.Context, ref string) (*aquag2.Index, error)
- func (c *Client) IndexPullRequest(ctx context.Context) (*gogithub.PullRequest, error)
- func (c *Client) PackageBranches(ctx context.Context) ([]string, error)
- func (c *Client) PackagesInFlight(ctx context.Context) (map[string]struct{}, error)
- func (c *Client) RegistryConfig(ctx context.Context, ref string) (*RegistryConfig, error)
- func (c *Client) RenamePackage(ctx context.Context, from, to string) (bool, error)
- func (c *Client) Version(ctx context.Context, pkgName, version string) (*aquag2.Registry, error)
- func (c *Client) Versions(ctx context.Context, logger *slog.Logger, pkgName string) (map[string]struct{}, error)
- type File
- type IgnoredPackage
- type RegistryConfig
Constants ¶
const BranchPrefix = "pkg_"
BranchPrefix marks the branches holding a package's generated registry.json. It keeps them out of the way of main and of any operational branch, and lets one branch ruleset cover all of them with a single pattern.
const ConfigFileName = aquag2.ConfigFileName
ConfigFileName is the definition at the root of a package's branch, the file registry.json is generated from.
const HeadBranchPrefix = "ar2_"
HeadBranchPrefix marks the branches ar2 opens pull requests from. They are separate from the package branches so that a ruleset protecting the latter doesn't have to make an exception for the branch a pull request comes from.
const IndexBranch = HeadBranchPrefix + "index"
IndexBranch is where an update to the catalogue is committed.
One fixed branch rather than one per run: the catalogue is only ever added to, so two runs writing to the same branch produce the same thing as two runs one after the other. A run whose pull request is still open adds to it instead of opening another, which is what keeps a scheduled reconciliation from leaving a trail of them behind.
const IndexFileName = aquag2.IndexFileName
IndexFileName is the catalogue on the default branch.
const RegistryConfigFileName = "ar2.yaml"
RegistryConfigFileName is the registry's own configuration, on the default branch.
It says what ar2 should do differently for this registry, which is nothing about any one package's contents: those live on the package's branch. Today that is the list of packages to leave alone.
const TemplateDir = "template"
TemplateDir is the directory on main holding the files a new package branch starts with. Its contents are copied to the branch at the same paths, so a workflow lives at templates/.github/workflows/ and lands at .github/workflows/.
A package branch needs the CI workflow before a pull request into it can be checked, because a pull_request workflow is read from the branch the pull request targets. The files live on main so that changing them changes what every new branch gets.
Variables ¶
This section is empty.
Functions ¶
func BranchName ¶
BranchName returns the branch holding the package's generated registry.json.
func EncodePackageName ¶
EncodePackageName escapes a package name so it can be used as a git ref.
Every character outside [A-Za-z0-9.-] becomes an underscore followed by two lowercase hex digits. Escaping the underscore itself as well makes the mapping reversible, which "/" -> "__" is not: 12 package names already contain an underscore, so that scheme is ambiguous in principle.
cli/cli -> cli_2fcli ipinfo/cli/grepip -> ipinfo_2fcli_2fgrepip sr.ht/~charles/rq -> sr.ht_2f_7echarles_2frq po3rin/github_link_creator -> po3rin_2fgithub_5flink_5fcreator
The result stays within [A-Za-z0-9._-], so it needs no further escaping in a URL. Percent-encoding would not: a branch named with "%7E" has to be written "%257E" in a raw URL, because the server decodes the escape before looking the ref up.
The encoded name also contains no "/", so it is a single ref segment. That removes the directory/file conflict that would otherwise make "ipinfo/cli" and "ipinfo/cli/grepip" unable to coexist as branches; aqua-registry has 30 such pairs.
func ErrNoTemplate ¶
ErrNoTemplate reports whether an error is the missing template. It stops a run rather than being retried per package: every package would hit it, and walking the whole registry to say so wastes a run's worth of API calls.
func HeadBranchName ¶
HeadBranchName returns the branch a pull request for the package is opened from.
It carries no version: one pull request adds every version of the package that a run found, which keeps the number of pull requests down and, more importantly, keeps two of them from targeting the same package branch at once. Two would conflict on merge, since the second is written against a base the first has moved.
Types ¶
type Breadth ¶ added in v0.0.15
type Breadth struct {
// Versions ends the turn once the package has that many. Default 5.
Versions int `yaml:"versions,omitempty"`
// Attempts ends the turn after that many versions have been tried, whether or
// not they worked.
//
// A release with no assets, or one whose signature can't be verified yet,
// produces nothing, and the newest versions are the likeliest to -- which are
// the ones a turn starts from. Without this a package stops short of Versions
// every turn and takes one every lap forever.
//
// Unset, a turn tries exactly as many versions as it wants and no more, so a
// version that fails costs the package a lap.
Attempts int `yaml:"attempts,omitempty"`
}
Breadth is how far a run goes with a package the registry holds little of.
A run is bounded, and every package wants all of its versions. Spending the whole run on the first one leaves the rest with nothing at all, and a package with nothing can't be installed from the registry; so a package short of Versions gets enough of a turn to reach it and the run moves on.
What the right number is changes. While the registry is being filled it wants to be small, so that every package gets its newest versions before any package gets its history. Once that is done the packages arriving are new ones, and a new package with two usable versions is barely better than one with none, so it wants to be larger. The registry says which it is, because nothing here can tell.
func (*Breadth) GetAttempts ¶ added in v0.0.15
GetAttempts is how many versions may be tried to get there, or zero for as many as are wanted and no more.
func (*Breadth) GetVersions ¶ added in v0.0.15
GetVersions is how many versions ends a package's turn.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client reads and writes aqua-registry-g2.
func (*Client) BranchSHA ¶
BranchSHA returns the commit a branch points at, or an empty string when the branch doesn't exist.
func (*Client) Commit ¶
Commit writes the files onto a new commit on top of parent and points branch at it.
The commit is built through the Git data API rather than a checkout. A package branch is an orphan and there are as many of them as there are packages, so cloning to write one file would be the expensive way to do this.
GitHub signs the commits it creates this way when the caller is a GitHub App installation or GitHub Actions, which is how ar2 runs, so they satisfy a ruleset requiring signatures. A user access token produces unsigned commits, so a local run can't produce something a signature ruleset accepts; that only affects trying ar2 out by hand.
It is the pull request's client that writes, not the reading one. A commit onto a branch that already has an open pull request is a synchronize event, and one made with GITHUB_TOKEN leaves that pull request's checks waiting for approval.
func (*Client) Config ¶
Config returns the package's definition, or nil when its branch has none.
A package without one is a package aqua-registry-g2 hasn't taken over yet: its definition still lives in aqua-registry, and the next pull request for it carries the converted one along with the generated files.
func (*Client) CreateIndexPullRequest ¶
func (c *Client) CreateIndexPullRequest(ctx context.Context, base, title, body string) (*gogithub.PullRequest, error)
CreateIndexPullRequest opens a pull request updating the catalogue.
func (*Client) CreatePullRequest ¶
func (c *Client) CreatePullRequest(ctx context.Context, pkgName, title, body string) (*gogithub.PullRequest, error)
CreatePullRequest opens a pull request from the package's head branch into its package branch.
func (*Client) EnsurePackageBranch ¶
EnsurePackageBranch creates the package's branch if it doesn't exist and returns the commit it points at.
The branch is an orphan: it shares no history with main, which is what keeps a commit to one package from rewriting a tree that every other package is under. Its first commit carries the template files rather than being empty, so that a pull request into it is checked by the same CI as everywhere else.
func (*Client) File ¶ added in v0.0.19
File returns a file of the repository, or an empty string when there is none.
Absent isn't a failure. A file the registry hasn't been written with yet -- one added to what a run maintains after the registry was created -- is missing rather than wrong, and what reads it is deciding whether to write it.
func (*Client) Index ¶
Index returns the catalogue, or an empty one when the repository has none yet.
func (*Client) IndexPullRequest ¶
IndexPullRequest returns the open pull request updating the catalogue, or nil.
func (*Client) PackageBranches ¶
PackageBranches returns every package that has a branch.
The repository is the record of what it holds. A package whose branch exists but which never reached the catalogue is exactly what a reconciliation is looking for, so the branches are what it counts.
func (*Client) PackagesInFlight ¶
PackagesInFlight returns the packages that already have an open pull request.
Asking the repository which versions it holds can't see a pull request that hasn't merged yet, so without this a run would open a second pull request for work that is already waiting — including work waiting precisely because its CI failed.
Every open pull request is listed once per run rather than checking each package, because a run touches many packages and there are rarely many pull requests.
func (*Client) RegistryConfig ¶ added in v0.0.9
RegistryConfig returns the registry's configuration, or an empty one when the repository has none. A registry that asks for nothing different needs no file.
func (*Client) RenamePackage ¶ added in v0.0.20
RenamePackage puts a package's branch under its new name, carrying everything on it.
The branch is created rather than the versions generated again. A package's history is every release it ever published, downloaded and hashed and opened on six machines to get there; the new name is the same history under another word for it. The old commit becomes the new branch's parent, so nothing is copied and the record of how each version arrived is still readable.
Creating a branch is what this can do. Committing onto one takes a pull request -- the ruleset says so and no app bypasses it -- so the corrected definition is part of the commit the branch starts at rather than a change made afterwards.
It returns false when there was nothing to move: no branch under the old name, or one already under the new one. Both are what a second run of the same rename sees.
func (*Client) Version ¶
Version returns the registry.json the repository holds for one version of a package, or nil when it holds none.
func (*Client) Versions ¶
func (c *Client) Versions(ctx context.Context, logger *slog.Logger, pkgName string) (map[string]struct{}, error)
Versions returns the versions of the package whose registry.json is in the repository.
The repository is asked rather than a local record of what has been generated, because what matters is whether a version is merged, not whether it was once produced. A pull request that fails CI is never merged, and a record of "already generated" would stop it from ever being retried — exactly for the packages that need attention. Asking the repository makes a run idempotent instead.
The listing is aqua's own, which reads the branch through the Git Data API. The Contents API stops at 1,000 entries in a directory and says so only by returning fewer, so a package with more versions than that would have looked as though the ones it didn't return were missing, and been generated again on every run.
type File ¶
type File struct {
// Path is where the file goes on the package branch, such as
// versions/v1.2.3/registry-1.json.
Path string
Content string
}
File is a file to commit.
type IgnoredPackage ¶ added in v0.0.9
type IgnoredPackage struct {
Name string `yaml:"name"`
// Reason is for whoever reads the file later, which is usually the person
// wondering why a package they expected isn't there.
Reason string `yaml:"reason,omitempty"`
}
IgnoredPackage is a package to leave alone, and why.
type RegistryConfig ¶ added in v0.0.9
type RegistryConfig struct {
// IgnoredPackages are the packages a run doesn't look at.
//
// A package that can't be generated is tried again on every run: it costs API
// calls, and it fills the summary of what wasn't published with the same names
// until whoever reads it stops reading. A repository that was deleted or renamed
// will never generate, and a package somebody decided not to migrate never
// should, and neither is a thing to rediscover every half hour.
IgnoredPackages []*IgnoredPackage `yaml:"ignored_packages,omitempty"`
// Breadth is how much of a run one package may take while it has little in the
// registry.
Breadth *Breadth `yaml:"breadth,omitempty"`
}
RegistryConfig is what RegistryConfigFileName holds.
func (*RegistryConfig) Ignored ¶ added in v0.0.9
func (c *RegistryConfig) Ignored() map[string]struct{}
Ignored returns the names to leave alone, as a set.