Example #1
0
// 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)
	}
}
Example #2
0
// 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)
}
Example #3
0
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
	}
}
Example #4
0
// 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)
		}
	}
}
Example #5
0
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
	}
}
Example #6
0
// 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")
	}
}
Example #7
0
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
}
Example #8
0
// 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

}
Example #9
0
// 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)
	}
}
Example #10
0
// 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)
	}
}
Example #11
0
File: cla.go Project: spxtr/contrib
// 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)
	}
}
Example #14
0
// 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})
	}
}
Example #15
0
// 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)
	}
}
Example #17
0
// 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
}
Example #18
0
// 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)
}
Example #19
0
// 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)
}
Example #20
0
// 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
}
Example #21
0
// 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)
				}
			}
		}
	}
}
Example #22
0
// 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
		}
	}
}
Example #23
0
// 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)
				}
			}
		}
	}
}
Example #24
0
// 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)
				}
			}
		}
	}
}
Example #25
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, 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
}
Example #26
0
// 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)
	}
}
Example #27
0
// 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
}
Example #28
0
// 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
}
Example #29
0
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
}