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 required 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(_ context.Context, baseBranch *BaseBranch, prNumber int) (*PullRequest, error)
- func (a *Autoupdater) Enqueue(_ context.Context, baseBranch *BaseBranch, pr *PullRequest) error
- func (a *Autoupdater) HTTPService() *HTTPService
- func (a *Autoupdater) InitSync(ctx context.Context) error
- func (a *Autoupdater) Resume(_ context.Context, baseBranch *BaseBranch, prNumber int) error
- func (a *Autoupdater) ResumeAllForBaseBranch(_ context.Context, baseBranch *BaseBranch)
- func (a *Autoupdater) ResumeIfStatusPositive(ctx context.Context, owner, repo string, branchNames []string)
- func (a *Autoupdater) SetPRStaleSinceIfNewer(_ context.Context, baseBranch *BaseBranch, prNumber int, updatedAt time.Time) error
- func (a *Autoupdater) SetPRStaleSinceIfNewerByBranch(_ context.Context, owner, repo string, branchNames []string, ...) (notFound []string)
- func (a *Autoupdater) Start()
- func (a *Autoupdater) Stop()
- func (a *Autoupdater) SuspendUpdates(_ context.Context, owner string, repo string, branchNames []string) ([]*PullRequest, []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 (*DryGithubClient) AddLabel(context.Context, string, string, int, string) error
- func (c *DryGithubClient) CreateIssueComment(context.Context, string, string, int, string) error
- func (c *DryGithubClient) ListPullRequests(ctx context.Context, owner, repo, state, sort, sortDirection string) githubclt.PRIterator
- func (c *DryGithubClient) ReadyForMerge(context.Context, string, string, int) (*githubclt.ReadyForMergeStatus, error)
- func (*DryGithubClient) RemoveLabel(context.Context, string, string, int, string) error
- func (c *DryGithubClient) UpdateBranch(context.Context, string, string, int) (*githubclt.UpdateBranchResult, 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, headLabel 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. headLabel is the name of the GitHub label that is applied to the PR that is the first in the queue.
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(_ 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(_ 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) InitSync ¶ added in v0.13.0
func (a *Autoupdater) InitSync(ctx context.Context) error
InitSync does an initial synchronization of the autoupdater queues with the pull request state at GitHub. This is intended to be run before Autoupdater is started. 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. If a PR has the [a.headLabel] set it is removed.
func (*Autoupdater) Resume ¶
func (a *Autoupdater) Resume(_ 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(_ 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) SetPRStaleSinceIfNewer ¶ added in v0.10.0
func (a *Autoupdater) SetPRStaleSinceIfNewer( _ context.Context, baseBranch *BaseBranch, prNumber int, updatedAt time.Time, ) error
SetPRStaleSinceIfNewer sets the staleSince timestamp of the PR to updatedAt, if it is newer then the current staleSince timestamp. If the PR is not queued for autoupdates, ErrNotFound is returned.
func (*Autoupdater) SetPRStaleSinceIfNewerByBranch ¶ added in v0.10.0
func (a *Autoupdater) SetPRStaleSinceIfNewerByBranch( _ context.Context, owner, repo string, branchNames []string, updatedAt time.Time, ) (notFound []string)
SetPRStaleSinceIfNewerByBranch sets the staleSince timestamp of the PRs for the given branch names to updatdAt, if it is newer then the current staleSince timestamp. The method returns a list of branch names for that no queued PR could be found.
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( _ 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) 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) CreateIssueComment ¶
func (*DryGithubClient) ListPullRequests ¶
func (c *DryGithubClient) ListPullRequests(ctx context.Context, owner, repo, state, sort, sortDirection string) githubclt.PRIterator
func (*DryGithubClient) ReadyForMerge ¶ added in v0.12.0
func (c *DryGithubClient) ReadyForMerge(context.Context, string, string, int) (*githubclt.ReadyForMergeStatus, error)
func (*DryGithubClient) RemoveLabel ¶ added in v0.13.0
func (*DryGithubClient) UpdateBranch ¶
func (c *DryGithubClient) UpdateBranch(context.Context, string, string, int) (*githubclt.UpdateBranchResult, error)
type GithubClient ¶
type GithubClient interface { UpdateBranch(ctx context.Context, owner, repo string, pullRequestNumber int) (*githubclt.UpdateBranchResult, 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 ReadyForMerge(ctx context.Context, owner, repo string, prNumber int) (*githubclt.ReadyForMergeStatus, error) RemoveLabel(ctx context.Context, owner, repo string, pullRequestOrIssueNumber int, label string) error AddLabel(ctx context.Context, owner, repo string, pullRequestOrIssueNumber int, label string) 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, _ *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 *PullRequest) 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
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
Package mocks is a generated GoMock package.
|
Package mocks is a generated GoMock package. |