Example #1
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 #2
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 #3
0
// Process does the necessary processing to compute whether to stay in
// this state, or proceed to the next.
func (c *ChangesNeeded) Process(obj *github.MungeObject) (State, error) {
	if !obj.HasLabel(labelChangesNeeded) {
		obj.AddLabel(labelChangesNeeded)
		glog.Infof("PR #%v needs changes from author", *obj.Issue.Number)
	}
	return &End{}, nil
}
Example #4
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 #5
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 #6
0
func (h *LGTMHandler) addLGTMIfCommented(obj *github.MungeObject, comments []*githubapi.IssueComment, reviewers mungerutil.UserSet) {
	// Assumption: The comments should be sorted (by default from github api) from oldest to latest
	for i := len(comments) - 1; i >= 0; i-- {
		comment := comments[i]
		if !mungerutil.IsValidUser(comment.User) {
			continue
		}

		// TODO: An approver should be acceptable.
		// See https://github.com/kubernetes/contrib/pull/1428#discussion_r72563935
		if !mungerutil.IsMungeBot(comment.User) && !isReviewer(comment.User, reviewers) {
			continue
		}

		fields := getFields(*comment.Body)
		if isCancelComment(fields) {
			// "/lgtm cancel" if commented more recently than "/lgtm"
			return
		}

		if !isLGTMComment(fields) {
			continue
		}

		// TODO: support more complex policies for multiple reviewers.
		// See https://github.com/kubernetes/contrib/issues/1389#issuecomment-235161164
		glog.Infof("Adding lgtm label. Reviewer (%s) LGTM", *comment.User.Login)
		obj.AddLabel(lgtmLabel)
		return
	}
}
Example #7
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 #8
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)
}
Example #9
0
// Munge is the workhorse the will actually make updates to the PR
func (c *CherrypickAutoApprove) Munge(obj *github.MungeObject) {
	if !obj.IsPR() {
		return
	}
	if obj.IsForBranch("master") {
		return
	}
	if obj.HasLabel(cpApprovedLabel) && obj.ReleaseMilestone() != "" {
		return
	}

	parents := getCherrypickParentPRs(obj, c.config)
	if len(parents) == 0 {
		return
	}

	major := 0
	minor := 0
	branch := obj.Branch()
	if l, err := fmt.Sscanf(branch, "release-%d.%d", &major, &minor); err != nil || l != 2 {
		return
	}
	branchImpliedMilestone := fmt.Sprintf("v%d.%d", major, minor)

	milestone := obj.ReleaseMilestone()
	if milestone != "" && milestone != branchImpliedMilestone {
		glog.Errorf("Found PR %d on branch %q but have milestone %q", *obj.Issue.Number, branch, milestone)
		return
	}

	for _, parent := range parents {
		if !parent.HasLabel(cpApprovedLabel) {
			return
		}

		// If the parent was for milestone v1.2 but this PR has
		// comments saying it was 'on branch release-1.1' we should
		// not auto approve
		parentMilestone := parent.ReleaseMilestone()
		if parentMilestone != branchImpliedMilestone {
			glog.Errorf("%d: parentReleaseMilestone=%q but branch is %q", *obj.Issue.Number, parentMilestone, obj.Branch())
			return
		}
	}
	if milestone == "" {
		obj.SetMilestone(branchImpliedMilestone)
	}
	if !obj.HasLabel(cpApprovedLabel) {
		obj.AddLabel(cpApprovedLabel)
	}
}
Example #10
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 #11
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)
	}
}
Example #12
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 #13
0
// Munge is the workhorse the will actually make updates to the PR
// The algorithm goes as:
// - Initially, we set up approverSet
//   - Go through all comments after latest commit. If any approver said "/approve", add him to approverSet.
// - For each file, we see if any approver of this file is in approverSet.
//   - An approver of a file is defined as:
//     - It's known that each dir has a list of approvers. (This might not hold true. For usability, current situation is enough.)
//     - Approver of a dir is also the approver of child dirs.
//   - We look at top N (default 3) level dir approvers. For example, for file "/a/b/c/d/e", we might search for approver from
//     "/", "/a/", "/a/b/"
// - Iff all files has been approved, the bot will add "approved" label.
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
	}

	approverSet := sets.String{}

	// from oldest to latest
	for i := len(comments) - 1; i >= 0; i-- {
		c := comments[i]

		if !mungerutil.IsValidUser(c.User) {
			continue
		}

		fields := strings.Fields(strings.TrimSpace(*c.Body))

		if len(fields) == 1 && strings.ToLower(fields[0]) == "/approve" {
			approverSet.Insert(*c.User.Login)
			continue
		}

		if len(fields) == 2 && strings.ToLower(fields[0]) == "/approve" && strings.ToLower(fields[1]) == "cancel" {
			approverSet.Delete(*c.User.Login)
		}
	}

	for _, file := range files {
		if !h.hasApproval(*file.Filename, approverSet, maxDepth) {
			return
		}
	}
	obj.AddLabel(approvedLabel)
}
// Munge is the workhorse the will actually make updates to the PR
func (LabelUnapprovedPicks) Munge(obj *github.MungeObject) {
	if !obj.IsPR() {
		return
	}

	if obj.IsForBranch("master") {
		return
	}

	if obj.HasLabel(cpApprovedLabel) {
		return
	}

	obj.AddLabel(doNotMergeLabel)

	msg := fmt.Sprintf("This PR is not for the master branch but does not have the `%s` label. Adding the `%s` label.", cpApprovedLabel, doNotMergeLabel)
	obj.WriteComment(msg)
}
// Munge is the workhorse the will actually make updates to the PR
func (LabelUnapprovedPicks) Munge(obj *github.MungeObject) {
	if !obj.IsPR() {
		return
	}

	if obj.IsForBranch("master") {
		return
	}

	if obj.HasLabel(cpApprovedLabel) {
		return
	}

	if obj.HasLabel(doNotMergeLabel) {
		return
	}

	obj.AddLabel(doNotMergeLabel)

	obj.WriteComment(labelUnapprovedBody)
}
Example #16
0
func (h *LGTMHandler) addLGTMIfCommented(obj *github.MungeObject, comments []*githubapi.IssueComment, events []*githubapi.IssueEvent, reviewers mungerutil.UserSet) {
	// Get the last time when the someone applied lgtm manually.
	removeLGTMTime := e.LastEvent(events, e.And{e.RemoveLabel{}, e.LabelName(lgtmLabel), e.HumanActor()}, nil)

	// Assumption: The comments should be sorted (by default from github api) from oldest to latest
	for i := len(comments) - 1; i >= 0; i-- {
		comment := comments[i]
		if !mungerutil.IsValidUser(comment.User) {
			continue
		}

		// TODO: An approver should be acceptable.
		// See https://github.com/kubernetes/contrib/pull/1428#discussion_r72563935
		if !mungerutil.IsMungeBot(comment.User) && !isReviewer(comment.User, reviewers) {
			continue
		}

		fields := getFields(*comment.Body)
		if isCancelComment(fields) {
			// "/lgtm cancel" if commented more recently than "/lgtm"
			return
		}

		if !isLGTMComment(fields) {
			continue
		}

		// check if someone manually removed the lgtm label after the `/lgtm` comment
		// and honor it.
		if removeLGTMTime != nil && removeLGTMTime.After(*comment.CreatedAt) {
			return
		}

		// TODO: support more complex policies for multiple reviewers.
		// See https://github.com/kubernetes/contrib/issues/1389#issuecomment-235161164
		glog.Infof("Adding lgtm label. Reviewer (%s) LGTM", *comment.User.Login)
		obj.AddLabel(lgtmLabel)
		return
	}
}
Example #17
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)
}