Skip to content

Commit f68178c

Browse files
authored
Implement support for requesting reviewers (#104)
1 parent ed97a6d commit f68178c

File tree

11 files changed

+71
-14
lines changed

11 files changed

+71
-14
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,8 @@ echo "gruntwork-io/terragrunt gruntwork-io/terratest" | git-xargs \
424424
| `--max-pr-retries` | The number of seconds to wait between opening serial pull requests. If you are being rate limited, continue to increase this value until rate limiting eases. Default: `3` seconds. | Integer | No |
425425
| `--seconds-to-wait-when-rate-limited` | The number of seconds to pause once git-xargs has detected it has been rate limited. Note that this buffer is in addition to the value of --seconds-between-prs. If you are regularly being rate limited, increase this value until rate limiting eases. Default: `60` seconds. | Integer | No |
426426
| `--no-skip-ci` | By default, git-xargs will prepend \"[skip ci]\" to its commit messages to prevent large git-xargs jobs from creating expensive CI jobs excessively. If you pass the `--no-skip-ci` flag, then git-xargs will not prepend \"[skip ci]\". Default: false, meaning that \"[skip ci]\" will be prepended to commit messages. | Bool | No |
427-
427+
| `--reviewers` | An optional slice of GitHub usernames, separated by commas, to request reviews from after a pull request is successfully opened. Default: empty slice, meaning that no reviewers will be requested. | String | No |
428+
| `--team-reviewers` | An optional slice of GitHub team names, separated by commas, to request reviews from after a pull request is successfully opened. Default: empty slice, meaning that no team reviewers will be requested. IMPORTANT: Please read and understand [the GitHub restrictions](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/requesting-a-pull-request-review) on this functionality before using it! Only certain GitHub organizations / payment plans support this functionality. | String | No |
428429
## Best practices, tips and tricks
429430

430431
### Write your script to run against a single repo

auth/auth.go

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
type githubPullRequestService interface {
1616
Create(ctx context.Context, owner string, name string, pr *github.NewPullRequest) (*github.PullRequest, *github.Response, error)
1717
List(ctx context.Context, owner string, repo string, opts *github.PullRequestListOptions) ([]*github.PullRequest, *github.Response, error)
18+
RequestReviewers(ctx context.Context, owner, repo string, number int, reviewers github.ReviewersRequest) (*github.PullRequest, *github.Response, error)
1819
}
1920

2021
// The go-github package satisfies this Repositories service's interface in production

cmd/git-xargs.go

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ func parseGitXargsConfig(c *cli.Context) (*config.GitXargsConfig, error) {
3232
config.CommitMessage = c.String("commit-message")
3333
config.PullRequestTitle = c.String("pull-request-title")
3434
config.PullRequestDescription = c.String("pull-request-description")
35+
config.Reviewers = c.StringSlice("reviewers")
36+
config.TeamReviewers = c.StringSlice("team-reviewers")
3537
config.ReposFile = c.String("repos")
3638
config.GithubOrg = c.String("github-org")
3739
config.RepoSlice = c.StringSlice("repo")

common/common.go

+10
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ const (
1515
BaseBranchFlagName = "base-branch-name"
1616
PullRequestTitleFlagName = "pull-request-title"
1717
PullRequestDescriptionFlagName = "pull-request-description"
18+
PullRequestReviewersFlagName = "reviewers"
19+
PullRequestTeamReviewersFlagName = "team-reviewers"
1820
SecondsToWaitBetweenPrsFlagName = "seconds-between-prs"
1921
DefaultCommitMessage = "git-xargs programmatic commit"
2022
DefaultPullRequestTitle = "git-xargs programmatic pull request"
@@ -80,6 +82,14 @@ var (
8082
Usage: "The description to add to pull requests opened by git-xargs",
8183
Value: DefaultPullRequestDescription,
8284
}
85+
GenericPullRequestReviewersFlag = cli.StringSliceFlag{
86+
Name: PullRequestReviewersFlagName,
87+
Usage: "A list of GitHub usernames to request reviews from",
88+
}
89+
GenericPullRequestTeamReviewersFlag = cli.StringSliceFlag{
90+
Name: PullRequestTeamReviewersFlagName,
91+
Usage: "A list of GitHub team names to request reviews from",
92+
}
8393
GenericSecondsToWaitFlag = cli.IntFlag{
8494
Name: SecondsToWaitBetweenPrsFlagName,
8595
Usage: "The number of seconds to sleep between pull requests in order to respect GitHub API rate limits. Increase this number if you are being rate limited regularly. Defaults to 12 seconds.",

config/config.go

+8
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ type GitXargsConfig struct {
2424
CommitMessage string
2525
PullRequestTitle string
2626
PullRequestDescription string
27+
Reviewers []string
28+
TeamReviewers []string
2729
ReposFile string
2830
GithubOrg string
2931
RepoSlice []string
@@ -53,6 +55,8 @@ func NewGitXargsConfig() *GitXargsConfig {
5355
CommitMessage: common.DefaultCommitMessage,
5456
PullRequestTitle: common.DefaultPullRequestTitle,
5557
PullRequestDescription: common.DefaultPullRequestDescription,
58+
Reviewers: []string{},
59+
TeamReviewers: []string{},
5660
ReposFile: "",
5761
GithubOrg: "",
5862
RepoSlice: []string{},
@@ -81,3 +85,7 @@ func NewGitXargsTestConfig() *GitXargsConfig {
8185

8286
return config
8387
}
88+
89+
func (c *GitXargsConfig) HasReviewers() bool {
90+
return len(c.Reviewers) > 0 || len(c.TeamReviewers) > 0
91+
}

go.mod

+2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ go 1.14
55
require (
66
github.com/go-git/go-git/v5 v5.3.0
77
github.com/golang/protobuf v1.4.3 // indirect
8+
github.com/google/go-github v17.0.0+incompatible // indirect
89
github.com/google/go-github/v43 v43.0.0
10+
github.com/google/go-github/v48 v48.0.0 // indirect
911
github.com/gruntwork-io/go-commons v0.8.2
1012
github.com/pterm/pterm v0.12.42
1113
github.com/sirupsen/logrus v1.7.0

go.sum

+6
Original file line numberDiff line numberDiff line change
@@ -276,10 +276,16 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
276276
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
277277
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
278278
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
279+
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
280+
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
279281
github.com/google/go-containerregistry v0.0.0-20200110202235-f4fb41bf00a3/go.mod h1:2wIuQute9+hhWqvL3vEI7YB0EKluF4WcPzI1eAliazk=
282+
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
283+
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
280284
github.com/google/go-github/v41 v41.0.0/go.mod h1:XgmCA5H323A9rtgExdTcnDkcqp6S30AVACCBDOonIxg=
281285
github.com/google/go-github/v43 v43.0.0 h1:y+GL7LIsAIF2NZlJ46ZoC/D1W1ivZasT0lnWHMYPZ+U=
282286
github.com/google/go-github/v43 v43.0.0/go.mod h1:ZkTvvmCXBvsfPpTHXnH/d2hP9Y0cTbvN9kr5xqyXOIc=
287+
github.com/google/go-github/v48 v48.0.0 h1:9H5fWVXFK6ZsRriyPbjtnFAkJnoj0WKFtTYfpCRrTm8=
288+
github.com/google/go-github/v48 v48.0.0/go.mod h1:dDlehKBDo850ZPvCTK0sEqTCVWcrGl2LcDiajkYi89Y=
283289
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
284290
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
285291
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=

main.go

+2
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ func setupApp() *cli.App {
7171
common.GenericCommitMessageFlag,
7272
common.GenericPullRequestTitleFlag,
7373
common.GenericPullRequestDescriptionFlag,
74+
common.GenericPullRequestReviewersFlag,
75+
common.GenericPullRequestTeamReviewersFlag,
7476
common.GenericSecondsToWaitFlag,
7577
common.GenericMaxPullRequestRetriesFlag,
7678
common.GenericSecondsToWaitWhenRateLimitedFlag,

mocks/mocks.go

+20-13
Original file line numberDiff line numberDiff line change
@@ -11,41 +11,45 @@ import (
1111
// Mock *github.Repository slice that is returned from the mock Repositories service in test
1212
var ownerName = "gruntwork-io"
1313

14-
var repoName1 = "terragrunt"
15-
var repoName2 = "terratest"
16-
var repoName3 = "fetch"
17-
var repoName4 = "terraform-kubernetes-helm"
14+
var (
15+
repoName1 = "terragrunt"
16+
repoName2 = "terratest"
17+
repoName3 = "fetch"
18+
repoName4 = "terraform-kubernetes-helm"
19+
)
1820

19-
var repoURL1 = "https://github.com/gruntwork-io/terragrunt"
20-
var repoURL2 = "https://github.com/gruntwork-io/terratest"
21-
var repoURL3 = "https://github.com/gruntwork-io/fetch"
22-
var repoURL4 = "https://github.com/gruntwork-io/terraform-kubernetes-helm"
21+
var (
22+
repoURL1 = "https://github.com/gruntwork-io/terragrunt"
23+
repoURL2 = "https://github.com/gruntwork-io/terratest"
24+
repoURL3 = "https://github.com/gruntwork-io/fetch"
25+
repoURL4 = "https://github.com/gruntwork-io/terraform-kubernetes-helm"
26+
)
2327

2428
var archivedFlag = true
2529

2630
var MockGithubRepositories = []*github.Repository{
27-
&github.Repository{
31+
{
2832
Owner: &github.User{
2933
Login: &ownerName,
3034
},
3135
Name: &repoName1,
3236
HTMLURL: &repoURL1,
3337
},
34-
&github.Repository{
38+
{
3539
Owner: &github.User{
3640
Login: &ownerName,
3741
},
3842
Name: &repoName2,
3943
HTMLURL: &repoURL2,
4044
},
41-
&github.Repository{
45+
{
4246
Owner: &github.User{
4347
Login: &ownerName,
4448
},
4549
Name: &repoName3,
4650
HTMLURL: &repoURL3,
4751
},
48-
&github.Repository{
52+
{
4953
Owner: &github.User{
5054
Login: &ownerName,
5155
},
@@ -69,6 +73,10 @@ func (m mockGithubPullRequestService) List(ctx context.Context, owner string, re
6973
return []*github.PullRequest{m.PullRequest}, m.Response, nil
7074
}
7175

76+
func (m mockGithubPullRequestService) RequestReviewers(ctx context.Context, owner, repo string, number int, reviewers github.ReviewersRequest) (*github.PullRequest, *github.Response, error) {
77+
return m.PullRequest, m.Response, nil
78+
}
79+
7280
// This mocks the Repositories service in go-github that is used in production to call the associated GitHub endpoint
7381
type mockGithubRepositoriesService struct {
7482
Repository *github.Repository
@@ -98,7 +106,6 @@ func ConfigureMockGithubClient() auth.GithubClient {
98106
Repository: MockGithubRepositories[0],
99107
Repositories: MockGithubRepositories,
100108
Response: &github.Response{
101-
102109
Response: &http.Response{
103110
StatusCode: 200,
104111
},

repository/repo-operations.go

+15
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,21 @@ func openPullRequest(config *config.GitXargsConfig, pr types.OpenPrRequest) erro
575575
"Pull Request URL": githubPR.GetHTMLURL(),
576576
}).Debug("Successfully opened pull request")
577577

578+
reviewersRequest := github.ReviewersRequest{
579+
NodeID: githubPR.NodeID,
580+
Reviewers: config.Reviewers,
581+
TeamReviewers: config.TeamReviewers,
582+
}
583+
584+
// If the user supplied reviewer information on the pull request, initiate a separate request to ask for reviews
585+
if config.HasReviewers() {
586+
_, _, reviewRequestErr := config.GithubClient.PullRequests.RequestReviewers(context.Background(), *pr.Repo.GetOwner().Login, pr.Repo.GetName(), githubPR.GetNumber(), reviewersRequest)
587+
if reviewRequestErr != nil {
588+
config.Stats.TrackSingle(stats.RequestReviewersErr, pr.Repo)
589+
}
590+
591+
}
592+
578593
if config.Draft {
579594
config.Stats.TrackDraftPullRequest(pr.Repo.GetName(), githubPR.GetHTMLURL())
580595
} else {

stats/stats.go

+3
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ const (
7676
PRFailedDueToRateLimitsErr types.Event = "pr-failed-due-to-rate-limits"
7777
// PRFailedAfterMaximumRetriesErr denotes a repo whose pull requests all failed to be created via GitHub following the maximum number of retries
7878
PRFailedAfterMaximumRetriesErr types.Event = "pr-failed-after-maximum-retries"
79+
// RequestReviewersErr denotes a repo whose follow up request to add reviewers to the opened pull request failed
80+
RequestReviewersErr types.Event = "request-reviewers-error"
7981
)
8082

8183
var allEvents = []types.AnnotatedEvent{
@@ -109,6 +111,7 @@ var allEvents = []types.AnnotatedEvent{
109111
{Event: BaseBranchTargetInvalidErr, Description: "Repos that did not have the branch specified by --base-branch-name"},
110112
{Event: PRFailedDueToRateLimitsErr, Description: "Repos whose initial Pull Request failed to be created due to GitHub rate limits"},
111113
{Event: PRFailedAfterMaximumRetriesErr, Description: "Repos whose Pull Request failed to be created after the maximum number of retries"},
114+
{Event: RequestReviewersErr, Description: "Repos whose request to add reviewers to the opened pull request failed"},
112115
}
113116

114117
// RunStats will be a stats-tracker class that keeps score of which repos were touched, which were considered for update, which had branches made, PRs made, which were missing workflows or contexts, or had out of date workflows syntax values, etc

0 commit comments

Comments
 (0)