Skip to content

Commit 46700ad

Browse files
committed
Support outside collaborators to owners check
1 parent 84b4fa2 commit 46700ad

File tree

1 file changed

+43
-3
lines changed

1 file changed

+43
-3
lines changed

internal/check/valid_owner.go

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ type ValidOwner struct {
4848
orgName string
4949
orgTeams []*github.Team
5050
orgRepoName string
51+
outsideCollaborators map[string]struct{}
5152
ignOwners map[string]struct{}
5253
allowUnownedPatterns bool
5354
ownersMustBeTeams bool
@@ -297,6 +298,12 @@ func (v *ValidOwner) validateGitHubUser(ctx context.Context, name string) *valid
297298
}
298299
}
299300

301+
if v.outsideCollaborators == nil { // TODO(mszostok): lazy init, make it more robust.
302+
if err := v.initOutsideCollaboratorsList(ctx); err != nil {
303+
return newValidateError("Cannot initialize outside collaborators list: %v", err).AsPermanent()
304+
}
305+
}
306+
300307
userName := strings.TrimPrefix(name, "@")
301308
_, _, err := v.ghClient.Users.Get(ctx, userName)
302309
if err != nil { // TODO(mszostok): implement retry?
@@ -314,15 +321,18 @@ func (v *ValidOwner) validateGitHubUser(ctx context.Context, name string) *valid
314321
}
315322

316323
_, isMember := (*v.orgMembers)[userName]
317-
if !isMember {
318-
return newValidateError("User %q is not a member of the organization", name)
324+
_, isOutsideCollaborator := (v.outsideCollaborators)[userName]
325+
if !(isMember || isOutsideCollaborator) {
326+
return newValidateError("The user %q is neither a collaborator nor a member of this repository.", name)
319327
}
320328

321329
return nil
322330
}
323331

324332
// There is a method to check if user is a org member
325-
// client.Organizations.IsMember(context.Background(), "org-name", "user-name")
333+
//
334+
// client.Organizations.IsMember(context.Background(), "org-name", "user-name")
335+
//
326336
// But latency is too huge for checking each single user independent
327337
// better and faster is to ask for all members and cache them.
328338
func (v *ValidOwner) initOrgListMembers(ctx context.Context) error {
@@ -351,6 +361,36 @@ func (v *ValidOwner) initOrgListMembers(ctx context.Context) error {
351361
return nil
352362
}
353363

364+
// Add all outside collaborators who are part of the repository to
365+
//
366+
// outsideCollaborators *map[string]struct{}
367+
func (v *ValidOwner) initOutsideCollaboratorsList(ctx context.Context) error {
368+
opt := &github.ListCollaboratorsOptions{
369+
ListOptions: github.ListOptions{PerPage: 100},
370+
Affiliation: "outside",
371+
}
372+
373+
var allMembers []*github.User
374+
for {
375+
collaborators, resp, err := v.ghClient.Repositories.ListCollaborators(ctx, v.orgName, v.orgRepoName, opt)
376+
if err != nil {
377+
return err
378+
}
379+
allMembers = append(allMembers, collaborators...)
380+
if resp.NextPage == 0 {
381+
break
382+
}
383+
opt.Page = resp.NextPage
384+
}
385+
386+
v.outsideCollaborators = map[string]struct{}{}
387+
for _, u := range allMembers {
388+
(v.outsideCollaborators)[u.GetLogin()] = struct{}{}
389+
}
390+
391+
return nil
392+
}
393+
354394
// Name returns human-readable name of the validator
355395
func (ValidOwner) Name() string {
356396
return "Valid Owner Checker"

0 commit comments

Comments
 (0)