Example #1
0
// Munge is the workhorse that will actually close the PRs
func (CloseStalePR) Munge(obj *github.MungeObject) {
	if !obj.IsPR() {
		return
	}

	if obj.HasLabel(keepOpenLabel) {
		return
	}

	lastModif, err := findLastModificationTime(obj)
	if err != nil {
		glog.Errorf("Failed to find last modification: %v", err)
		return
	}

	closeIn := -time.Since(lastModif.Add(stalePullRequest))
	inactiveFor := time.Since(*lastModif)
	if closeIn <= 0 {
		closePullRequest(obj, inactiveFor)
	} else if closeIn <= startWarning {
		checkAndWarn(obj, inactiveFor, closeIn)
	} else {
		// Pull-request is active. Remove previous potential warning
		// Ignore potential errors, we just want to remove old comments ...
		comment, _ := findLatestWarningComment(obj)
		if comment != nil {
			obj.DeleteComment(comment)
		}
	}
}
Example #2
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
}
Example #3
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 (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)
		}
	}
}
Example #5
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
			}
		}
	}
}
Example #6
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
			}
		}
	}
}
Example #7
0
func closePullRequest(obj *github.MungeObject, inactiveFor time.Duration) {
	comment := findLatestWarningComment(obj)
	if comment != nil {
		obj.DeleteComment(comment)
	}

	obj.WriteComment(fmt.Sprintf(closingComment, durationToDays(inactiveFor)))
	obj.ClosePR()
}
func closePullRequest(obj *github.MungeObject, inactiveFor time.Duration) {
	mention := mungerutil.GetIssueUsers(obj.Issue).AllUsers().Mention().Join()
	if mention != "" {
		mention = "cc " + mention + "\n"
	}

	comment := findLatestWarningComment(obj)
	if comment != nil {
		obj.DeleteComment(comment)
	}

	obj.WriteComment(fmt.Sprintf(closingComment, durationToDays(inactiveFor), mention))
	obj.ClosePR()
}
func checkAndWarn(obj *github.MungeObject, inactiveFor time.Duration, closeIn time.Duration) {
	if closeIn < day {
		// We are going to close the PR in less than a day. Too late to warn
		return
	}
	comment := findLatestWarningComment(obj)
	if comment == nil {
		// We don't already have the comment. Post it
		postWarningComment(obj, inactiveFor, closeIn)
	} else if time.Since(*comment.UpdatedAt) > remindWarning {
		// It's time to warn again
		obj.DeleteComment(comment)
		postWarningComment(obj, inactiveFor, closeIn)
	} else {
		// We already have a warning, and it's not expired. Do nothing
	}
}
Example #10
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 #11
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)
				}
			}
		}
	}
}
// getAssigneesAndUnassignees checks to see when someone comments "/assign" or "/unassign"
// returns two sets.String
// 1. github handles to be assigned
// 2. github handles to be unassigned
// Note* Could possibly assign directly in the function call, but easier to test if function returns toAssign, toUnassign
func (h *AssignUnassignHandler) getAssigneesAndUnassignees(obj *github.MungeObject, comments []*githubapi.IssueComment, fileList []*githubapi.CommitFile, potentialOwners weightMap) (toAssign, toUnassign sets.String) {
	toAssign = sets.String{}
	toUnassign = sets.String{}

	assignComments := c.FilterComments(comments, c.CommandName(assignCommand))
	unassignComments := c.FilterComments(comments, c.CommandName(unassignCommand))
	invalidUsers := sets.String{}

	//collect all the people that should be assigned
	for _, cmt := range assignComments {
		if isValidReviewer(potentialOwners, cmt.User) {
			obj.DeleteComment(cmt)
			toAssign.Insert(*cmt.User.Login)
		} else {
			// build the set of people who asked to be assigned but aren't in reviewers
			// use the @ as a prefix so github notifies invalid users
			invalidUsers.Insert("@" + *cmt.User.Login)
		}

	}

	// collect all the people that should be unassigned
	for _, cmt := range unassignComments {
		if isAssignee(obj.Issue.Assignees, cmt.User) {
			obj.DeleteComment(cmt)
			toUnassign.Insert(*cmt.User.Login)
		}
	}

	// Create a notification if someone tried to self assign, but could not because they weren't in the owners files
	if invalidUsers.Len() != 0 {
		previousNotifications := c.FilterComments(comments, c.MungerNotificationName(invalidReviewer))
		if assignComments.Empty() || (!previousNotifications.Empty() && previousNotifications.GetLast().CreatedAt.After(*assignComments.GetLast().CreatedAt)) {
			// if there were no assign comments, no need to notify
			// if the last notification happened after the last assign comment, no need to notify again
			return toAssign, toUnassign
		}
		if !previousNotifications.Empty() {
			for _, c := range previousNotifications {
				obj.DeleteComment(c)
			}
		}
		context := bytes.NewBufferString("The following people cannot be assigned because they are not in the OWNERS files\n")
		for user := range invalidUsers {
			context.WriteString(fmt.Sprintf("- %s\n", user))
		}
		context.WriteString("\n")
		c.Notification{Name: invalidReviewer, Arguments: "", Context: context.String()}.Post(obj)

	}
	return toAssign, toUnassign
}