// 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()...)
}
Esempio n. 2
0
// 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
		}
	}
}
Esempio n. 3
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)
	}
}
Esempio n. 4
0
// 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)
		}
	}
}
Esempio n. 5
0
// 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)
	}
}
Esempio n. 6
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)
	}
}
Esempio n. 7
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)
}
Esempio n. 8
0
// 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)
}
Esempio n. 9
0
// 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)
	}
}
Esempio n. 10
0
// 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)
}