Skip to content

Commit

Permalink
feat: add possibility to add assignees to pull request (#196)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: the previous mapping in GitLab was reviewers -> assignees. It has now been corrected.
  • Loading branch information
Berreek authored Oct 20, 2021
1 parent 099f019 commit 52cde84
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 28 deletions.
3 changes: 3 additions & 0 deletions cmd/cmd-run.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func RunCmd() *cobra.Command {
cmd.Flags().StringP("pr-body", "b", "", "The body of the commit message. Will default to everything but the first line of the commit message if none is set.")
cmd.Flags().StringP("commit-message", "m", "", "The commit message. Will default to title + body if none is set.")
cmd.Flags().StringSliceP("reviewers", "r", nil, "The username of the reviewers to be added on the pull request.")
cmd.Flags().StringSliceP("assignees", "a", nil, "The username of the assignees to be added on the pull request.")
cmd.Flags().IntP("max-reviewers", "M", 0, "If this value is set, reviewers will be randomized.")
cmd.Flags().IntP("concurrent", "C", 1, "The maximum number of concurrent runs.")
cmd.Flags().BoolP("skip-pr", "", false, "Skip pull request and directly push to the branch.")
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
40 changes: 25 additions & 15 deletions internal/scm/bitbucketserver/bitbucket_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,26 +246,15 @@ 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
}

usersWithMetadata = append(usersWithMetadata, bitbucketv1.UserWithMetadata{User: userWithLinks})
reviewers, err := b.getUsersWithLinks(newPR.Reviewers, 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,
FromRef: bitbucketv1.PullRequestRef{
ID: fmt.Sprintf("refs/heads/%s", newPR.Head),
Repository: bitbucketv1.Repository{
Expand Down Expand Up @@ -297,6 +286,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
9 changes: 5 additions & 4 deletions internal/scm/gitea/gitea.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,11 @@ func (g *Gitea) CreatePullRequest(ctx context.Context, repo git.Repository, prRe
head := fmt.Sprintf("%s:%s", prR.ownerName, newPR.Head)

pr, _, err := g.giteaClient(ctx).CreatePullRequest(r.ownerName, r.name, gitea.CreatePullRequestOption{
Head: head,
Base: newPR.Base,
Title: newPR.Title,
Body: newPR.Body,
Head: head,
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)
require.Len(t, vcMock.PullRequests[0].Assignees, 2)
assert.Contains(t, vcMock.PullRequests[0].Assignees, "assignee1")
assert.Contains(t, vcMock.PullRequests[0].Assignees, "assignee2")
},
},
}

for _, gitBackend := range gitBackends {
Expand Down

0 comments on commit 52cde84

Please sign in to comment.