Documentation
¶
Overview ¶
Package circleciapp sets a repository up on a CircleCI GitHub App pipeline, as the person running devctl: a pipeline definition reading .circleci/config.yml through the CircleCI GitHub App, an all-pushes trigger on it, and optionally the OAuth pipeline's triggers deleted.
CircleCI has no service accounts, so the writes are the person's, with their devctl CircleCI login (granted Write). Afterwards nobody is load-bearing: checkout, events and statuses go through the App's installation, and the pipeline keeps building when its creator leaves. That is what an OAuth follow cannot do, whose builds run through the follower's GitHub grant.
Index ¶
- func DefinitionName(repo string) string
- func IsNoConfig(err error) bool
- func IsProjectNotFound(err error) bool
- func OpenAsPerson(ctx context.Context, owner string, warn func(string)) (GitHub, CircleCI, error)
- type CircleCI
- type GitHub
- type Options
- type Outcome
- type Report
- type Repository
- type Result
- type Run
- type Waiter
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DefinitionName ¶
DefinitionName is the name of the GitHub App pipeline Ensure creates, <repository>-app: what the CircleCI UI suggests, and what the pipelines set up by hand on 2026-10-22 and 2026-11-02 are named.
func IsNoConfig ¶
IsNoConfig asserts noConfigError: the repository's default branch carries no CircleCI configuration.
func IsProjectNotFound ¶
IsProjectNotFound asserts projectNotFoundError: CircleCI knows no project for the repository.
func OpenAsPerson ¶
OpenAsPerson builds the two clients Setup takes from the keychain: GitHub on the identity devctl pr wait picks for owner (the devctl App login for giantswarm, the person's gh login for every other owner), CircleCI on the person's devctl CircleCI login. warn receives the login's expiry warning.
Types ¶
type CircleCI ¶
type CircleCI interface {
GetProject(ctx context.Context, org, repo string) (*circleciclient.Project, error)
ListPipelineDefinitions(ctx context.Context, projectID string) ([]circleciclient.PipelineDefinition, error)
CreatePipelineDefinition(ctx context.Context, projectID string, def circleciclient.PipelineDefinition) (*circleciclient.PipelineDefinition, error)
ListTriggers(ctx context.Context, projectID, definitionID string) ([]circleciclient.Trigger, error)
CreateTrigger(ctx context.Context, projectID, definitionID string, trigger circleciclient.Trigger) (*circleciclient.Trigger, error)
DeleteTrigger(ctx context.Context, projectID, triggerID string) error
RunPipeline(ctx context.Context, org, repo, definitionID string, ref circleciclient.Ref) (*circleciclient.Pipeline, error)
GetPipeline(ctx context.Context, id string) (*circleciclient.PipelineDetail, error)
GetProjectSettings(ctx context.Context, org, repo string) (*circleciclient.ProjectSettings, error)
UpdateProjectSettings(ctx context.Context, org, repo string, settings circleciclient.ProjectSettings) (*circleciclient.ProjectSettings, error)
ListPipelineWorkflows(ctx context.Context, pipelineID string) ([]circleciclient.Workflow, error)
}
CircleCI is what Ensure and Wait ask of CircleCI, as the person.
type GitHub ¶
type GitHub interface {
// Repository returns GitHub's id of owner/repo and its default branch.
Repository(ctx context.Context, owner, repo string) (id int64, defaultBranch string, err error)
// LatestRelease returns the repository's latest release, nil when it has
// none.
LatestRelease(ctx context.Context, owner, repo string) (*github.RepositoryRelease, error)
// Releases returns the repository's published releases, drafts and
// pre-releases left out.
Releases(ctx context.Context, owner, repo string) ([]*github.RepositoryRelease, error)
// CircleCIStatuses returns the CircleCI statuses of ref's commit, newest
// first.
CircleCIStatuses(ctx context.Context, owner, repo, ref string) ([]*github.RepoStatus, error)
// HasConfig says whether ref carries .circleci/config.yml.
HasConfig(ctx context.Context, owner, repo, ref string) (bool, error)
// CommitSHA returns the commit ref points at.
CommitSHA(ctx context.Context, owner, repo, ref string) (string, error)
}
GitHub is what Setup reads on GitHub.
type Options ¶
type Options struct {
// Migrate deletes the OAuth pipeline's triggers (Ensure).
Migrate bool
// Wait follows the runs Setup starts until they settle.
Wait bool
// Waiter follows them when Wait is set.
Waiter Waiter
// Progress receives one line per step; nil discards.
Progress func(format string, args ...any)
// Now is the clock; nil means time.Now.
Now func() time.Time
}
Options are Setup's.
type Outcome ¶
type Outcome struct {
Pipeline circleciclient.Pipeline
// Green says every workflow succeeded; Failed names the ones that did
// not, with their status.
Green bool
Failed []string
// OnHold names the workflows waiting for an approval: settled as far as
// the wait goes, a person approves them in the CircleCI UI.
OnHold []string
}
Outcome is how a pipeline Wait followed ended.
type Report ¶
Report is what Setup found and did.
func Setup ¶
func Setup(ctx context.Context, ci CircleCI, gh GitHub, owner, repo string, opts Options) (*Report, error)
Setup sets owner/repo up on a GitHub App pipeline as the person (Ensure) and runs what the pipeline has not built. First the release tags whose commit carries no status of their own build (reconcile.TagBuildStatuses on the App pipeline's statuses; any OAuth status after the release counts as built): a tag cut before the trigger existed reached no pipeline, as the scaffold's v0.1.0 of a new repository does. Then the default branch, when Ensure wrote anything or its head carries no App status, unless a tag run builds the branch's head already. A repository without .circleci/config.yml on its default branch is IsNoConfig, before any write. With Wait set the runs are followed until they settle.
type Repository ¶
Repository is the GitHub repository to set up: its owner and name, and GitHub's id of it, which CircleCI names the repository by.
type Result ¶
type Result struct {
ProjectID string
// Definition is the GitHub App pipeline, found or created.
Definition circleciclient.PipelineDefinition
// CreatedDefinition and CreatedTrigger say Ensure created them.
CreatedDefinition, CreatedTrigger bool
// OAuthTriggers are the enabled triggers of the OAuth pipeline left in
// place (without migrate): every push builds on both pipelines.
OAuthTriggers []string
// DeletedOAuthTriggers are the OAuth pipeline's triggers migrate
// deleted.
DeletedOAuthTriggers []string
// EnabledSetupWorkflows says Ensure switched the project's setup
// workflows on.
EnabledSetupWorkflows bool
// SetupWorkflowsUnread is why the setting could not be read or written,
// empty when it was: CircleCI may refuse the person its project
// settings, and a project created since late 2023 has them on anyway.
SetupWorkflowsUnread string
}
Result is what Ensure found and did.
func Ensure ¶
Ensure makes repo build on a GitHub App pipeline, idempotently: it finds the project's GitHub App definition or creates DefinitionName(repo), and gives it an all-pushes trigger unless it has an enabled trigger already. With migrate the OAuth pipeline's triggers are deleted after, so a push builds once, on the App; the OAuth definition itself is CircleCI's and stays. The project's setup workflows, which the generated dynamic configuration needs, are switched on when they are off; CircleCI refusing the person the settings (403) is noted in the result, not an error. A login that granted Read access only is refused with circleciclient.IsForbidden on the first write.
type Run ¶
type Run struct {
Ref circleciclient.Ref
Pipeline circleciclient.Pipeline
Outcome *Outcome
}
Run is a pipeline Setup started, and how it ended when waited for.
type Waiter ¶
type Waiter struct {
CircleCI CircleCI
// Sleep waits between reads; Interval is how long.
Sleep func(ctx context.Context, d time.Duration) error
Interval time.Duration
}
Waiter follows one pipeline until it settles.