releasepromote

package
v8.145.2 Latest Latest
Warning

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

Go to latest
Published: Oct 18, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

Documentation

Overview

Package releasepromote promotes the latest release candidate of auto-release repositories to a stable release: what `devctl release promote` does. The auto-release workflow cuts a candidate vX.Y.Z-rc.N, a GitHub pre-release, on every releasable push; a stable release is cut only by running that workflow by hand with release-type stable, which promotes the latest candidate since the last stable release. The package checks that the workflow on the default branch is one that promotes, picks that candidate from the repository's releases reachable from the default branch, checks that its commit is built (the rule the workflow applies) and dispatches the workflow on the default branch. The workflow does the promotion and its own checks, the repository's stable gate among them; with Config.Follow the package follows the dispatched run until it concludes and reports a run that cut no release as refused, with its errors.

Index

Constants

View Source
const (
	// StateDispatched: the workflow was dispatched with release-type stable.
	StateDispatched = "dispatched"
	// StateWouldDispatch: with --dry-run, the workflow would be dispatched.
	StateWouldDispatch = "would_dispatch"
	// StateNothingToPromote: no candidate pre-release newer than the latest
	// stable release.
	StateNothingToPromote = "nothing_to_promote"
	// StateNotBuilt: a commit status of the candidate's commit is not
	// success; statusState names the combined state, newestBuilt the
	// highest other candidate that is built.
	StateNotBuilt = "not_built"
	// StateNotAutoRelease: the repository has no auto-release workflow.
	StateNotAutoRelease = "not_auto_release"
	// StateOutdatedWorkflow: the auto-release workflow on the default branch
	// has no promotion step: its release-type stable tags the branch head; or,
	// with --version, its promotion step takes no version input.
	StateOutdatedWorkflow = "outdated_workflow"
	// StateFailed: a read or the dispatch failed; the message has GitHub's
	// answer.
	StateFailed = "failed"
	// StateRefused: the dispatched run concluded without cutting the stable
	// release, the repository's stable gate refusing it for one; the message
	// names the run and the errors its failed jobs annotated, failedJobs the
	// end of their logs.
	StateRefused = "refused"
)

The states of one repository.

View Source
const Command = "release promote"

Command is the envelope's command name.

View Source
const DefaultFollowTimeout = 20 * time.Minute

DefaultFollowTimeout bounds following the dispatched runs when Config.FollowTimeout is zero: an Auto Release run decides in minutes.

View Source
const Workflow = "zz_generated.auto_release.yaml"

Workflow is the file name of the generated auto-release workflow.

Variables

View Source
var ErrNotCandidate = errors.New("not a release candidate to promote")

ErrNotCandidate is a version named for promotion that is not a release candidate the workflow promotes: no published release of that tag, not newer than the last stable release, or not on the default branch.

View Source
var ErrNotPrerelease = errors.New("not a GitHub pre-release")

ErrNotPrerelease is the highest candidate being a full GitHub release, not a pre-release: the workflow stops on it rather than promote a lower one.

Functions

func LaterCandidates added in v8.144.0

func LaterCandidates(releases []githubclient.Release, reachable func(tag string) (bool, error), candidate string) ([]string, error)

LaterCandidates are the release candidates higher than candidate that are reachable from the default branch, lowest first: those a promotion of candidate leaves out, since the workflow tags candidate's commit. Nil when there is none.

func NewestBuilt added in v8.142.0

func NewestBuilt(releases []githubclient.Release, reachable, built func(tag string) (bool, error), except string) (string, error)

NewestBuilt is the highest release candidate other than except that the workflow would promote with `version` and that built accepts, "" when there is none: a published pre-release newer than the highest stable release reachable from the default branch, and reachable itself. Candidates are asked highest first, built only for reachable pre-releases, and only until the first built one.

func Promote

func Promote(ctx context.Context, config Config, repositories []string, result *Result) error

Promote runs the promotion of every repository, owner/repo each, in order, and fills result.Repositories. A repository that is not dispatched is reported in its entry and does not stop the others; the error is then an agentcli.ExitRed outcome naming them. With config.Follow the dispatched runs are followed once every repository is dispatched, and a refused run is such an entry too. Nil when every repository was dispatched (and its run passed or still runs), would be, or has nothing to promote.

func SelectCandidate

func SelectCandidate(releases []githubclient.Release, reachable func(tag string) (bool, error)) (stable, candidate string, err error)

SelectCandidate returns the tag of the highest stable release reachable from the default branch and of the highest reachable candidate newer than it, "" for either when there is none: the tags the workflow's `--merged HEAD` lets it see. reachable says whether a tag is on the default branch; it is asked from the highest tag down and only until the first reachable one, so a higher stable release or candidate of another branch (a backport line) is skipped without hiding the default branch's. Drafts are no releases. The highest candidate must be a GitHub pre-release, the form the workflow promotes: a full release there is ErrNotPrerelease with its tag, never a lower candidate, as the workflow refuses it too. Versions are compared as semver, so rc.10 follows rc.9 and a candidate for a higher version follows one it replaced.

func SelectVersion added in v8.142.0

func SelectVersion(releases []githubclient.Release, reachable func(tag string) (bool, error), version string) (stable, candidate string, err error)

SelectVersion is SelectCandidate for the named candidate version instead of the highest: the tag of the highest stable release reachable from the default branch, and version once it is a published release newer than that stable release and reachable, the candidates the workflow's `version` input accepts. Anything else is ErrNotCandidate saying why; a candidate that is a full release is ErrNotPrerelease, as the workflow refuses it.

func SplitRepository

func SplitRepository(s string) (owner, repo string, ok bool)

SplitRepository reads owner/repo.

func StableVersion added in v8.145.0

func StableVersion(candidate string) string

StableVersion is the stable release a candidate vX.Y.Z-rc.N promotes to, vX.Y.Z.

func TeamRepositories

func TeamRepositories(ctx context.Context, gh *github.Client, team string) ([]string, error)

TeamRepositories lists owner/repo of every entry of a team's file in giantswarm/github whose release model is auto-release (releaseWorkflow, defaulting from gen.ci.generate) and whose life is not over (archived or deleted), in file order. The repositories are in the organisation the team files describe. A team without a file is a usage error.

func ValidVersion added in v8.142.0

func ValidVersion(version string) bool

ValidVersion says whether version has the form of a candidate tag, vX.Y.Z-rc.N.

Types

type Config

type Config struct {
	// GitHub is required.
	GitHub GitHub
	// DryRun checks every repository and dispatches nothing.
	DryRun bool
	// Version is the candidate vX.Y.Z-rc.N to promote instead of the
	// latest; empty promotes the latest. [ValidVersion] checks its form.
	Version string
	// NotFoundHint is added to GitHub's 404 for a repository: what the
	// token reaches. Empty adds nothing.
	NotFoundHint string
	// Follow reads the run each dispatch started until it concludes, at
	// most FollowTimeout: a run that cuts no release, the stable gate's
	// refusal for one, is refused.
	Follow bool
	// FollowTimeout bounds following; zero means DefaultFollowTimeout.
	FollowTimeout time.Duration
	// Clock paces following; the zero value is the wall clock.
	Clock agentcli.Clock
	// Progress receives one line per repository; nil is silent.
	Progress *agentcli.Progress
}

Config configures a promotion.

type Document

type Document struct {
	agentcli.Envelope
	Result
}

Document is the command's JSON: the envelope and the result.

type GitHub

type GitHub interface {
	DefaultBranch(ctx context.Context, owner, repo string) (string, error)
	ReadFile(ctx context.Context, owner, repo, path, ref string) ([]byte, bool, error)
	ListReleases(ctx context.Context, owner, repo string) ([]githubclient.Release, error)
	Reachable(ctx context.Context, owner, repo, ref, branch string) (bool, error)
	GetCombinedStatus(ctx context.Context, owner, repo, ref string) (githubclient.CombinedStatus, error)
	DispatchWorkflowRun(ctx context.Context, owner, repo, file, ref string, inputs map[string]any) (githubclient.DispatchedRun, error)
	GetWorkflowRun(ctx context.Context, owner, repo string, id int64) (githubclient.WorkflowRun, error)
	ListWorkflowRunJobs(ctx context.Context, owner, repo string, runID int64) ([]githubclient.WorkflowJob, error)
	CheckRunAnnotations(ctx context.Context, owner, repo string, id int64) ([]githubclient.Annotation, error)
	JobLog(ctx context.Context, owner, repo string, jobID int64) (io.ReadCloser, error)
}

GitHub is what a promotion reads from and writes to GitHub; *githubclient.Client is one.

type Identity added in v8.105.0

type Identity struct {
	// Source is "keychain", the devctl GitHub App login, or "$NAME", the
	// environment variable that overrides it; empty until the token is
	// resolved.
	Source string `json:"source"`
	// Login is the account of the App login; empty for a token from the
	// environment, whose account devctl does not look up.
	Login string `json:"login"`
}

Identity is the GitHub token a promotion acts with.

type Repository

type Repository struct {
	// Repository is owner/repo.
	Repository string `json:"repository"`
	// Stable is the tag of the latest stable release; empty when there is
	// none.
	Stable string `json:"stable"`
	// Candidate is the tag of the candidate to promote; empty when there is
	// none.
	Candidate string `json:"candidate"`
	// NewestBuilt is, for not_built, the highest other candidate the
	// workflow would promote whose commit is built: promote it with
	// --version instead of waiting. Empty when there is none or for another
	// state.
	NewestBuilt string `json:"newestBuilt,omitempty"`
	// Later are, with --version, the release candidates on the default
	// branch cut after the promoted one, lowest first: the stable release
	// is the promoted candidate's commit, so they are not in it. Empty
	// without --version or when there is none.
	Later []string `json:"later,omitempty"`
	// StatusState is the combined commit status of the candidate: success,
	// pending, failure or error; empty until read.
	StatusState string `json:"statusState"`
	// State is one of the State constants.
	State string `json:"state"`
	// Message says what was done or why not.
	Message string `json:"message"`
	// Run is the run the dispatch started; nil before a dispatch or when
	// GitHub named no run.
	Run *Run `json:"run,omitempty"`
	// FailedJobs are, for refused, the run's failed jobs with the end of
	// their logs, where the stable gate's own output is.
	FailedJobs []prwait.FailedJob `json:"failedJobs,omitempty"`
	// Warnings name what the promotion went on despite: a failed commit
	// status of an advisory job (circleciclient.AdvisoryJobs), which the
	// release does not require. Empty when there is none.
	Warnings []string `json:"warnings,omitempty"`
}

Repository is one repository's promotion as the document reports it.

func (Repository) OK

func (r Repository) OK() bool

OK says whether the repository is dispatched, would be, or has nothing to promote.

type Result

type Result struct {
	// Team is the team whose repositories were promoted; empty for named
	// repositories.
	Team string `json:"team"`
	// DryRun is set with --dry-run: nothing was dispatched.
	DryRun bool `json:"dryRun"`
	// Identity is who dispatched the workflows, or would with --dry-run.
	Identity Identity `json:"identity"`
	// Repositories are the repositories in the order they were given or
	// the team file lists them.
	Repositories []Repository `json:"repositories"`
}

Result is the command's document below the envelope.

func NewResult

func NewResult(team string) Result

NewResult is the result before anything is known: no repositories.

type Run added in v8.145.0

type Run struct {
	ID  int64  `json:"id"`
	URL string `json:"url"`
	// Status is queued, in_progress or completed; empty until read.
	Status string `json:"status"`
	// Conclusion is success, failure, cancelled, ... once completed.
	Conclusion string `json:"conclusion"`
}

Run is the Auto Release run a dispatch started, as last read.

type Sources

type Sources struct {
	GitHub GitHub
	// Team lists the auto-release repositories of a team.
	Team func(ctx context.Context, team string) ([]string, error)
	// NotFoundHint is [Config]'s.
	NotFoundHint string
	// Identity is who the token acts as, for the document.
	Identity Identity
}

Sources are what one promotion reads and writes, opened after the version gate.

func OpenSources

func OpenSources(ctx context.Context, endpoints agentcli.Endpoints, transport http.RoundTripper, warn func(string)) (*Sources, error)

OpenSources is the production wiring: the GitHub token of authstore.ResolveGitHub (the App login, or a token in the environment that overrides it; its warning goes to warn), conditional requests below it, and the team files of the organisation. The requests go through transport.

Jump to

Keyboard shortcuts

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