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

Add label option #135

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -436,6 +436,7 @@ echo "gruntwork-io/terragrunt gruntwork-io/terratest" | git-xargs \
| `--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 |
| `--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 |
| `--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 |
| `--label` | By default, the PR is opened without any labels. Use this flag multiple times to specify multiple labels that should be attached to the PR upon creation (e.g., --label bug --label enhancement). | String | No |
| `--keep-cloned-repositories` | By default, git-xargs will delete the repositories it clones to your temporary file directory once it has completed processing that repo, to save space on your machine. If you wish to retain the local repositories, pass this flag. | Bool | No |
## Best practices, tips and tricks

6 changes: 6 additions & 0 deletions auth/auth.go
Original file line number Diff line number Diff line change
@@ -24,6 +24,10 @@ type githubRepositoriesService interface {
ListByOrg(ctx context.Context, org string, opts *github.RepositoryListByOrgOptions) ([]*github.Repository, *github.Response, error)
}

type githubIssuesService interface {
AddLabelsToIssue(ctx context.Context, owner string, repo string, number int, labels []string) ([]*github.Label, *github.Response, error)
}

// GithubClient is the data structure that is common between production code and test code. In production code,
// go-github satisfies the PullRequests and Repositories service interfaces, whereas in test the concrete
// implementations for these same services are mocks that return a static slice of pointers to GitHub repositories,
@@ -32,12 +36,14 @@ type githubRepositoriesService interface {
type GithubClient struct {
PullRequests githubPullRequestService
Repositories githubRepositoriesService
Issues githubIssuesService
}

func NewClient(client *github.Client) GithubClient {
return GithubClient{
PullRequests: client.PullRequests,
Repositories: client.Repositories,
Issues: client.Issues,
}
}

1 change: 1 addition & 0 deletions cmd/git-xargs.go
Original file line number Diff line number Diff line change
@@ -34,6 +34,7 @@ func parseGitXargsConfig(c *cli.Context) (*config.GitXargsConfig, error) {
config.PullRequestDescription = c.String("pull-request-description")
config.Reviewers = c.StringSlice("reviewers")
config.TeamReviewers = c.StringSlice("team-reviewers")
config.PullRequestLabelSlice = c.StringSlice("label")
config.ReposFile = c.String("repos")
config.GithubOrg = c.String("github-org")
config.RepoSlice = c.StringSlice("repo")
5 changes: 5 additions & 0 deletions common/common.go
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ const (
PullRequestDescriptionFlagName = "pull-request-description"
PullRequestReviewersFlagName = "reviewers"
PullRequestTeamReviewersFlagName = "team-reviewers"
PullRequestLabelFlagName = "label"
SecondsToWaitBetweenPrsFlagName = "seconds-between-prs"
DefaultCommitMessage = "git-xargs programmatic commit"
DefaultPullRequestTitle = "git-xargs programmatic pull request"
@@ -91,6 +92,10 @@ var (
Name: PullRequestTeamReviewersFlagName,
Usage: "A list of GitHub team names to request reviews from",
}
GenericPullRequestLabelFlag = cli.StringSliceFlag{
Name: PullRequestLabelFlagName,
Usage: "A single GitHub label to add to the pull request. Can be invoked multiple times with different labels",
}
GenericSecondsToWaitFlag = cli.IntFlag{
Name: SecondsToWaitBetweenPrsFlagName,
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.",
6 changes: 6 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@ type GitXargsConfig struct {
PullRequestDescription string
Reviewers []string
TeamReviewers []string
PullRequestLabelSlice []string
ReposFile string
GithubOrg string
RepoSlice []string
@@ -58,6 +59,7 @@ func NewGitXargsConfig() *GitXargsConfig {
PullRequestDescription: common.DefaultPullRequestDescription,
Reviewers: []string{},
TeamReviewers: []string{},
PullRequestLabelSlice: []string{},
ReposFile: "",
GithubOrg: "",
RepoSlice: []string{},
@@ -91,3 +93,7 @@ func NewGitXargsTestConfig() *GitXargsConfig {
func (c *GitXargsConfig) HasReviewers() bool {
return len(c.Reviewers) > 0 || len(c.TeamReviewers) > 0
}

func (c *GitXargsConfig) HasLabels() bool {
return len(c.PullRequestLabelSlice) > 0
}
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -73,6 +73,7 @@ func setupApp() *cli.App {
common.GenericPullRequestDescriptionFlag,
common.GenericPullRequestReviewersFlag,
common.GenericPullRequestTeamReviewersFlag,
common.GenericPullRequestLabelFlag,
common.GenericSecondsToWaitFlag,
common.GenericMaxPullRequestRetriesFlag,
common.GenericSecondsToWaitWhenRateLimitedFlag,
13 changes: 13 additions & 0 deletions mocks/mocks.go
Original file line number Diff line number Diff line change
@@ -92,6 +92,15 @@ func (m mockGithubRepositoriesService) ListByOrg(ctx context.Context, org string
return m.Repositories, m.Response, nil
}

type mockGithubIssuesService struct {
Issue *github.Issue
Response *github.Response
}

func (m mockGithubIssuesService) AddLabelsToIssue(ctx context.Context, owner string, repo string, number int, labels []string) ([]*github.Label, *github.Response, error) {
return []*github.Label{}, m.Response, nil
}

// ConfigureMockGithubClient returns a valid GithubClient configured for testing purposes, complete with the mocked services
func ConfigureMockGithubClient() auth.GithubClient {
// Call the same NewClient method that is used by the actual CLI to obtain a GitHub client that calls the
@@ -126,6 +135,10 @@ func ConfigureMockGithubClient() auth.GithubClient {
},
Response: &github.Response{},
}
client.Issues = mockGithubIssuesService{
Issue: &github.Issue{},
Response: &github.Response{},
}

return client
}
9 changes: 9 additions & 0 deletions repository/repo-operations.go
Original file line number Diff line number Diff line change
@@ -594,6 +594,15 @@ func openPullRequest(config *config.GitXargsConfig, pr types.OpenPrRequest) erro

}

// if the user supplied label information on the pull request, initiate a separate request to add label(s)
if config.HasLabels() {
_, _, labelRequestErr := config.GithubClient.Issues.AddLabelsToIssue(context.Background(), *pr.Repo.GetOwner().Login, pr.Repo.GetName(), githubPR.GetNumber(), config.PullRequestLabelSlice)
if labelRequestErr != nil {
config.Stats.TrackSingle(stats.AddLabelsToIssueErr, pr.Repo)
}

}

if config.Draft {
config.Stats.TrackDraftPullRequest(pr.Repo.GetName(), githubPR.GetHTMLURL())
} else {
3 changes: 3 additions & 0 deletions stats/stats.go
Original file line number Diff line number Diff line change
@@ -78,6 +78,8 @@ const (
PRFailedAfterMaximumRetriesErr types.Event = "pr-failed-after-maximum-retries"
// RequestReviewersErr denotes a repo whose follow up request to add reviewers to the opened pull request failed
RequestReviewersErr types.Event = "request-reviewers-error"
// AddLabelsToIssueErr denotes a repo whose follow up request to add labels to the opened pull request failed
AddLabelsToIssueErr types.Event = "add-labels-to-issue-error"
)

var allEvents = []types.AnnotatedEvent{
@@ -112,6 +114,7 @@ var allEvents = []types.AnnotatedEvent{
{Event: PRFailedDueToRateLimitsErr, Description: "Repos whose initial Pull Request failed to be created due to GitHub rate limits"},
{Event: PRFailedAfterMaximumRetriesErr, Description: "Repos whose Pull Request failed to be created after the maximum number of retries"},
{Event: RequestReviewersErr, Description: "Repos whose request to add reviewers to the opened pull request failed"},
{Event: AddLabelsToIssueErr, Description: "Repos whose request to add labels to the opened pull request failed"},
}

// 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