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 } }
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 } }
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 } }
// 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) }
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 } }