Beispiel #1
0
// 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
}
Beispiel #4
0
// 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
}
Beispiel #5
0
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
}