Skip to content

Commit

Permalink
feat: add possibility to add assignees to pull request (lindell#194)
Browse files Browse the repository at this point in the history
Add parameter assignees (-a) to the pull request command that allows to specify assignees usernames that should be added to the created pull request.
  • Loading branch information
Bartosz Behring committed Oct 14, 2021
1 parent df64360 commit 6d18909
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 23 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,7 @@ Flags:
-P, --project strings The name, including owner of a GitLab project in the format "ownerName/repoName".
-R, --repo strings The name, including owner of a GitHub repository in the format "ownerName/repoName".
-r, --reviewers strings The username of the reviewers to be added on the pull request.
-a, --assignees strings The username of the assignees to be added on the pull request.
--skip-pr Skip pull request and directly push to the branch.
-T, --token string The GitHub/GitLab personal access token. Can also be set using the GITHUB_TOKEN/GITLAB_TOKEN/GITEA_TOKEN/BITBUCKET_SERVER_TOKEN environment variable.
-U, --user strings The name of a user. All repositories owned by that user will be used.
Expand Down
3 changes: 3 additions & 0 deletions cmd/cmd-run.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func RunCmd() *cobra.Command {
cmd.Flags().BoolP("dry-run", "d", false, "Run without pushing changes or creating pull requests.")
cmd.Flags().StringP("author-name", "", "", "Name of the committer. If not set, the global git config setting will be used.")
cmd.Flags().StringP("author-email", "", "", "Email of the committer. If not set, the global git config setting will be used.")
cmd.Flags().StringSliceP("assignees", "a", nil, "The username of the assignees to be added on the pull request.")
configureGit(cmd)
configurePlatform(cmd)
configureRunPlatform(cmd, true)
Expand Down Expand Up @@ -74,6 +75,7 @@ func run(cmd *cobra.Command, args []string) error {
authorName, _ := flag.GetString("author-name")
authorEmail, _ := flag.GetString("author-email")
strOutput, _ := flag.GetString("output")
assignees, _ := flag.GetStringSlice("assignees")

if concurrent < 1 {
return errors.New("concurrent runs can't be less than one")
Expand Down Expand Up @@ -168,6 +170,7 @@ func run(cmd *cobra.Command, args []string) error {
SkipPullRequest: skipPullRequest,
CommitAuthor: commitAuthor,
BaseBranch: baseBranchName,
Assignees: assignees,

Concurrent: concurrent,

Expand Down
1 change: 1 addition & 0 deletions internal/git/pullrequest.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type NewPullRequest struct {
Base string

Reviewers []string // The username of all reviewers
Assignees []string
}

// PullRequestStatus is the status of a pull request, including statuses of the last commit
Expand Down
2 changes: 2 additions & 0 deletions internal/multigitter/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ type Runner struct {
DryRun bool
CommitAuthor *git.CommitAuthor
BaseBranch string // The base branch of the PR, use default branch if not set
Assignees []string

Concurrent int
SkipPullRequest bool // If set, the script will run directly on the base-branch without creating any PR
Expand Down Expand Up @@ -278,6 +279,7 @@ func (r *Runner) runSingleRepo(ctx context.Context, repo git.Repository) (git.Pu
Head: r.FeatureBranch,
Base: baseBranch,
Reviewers: getReviewers(r.Reviewers, r.MaxReviewers),
Assignees: r.Assignees,
})
if err != nil {
return nil, err
Expand Down
44 changes: 30 additions & 14 deletions internal/scm/bitbucketserver/bitbucket_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,26 +246,21 @@ func (b *BitbucketServer) CreatePullRequest(ctx context.Context, repo git.Reposi

client := newClient(ctx, b.config)

var usersWithMetadata []bitbucketv1.UserWithMetadata
for _, reviewer := range newPR.Reviewers {
response, err := client.DefaultApi.GetUser(reviewer)
if err != nil {
return nil, err
}

var userWithLinks bitbucketv1.UserWithLinks
err = mapstructure.Decode(response.Values, &userWithLinks)
if err != nil {
return nil, err
}
reviewers, err := b.getUsersWithLinks(newPR.Reviewers, client)
if err != nil {
return nil, err
}

usersWithMetadata = append(usersWithMetadata, bitbucketv1.UserWithMetadata{User: userWithLinks})
assignees, err := b.getUsersWithLinks(newPR.Assignees, client)
if err != nil {
return nil, err
}

response, err := client.DefaultApi.CreatePullRequest(r.project, r.name, bitbucketv1.PullRequest{
Title: newPR.Title,
Description: newPR.Body,
Reviewers: usersWithMetadata,
Reviewers: reviewers,
Participants: assignees,
FromRef: bitbucketv1.PullRequestRef{
ID: fmt.Sprintf("refs/heads/%s", newPR.Head),
Repository: bitbucketv1.Repository{
Expand Down Expand Up @@ -297,6 +292,27 @@ func (b *BitbucketServer) CreatePullRequest(ctx context.Context, repo git.Reposi
return newPullRequest(pullRequestResp), nil
}

func (b *BitbucketServer) getUsersWithLinks(usernames []string, client *bitbucketv1.APIClient) ([]bitbucketv1.UserWithMetadata, error) {
var usersWithMetadata []bitbucketv1.UserWithMetadata

for _, username := range usernames {
response, err := client.DefaultApi.GetUser(username)
if err != nil {
return nil, err
}

var userWithLinks bitbucketv1.UserWithLinks
err = mapstructure.Decode(response.Values, &userWithLinks)
if err != nil {
return nil, err
}

usersWithMetadata = append(usersWithMetadata, bitbucketv1.UserWithMetadata{User: userWithLinks})
}

return usersWithMetadata, nil
}

// GetPullRequests Gets the latest pull requests from repositories based on the scm configuration
func (b *BitbucketServer) GetPullRequests(ctx context.Context, branchName string) ([]git.PullRequest, error) {
client := newClient(ctx, b.config)
Expand Down
1 change: 1 addition & 0 deletions internal/scm/gitea/gitea.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ func (g *Gitea) CreatePullRequest(ctx context.Context, repo git.Repository, prRe
Base: newPR.Base,
Title: newPR.Title,
Body: newPR.Body,
Assignees: newPR.Assignees,
})
if err != nil {
return nil, errors.Wrap(err, "could not create pull request")
Expand Down
12 changes: 12 additions & 0 deletions internal/scm/github/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,10 @@ func (g Github) CreatePullRequest(ctx context.Context, repo git.Repository, prRe
return nil, err
}

if err := g.addAssignees(ctx, r, newPR, pr); err != nil {
return nil, err
}

return convertPullRequest(pr), nil
}

Expand Down Expand Up @@ -274,6 +278,14 @@ func (g Github) addReviewers(ctx context.Context, repo repository, newPR git.New
return err
}

func (g Github) addAssignees(ctx context.Context, repo repository, newPR git.NewPullRequest, createdPR *github.PullRequest) error {
if len(newPR.Assignees) == 0 {
return nil
}
_, _, err := g.ghClient.Issues.AddAssignees(ctx, repo.ownerName, repo.name, createdPR.GetNumber(), newPR.Assignees)
return err
}

// GetPullRequests gets all pull requests of with a specific branch
func (g Github) GetPullRequests(ctx context.Context, branchName string) ([]git.PullRequest, error) {
// TODO: If this is implemented with the GitHub v4 graphql api, it would be much faster
Expand Down
34 changes: 25 additions & 9 deletions internal/scm/gitlab/gitlab.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,14 +206,14 @@ func (g *Gitlab) CreatePullRequest(ctx context.Context, repo git.Repository, prR
r := repo.(repository)
prR := prRepo.(repository)

// Convert from usernames to user ids
var assigneeIDs []int
if len(newPR.Reviewers) > 0 {
var err error
assigneeIDs, err = g.getUserIDs(ctx, newPR.Reviewers)
if err != nil {
return nil, err
}
reviewersIDs, err := g.getUserIds(ctx, newPR.Reviewers)
if err != nil {
return nil, err
}

assigneesIDs, err := g.getUserIds(ctx, newPR.Assignees)
if err != nil {
return nil, err
}

removeSourceBranch := true
Expand All @@ -223,8 +223,9 @@ func (g *Gitlab) CreatePullRequest(ctx context.Context, repo git.Repository, prR
SourceBranch: &newPR.Head,
TargetBranch: &newPR.Base,
TargetProjectID: &r.pid,
AssigneeIDs: assigneeIDs,
ReviewerIDs: reviewersIDs,
RemoveSourceBranch: &removeSourceBranch,
AssigneeIDs: assigneesIDs,
})
if err != nil {
return nil, err
Expand All @@ -241,6 +242,21 @@ func (g *Gitlab) CreatePullRequest(ctx context.Context, repo git.Repository, prR
}, nil
}

func (g *Gitlab) getUserIds(ctx context.Context, usernames []string) ([]int, error) {
// Convert from usernames to user ids
var assigneeIDs []int

if len(usernames) > 0 {
var err error
assigneeIDs, err = g.getUserIDs(ctx, usernames)
if err != nil {
return nil, err
}
}

return assigneeIDs, nil
}

func (g *Gitlab) getUserIDs(ctx context.Context, usernames []string) ([]int, error) {
userIDs := make([]int, len(usernames))
for i := range usernames {
Expand Down
25 changes: 25 additions & 0 deletions tests/table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,31 @@ Repositories with a successful run:
`, runData.out)
},
},

{
name: "assignees",
vcCreate: func(t *testing.T) *vcmock.VersionController {
return &vcmock.VersionController{
Repositories: []vcmock.Repository{
createRepo(t, "owner", "should-change", "i like apples"),
},
}
},
args: []string{
"run",
"--author-name", "Test Author",
"--author-email", "test@example.com",
"-m", "custom message",
"-a", "assignee1,assignee2",
changerBinaryPath,
},
verify: func(t *testing.T, vcMock *vcmock.VersionController, runData runData) {
require.Len(t, vcMock.PullRequests, 1)
assert.Len(t, vcMock.PullRequests[0].Assignees, 2)
assert.Contains(t, vcMock.PullRequests[0].Assignees, "assignee1")
assert.Contains(t, vcMock.PullRequests[1].Assignees, "assignee2")
},
},
}

for _, gitBackend := range gitBackends {
Expand Down

0 comments on commit 6d18909

Please sign in to comment.