Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Approval Implementation #1846

Merged

Conversation

grodrigues3
Copy link
Contributor

No description provided.

@k8s-ci-robot k8s-ci-robot added cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Feb 9, 2017
@grodrigues3 grodrigues3 force-pushed the refactor-approvers-implementation branch 2 times, most recently from a3cb0a6 to ad8d40f Compare February 9, 2017 01:39
Copy link
Member

@apelisse apelisse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm done for now, I think this can still be vastly simplified

for _, fn := range o.filenames {
owners.Insert(o.repo.FindOwnersForPath(fn))
}
files := owners.List()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sets.String.List() returns a sorted list, no need to re-sort.


func (o Owners) IsApproved(currentlyApproved sets.String) bool {
fnMap := o.GetApprovers() //ownersfiles -> Relevant Approvers
for _, file := range o.ListOwnersFiles() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why don't you loop over fnmap?


// removeSubdirs takes a list of directories as an input and returns a set of directories with all
// subdirectories removed. E.g. [/a,/a/b/c,/d/e,/d/e/f] -> [/a, /d/e]
func removeSubdirs(dirList []string) sets.String {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not used yet?

}
files := owners.List()
sort.Strings(files)
return files
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should return the set rather than the list here.

copyOfFiles := sets.NewString()
for _, fn := range o.filenames {
copyOfFiles.Insert(fn)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be replaced by ListOwnersFiles (especially if we make it return a set)

for approver := range approverOwnersfiles {
sliceOfKeys = append(sliceOfKeys, approver)
}
sort.Strings(sliceOfKeys)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe make a function for that GetAllPotentialApprovers()

people = append(people, sliceOfKeys[order[i]])
}

files = []sets.String{}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The following code then can be removed

sliceOfKeys = append(sliceOfKeys, approver)
}
sort.Strings(sliceOfKeys)
order := rand.Perm(len(approverOwnersfiles))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These 4 lines are the only thing left

toDelete := sets.NewString()
// remove all files in the directories that our approver approved AND
// in the subdirectories that s/he approved. HasPrefix finds subdirs
for fn := range copyOfFiles {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't exactly get that.

At this point you have the suggested person, don't you just want to remove from copyOfFiles (which is a terrible name) the list of files that person can approve? copyOfFiles = copyOfFiles.Difference(filesCanApprove[bestPerson]) and remove a bunch of temporary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suppose for some reason we choose Tim or Brian as the best person. They are only in /OWNERS but not /pkg/api/OWNERS. Checking the prefix makes sure that we not only include the fact that they cover the root, but also all subdirs. Does that make sense?

var bestPerson string
var bestPersonFiles sets.String
for copyOfFiles.Len() > 0 {
maxCovered := 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The following 8-10 lines is basically: FindPersonWithBiggestCoverage(toBeApproved, randomizedApprovers, reverseMap) (person string)

@apelisse
Copy link
Member

apelisse commented Feb 9, 2017

Many functions are really only related to finding the list of suggested approvers, I'm wondering if it's a sign that we should cut a new type there.

revMap := o.GetReverseMap()

setToCover := sets.NewString()
for _, fn := range o.filenames {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better variable name?


approverGroup := sets.NewString()
for setToCover.Len() > 0 {
bestPerson := findPersonWithBiggestCoverage(setToCover, randomizedApprovers, revMap)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

turned it into a function.

// IsApproved returns a bool indicating if the entire PR is approved
func (o Owners) IsApproved(currentlyApproved sets.String) bool {
//o.GetApprovers returns map ownersfiles -> Relevant Approvers
for _, approvers := range o.GetApprovers() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't right. o.GetApprovers returns the leafApprovers only. We also need to check further up the tree.

@grodrigues3
Copy link
Contributor Author

@apelisse ping. what do you think about the tests?

@grodrigues3 grodrigues3 force-pushed the refactor-approvers-implementation branch 2 times, most recently from b422f66 to 4123a36 Compare February 10, 2017 18:40
unapprovedFiles := o.UnapprovedFiles(alreadyApproved)
for _, path := range o.GetOwnersSet().List() {
fullOwnersPath := filepath.Join(path, ownersFileName)
approverSet := o.repo.Approvers(path).Intersection(alreadyApproved)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I create a method attached to owners that returns a map from ownersFiles -> people that have approved them?

}
context.WriteString("\n")
suggestedApprovers := o.GetSuggestedApprovers()
toBeAssigned := sets.NewString(suggestedApprovers...).Difference(alreadyApproved)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function for getting people that should be assigned or is it okay to have the logic here?

@grodrigues3 grodrigues3 force-pushed the refactor-approvers-implementation branch from 64daca8 to b3ebae1 Compare February 16, 2017 01:11

type UnapprovedFile struct {
filepath string
approvers sets.String
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not needed, that's the beauty of it.

// - how an approver can indicate their approval
// - how an approver can cancel their approval
func getMessage(ap Approvers, org, project string) string {
context := bytes.NewBufferString("The following people have approved this PR: ")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: can we turn that into a big string?

testName: "Single Root File PR No One Approved",
filenames: []string{"kubernetes.go"},
currentlyApproved: sets.NewString(),
expectedUnapproved: sets.NewString(""),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty string means root, that's kind of weird

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, but that's what github sends us back. E.g the makefile in the root dir is listed as Makefile not /Makefile

testName: "Combo and Other; Combo Approved",
filenames: []string{"a/combo/test.go", "a/d/test.go"},
currentlyApproved: eApprovers,
expectedUnapproved: sets.NewString("a/d"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading this test is far from obvious. eApprovers has approved, and somehow a/combo is approved ... Following the top structure helps, but it's still puzzling

testName: "B Only UnApproved",
filenames: []string{"b/test_1.go", "b/test.go"},
currentlyApproved: bApprovers,
expectedUnapproved: sets.NewString(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a test for:

filenames: []string{"b/test_1.go", "b/test.go"},
currentlyApproved: rootApprovers,

{
testName: "Combo and Other; Both Approved",
filenames: []string{"a/combo/test.go", "a/d/test.go"},
currentlyApproved: edcApprovers.Intersection(dApprovers),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK I have no idea what this is. Isn't ecdApprovers.Intersection(dApprovers) equal to dApprovers?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah it is. I added that so it's clear that the dApprovers are present in both sets (otherwise the intersection would be empty set. better to just be dApprovers?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WDYT?

filenames: []string{"a/combo/test.go", "a/d/test.go", "c/test"},
currentlyApproved: cApprovers,
expectedFiles: []File{ApprovedFile{"a/combo", cApprovers}, UnapprovedFile{"a/d"}, ApprovedFile{"c", cApprovers}},
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have no test where a group of people approve a dir, and another group approves another dir, and maybe a group approves multiple dirs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this. Can you elaborate more?

Copy link
Member

@apelisse apelisse Feb 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Look at your tests, you always have only one set of approvers.
You don't have a test like:

filenames: []string{"a/something", "b/something"}
currentlyApproved: aApprovers, bApprovers

filenames []string
currentlyApproved sets.String
// testSeed affects who is chosen for CC
testSeed int64
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there any test where the seed has an impact? Are you testing that different seeds have different results?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, the GetCCs test

func (ap Approvers) UnapprovedFiles() sets.String {
unapproved := sets.NewString()
for fn := range ap.owners.GetOwnersSet() {
approvers := ap.owners.repo.Approvers(fn)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really don't think you should access members from owners directly

@grodrigues3 grodrigues3 force-pushed the refactor-approvers-implementation branch 2 times, most recently from 92f37a9 to e981108 Compare February 21, 2017 18:21
@@ -159,7 +143,7 @@ func getApproveComments(comments []*githubapi.IssueComment) c.FilteredComments {
return c.FilterComments(comments, c.Or{approverMatcher, lgtmMatcher})
}

func (h *ApprovalHandler) updateNotification(obj *github.MungeObject, ownersMap map[string]sets.String, approverSet sets.String, isFullyApproved bool) error {
func (h *ApprovalHandler) updateNotification(obj *github.MungeObject, approversHandler approvers.Approvers) error {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how do we fix this?

@grodrigues3 grodrigues3 force-pushed the refactor-approvers-implementation branch 2 times, most recently from 4d68cf5 to 0b2a82d Compare February 24, 2017 18:47
@grodrigues3 grodrigues3 force-pushed the refactor-approvers-implementation branch from 0b2a82d to 6bfbe72 Compare March 1, 2017 17:23
expectedUnapproved: sets.NewString(),
},
{
testName: "B Files Approved at Root",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the root test

@grodrigues3
Copy link
Contributor Author

@k8s-bot bazel test this

@grodrigues3 grodrigues3 force-pushed the refactor-approvers-implementation branch from 73f771c to b2f5f77 Compare March 1, 2017 18:42
@grodrigues3 grodrigues3 force-pushed the refactor-approvers-implementation branch from b2f5f77 to 2e02db9 Compare March 1, 2017 18:46
@apelisse
Copy link
Member

apelisse commented Mar 1, 2017

Once you've validated on your repo that everything is fine, please feel free to merge.

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Mar 1, 2017
- Redesign with multiple classes each with specific behaviors
- Modular design and comprehensive testing
@grodrigues3 grodrigues3 force-pushed the refactor-approvers-implementation branch from d9dcab9 to 8d1d807 Compare March 1, 2017 21:57
@grodrigues3 grodrigues3 merged commit 1059565 into kubernetes:master Mar 1, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants