Skip to content

Commit c6e8e2e

Browse files
authored
fix: Add label for PRs on release init (#1911)
Fixes: #1888 Changes: Each command can configure a list of labels to add. The configured labels are added at the end of `commitAndPush` step. Note: Not sure if there is a way to test that the Github UI has a label added. I can add a create a potential dry-run of the `librarian release init` command with screenshots. Successful PR generation: #1935 <img width="2566" height="920" alt="image" src="https://github.com/user-attachments/assets/c12241c4-31ab-4aee-9c01-0a34bca26602" /> Logs: <img width="2038" height="258" alt="image" src="https://github.com/user-attachments/assets/e9e91eab-c7b6-4e1d-9327-2809c472f19a" /> --------- Signed-off-by: Lawrence Qiu <[email protected]>
1 parent eebb5fe commit c6e8e2e

File tree

4 files changed

+96
-11
lines changed

4 files changed

+96
-11
lines changed

internal/librarian/command.go

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,15 @@ type GitHubClientFactory func(token string, repo *github.Repository) (GitHubClie
4848
type ContainerClientFactory func(workRoot, image, userUID, userGID string) (ContainerClient, error)
4949

5050
type commitInfo struct {
51-
cfg *config.Config
52-
state *config.LibrarianState
53-
repo gitrepo.Repository
54-
ghClient GitHubClient
55-
idToCommits map[string]string
56-
failedLibraries []string
57-
commitMessage string
58-
prType string
51+
cfg *config.Config
52+
state *config.LibrarianState
53+
repo gitrepo.Repository
54+
ghClient GitHubClient
55+
idToCommits map[string]string
56+
failedLibraries []string
57+
commitMessage string
58+
prType string
59+
pullRequestLabels []string
5960
}
6061

6162
type commandRunner struct {
@@ -328,8 +329,7 @@ func copyLibrary(dst, src string, library *config.LibraryState) error {
328329
return nil
329330
}
330331

331-
// commitAndPush creates a commit and push request to GitHub for the generated
332-
// changes.
332+
// commitAndPush creates a commit and push request to GitHub for the generated changes.
333333
// It uses the GitHub client to create a PR with the specified branch, title, and
334334
// description to the repository.
335335
func commitAndPush(ctx context.Context, info *commitInfo) error {
@@ -380,10 +380,28 @@ func commitAndPush(ctx context.Context, info *commitInfo) error {
380380
return fmt.Errorf("failed to create pull request body: %w", err)
381381
}
382382

383-
if _, err = info.ghClient.CreatePullRequest(ctx, gitHubRepo, branch, cfg.Branch, title, prBody); err != nil {
383+
pullRequestMetadata, err := info.ghClient.CreatePullRequest(ctx, gitHubRepo, branch, cfg.Branch, title, prBody)
384+
if err != nil {
384385
return fmt.Errorf("failed to create pull request: %w", err)
385386
}
386387

388+
return addLabelsToPullRequest(ctx, info.ghClient, info.pullRequestLabels, pullRequestMetadata)
389+
}
390+
391+
// addLabelsToPullRequest adds a list of labels to a single pull request (specified by the id number).
392+
// Should only be called on a valid Github pull request.
393+
// Passing in `nil` for labels will no-op and an empty list for labels will clear all labels on the PR.
394+
// TODO: Consolidate the params to a potential PullRequestInfo struct.
395+
func addLabelsToPullRequest(ctx context.Context, ghClient GitHubClient, pullRequestLabels []string, prMetadata *github.PullRequestMetadata) error {
396+
// Do not update if there are aren't labels provided
397+
if pullRequestLabels == nil {
398+
return nil
399+
}
400+
// Github API treats Issues and Pull Request the same
401+
// https://docs.github.com/en/rest/issues/labels#add-labels-to-an-issue
402+
if err := ghClient.AddLabelsToIssue(ctx, prMetadata.Repo, prMetadata.Number, pullRequestLabels); err != nil {
403+
return fmt.Errorf("failed to add labels to pull request: %w", err)
404+
}
387405
return nil
388406
}
389407

internal/librarian/command_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1403,6 +1403,69 @@ func TestCommitAndPush(t *testing.T) {
14031403
}
14041404
}
14051405

1406+
func TestAddLabelsToPullRequest(t *testing.T) {
1407+
for _, test := range []struct {
1408+
name string
1409+
setupMockRepo func(t *testing.T) gitrepo.Repository
1410+
mockGithubClient *mockGitHubClient
1411+
prMetadata github.PullRequestMetadata
1412+
wantPullRequestLabels []string
1413+
wantErr bool
1414+
expectedErrMsg string
1415+
}{
1416+
{
1417+
name: "Add All Labels",
1418+
setupMockRepo: func(t *testing.T) gitrepo.Repository {
1419+
return &MockRepository{}
1420+
},
1421+
mockGithubClient: &mockGitHubClient{},
1422+
prMetadata: github.PullRequestMetadata{
1423+
Repo: &github.Repository{Owner: "test-owner", Name: "test-repo"},
1424+
Number: 7,
1425+
},
1426+
wantPullRequestLabels: []string{"release:pending", "1234", "label1234"},
1427+
},
1428+
{
1429+
name: "Failed to add labels",
1430+
setupMockRepo: func(t *testing.T) gitrepo.Repository {
1431+
return &MockRepository{}
1432+
},
1433+
mockGithubClient: &mockGitHubClient{
1434+
addLabelsToIssuesErr: errors.New("Can't add labels"),
1435+
},
1436+
prMetadata: github.PullRequestMetadata{
1437+
Repo: &github.Repository{Owner: "test-owner", Name: "test-repo"},
1438+
Number: 7,
1439+
},
1440+
wantPullRequestLabels: []string{"release:pending", "1234", "label1234"},
1441+
wantErr: true,
1442+
expectedErrMsg: "failed to add labels to pull request",
1443+
},
1444+
} {
1445+
t.Run(test.name, func(t *testing.T) {
1446+
client := test.mockGithubClient
1447+
prMetadata := test.prMetadata
1448+
err := addLabelsToPullRequest(context.Background(), client, test.wantPullRequestLabels, &prMetadata)
1449+
1450+
if test.wantErr {
1451+
if err == nil {
1452+
t.Errorf("addLabelsToPullRequest() expected error, got nil")
1453+
}
1454+
if test.expectedErrMsg != "" && !strings.Contains(err.Error(), test.expectedErrMsg) {
1455+
t.Errorf("addLabelsToPullRequest() error = %v, expected to contain: %q", err, test.expectedErrMsg)
1456+
}
1457+
return
1458+
}
1459+
if err != nil {
1460+
t.Errorf("%s: addLabelsToPullRequest() returned unexpected error: %v", test.name, err)
1461+
}
1462+
if diff := cmp.Diff(test.wantPullRequestLabels, test.mockGithubClient.labels); diff != "" {
1463+
t.Errorf("addLabelsToPullRequest() mismatch (-want +got):\n%s", diff)
1464+
}
1465+
})
1466+
}
1467+
}
1468+
14061469
func TestCopyLibraryFiles(t *testing.T) {
14071470
t.Parallel()
14081471
setup := func(src string, files []string) {

internal/librarian/mocks_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ func (m *mockGitHubClient) CreatePullRequest(ctx context.Context, repo *github.R
6868

6969
func (m *mockGitHubClient) AddLabelsToIssue(ctx context.Context, repo *github.Repository, number int, labels []string) error {
7070
m.addLabelsToIssuesCalls++
71+
m.labels = append(m.labels, labels...)
7172
return m.addLabelsToIssuesErr
7273
}
7374

internal/librarian/release_init.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,9 @@ func (r *initRunner) run(ctx context.Context) error {
112112
ghClient: r.ghClient,
113113
commitMessage: "chore: create a release",
114114
prType: release,
115+
// Newly created PRs from the `release init` command should have a
116+
// `release:pending` Github tab to be tracked for release.
117+
pullRequestLabels: []string{"release:pending"},
115118
}
116119
if err := commitAndPush(ctx, commitInfo); err != nil {
117120
return fmt.Errorf("failed to commit and push: %w", err)

0 commit comments

Comments
 (0)