Documentation
¶
Overview ¶
Package autoupdate provides automatic serialized updating of GitHub pull request branches with their base-branch.
The autoupdater can be used in combination with a automerge feature like the one from GitHub and specific branch protection rules to provide a serialized merge-queue. The GitHub branch protection rules must be configured to require >=1 status checks to pass before merging and branches being up to date before merging.
The autoupdater reacts on GitHub webhook-events and interacts with the GitHub API. When a webhook event for an enqueue condition is received, the pull request is enqueued for autoupdates. When all Status Checks succeed for the PR, it is uptodate with it's base branch and all configured mandatory PR reviewers approved, the auto-merge feature will merge the PR into it's base branch. Per base-branch, the autoupdater updates automatically the first PR in the queue with it's base branch. It serializes updating per basebranch, to avoid a race between multiples pull requests that result in unnecessary CI runs and merges. When updating a pull request branch with it's base-branch is not possible, a failed status check for the pull request was reported, or the PR became stale updates for it are suspended. This prevents that pull requests that can not be merged block the autoupdate fifo-queue. When a webhook event is received about a positive status check report, the base branch or the pull request branch changed, updates will be resumed. The pull request will be enqueued in the fifo list for updates again.
pull requests are removed from the autoupdate queue, when it was closed, or an inverse trigger event (auto_merge_disabled, unlabeled) was received.
Components ¶
The main components are the queue and autoupdater.
The autoupdater manages and coordinates operations on queues. It listens for GitHub Webhook events, creates/removes removes, enqueues/dequeues pull requests for updates and triggers update operations on queues. It can also synchronize the queue with the current state at GitHub, by querying information via the GitHub API. It also provides a minimal webinterface to view the current state.
Queues serialize updates per base-branch. For each base-branch the Autoupdater manages one queue. pull requests in the queue are either in active or suspended state. If they are active, they are queued in FIFO datastructure and update operations can be run on the first element in the queue. If they are suspended they are currently not considered for autoupdates and stored in a separate datastructure.
Index ¶
- Constants
- Variables
- type Autoupdater
- func (a *Autoupdater) ChangeBaseBranch(ctx context.Context, oldBaseBranch, newBaseBranch *BaseBranch, prNumber int) error
- func (a *Autoupdater) Dequeue(ctx context.Context, baseBranch *BaseBranch, prNumber int) (*PullRequest, error)
- func (a *Autoupdater) Enqueue(ctx context.Context, baseBranch *BaseBranch, pr *PullRequest) error
- func (a *Autoupdater) HTTPService() *HTTPService
- func (a *Autoupdater) Resume(ctx context.Context, baseBranch *BaseBranch, prNumber int) error
- func (a *Autoupdater) ResumeAllForBaseBranch(ctx context.Context, baseBranch *BaseBranch)
- func (a *Autoupdater) ResumeIfStatusPositive(ctx context.Context, owner, repo string, branchNames []string)
- func (a *Autoupdater) Start()
- func (a *Autoupdater) Stop()
- func (a *Autoupdater) SuspendUpdates(ctx context.Context, owner string, repo string, branchNames []string) ([]*PullRequest, []error)
- func (a *Autoupdater) Sync(ctx context.Context) error
- func (a *Autoupdater) TriggerUpdateIfFirst(ctx context.Context, baseBranch *BaseBranch, prSpec PRSpecifier) (*PullRequest, error)
- func (a *Autoupdater) TriggerUpdateIfFirstAllQueues(ctx context.Context, repoOwner string, repo string, prSpec PRSpecifier) (*PullRequest, error)
- func (a *Autoupdater) UpdateBranch(ctx context.Context, baseBranch *BaseBranch) error
- type BaseBranch
- type BranchID
- type DryGithubClient
- func (c *DryGithubClient) CombinedStatus(ctx context.Context, owner, repo, ref string) (string, time.Time, error)
- func (c *DryGithubClient) CreateIssueComment(ctx context.Context, owner, repo string, issueOrPRNr int, comment string) error
- func (c *DryGithubClient) ListPullRequests(ctx context.Context, owner, repo, state, sort, sortDirection string) githubclt.PRIterator
- func (c *DryGithubClient) PullRequestIsApproved(ctx context.Context, owner, repo string, prNumber int) (bool, error)
- func (c *DryGithubClient) UpdateBranch(ctx context.Context, owner, repo string, pullRequestNumber int) (bool, error)
- type GithubClient
- type HTTPService
- type Opt
- type PRBranch
- type PRNumber
- type PRSpecifier
- type PRSpecifierType
- type PullRequest
- type Repository
- type Retryer
Constants ¶
const DefStaleTimeout = 3 * time.Hour
DefStaleTimeout is the default stale timeout. A pull request is considered as stale, when it is the first element in the queue it's state has not changed for longer then this timeout.
Variables ¶
var ( ErrAlreadyExists = errors.New("already exist") ErrNotFound = errors.New("not found") )
Functions ¶
This section is empty.
Types ¶
type Autoupdater ¶
type Autoupdater struct {
// contains filtered or unexported fields
}
Autoupdater implements processing webhook events, querying the GitHub API and enqueueing/dequeuing/triggering updating pull requests with the base-branch. Pull request branch updates are serialized per base-branch.
func NewAutoupdater ¶
func NewAutoupdater( ghClient GithubClient, eventChan <-chan *github_prov.Event, retryer Retryer, monitoredRepositories []Repository, triggerOnAutomerge bool, triggerLabels []string, opts ...Opt, ) *Autoupdater
NewAutoupdater creates an Autoupdater instance. Only webhook events for repositories listed in monitoredRepositories are processed. At least one trigger (triggerOnAutomerge or a label in triggerLabels) must be provided to trigger enqueuing pull requests for autoupdates via webhook events. When multiple event triggers are configured, the autoupdater reacts on each received Event individually.
func (*Autoupdater) ChangeBaseBranch ¶
func (a *Autoupdater) ChangeBaseBranch( ctx context.Context, oldBaseBranch, newBaseBranch *BaseBranch, prNumber int, ) error
ChangeBaseBranch dequeues a Pull Request from the queue oldBaseBranch and enqueues it at the queue for newBaseBranch.
func (*Autoupdater) Dequeue ¶
func (a *Autoupdater) Dequeue(ctx context.Context, baseBranch *BaseBranch, prNumber int) (*PullRequest, error)
Dequeue removes the pull request with number prNumber from the autoupdate queue of baseBranch. This disables keeping the pull request update with baseBranch. If no pull request is queued with prNumber a ErrNotFound error is returned.
If the pull request was the only element in the baseBranch queue, the queue is removed.
func (*Autoupdater) Enqueue ¶
func (a *Autoupdater) Enqueue(ctx context.Context, baseBranch *BaseBranch, pr *PullRequest) error
Enqueue appends the pull request to the autoupdate queue for baseBranch. When it becomes the first element in the queue, it will be kept uptodate with it's baseBranch. If the pr is already enqueued a ErrAlreadyExists error is returned.
If no queue for the baseBranch exist, it will be created.
func (*Autoupdater) HTTPService ¶
func (a *Autoupdater) HTTPService() *HTTPService
func (*Autoupdater) Resume ¶
func (a *Autoupdater) Resume(ctx context.Context, baseBranch *BaseBranch, prNumber int) error
Resume resumes updates for a pull request. If the pull request is not queued for updates and in suspended state ErrNotFound is returned.
func (*Autoupdater) ResumeAllForBaseBranch ¶
func (a *Autoupdater) ResumeAllForBaseBranch(ctx context.Context, baseBranch *BaseBranch)
ResumeAllForBaseBranch resumes updates for all Pull Requests that are based on baseBranch and for which updates are currently suspended.
func (*Autoupdater) ResumeIfStatusPositive ¶
func (a *Autoupdater) ResumeIfStatusPositive(ctx context.Context, owner, repo string, branchNames []string)
ResumeIfStatusPositive calls ScheduleResumePRIfStatusPositive for all queued PRs of the passed branchNames.
func (*Autoupdater) Start ¶
func (a *Autoupdater) Start()
Start starts the event-loop in a go-routine. The event-loop reads events from the eventChan and processes them.
func (*Autoupdater) Stop ¶
func (a *Autoupdater) Stop()
Stop stops the event-loop and waits until it terminates. All queues will be deleted, operations that are in progress will be canceled.
func (*Autoupdater) SuspendUpdates ¶
func (a *Autoupdater) SuspendUpdates( ctx context.Context, owner string, repo string, branchNames []string, ) ([]*PullRequest, []error)
SuspendUpdates suspend updates for all pull requests that are queued and their branch name matches one of branchNames. It returns a list of pull requests for which updates were suspended.
func (*Autoupdater) Sync ¶
func (a *Autoupdater) Sync(ctx context.Context) error
Sync synchronized the states of the autoupdater queues with the current pull request state at GitHub. Pull request information is queried from github. If a PR meets a condition to be enqueued for auto-updates it is enqueued. If it meets a condition for not being autoupdated, it is dequeued.
func (*Autoupdater) TriggerUpdateIfFirst ¶
func (a *Autoupdater) TriggerUpdateIfFirst( ctx context.Context, baseBranch *BaseBranch, prSpec PRSpecifier, ) (*PullRequest, error)
TriggerUpdateIfFirst schedules the update operation for the first pull request in the queue if it matches prSpec. If an update was triggered, the PullRequest is returned. If the first PR does not match prSpec, ErrNotFound is returned.
func (*Autoupdater) TriggerUpdateIfFirstAllQueues ¶
func (a *Autoupdater) TriggerUpdateIfFirstAllQueues( ctx context.Context, repoOwner string, repo string, prSpec PRSpecifier, ) (*PullRequest, error)
TriggerUpdateIfFirstAllQueues does the same then _triggerUpdateIfFirst but does not require to specify the base branch name.
func (*Autoupdater) UpdateBranch ¶
func (a *Autoupdater) UpdateBranch(ctx context.Context, baseBranch *BaseBranch) error
UpdateBranch triggers updating the first PR queued for updates for the given baseBranch.
See documentation on queue for more information.
type BaseBranch ¶
BaseBranch represents a base branch of a pull request
func NewBaseBranch ¶
func NewBaseBranch(owner, repo, branch string) (*BaseBranch, error)
func (*BaseBranch) String ¶
func (b *BaseBranch) String() string
type DryGithubClient ¶
type DryGithubClient struct {
// contains filtered or unexported fields
}
DryGithubClient is a github-client that does not do any changes on github. All operations that could cause a change are simulated and always succeed. All all other operations are forwarded to a wrapped GithubClient.
func NewDryGithubClient ¶
func NewDryGithubClient(clt GithubClient, logger *zap.Logger) *DryGithubClient
func (*DryGithubClient) CombinedStatus ¶
func (*DryGithubClient) CreateIssueComment ¶
func (*DryGithubClient) ListPullRequests ¶
func (c *DryGithubClient) ListPullRequests(ctx context.Context, owner, repo, state, sort, sortDirection string) githubclt.PRIterator
func (*DryGithubClient) PullRequestIsApproved ¶ added in v0.9.0
func (*DryGithubClient) UpdateBranch ¶
type GithubClient ¶
type GithubClient interface {
UpdateBranch(ctx context.Context, owner, repo string, pullRequestNumber int) (bool, error)
CombinedStatus(ctx context.Context, owner, repo, ref string) (string, time.Time, error)
CreateIssueComment(ctx context.Context, owner, repo string, issueOrPRNr int, comment string) error
ListPullRequests(ctx context.Context, owner, repo, state, sort, sortDirection string) githubclt.PRIterator
PullRequestIsApproved(ctx context.Context, owner, repo string, prNumber int) (bool, error)
}
GithubClient defines the methods of a GithubAPI Client that are used by the autoupdate implementation.
type HTTPService ¶
type HTTPService struct {
// contains filtered or unexported fields
}
func NewHTTPService ¶
func NewHTTPService(autoupdater *Autoupdater) *HTTPService
func (*HTTPService) HandlerListFunc ¶
func (h *HTTPService) HandlerListFunc(respWr http.ResponseWriter, req *http.Request)
func (*HTTPService) HandlerStaticFiles ¶
func (h *HTTPService) HandlerStaticFiles() http.Handler
func (*HTTPService) RegisterHandlers ¶
func (h *HTTPService) RegisterHandlers(mux *http.ServeMux, endpoint string)
type Opt ¶
type Opt func(*Autoupdater)
type PRBranch ¶
type PRBranch struct {
BranchName string
}
func (*PRBranch) Type ¶
func (p *PRBranch) Type() PRSpecifierType
type PRNumber ¶
type PRNumber struct {
Number int
}
func (*PRNumber) Type ¶
func (p *PRNumber) Type() PRSpecifierType
type PRSpecifier ¶
type PRSpecifier interface {
Type() PRSpecifierType
String() string
LogField() zap.Field
}
type PRSpecifierType ¶
type PRSpecifierType uint8
const ( PRSpecifierTypeUndefined PRSpecifierType = iota PullRequestNumber PullRequestBranch )
type PullRequest ¶
type PullRequest struct {
Number int
Branch string
Author string
Title string
Link string
LogFields []zap.Field
EnqueuedSince time.Time
// contains filtered or unexported fields
}
func NewPullRequest ¶
func NewPullRequest(nr int, branch, author, title, link string) (*PullRequest, error)
func NewPullRequestFromEvent ¶
func NewPullRequestFromEvent(ev *github.PullRequest) (*PullRequest, error)
func (*PullRequest) Equal ¶
func (p *PullRequest) Equal(other interface{}) bool
Equal returns true if p and other are of type PullRequest and its Number field contains the same value.
func (*PullRequest) GetStateUnchangedSince ¶
func (p *PullRequest) GetStateUnchangedSince() time.Time
func (*PullRequest) SetStateUnchangedSince ¶
func (p *PullRequest) SetStateUnchangedSince(t time.Time)
func (*PullRequest) SetStateUnchangedSinceIfNewer ¶
func (p *PullRequest) SetStateUnchangedSinceIfNewer(t time.Time)
func (*PullRequest) SetStateUnchangedSinceIfZero ¶
func (p *PullRequest) SetStateUnchangedSinceIfZero(t time.Time)
type Repository ¶
Repository is a datastructure that uniquely identifies a GitHub Repository.
func (*Repository) String ¶
func (r *Repository) String() string