Beispiel #1
0
// Find the last warning comment that the bot has posted.
// It can return an empty comment if it fails to find one, even if there are no errors.
func findLatestWarningComment(obj *github.MungeObject) (*githubapi.IssueComment, error) {
	var lastFoundComment *githubapi.IssueComment

	comments, err := obj.ListComments()
	if err != nil {
		return nil, err
	}

	for i := range comments {
		comment := comments[i]
		if !validComment(comment) {
			continue
		}
		if !mergeBotComment(comment) {
			continue
		}

		if !warningCommentRE.MatchString(*comment.Body) {
			continue
		}

		if lastFoundComment == nil || lastFoundComment.CreatedAt.Before(*comment.UpdatedAt) {
			if lastFoundComment != nil {
				obj.DeleteComment(lastFoundComment)
			}
			lastFoundComment = comment
		}
	}

	return lastFoundComment, nil
}
// Munge is the workhorse the will actually make updates to the PR
func (h AssignUnassignHandler) Munge(obj *github.MungeObject) {
	if !obj.IsPR() {
		return
	}

	comments, err := obj.ListComments()
	if err != nil {
		glog.Errorf("unexpected error getting comments: %v", err)
		return
	}

	fileList, err := obj.ListFiles()
	if err != nil {
		glog.Errorf("Could not list the files for PR %v: %v", obj.Issue.Number, err)
		return
	}

	//get ALL (not just leaf) the people that could potentially own the file based on the blunderbuss.go implementation
	potentialOwners, _ := getPotentialOwners(*obj.Issue.User.Login, h.features, fileList, false)

	toAssign, toUnassign := h.getAssigneesAndUnassignees(obj, comments, fileList, potentialOwners)
	for _, username := range toAssign.List() {
		obj.AssignPR(username)
	}
	obj.UnassignPR(toUnassign.List()...)
}
// Munge is the workhorse the will actually make updates to the PR
func (CommentDeleter) Munge(obj *github.MungeObject) {
	if !obj.IsPR() {
		return
	}

	comments, err := obj.ListComments()
	if err != nil {
		return
	}

	validComments := []githubapi.IssueComment{}
	for i := range comments {
		comment := comments[i]
		if !validComment(comment) {
			continue
		}
		validComments = append(validComments, comment)
	}
	for _, d := range deleters {
		stale := d.StaleComments(obj, validComments)
		for _, comment := range stale {
			obj.DeleteComment(&comment)
		}
	}
}
Beispiel #4
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
			}
		}
	}
}
Beispiel #5
0
func (h *ApprovalHandler) updateNotification(obj *github.MungeObject, ownersMap map[string]sets.String) error {
	notificationMatcher := c.MungerNotificationName(approvalNotificationName)
	comments, err := obj.ListComments()
	if err != nil {
		glog.Error("Could not list the comments for PR%v", obj.Issue.Number)
		return err
	}
	notifications := c.FilterComments(comments, notificationMatcher)
	latestNotification := notifications.GetLast()
	if latestNotification == nil {
		return h.createMessage(obj, ownersMap)
	}
	latestApprove := c.FilterComments(comments, c.CommandName(approveCommand)).GetLast()
	if latestApprove == nil || latestApprove.CreatedAt == nil {
		// there was already a bot notification and nothing has changed since
		// or we wouldn't tell when the latestApproval occurred
		return nil
	}
	if latestApprove.CreatedAt.After(*latestNotification.CreatedAt) {
		// if we can't tell when latestApprove happened, we should make a new one
		obj.DeleteComment(latestNotification)
		return h.createMessage(obj, ownersMap)
	}
	lastModified := obj.LastModifiedTime()
	if latestNotification.CreatedAt.Before(*lastModified) {
		obj.DeleteComment(latestNotification)
		return h.createMessage(obj, ownersMap)
	}
	return nil
}
// Munge is the workhorse the will actually make updates to the PR
func (NagFlakeIssues) Munge(obj *mgh.MungeObject) {
	if obj.IsPR() || !obj.HasLabel("kind/flake") {
		return
	}

	comments, err := obj.ListComments()
	if err != nil {
		glog.Error(err)
		return
	}

	// Use the pinger to notify assignees:
	// - Set time period based on configuration (at the top of this file)
	// - Mention list of assignees as an argument
	// - Start the ping timer after the last HumanActor comment

	// How often should we ping
	period := findTimePeriod(obj.Issue.Labels)

	// Who are we pinging
	who := mungerutil.GetIssueUsers(obj.Issue).Assignees.Mention().Join()

	// When does the pinger start
	startDate := c.LastComment(comments, c.HumanActor(), obj.Issue.CreatedAt)

	// Get a notification if it's time to ping.
	notif := pinger.SetTimePeriod(period).PingNotification(
		comments,
		who,
		startDate,
	)
	if notif != nil {
		obj.WriteComment(notif.String())
	}
}
Beispiel #7
0
// Munge is the workhorse the will actually make updates to the PR
func (CommentDeleter) Munge(obj *github.MungeObject) {
	if !obj.IsPR() {
		return
	}

	comments, err := obj.ListComments()
	if err != nil {
		return
	}

	for i := range comments {
		comment := &comments[i]
		if comment.User == nil || comment.User.Login == nil {
			continue
		}
		if *comment.User.Login != botName {
			continue
		}
		if comment.Body == nil {
			continue
		}
		for _, f := range funcs {
			if f(obj, comment) {
				obj.DeleteComment(comment)
				break
			}
		}
	}
}
Beispiel #8
0
// Priority implements IssueSource
func (p *brokenJobSource) Priority(obj *github.MungeObject) (sync.Priority, error) {
	comments, err := obj.ListComments()
	if err != nil {
		return sync.PriorityP2, fmt.Errorf("Failed to list comment of issue: %v", err)
	}
	// Different IssueSource's Priority calculation may differ
	return autoPrioritize(comments, obj.Issue.CreatedAt), nil
}
Beispiel #9
0
// 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)
}
Beispiel #10
0
func getCommentsAfterLastModified(obj *github.MungeObject) ([]*githubapi.IssueComment, error) {
	afterLastModified := func(opt *githubapi.IssueListCommentsOptions) *githubapi.IssueListCommentsOptions {
		// Only comments updated at or after this time are returned.
		// One possible case is that reviewer might "/lgtm" first, contributor updated PR, and reviewer updated "/lgtm".
		// This is still valid. We don't recommend user to update it.
		lastModified := *obj.LastModifiedTime()
		opt.Since = lastModified
		return opt
	}
	return obj.ListComments(afterLastModified)
}
Beispiel #11
0
// Search through the body and comments to see if the given item is already
// mentioned in the given github issue.
func (s *IssueSyncer) isRecorded(obj *github.MungeObject, source IssueSource) (bool, error) {
	id := source.ID()
	if obj.Issue.Body != nil && strings.Contains(*obj.Issue.Body, id) {
		// We already wrote this item
		return true, nil
	}
	comments, err := obj.ListComments()
	if err != nil {
		return false, fmt.Errorf("error getting comments for %v: %v", *obj.Issue.Number, err)
	}
	for _, c := range comments {
		if c.Body == nil {
			continue
		}
		if strings.Contains(*c.Body, id) {
			// We already wrote this item
			return true, nil
		}
	}
	return false, nil
}
Beispiel #12
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)
				}
			}
		}
	}
}
Beispiel #13
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)
				}
			}
		}
	}
}
Beispiel #14
0
func findLastHumanIssueUpdate(obj *github.MungeObject) (*time.Time, error) {
	lastHuman := obj.Issue.CreatedAt

	comments, err := obj.ListComments()
	if err != nil {
		return nil, err
	}

	for i := range comments {
		comment := comments[i]
		if !validComment(comment) {
			continue
		}
		if mergeBotComment(comment) || jenkinsBotComment(comment) {
			continue
		}
		if lastHuman.Before(*comment.UpdatedAt) {
			lastHuman = comment.UpdatedAt
		}
	}

	return lastHuman, nil
}
Beispiel #15
0
// assigneeActionNeeded returns true if we are waiting on an action from the reviewer.
func isReviewerActionNeeded(obj *github.MungeObject) (bool, error) {
	comments, err := obj.ListComments()
	if err != nil {
		return false, err
	}

	lastAuthorCommentTime := comment.LastComment(comments, comment.Author(*obj.Issue.User), nil)
	lastReviewerCommentTime := getLastReviewerComment(obj, comments)

	if lastReviewerCommentTime == nil {
		// this implies that no reviewer has commented on the PR yet.
		return true, nil
	}

	if obj.LastModifiedTime().After(*lastReviewerCommentTime) {
		return true, nil
	}

	if lastAuthorCommentTime == nil {
		return false, nil
	}

	return lastReviewerCommentTime.Before(*lastAuthorCommentTime), nil
}