// Munge is the workhorse the will actually make updates to the PR func (c *CherrypickQueue) Munge(obj *github.MungeObject) { if !obj.HasLabel(cpCandidateLabel) { return } if !obj.IsPR() { return } // This will cache the PR and events so when we try to view the queue we don't // hit github while trying to load the page obj.GetPR() num := *obj.Issue.Number c.Lock() merged, _ := obj.IsMerged() if merged { if obj.HasLabel(cpApprovedLabel) { c.mergedAndApproved[num] = obj } else { c.merged[num] = obj } } else { c.unmerged[num] = obj } c.Unlock() return }
// Munge is the workhorse the will actually make updates to the PR func (c *ClearPickAfterMerge) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } if !obj.HasLabel(cpCandidateLabel) { return } if merged, err := obj.IsMerged(); !merged || err != nil { return } releaseMilestone := obj.ReleaseMilestone() if releaseMilestone == "" || len(releaseMilestone) != 4 { glog.Errorf("Found invalid milestone: %q", releaseMilestone) return } rel := releaseMilestone[1:] branch := "release-" + rel sha := obj.MergeCommit() if sha == nil { glog.Errorf("Unable to get SHA of merged %d", sha) return } logMsg := fmt.Sprintf("Merge pull request #%d from ", *obj.Issue.Number) bLogMsg := []byte(logMsg) cherrypickMsg := fmt.Sprintf("(cherry picked from commit %s)", *sha) args := []string{"log", "--pretty=tformat:%H%n%s%n%b", "--grep", cherrypickMsg, "origin/" + branch} out, err := c.features.Repos.GitCommand(args) if err != nil { glog.Errorf("Error grepping for cherrypick -x message out=%q: %v", string(out), err) return } if bytes.Contains(out, bLogMsg) { glog.Infof("Found cherry-pick using -x information") handleFound(obj, out, branch) return } args = []string{"log", "--pretty=tformat:%H%n%s%n%b", "--grep", logMsg, "origin/" + branch} out, err = c.features.Repos.GitCommand(args) if err != nil { glog.Errorf("Error grepping for log message out=%q: %v", string(out), err) return } if bytes.Contains(out, bLogMsg) { glog.Infof("Found cherry-pick using log matching") handleFound(obj, out, branch) return } return }
// Munge is the workhorse the will actually make updates to the PR func (c *ClearPickAfterMerge) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } if !obj.HasLabel(cpCandidateLabel) { return } if merged, err := obj.IsMerged(); !merged || err != nil { return } releaseMilestone := obj.ReleaseMilestone() if releaseMilestone == "" || len(releaseMilestone) != 4 { glog.Errorf("Found invalid milestone: %q", releaseMilestone) return } rel := releaseMilestone[1:] branch := "release-" + rel if c.foundByPickDashX(obj, branch) { handleFound(obj, branch) return } if c.foundByAllCommits(obj, branch) { handleFound(obj, branch) return } if c.foundByPickWithoutDashX(obj, branch) { handleFound(obj, branch) return } if c.foundByScript(obj, branch) { handleFound(obj, branch) return } return }
// 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, mergedByHand) return false } if milestone := obj.Issue.Milestone; true { title := "" // Net set means the empty milestone, "" if milestone != nil && milestone.Title != nil { title = *milestone.Title } for _, blocked := range sq.DoNotMergeMilestones { if title == blocked { sq.SetMergeStatus(obj, unmergeableMilestone) return false } } } // Must pass CLA checks if !obj.HasLabel(claYesLabel) && !obj.HasLabel(claHumanLabel) && !obj.HasLabel(cncfClaYesLabel) { 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 if len(sq.RequiredStatusContexts) > 0 { if ok := obj.IsStatusSuccess(sq.RequiredStatusContexts); !ok { sq.SetMergeStatus(obj, ciFailure) return false } } if len(sq.RequiredRetestContexts) > 0 { if ok := obj.IsStatusSuccess(sq.RequiredRetestContexts); !ok { sq.SetMergeStatus(obj, ciFailure) return false } } // Clearly if !(obj.HasLabel(lgtmLabel) || obj.HasLabel(approvedLabel)) { sq.SetMergeStatus(obj, noLGTM) return false } // PR cannot change since LGTM was added lastModifiedTime := obj.LastModifiedTime() // lgtmTime and approvedTime cannot both be nil at this point (see check above) earliestApproved := getEarliestApprovedTime(obj) if lastModifiedTime == nil || earliestApproved == 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(*earliestApproved) { 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 }
func (sq *SubmitQueue) doGithubE2EAndMerge(obj *github.MungeObject) { _, err := obj.RefreshPR() if err != nil { glog.Errorf("%d: unknown err: %v", *obj.Issue.Number, err) sq.SetMergeStatus(obj, unknown, true) return } if m, err := obj.IsMerged(); err != nil { glog.Errorf("%d: unknown err: %v", *obj.Issue.Number, err) sq.SetMergeStatus(obj, unknown, true) return } else if m { sq.SetMergeStatus(obj, merged, true) return } if mergeable, err := obj.IsMergeable(); err != nil { sq.SetMergeStatus(obj, undeterminedMergability, true) return } else if !mergeable { sq.SetMergeStatus(obj, unmergeable, true) return } body := "@k8s-bot test this [submit-queue is verifying that this PR is safe to merge]" if err := obj.WriteComment(body); err != nil { glog.Errorf("%d: unknown err: %v", *obj.Issue.Number, err) sq.SetMergeStatus(obj, unknown, true) return } // Wait for the build to start sq.SetMergeStatus(obj, ghE2EWaitingStart, true) err = obj.WaitForPending([]string{sq.E2EStatusContext, sq.UnitStatusContext}) if err != nil { s := fmt.Sprintf("Failed waiting for PR to start testing: %v", err) sq.SetMergeStatus(obj, s, true) return } // Wait for the status to go back to something other than pending sq.SetMergeStatus(obj, ghE2ERunning, true) err = obj.WaitForNotPending([]string{sq.E2EStatusContext, sq.UnitStatusContext}) if err != nil { s := fmt.Sprintf("Failed waiting for PR to finish testing: %v", err) sq.SetMergeStatus(obj, s, true) return } // Check if the thing we care about is success if ok := obj.IsStatusSuccess([]string{sq.E2EStatusContext, sq.UnitStatusContext}); !ok { sq.SetMergeStatus(obj, ghE2EFailed, true) return } if !sq.e2e.Stable() { sq.flushGithubE2EQueue(e2eFailure) sq.SetMergeStatus(obj, e2eFailure, true) return } obj.MergePR("submit-queue") sq.SetMergeStatus(obj, merged, true) return }
// 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 }