// 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 (b *BlockPath) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } if obj.HasLabel(doNotMergeLabel) { return } files, err := obj.ListFiles() if err != nil { return } for _, f := range files { if matchesAny(*f.Filename, b.blockRegexp) { if matchesAny(*f.Filename, b.doNotBlockRegexp) { continue } obj.WriteComment(blockPathBody) obj.AddLabels([]string{doNotMergeLabel}) return } } }
// 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) } }
// 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) } } }
// 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) } }
// 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 // 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 (b *BlunderbussMunger) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } issue := obj.Issue if !b.BlunderbussReassign && issue.Assignee != nil { glog.V(6).Infof("skipping %v: reassign: %v assignee: %v", *issue.Number, b.BlunderbussReassign, github.DescribeUser(issue.Assignee)) return } files, err := obj.ListFiles() if err != nil { return } potentialOwners, weightSum := getPotentialOwners(*obj.Issue.User.Login, b.features, files, true) if len(potentialOwners) == 0 { potentialOwners, weightSum = getPotentialOwners(*obj.Issue.User.Login, b.features, files, false) if len(potentialOwners) == 0 { glog.Errorf("No OWNERS found for PR %d", *issue.Number) return } } printChance(potentialOwners, weightSum) if issue.Assignee != nil { cur := *issue.Assignee.Login c := chance(potentialOwners[cur], weightSum) glog.Infof("Current assignee %v has a %02.2f%% chance of having been chosen", cur, c) } selection := rand.Int63n(weightSum) owner := "" for o, w := range potentialOwners { owner = o selection -= w if selection <= 0 { break } } c := chance(potentialOwners[owner], weightSum) glog.Infof("Assigning %v to %v who had a %02.2f%% chance to be assigned (previously assigned to %v)", *issue.Number, owner, c, github.DescribeUser(issue.Assignee)) obj.AssignPR(owner) }
// 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 files, err := obj.ListFiles() if err != nil { return } for _, f := range 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) } }
// Munge is the workhorse the will actually make updates to the PR func (b *BlunderbussMunger) Munge(obj *github.MungeObject) { if !obj.IsPR() { return } issue := obj.Issue if !b.BlunderbussReassign && issue.Assignee != nil { glog.V(6).Infof("skipping %v: reassign: %v assignee: %v", *issue.Number, b.BlunderbussReassign, github.DescribeUser(issue.Assignee)) return } files, err := obj.ListFiles() if err != nil { return } potentialOwners := weightMap{} weightSum := int64(0) for _, file := range files { fileWeight := int64(1) if file.Changes != nil && *file.Changes != 0 { fileWeight = int64(*file.Changes) } // Judge file size on a log scale-- effectively this // makes three buckets, we shouldn't have many 10k+ // line changes. fileWeight = int64(math.Log10(float64(fileWeight))) + 1 fileOwners := b.features.Repos.LeafAssignees(*file.Filename) if fileOwners.Len() == 0 { glog.Warningf("Couldn't find an owner for: %s", *file.Filename) } if b.features.Aliases != nil && b.features.Aliases.IsEnabled { fileOwners = b.features.Aliases.Expand(fileOwners) } for _, owner := range fileOwners.List() { if owner == *issue.User.Login { continue } potentialOwners[owner] = potentialOwners[owner] + fileWeight weightSum += fileWeight } } if len(potentialOwners) == 0 { glog.Errorf("No owners found for PR %d", *issue.Number) return } printChance(potentialOwners, weightSum) if issue.Assignee != nil { cur := *issue.Assignee.Login c := chance(potentialOwners[cur], weightSum) glog.Infof("Current assignee %v has a %02.2f%% chance of having been chosen", cur, c) } selection := rand.Int63n(weightSum) owner := "" for o, w := range potentialOwners { owner = o selection -= w if selection <= 0 { break } } c := chance(potentialOwners[owner], weightSum) glog.Infof("Assigning %v to %v who had a %02.2f%% chance to be assigned (previously assigned to %v)", *issue.Number, owner, c, github.DescribeUser(issue.Assignee)) obj.AssignPR(owner) }