From c79d20c3f1f9ff5263d5d6f46745a09b319bf992 Mon Sep 17 00:00:00 2001 From: Galo Navarro Date: Mon, 2 Sep 2024 22:52:45 +0200 Subject: [PATCH] Support author-in-team Signed-off-by: Galo Navarro --- README.md | 12 +++++++++ cmd/action.go | 7 ++++++ pkg/condition_author_in_team.go | 23 +++++++++++++++++ pkg/labeler.go | 9 ++++--- pkg/labeler_test.go | 44 +++++++++++++++++++++++++++++++++ 5 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 pkg/condition_author_in_team.go diff --git a/README.md b/README.md index 922baa1..456511e 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ configurable matching rules. Available conditions: * [Age](#age): label based on the age of a PR or Issue. * [Author can merge](#author-can-merge): label based on whether the author can merge the PR +* [Author is member of team](#author-in-team): label based on whether the author is an active member of the given team * [Authors](#authors): label based on the PR/Issue authors * [Base branch](#base-branch): label based on the PR's base branch name * [Body](#body): label based on the PR/Issue body @@ -339,6 +340,17 @@ This is implemented by checking if the author is an owner of the repo. ```yaml author-can-merge: True ``` + + +### Author is member (PRs and Issues) + +This condition is satisfied when the author of the PR is an active +member of the given team. + +```yaml +author-is-member: CoreTeam +``` + ### Authors (PRs and Issues) This condition is satisfied when the author of the PR or Issue matches diff --git a/cmd/action.go b/cmd/action.go index 97a1c66..137be2d 100644 --- a/cmd/action.go +++ b/cmd/action.go @@ -244,6 +244,13 @@ func newLabeler(gh *github.Client, config *labeler.LabelerConfigV1) *labeler.Lab owner, repo, &github.PullRequestListOptions{}) return prs, err }, + IsUserMemberOfTeam: func(user, team string) (bool, error) { + membership, _, err := gh.Organizations.GetOrgMembership(ctx, user, team) + if err != nil { + return false, err + } + return membership.GetState() == "active", nil + }, }, Client: labeler.NewDefaultHttpClient(), } diff --git a/pkg/condition_author_in_team.go b/pkg/condition_author_in_team.go new file mode 100644 index 0000000..96c53d9 --- /dev/null +++ b/pkg/condition_author_in_team.go @@ -0,0 +1,23 @@ +package labeler + +import ( + "fmt" +) + +func AuthorInTeamCondition(l *Labeler) Condition { + return Condition{ + GetName: func() string { + return "Author is member of team" + }, + CanEvaluate: func(target *Target) bool { + return true + }, + Evaluate: func(target *Target, matcher LabelMatcher) (bool, error) { + if len(matcher.AuthorInTeam) <= 0 { + return false, fmt.Errorf("author-in-team is not set in config") + } + // check if author is a member of team + return l.GitHubFacade.IsUserMemberOfTeam(target.Author, matcher.AuthorInTeam) + }, + } +} diff --git a/pkg/labeler.go b/pkg/labeler.go index 21250b5..1f61624 100644 --- a/pkg/labeler.go +++ b/pkg/labeler.go @@ -22,6 +22,7 @@ type LabelMatcher struct { Age string AuthorCanMerge string `yaml:"author-can-merge"` Authors []string + AuthorInTeam string `yaml:"author-in-team"` BaseBranch string `yaml:"base-branch"` Body string Branch string @@ -65,9 +66,10 @@ type LabelUpdates struct { // Just to make this mockable.. type GitHubFacade struct { - GetRawDiff func(owner, repo string, prNumber int) (string, error) - ListIssuesByRepo func(owner, repo string) ([]*gh.Issue, error) - ListPRs func(owner, repo string) ([]*gh.PullRequest, error) + GetRawDiff func(owner, repo string, prNumber int) (string, error) + ListIssuesByRepo func(owner, repo string) ([]*gh.Issue, error) + ListPRs func(owner, repo string) ([]*gh.PullRequest, error) + IsUserMemberOfTeam func(user, team string) (bool, error) } type Labeler struct { @@ -225,6 +227,7 @@ func (l *Labeler) findMatches(target *Target, config *LabelerConfigV1) (LabelUpd AgeCondition(l), AuthorCondition(), AuthorCanMergeCondition(), + AuthorInTeamCondition(l), BaseBranchCondition(), BodyCondition(), BranchCondition(), diff --git a/pkg/labeler_test.go b/pkg/labeler_test.go index 6a24c92..50316b6 100644 --- a/pkg/labeler_test.go +++ b/pkg/labeler_test.go @@ -983,6 +983,46 @@ func TestHandleEvent(t *testing.T) { initialLabels: []string{"Meh"}, expectedLabels: []string{"Meh", "Test"}, }, + { + event: "issues", + payloads: []string{"issue_open"}, + name: "Add a label to issue when author is in team", + config: LabelerConfigV1{ + Version: 1, + Labels: []LabelMatcher{ + { + Label: "ShouldAppear", + AuthorInTeam: "team-with-srvaroa", + }, + { + Label: "ShouldNotAppear", + AuthorInTeam: "team-with", + }, + }, + }, + initialLabels: []string{"Meh"}, + expectedLabels: []string{"Meh", "ShouldAppear"}, + }, + { + event: "pull_request", + payloads: []string{"create_pr"}, + name: "Add a label to PR when author is in team", + config: LabelerConfigV1{ + Version: 1, + Labels: []LabelMatcher{ + { + Label: "ShouldAppear", + AuthorInTeam: "team-with-srvaroa", + }, + { + Label: "ShouldNotAppear", + AuthorInTeam: "team-with", + }, + }, + }, + initialLabels: []string{"Meh"}, + expectedLabels: []string{"Meh", "ShouldAppear"}, + }, } for _, tc := range testCases { @@ -1034,6 +1074,10 @@ func NewTestLabeler(t *testing.T, tc TestCase) Labeler { data, err := ioutil.ReadAll(file) return string(data), nil }, + // Will return true whenever team contains the given user name + IsUserMemberOfTeam: func(user, team string) (bool, error) { + return strings.Contains(team, user), nil + }, }, } }