// Munge is the workhorse the will actually make updates to the PR // The algorithm goes as: // - Initially, we build an approverSet // - Go through all comments after latest commit. // - If anyone said "/approve", add them to approverSet. // - Then, for each file, we see if any approver of this file is in approverSet and keep track of files without approval // - An approver of a file is defined as: // - Someone listed as an "approver" in an OWNERS file in the files directory OR // - in one of the file's parent directorie // - Iff all files have been approved, the bot will add the "approved" label. // - Iff a cancel command is found, that reviewer will be removed from the approverSet // and the munger will remove the approved label if it has been applied func (h *ApprovalHandler) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } files, err := obj.ListFiles() if err != nil { glog.Errorf("failed to list files in this PR: %v", err) return } comments, err := getCommentsAfterLastModified(obj) if err != nil { glog.Errorf("failed to get comments in this PR: %v", err) return } ownersMap := h.getApprovedOwners(files, createApproverSet(comments)) if err := h.updateNotification(obj, ownersMap); err != nil { return } for _, approverSet := range ownersMap { if approverSet.Len() == 0 { if obj.HasLabel(approvedLabel) { obj.RemoveLabel(approvedLabel) } return } } if !obj.HasLabel(approvedLabel) { obj.AddLabel(approvedLabel) } }
// Munge is the workhorse the will actually make updates to the PR func (r *ReleaseNoteLabel) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } if completedReleaseNoteProcess(obj) { r.ensureNoRelNoteNeededLabel(obj) return } if !r.prMustFollowRelNoteProcess(obj) { r.ensureNoRelNoteNeededLabel(obj) return } if !obj.HasLabel(releaseNoteLabelNeeded) { obj.AddLabel(releaseNoteLabelNeeded) } if !obj.HasLabel(lgtmLabel) { return } obj.WriteComment(releaseNoteBody) obj.RemoveLabel(lgtmLabel) }
func (h *LGTMHandler) removeLGTMIfCancelled(obj *github.MungeObject, comments []*githubapi.IssueComment, reviewers mungerutil.UserSet) { for i := len(comments) - 1; i >= 0; i-- { comment := comments[i] if !mungerutil.IsValidUser(comment.User) { continue } if !mungerutil.IsMungeBot(comment.User) && !isReviewer(comment.User, reviewers) { continue } fields := getFields(*comment.Body) if isLGTMComment(fields) { // "/lgtm" if commented more recently than "/lgtm cancel" return } if !isCancelComment(fields) { continue } glog.Infof("Removing lgtm label. Reviewer (%s) cancelled", *comment.User.Login) obj.RemoveLabel(lgtmLabel) return } }
// Munge is the workhorse the will actually make updates to the PR func (p *PathLabelMunger) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } files, err := obj.ListFiles() if err != nil { return } needsLabels := sets.NewString() for _, f := range files { for _, lm := range p.labelMap { if lm.regexp.MatchString(*f.Filename) { needsLabels.Insert(lm.label) } } } // This is all labels on the issue that the path munger controls hasLabels := obj.LabelSet().Intersection(p.allLabels) missingLabels := needsLabels.Difference(hasLabels) if missingLabels.Len() != 0 { obj.AddLabels(needsLabels.List()) } extraLabels := hasLabels.Difference(needsLabels) for _, label := range extraLabels.List() { creator := obj.LabelCreator(label) if creator == botName { obj.RemoveLabel(label) } } }
func (h *LGTMHandler) removeLGTMIfCancelled(obj *github.MungeObject, comments []*githubapi.IssueComment, events []*githubapi.IssueEvent, reviewers mungerutil.UserSet) { // Get time when the last (unlabeled, lgtm) event occurred. addLGTMTime := e.LastEvent(events, e.And{e.AddLabel{}, e.LabelName(lgtmLabel), e.HumanActor()}, nil) for i := len(comments) - 1; i >= 0; i-- { comment := comments[i] if !mungerutil.IsValidUser(comment.User) { continue } if !mungerutil.IsMungeBot(comment.User) && !isReviewer(comment.User, reviewers) { continue } fields := getFields(*comment.Body) if isLGTMComment(fields) { // "/lgtm" if commented more recently than "/lgtm cancel" return } if !isCancelComment(fields) { continue } // check if someone manually added the lgtm label after the `/lgtm cancel` comment // and honor it. if addLGTMTime != nil && addLGTMTime.After(*comment.CreatedAt) { return } glog.Infof("Removing lgtm label. Reviewer (%s) cancelled", *comment.User.Login) obj.RemoveLabel(lgtmLabel) return } }
// Munge is the workhorse the will actually make updates to the PR func (LGTMAfterCommitMunger) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } if !obj.HasLabel("lgtm") { return } lastModified := obj.LastModifiedTime() lgtmTime := obj.LabelTime("lgtm") if lastModified == nil || lgtmTime == nil { glog.Errorf("PR %d unable to determine lastModified or lgtmTime", *obj.Issue.Number) return } if lastModified.After(*lgtmTime) { glog.Infof("PR: %d lgtm:%s lastModified:%s", *obj.Issue.Number, lgtmTime.String(), lastModified.String()) lgtmRemovedBody := "PR changed after LGTM, removing LGTM." if err := obj.WriteComment(lgtmRemovedBody); err != nil { return } obj.RemoveLabel("lgtm") } }
func getHumanCorrectedLabel(obj *github.MungeObject, s string) *string { myEvents, err := obj.GetEvents() if err != nil { glog.Errorf("Could not get the events associated with Issue %d", obj.Issue.Number) return nil } botEvents := event.FilterEvents(myEvents, event.And([]event.Matcher{event.BotActor(), event.AddLabel{}, event.LabelPrefix(s)})) if botEvents.Empty() { return nil } humanEventsAfter := event.FilterEvents( myEvents, event.And([]event.Matcher{ event.HumanActor(), event.AddLabel{}, event.LabelPrefix(s), event.CreatedAfter(*botEvents.GetLast().CreatedAt), }), ) if humanEventsAfter.Empty() { return nil } lastHumanLabel := humanEventsAfter.GetLast() glog.Infof("Recopying human-added label: %s for PR %d", *lastHumanLabel.Label.Name, *obj.Issue.Number) obj.RemoveLabel(*lastHumanLabel.Label.Name) obj.AddLabel(*lastHumanLabel.Label.Name) return lastHumanLabel.Label.Name }
// Process does the necessary processing to compute whether to stay in // this state, or proceed to the next. func (p *PreReview) Process(obj *githubhelper.MungeObject) (State, error) { success := true if !p.checkCLA(obj) { success = false } if !p.checkReleaseNotes(obj) { success = false } if !p.checkAssignees(obj) { success = false } if success { if obj.HasLabel(labelPreReview) { obj.RemoveLabel(labelPreReview) } return &NeedsReview{}, nil } if !obj.HasLabel(labelPreReview) { obj.AddLabel(labelPreReview) } return &End{}, nil }
// Munge is the workhorse the will actually make updates to the PR func (LGTMAfterCommitMunger) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } if !obj.HasLabel(lgtmLabel) { return } lastModified := obj.LastModifiedTime() lgtmTime := obj.LabelTime(lgtmLabel) if lastModified == nil || lgtmTime == nil { glog.Errorf("PR %d unable to determine lastModified or lgtmTime", *obj.Issue.Number) return } if lastModified.After(*lgtmTime) { glog.Infof("PR: %d lgtm:%s lastModified:%s", *obj.Issue.Number, lgtmTime.String(), lastModified.String()) body := fmt.Sprintf(lgtmRemovedBody, mungerutil.GetIssueUsers(obj.Issue).AllUsers().Mention().Join()) if err := obj.WriteComment(body); err != nil { return } obj.RemoveLabel(lgtmLabel) } }
// Munge is the workhorse the will actually make updates to the PR func (s *SizeMunger) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } issue := obj.Issue s.getGeneratedFiles(obj) genFiles := *s.genFiles genPrefixes := *s.genPrefixes files, err := obj.ListFiles() if err != nil { return } adds := 0 dels := 0 for _, f := range files { skip := false for _, p := range genPrefixes { if strings.HasPrefix(*f.Filename, p) { skip = true break } } if skip { continue } if genFiles.Has(*f.Filename) { continue } if f.Additions != nil { adds += *f.Additions } if f.Deletions != nil { dels += *f.Deletions } } newSize := calculateSize(adds, dels) newLabel := labelSizePrefix + newSize existing := github.GetLabelsWithPrefix(issue.Labels, labelSizePrefix) needsUpdate := true for _, l := range existing { if l == newLabel { needsUpdate = false continue } obj.RemoveLabel(l) } if needsUpdate { obj.AddLabels([]string{newLabel}) body := fmt.Sprintf("Labelling this PR as %s", newLabel) obj.WriteComment(body) } }
// Munge is unused by this munger. func (cla *ClaMunger) Munge(obj *githubhelper.MungeObject) { if !obj.IsPR() { return } if obj.HasLabel(claHumanLabel) { return } status := obj.GetStatusState([]string{cla.CLAStatusContext}) // Check for pending status and exit. if status == contextPending { // do nothing and wait for state to be updated. return } if status == contextSuccess { if obj.HasLabel(cncfClaYesLabel) { // status is success and we've already applied 'cncf-cla: yes'. return } if obj.HasLabel(cncfClaNoLabel) { obj.RemoveLabel(cncfClaNoLabel) } obj.AddLabel(cncfClaYesLabel) return } // If we are here, that means that the context is failure/error. comments, err := obj.ListComments() if err != nil { glog.Error(err) return } who := mungerutil.GetIssueUsers(obj.Issue).Author.Mention().Join() // Get a notification if it's time to ping. notif := cla.pinger.PingNotification( comments, who, nil, ) if notif != nil { obj.WriteComment(notif.String()) } if obj.HasLabel(cncfClaNoLabel) { // status reported error/failure and we've already applied 'cncf-cla: no' label. return } if obj.HasLabel(cncfClaYesLabel) { obj.RemoveLabel(cncfClaYesLabel) } obj.AddLabel(cncfClaNoLabel) }
func handleFound(obj *github.MungeObject, gitMsg []byte, branch string) error { msg := string(gitMsg) o := strings.SplitN(msg, "\n", 2) sha := o[0] msg = fmt.Sprintf("Commit %s found in the %q branch appears to be this PR. Removing the %q label. If this s an error find help to get your PR picked.", sha, branch, cpCandidateLabel) obj.WriteComment(msg) obj.RemoveLabel(cpCandidateLabel) return nil }
// Munge is the workhorse the will actually make updates to the PR func (PickMustHaveMilestone) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } if !obj.HasLabel(cpCandidateLabel) { return } releaseMilestone := obj.ReleaseMilestone() hasLabel := obj.HasLabel(cpCandidateLabel) if hasLabel && releaseMilestone == "" { obj.WriteComment(pickMustHaveMilestoneBody) obj.RemoveLabel(cpCandidateLabel) } }
// Munge is the workhorse the will actually make updates to the PR func (NeedsRebaseMunger) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } mergeable, err := obj.IsMergeable() if err != nil { glog.V(2).Infof("Skipping %d - problem determining mergeable", *obj.Issue.Number) return } if mergeable && obj.HasLabel(needsRebase) { obj.RemoveLabel(needsRebase) } if !mergeable && !obj.HasLabel(needsRebase) { obj.AddLabels([]string{needsRebase}) } }
// Munge is the workhorse the will actually make updates to the PR func (DocsNeedNoRetest) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } files, err := obj.ListFiles() if err != nil { glog.Errorf("Failed to list files for PR %d: %s", obj.Issue.Number, err) } docsOnly := areFilesDocOnly(files) if docsOnly && !obj.HasLabel(labelSkipRetest) { obj.AddLabel(labelSkipRetest) } else if !docsOnly && obj.HasLabel(labelSkipRetest) { obj.RemoveLabel(labelSkipRetest) } }
// Munge is the workhorse the will actually make updates to the PR func (PickMustHaveMilestone) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } if !obj.HasLabel(cpCandidateLabel) { return } releaseMilestone := obj.ReleaseMilestone() hasLabel := obj.HasLabel(cpCandidateLabel) if hasLabel && releaseMilestone == "" { msg := fmt.Sprintf("Removing label `%s` because no release milestone was set. This is an invalid state and thus this PR is not being considered for cherry-pick to any release branch. Please add an appropriate release milestone and then re-add the label.", cpCandidateLabel) obj.WriteComment(msg) obj.RemoveLabel(cpCandidateLabel) } }
// syncPriority will sync the input priority to the issue if the input priority is higher than the existing ones func (s *IssueSyncer) syncPriority(obj *github.MungeObject, priority Priority) error { if obj.Priority() <= priority.Priority() { return nil } plabels := github.GetLabelsWithPrefix(obj.Issue.Labels, priorityPrefix) err := obj.AddLabel(priority.String()) if err != nil { return nil } for _, l := range plabels { err = obj.RemoveLabel(l) if err != nil { return err } } return nil }
// Munge is unused by this munger. func (c *ClaMunger) Munge(obj *githubhelper.MungeObject) { if !obj.IsPR() { return } if obj.HasLabel(claHumanLabel) { return } status := obj.GetStatusState([]string{c.CLAStatusContext}) // Check for pending status and exit. if status == contextPending { // do nothing and wait for state to be updated. return } if status == contextSuccess { if obj.HasLabel(cncfClaYesLabel) { // status is success and we've already applied 'cncf-cla: yes'. return } if obj.HasLabel(cncfClaNoLabel) { obj.RemoveLabel(cncfClaNoLabel) } obj.AddLabel(cncfClaYesLabel) return } if obj.HasLabel(cncfClaNoLabel) { // status reported error/failure and we've already applied 'cncf-cla: no' label. return } // Write comment and then modify the labels. err := obj.WriteComment(fmt.Sprint(cncfclaNotFoundMessage, mungerutil.GetIssueUsers(obj.Issue).Author.Mention().Join())) if err != nil { return } if obj.HasLabel(cncfClaYesLabel) { obj.RemoveLabel(cncfClaYesLabel) } obj.AddLabel(cncfClaNoLabel) }
// Munge is the workhorse the will actually make updates to the PR func (r *ReleaseNoteLabel) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } if releaseNoteAlreadyAdded(obj) { r.ensureNoRelNoteNeededLabel(obj) return } if !r.prMustFollowRelNoteProcess(obj) { r.ensureNoRelNoteNeededLabel(obj) return } labelToAdd := determineReleaseNoteLabel(obj) if labelToAdd != releaseNoteLabelNeeded { //going to apply some other release-note-label if obj.HasLabel(releaseNoteLabelNeeded) { obj.RemoveLabel(releaseNoteLabelNeeded) } obj.AddLabel(labelToAdd) return } if !obj.HasLabel(releaseNoteLabelNeeded) { obj.AddLabel(releaseNoteLabelNeeded) } if !obj.HasLabel(lgtmLabel) { return } if obj.HasLabel(doNotMergeLabel) { return } obj.WriteComment(releaseNoteBody) obj.AddLabel(doNotMergeLabel) }
// Process does the necessary processing to compute whether to stay in // this state, or proceed to the next. func (nr *NeedsReview) Process(obj *github.MungeObject) (State, error) { if nr.checkLGTM(obj) { if obj.HasLabel(labelNeedsReview) { obj.RemoveLabel(labelNeedsReview) } if obj.HasLabel(labelChangesNeeded) { obj.RemoveLabel(labelChangesNeeded) } return &End{}, nil } reviewerActionNeeded, err := isReviewerActionNeeded(obj) if err != nil { return &End{}, err } if !reviewerActionNeeded { if obj.HasLabel(labelNeedsReview) { obj.RemoveLabel(labelNeedsReview) } return &ChangesNeeded{}, nil } if !obj.HasLabel(labelNeedsReview) { glog.Infof("PR #%v needs reviewer action", *obj.Issue.Number) obj.AddLabel(labelNeedsReview) } return &End{}, nil }
// Munge is the workhorse the will actually make updates to the PR func (r *RebuildMunger) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } comments, err := obj.ListComments(*obj.Issue.Number) if err != nil { glog.Errorf("unexpected error getting comments: %v", err) } for ix := range comments { comment := &comments[ix] // Skip all robot comments if r.robots.Has(*comment.User.Login) { glog.V(4).Infof("Skipping comment by robot %s: %s", *comment.User.Login, *comment.Body) continue } if isRebuildComment(comment) && rebuildCommentMissingIssueNumber(comment) { if err := obj.DeleteComment(comment); err != nil { glog.Errorf("Error deleting comment: %v", err) continue } body := fmt.Sprintf(`@%s You must link to the test flake issue which caused you to request this manual re-test. Re-test requests should be in the form of: `+"`"+`k8s-bot test this issue: #<number>`+"`"+` Here is the [list of open test flakes](https://github.com/kubernetes/kubernetes/issues?q=is:issue+label:kind/flake+is:open).`, *comment.User.Login) err := obj.WriteComment(body) if err != nil { glog.Errorf("unexpected error adding comment: %v", err) continue } if obj.HasLabel("lgtm") { if err := obj.RemoveLabel("lgtm"); err != nil { glog.Errorf("unexpected error removing lgtm label: %v", err) } } } } }
// Munge is the workhorse the will actually make updates to the PR func (NeedsRebaseMunger) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } mergeable, err := obj.IsMergeable() if err != nil { glog.V(2).Infof("Skipping %d - problem determining mergeable", *obj.Issue.Number) return } if mergeable && obj.HasLabel(needsRebase) { obj.RemoveLabel(needsRebase) } if !mergeable && !obj.HasLabel(needsRebase) { obj.AddLabels([]string{needsRebase}) body := fmt.Sprintf("@%s PR needs rebase", *obj.Issue.User.Login) if err := obj.WriteComment(body); err != nil { return } } }
// Munge is the workhorse the will actually make updates to the PR func (RebuildMunger) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } comments, err := obj.ListComments(*obj.Issue.Number) if err != nil { glog.Errorf("unexpected error getting comments: %v", err) } for ix := range comments { comment := &comments[ix] // Skip all robot comments for _, robot := range []string{"k8s-bot", "k8s-merge-robot", "googlebot"} { if *comment.User.Login == robot { glog.V(4).Infof("Skipping comment by robot %s: %s", robot, *comment.Body) continue } } if isRebuildComment(comment) && rebuildCommentMissingIssueNumber(comment) { if err := obj.DeleteComment(comment); err != nil { glog.Errorf("Error deleting comment: %v", err) continue } body := fmt.Sprintf(`@%s an issue is required for any manual rebuild. Expecting comment of the form 'github issue: #<number>' [Open test flakes](https://github.com/kubernetes/kubernetes/issues?q=is%3Aissue%20label%3Akind%2Fflake)`, *comment.User.Login) err := obj.WriteComment(body) if err != nil { glog.Errorf("unexpected error adding comment: %v", err) continue } if obj.HasLabel("lgtm") { if err := obj.RemoveLabel("lgtm"); err != nil { glog.Errorf("unexpected error removing lgtm label: %v", err) } } } } }
// Munge is the workhorse the will actually make updates to the PR func (r *RebuildMunger) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } comments, err := obj.ListComments() if err != nil { glog.Errorf("unexpected error getting comments: %v", err) } for ix := range comments { comment := &comments[ix] // Skip all robot comments if r.robots.Has(*comment.User.Login) { glog.V(4).Infof("Skipping comment by robot %s: %s", *comment.User.Login, *comment.Body) continue } if isRebuildComment(comment) && rebuildCommentMissingIssueNumber(comment) { if err := obj.DeleteComment(comment); err != nil { glog.Errorf("Error deleting comment: %v", err) continue } body := fmt.Sprintf(rebuildFormat, *comment.User.Login) err := obj.WriteComment(body) if err != nil { glog.Errorf("unexpected error adding comment: %v", err) continue } if obj.HasLabel(lgtmLabel) { if err := obj.RemoveLabel(lgtmLabel); err != nil { glog.Errorf("unexpected error removing lgtm label: %v", err) } } } } }
// validForMerge is the base logic about what PR can be automatically merged. // PRs must pass this logic to be placed on the queue and they must pass this // logic a second time to be retested/merged after they get to the top of // the queue. // // If you update the logic PLEASE PLEASE PLEASE update serveMergeInfo() as well. func (sq *SubmitQueue) validForMerge(obj *github.MungeObject) bool { // Can't merge an issue! if !obj.IsPR() { return false } // Can't merge something already merged. if m, err := obj.IsMerged(); err != nil { glog.Errorf("%d: unknown err: %v", *obj.Issue.Number, err) sq.SetMergeStatus(obj, unknown) return false } else if m { sq.SetMergeStatus(obj, merged) return false } userSet := sq.userWhitelist // Must pass CLA checks if !obj.HasLabel(claYesLabel) && !obj.HasLabel(claHumanLabel) { sq.SetMergeStatus(obj, noCLA) return false } // Obviously must be mergeable if mergeable, err := obj.IsMergeable(); err != nil { sq.SetMergeStatus(obj, undeterminedMergability) return false } else if !mergeable { sq.SetMergeStatus(obj, unmergeable) return false } // Validate the status information for this PR contexts := sq.requiredStatusContexts(obj) if ok := obj.IsStatusSuccess(contexts); !ok { sq.SetMergeStatus(obj, ciFailure) return false } // The user either must be on the whitelist or have ok-to-merge if !obj.HasLabel(okToMergeLabel) && !userSet.Has(*obj.Issue.User.Login) { if !obj.HasLabel(needsOKToMergeLabel) { obj.AddLabels([]string{needsOKToMergeLabel}) obj.WriteComment(notInWhitelistBody) } sq.SetMergeStatus(obj, needsok) return false } // Tidy up the issue list. if obj.HasLabel(needsOKToMergeLabel) { obj.RemoveLabel(needsOKToMergeLabel) } // Clearly if !obj.HasLabel(lgtmLabel) { sq.SetMergeStatus(obj, noLGTM) return false } // PR cannot change since LGTM was added lastModifiedTime := obj.LastModifiedTime() lgtmTime := obj.LabelTime(lgtmLabel) if lastModifiedTime == nil || lgtmTime == nil { glog.Errorf("PR %d was unable to determine when LGTM was added or when last modified", *obj.Issue.Number) sq.SetMergeStatus(obj, unknown) return false } if lastModifiedTime.After(*lgtmTime) { sq.SetMergeStatus(obj, lgtmEarly) return false } // PR cannot have the label which prevents merging. if obj.HasLabel(doNotMergeLabel) { sq.SetMergeStatus(obj, noMerge) return false } return true }
// Munge is the workhorse the will actually make updates to the PR func (s *SizeMunger) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } issue := obj.Issue pr, err := obj.GetPR() if err != nil { return } s.getGeneratedFiles(obj) genFiles := *s.genFiles genPrefixes := *s.genPrefixes if pr.Additions == nil { glog.Warningf("PR %d has nil Additions", *pr.Number) return } adds := *pr.Additions if pr.Deletions == nil { glog.Warningf("PR %d has nil Deletions", *pr.Number) return } dels := *pr.Deletions commits, err := obj.GetCommits() if err != nil { return } for _, c := range commits { for _, f := range c.Files { for _, p := range genPrefixes { if strings.HasPrefix(*f.Filename, p) { adds = adds - *f.Additions dels = dels - *f.Deletions continue } } if genFiles.Has(*f.Filename) { adds = adds - *f.Additions dels = dels - *f.Deletions continue } } } newSize := calculateSize(adds, dels) newLabel := labelSizePrefix + newSize existing := github.GetLabelsWithPrefix(issue.Labels, labelSizePrefix) needsUpdate := true for _, l := range existing { if l == newLabel { needsUpdate = false continue } obj.RemoveLabel(l) } if needsUpdate { obj.AddLabels([]string{newLabel}) body := fmt.Sprintf("Labelling this PR as %s", newLabel) obj.WriteComment(body) } }
// Munge is the workhorse the will actually make updates to the PR func (sq *SubmitQueue) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } e2e := sq.e2e userSet := sq.userWhitelist if !obj.HasLabels([]string{claYes}) && !obj.HasLabels([]string{claHuman}) { sq.SetMergeStatus(obj, noCLA, false) return } if mergeable, err := obj.IsMergeable(); err != nil { sq.SetMergeStatus(obj, undeterminedMergability, false) return } else if !mergeable { sq.SetMergeStatus(obj, unmergeable, false) return } // Validate the status information for this PR contexts := sq.requiredStatusContexts(obj) if ok := obj.IsStatusSuccess(contexts); !ok { sq.SetMergeStatus(obj, ciFailure, false) return } if !obj.HasLabel(sq.WhitelistOverride) && !userSet.Has(*obj.Issue.User.Login) { if !obj.HasLabel(needsOKToMergeLabel) { obj.AddLabels([]string{needsOKToMergeLabel}) body := "The author of this PR is not in the whitelist for merge, can one of the admins add the 'ok-to-merge' label?" obj.WriteComment(body) } sq.SetMergeStatus(obj, needsok, false) return } // Tidy up the issue list. if obj.HasLabel(needsOKToMergeLabel) { obj.RemoveLabel(needsOKToMergeLabel) } if !obj.HasLabels([]string{"lgtm"}) { sq.SetMergeStatus(obj, noLGTM, false) return } lastModifiedTime := obj.LastModifiedTime() lgtmTime := obj.LabelTime("lgtm") if lastModifiedTime == nil || lgtmTime == nil { glog.Errorf("PR %d was unable to determine when LGTM was added or when last modified", *obj.Issue.Number) sq.SetMergeStatus(obj, unknown, false) return } if lastModifiedTime.After(*lgtmTime) { sq.SetMergeStatus(obj, lgtmEarly, false) return } if !e2e.Stable() { sq.flushGithubE2EQueue(e2eFailure) sq.SetMergeStatus(obj, e2eFailure, false) return } // if there is a 'e2e-not-required' label, just merge it. if obj.HasLabel(e2eNotRequiredLabel) { obj.MergePR("submit-queue") sq.SetMergeStatus(obj, merged, true) return } added := false sq.Lock() if _, ok := sq.githubE2EQueue[*obj.Issue.Number]; !ok { sq.githubE2EQueue[*obj.Issue.Number] = obj sq.githubE2EWakeup <- true added = true } sq.Unlock() if added { sq.SetMergeStatus(obj, ghE2EQueued, true) } return }
// Munge is the workhorse the will actually make updates to the PR func (sq *SubmitQueue) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } userSet := sq.userWhitelist if !obj.HasLabels([]string{claYes}) && !obj.HasLabels([]string{claHuman}) { sq.SetMergeStatus(obj, noCLA, false) return } if mergeable, err := obj.IsMergeable(); err != nil { sq.SetMergeStatus(obj, undeterminedMergability, false) return } else if !mergeable { sq.SetMergeStatus(obj, unmergeable, false) return } // Validate the status information for this PR contexts := sq.requiredStatusContexts(obj) if ok := obj.IsStatusSuccess(contexts); !ok { sq.SetMergeStatus(obj, ciFailure, false) return } if !obj.HasLabel(sq.WhitelistOverride) && !userSet.Has(*obj.Issue.User.Login) { if !obj.HasLabel(needsOKToMergeLabel) { obj.AddLabels([]string{needsOKToMergeLabel}) body := "The author of this PR is not in the whitelist for merge, can one of the admins add the 'ok-to-merge' label?" obj.WriteComment(body) } sq.SetMergeStatus(obj, needsok, false) return } // Tidy up the issue list. if obj.HasLabel(needsOKToMergeLabel) { obj.RemoveLabel(needsOKToMergeLabel) } if !obj.HasLabels([]string{"lgtm"}) { sq.SetMergeStatus(obj, noLGTM, false) return } lastModifiedTime := obj.LastModifiedTime() lgtmTime := obj.LabelTime("lgtm") if lastModifiedTime == nil || lgtmTime == nil { glog.Errorf("PR %d was unable to determine when LGTM was added or when last modified", *obj.Issue.Number) sq.SetMergeStatus(obj, unknown, false) return } if lastModifiedTime.After(*lgtmTime) { sq.SetMergeStatus(obj, lgtmEarly, false) return } added := false sq.Lock() if _, ok := sq.githubE2EQueue[*obj.Issue.Number]; !ok { added = true } // Add this most-recent object in place of the existing object. It will // have more up2date information. Even though we explicitly refresh the // PR information before do anything with it, this allow things like the // queue order to change dynamically as labels are added/removed. sq.githubE2EQueue[*obj.Issue.Number] = obj sq.Unlock() if added { sq.SetMergeStatus(obj, ghE2EQueued, true) } return }
func (r *ReleaseNoteLabel) ensureNoRelNoteNeededLabel(obj *github.MungeObject) { if obj.HasLabel(releaseNoteLabelNeeded) { obj.RemoveLabel(releaseNoteLabelNeeded) } }
func handleFound(obj *github.MungeObject, branch string) error { msg := fmt.Sprintf("Commit found in the %q branch appears to be this PR. Removing the %q label. If this is an error find help to get your PR picked.", branch, cpCandidateLabel) obj.WriteComment(msg) obj.RemoveLabel(cpCandidateLabel) return nil }