Documentation
¶
Overview ¶
Package releasewait waits until a tag's images and charts are pullable: what `devctl release wait` does. A tag and a GitHub Release exist about a minute after a merge; the artifacts come from the CircleCI pipeline the tag triggers, minutes later, under names the repository's CI decides. The package reads those names from the sources that define them and never from the repository name: for generated CI from the team-file entry the generator renders (the `image.name` override included), for hand-written CI from the push jobs of the tag pipeline matched with the tag's CircleCI configuration. When the sources disagree, or yield nothing while a Dockerfile exists at the tag, the wait ends with the disagreement instead of a guess.
Availability is a digest: every image and chart resolves to one in the registry, the public registry probed anonymously (a stale docker login cannot produce a false UNAUTHORIZED), the private one with the docker keychain. A failed or cancelled workflow of the tag pipeline, the newest run per workflow name, ends the wait as the tag's CI failure with the failed jobs; a repository without CircleCI is judged by the Actions runs the tag triggered. A repository without image and chart is waited for through its published release and the tag's workflows.
Index ¶
- Constants
- func CatalogURLFromEnv() string
- func EntryGeneratesCI(entry *reposetup.Fields) bool
- func EntryReleaseModel(entry *reposetup.Fields) string
- type ActionsRun
- type Artifact
- type CatalogIndex
- type CatalogReader
- type CircleCI
- type Config
- type Document
- type EntryFinder
- type GitHub
- type Models
- type Pipeline
- type PipelineWorkflow
- type Prober
- type PushJob
- type RegistryAnswerError
- type RegistryProber
- type Result
- type TagContent
- type TeamFileEntries
- type Version
- type Waiter
Constants ¶
const ( IntervalFloor = 15 * time.Second IntervalCeiling = 60 * time.Second )
The poll interval's bounds at scale 1; between them it follows GitHub's rate-limit headers.
const ( // ReleaseModelAutoRelease tags the merge commit of every push to main // that warrants a bump, from conventional commits. ReleaseModelAutoRelease = "auto-release" // ReleaseModelLegacy tags from a release branch and a release pull // request; the tag is not on the merge commit of a feature pull request. ReleaseModelLegacy = "legacy" )
The release models: how a repository turns a merge into a tag.
const ( // CIModelGenerated: devctl renders .circleci/config.yml and workflows.yml // from the team-file entry; the entry names the artifacts. CIModelGenerated = "generated" // CIModelHandWritten: the repository maintains its CircleCI // configuration; its push jobs name the artifacts. CIModelHandWritten = "hand-written" // CIModelNone: no CircleCI; the tag is judged by its Actions runs. CIModelNone = "none" )
The CI models: where the names of the artifacts come from.
const ( KindImage = "image" KindChart = "chart" KindReleaseAsset = "release-asset" )
The artifact kinds.
const ( StateAvailable = "available" StateMissing = "missing" )
The artifact states.
const Command = "release wait"
Command is the envelope's command name.
const DefaultCatalogURL = "https://giantswarm.github.io"
DefaultCatalogURL is where the catalog indexes are published: <DefaultCatalogURL>/<catalog>/index.yaml.
const DefaultTimeout = 30 * time.Minute
DefaultTimeout bounds a wait that gets no --timeout.
const EnvCatalogURL = "DEVCTL_CATALOG_URL"
EnvCatalogURL points --catalog at another host for the catalog indexes; the default is the GitHub Pages host of the catalogs.
Variables ¶
This section is empty.
Functions ¶
func CatalogURLFromEnv ¶
func CatalogURLFromEnv() string
CatalogURLFromEnv is the base URL the environment selects.
func EntryGeneratesCI ¶
EntryGeneratesCI: the entry opts into devctl-generated CircleCI configuration.
func EntryReleaseModel ¶
EntryReleaseModel is the release model a team-file entry declares: releaseWorkflow when set, else auto-release with gen.ci.generate and legacy without. An entry without a gen block declares nothing ("").
Types ¶
type ActionsRun ¶
type ActionsRun struct {
Name string `json:"name"`
RunID int64 `json:"runId"`
Status string `json:"status"`
Conclusion string `json:"conclusion"`
URL string `json:"url"`
}
ActionsRun is one GitHub Actions run the tag triggered, for a repository without CircleCI.
type Artifact ¶
type Artifact struct {
// Kind is image, chart or release-asset.
Kind string `json:"kind"`
// Reference is the pullable reference (registry/name:tag) or, for a
// release asset, its download URL.
Reference string `json:"reference"`
// Digest is set once the artifact is available.
Digest string `json:"digest"`
// State is available or missing.
State string `json:"state"`
// contains filtered or unexported fields
}
Artifact is one thing the release ships and where it stands.
func GeneratedArtifacts ¶
func GeneratedArtifacts(entry reposetup.Fields, repo, version string, content TagContent, privateRepo bool, endpoints agentcli.Endpoints) ([]Artifact, error)
GeneratedArtifacts are the artifacts devctl's CircleCI generator emits for a team-file entry, the way the generator derives them: an image when the tag has a root Dockerfile or the entry names one elsewhere, called gen.ci.image.name or <owner>/<repo>; a chart for the app flavour of a non-template repository, called gen.ci.chartName or the repository, in gen.ci.appCatalog or the default catalog. The private registry holds a private-only image and the artifacts of a private repository that does not force them public.
func HandWrittenArtifacts ¶
func HandWrittenArtifacts(ctx context.Context, gh GitHub, owner, repo, sha, version string, content TagContent, pipelineJobs map[string]bool, privateRepo bool, endpoints agentcli.Endpoints) ([]Artifact, error)
HandWrittenArtifacts are the artifacts the tag pipeline of a hand-written configuration publishes: the push jobs of the configuration files at the tag that the pipeline runs (pipelineJobs are the job names CircleCI lists for it), each naming its image or chart. A Dockerfile at the tag with no image among them is a disagreement between the sources, reported as such.
type CatalogIndex ¶
type CatalogIndex struct {
// BaseURL is the host of the indexes; empty means DefaultCatalogURL.
BaseURL string
// HTTPClient sends the requests; nil means the default client.
HTTPClient *http.Client
}
CatalogIndex reads a catalog's index.yaml over HTTP.
func (CatalogIndex) Lists ¶
Lists implements CatalogReader: the index of catalog has an entry of chart at version. The index is fetched anew on every call, so a stale copy never answers.
type CatalogReader ¶
type CatalogReader interface {
Lists(ctx context.Context, catalog, chart, version string) (bool, error)
}
CatalogReader says whether a catalog's index lists a chart version.
type CircleCI ¶
type CircleCI interface {
FindPipelineByTag(ctx context.Context, org, repo, tag string) (*circleciclient.Pipeline, error)
ListPipelineWorkflows(ctx context.Context, pipelineID string) ([]circleciclient.Workflow, error)
ListWorkflowJobs(ctx context.Context, workflowID string) ([]circleciclient.Job, error)
}
CircleCI is what the wait reads from CircleCI; *circleciclient.Client is one.
type Config ¶
type Config struct {
// Owner and Repo name the repository. Required.
Owner, Repo string
// Version is the release to wait for, vX.Y.Z or X.Y.Z; empty with PR.
Version string
// PR is the merged pull request whose tag is waited for; 0 with Version.
PR int
// Timeout bounds the wait; zero means DefaultTimeout.
Timeout time.Duration
// Catalog also waits for the catalog index to list every chart.
Catalog bool
// GitHub is required. Entries and Registry are required; CircleCI is
// called once when the tag carries a .circleci/config.yml, so a
// repository without CircleCI needs no CircleCI token. CatalogIndex is
// required with Catalog.
GitHub GitHub
Entries EntryFinder
CircleCI func(ctx context.Context) (CircleCI, error)
Registry Prober
CatalogIndex CatalogReader
// Endpoints name the registries. Zero means production.
Endpoints agentcli.Endpoints
// Clock paces the polls; zero means the wall clock at scale 1.
Clock agentcli.Clock
// Rate is the rate limit of the last GitHub response, the interval's
// input; nil keeps the floor.
Rate func() githubclient.RateLimit
// Progress receives one line per step; nil is silent.
Progress *agentcli.Progress
}
Config configures a Waiter.
type EntryFinder ¶
type EntryFinder interface {
FindEntry(ctx context.Context, owner, repo string) (*reposetup.Fields, bool, error)
}
EntryFinder returns the team-file entry that declares a repository, and false when no team file does.
type GitHub ¶
type GitHub interface {
GetPullRequestMerge(ctx context.Context, owner, repo string, number int) (githubclient.PullRequestMerge, error)
GetTagSHA(ctx context.Context, owner, repo, tag string) (string, error)
FindTagForCommit(ctx context.Context, owner, repo, sha string) (string, error)
GetReleaseByTag(ctx context.Context, owner, repo, tag string) (githubclient.Release, error)
ListWorkflowRunsForSHA(ctx context.Context, owner, repo, sha string) ([]githubclient.WorkflowRun, error)
ListDirectory(ctx context.Context, owner, repo, path, ref string) ([]string, error)
GetFile(ctx context.Context, owner, repo, path, ref string) (githubclient.RepositoryFile, error)
IsPrivateRepository(ctx context.Context, owner, repo string) (bool, error)
}
GitHub is what the wait reads from GitHub; *githubclient.Client is one.
type Models ¶
Models are the settled release and CI models.
func ResolveModels ¶
func ResolveModels(entry *reposetup.Fields, content TagContent) (Models, error)
ResolveModels settles the models from the team-file entry (nil when no team file declares the repository) cross-checked against the tag's files. A disagreement between the two is an error, never a guess; a source that says nothing leaves the decision to the other; neither saying anything about the release model is an error too.
type Pipeline ¶
type Pipeline struct {
ID string `json:"id"`
Number int64 `json:"number"`
URL string `json:"url"`
// Workflows are the newest run of every workflow name.
Workflows []PipelineWorkflow `json:"workflows"`
// FailedJobs are workflow/job of every failed job when the tag's CI
// failed; empty otherwise.
FailedJobs []string `json:"failedJobs"`
}
Pipeline is the tag pipeline on CircleCI as the document reports it.
type PipelineWorkflow ¶
PipelineWorkflow is one workflow of the tag pipeline.
type Prober ¶
type Prober interface {
Probe(ctx context.Context, artifact Artifact) (digest string, found bool, err error)
}
Prober answers whether an artifact is in its registry: the digest when it is, found false when the registry knows no such manifest, and an error for every other answer, which is a tooling failure and not a slow pipeline.
type PushJob ¶
type PushJob struct {
// Name is the job's name in the workflow: its name parameter, else the
// orb job's name.
Name string
// Kind is image or chart.
Kind string
// Image is the image parameter; empty means the orb's default,
// <owner>/<repo>.
Image string
// Chart and Catalog are the chart and app_catalog parameters.
Chart, Catalog string
// Push is false for a build-only job (push: false, or a chart job that
// pushes to neither the catalog nor the registry).
Push bool
// PrivateOnly: registries-data names the private registry only.
PrivateOnly bool
// ForcePublic pushes a private repository's artifact to the public
// registry.
ForcePublic bool
}
PushJob is a push job of a hand-written CircleCI configuration: an architect push-to-registries (or push-to-docker) job naming an image, or a push-to-app-catalog job naming a chart.
func ParsePushJobs ¶
ParsePushJobs reads the push jobs of every workflow in a CircleCI configuration. Jobs the configuration defines itself are opaque and not returned; the orb's push jobs are recognised whatever the orb is called.
type RegistryAnswerError ¶
RegistryAnswerError is a registry answer that is neither a digest nor "manifest unknown": the probe's failure, which ends the wait at once.
func (*RegistryAnswerError) Error ¶
func (e *RegistryAnswerError) Error() string
type RegistryProber ¶
type RegistryProber struct {
// Endpoints name the registries and whether they speak plain HTTP.
Endpoints agentcli.Endpoints
// Keychain answers the private registry; nil means the docker keychain.
Keychain authn.Keychain
// Transport sends the requests; nil means the default.
Transport http.RoundTripper
}
RegistryProber probes OCI registries with go-containerregistry: the public registry anonymously, so a stale docker login cannot produce a false UNAUTHORIZED for a public artifact; the private registry with the docker keychain, the credentials `docker login` stored.
type Result ¶
type Result struct {
Repository string `json:"repository"`
Tag string `json:"tag"`
SHA string `json:"sha"`
ReleaseModel string `json:"releaseModel"`
CIModel string `json:"ciModel"`
// Artifacts are the expected images and charts, or the release assets
// of a repository that ships neither.
Artifacts []Artifact `json:"artifacts"`
// Pipeline is the tag pipeline; null for a repository without CircleCI.
Pipeline *Pipeline `json:"pipeline"`
// Actions are the runs the tag triggered; empty with CircleCI.
Actions []ActionsRun `json:"actions"`
}
Result is the command's document below the envelope.
type TagContent ¶
type TagContent struct {
// SHA the content was read at.
SHA string
// Root are the names at the repository root.
Root []string
// Workflows are the file names under .github/workflows.
Workflows []string
// CircleCI are the file names under .circleci.
CircleCI []string
}
TagContent is what the tag carries of the files the models and the artifacts are read from: the root listing, the workflow files and the CircleCI directory.
func ReadTagContent ¶
ReadTagContent lists the three directories at sha; a missing directory is an empty list.
func (TagContent) CIModel ¶
func (c TagContent) CIModel() string
CIModel is the CI model the files show.
func (TagContent) GeneratedCircleCI ¶
func (c TagContent) GeneratedCircleCI() bool
GeneratedCircleCI: the configuration is devctl's, recognised by the workflows.yml the generator writes beside config.yml.
func (TagContent) HasCircleCI ¶
func (c TagContent) HasCircleCI() bool
HasCircleCI: the repository has a CircleCI project to read.
func (TagContent) HasCustomCircleCI ¶
func (c TagContent) HasCustomCircleCI() bool
HasCustomCircleCI: a repo-owned custom.yml is merged into the generated workflows.
func (TagContent) HasDockerfile ¶
func (c TagContent) HasDockerfile() bool
HasDockerfile: a Dockerfile at the repository root, the signal that turns the generated image pipeline on and the reason a hand-written pipeline without a push job is a disagreement.
func (TagContent) ReleaseModel ¶
func (c TagContent) ReleaseModel() (string, error)
ReleaseModel is the release model the workflow files show: auto-release for an auto-release workflow, legacy for the create-release workflows, "" when neither is there. Both at once is an error: the files contradict each other.
type TeamFileEntries ¶
type TeamFileEntries struct {
// GitHub is the go-github client the team files are read with.
GitHub *github.Client
// Owner is the organisation whose team files are searched; empty means
// the default one.
Owner string
}
TeamFileEntries finds a repository's declaration in the team files of the organisation's github repository, read as the caller. Only the organisation the team files describe is searched; a repository of another owner has no entry.
type Version ¶
type Version struct {
// Given is the argument as typed.
Given string
// Bare is the version without a leading v: the tag of the image and the
// chart.
Bare string
}
Version is a release version as given on the command line.
func ParseVersion ¶
ParseVersion accepts vX.Y.Z and X.Y.Z, with a pre-release suffix.
type Waiter ¶
type Waiter struct {
// contains filtered or unexported fields
}
Waiter runs one wait.