Skip to content

Commit a5b5b68

Browse files
authored
feat: add labels to a github issue (#1642)
Fixes #1012
1 parent 04ba70b commit a5b5b68

File tree

4 files changed

+87
-1
lines changed

4 files changed

+87
-1
lines changed

internal/github/github.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,15 @@ func (c *Client) CreatePullRequest(ctx context.Context, repo *Repository, remote
135135
return pullRequestMetadata, nil
136136
}
137137

138+
// AddLabelsToIssue adds labels to an existing issue in a GitHub repository.
139+
func (c *Client) AddLabelsToIssue(ctx context.Context, repo *Repository, number int, labels []string) error {
140+
slog.Info("Labels added to issue", "number", number, "labels", labels)
141+
_, _, err := c.Issues.AddLabelsToIssue(ctx, repo.Owner, repo.Name, number, labels)
142+
return err
143+
}
144+
138145
// FetchGitHubRepoFromRemote parses the GitHub repo name from the remote for this repository.
139-
// There must be a remote named 'origin' with a Github URL (as the first URL), in order to
146+
// There must be a remote named 'origin' with a GitHub URL (as the first URL), in order to
140147
// provide an unambiguous result.
141148
// Remotes without any URLs, or where the first URL does not start with https://github.com/ are ignored.
142149
func FetchGitHubRepoFromRemote(repo gitrepo.Repository) (*Repository, error) {

internal/github/github_test.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,3 +400,74 @@ func TestCreatePullRequest(t *testing.T) {
400400
})
401401
}
402402
}
403+
404+
func TestAddLabelsToIssue(t *testing.T) {
405+
t.Parallel()
406+
for _, test := range []struct {
407+
name string
408+
handler http.HandlerFunc
409+
issueNum int
410+
labels []string
411+
wantErr bool
412+
wantErrSubstr string
413+
}{
414+
{
415+
name: "add labels to an issue",
416+
handler: func(w http.ResponseWriter, r *http.Request) {
417+
if r.Method != http.MethodPost {
418+
t.Errorf("unexpected method: got %s, want %s", r.Method, http.MethodPost)
419+
}
420+
wantPath := "/repos/owner/repo/issues/7/labels"
421+
if r.URL.Path != wantPath {
422+
t.Errorf("unexpected path: got %s, want %s", r.URL.Path, wantPath)
423+
}
424+
var labels []string
425+
if err := json.NewDecoder(r.Body).Decode(&labels); err != nil {
426+
t.Fatalf("failed to decode request body: %v", err)
427+
}
428+
expectedBody := []string{"new-label", "another-label"}
429+
if strings.Join(labels, ",") != strings.Join(expectedBody, ",") {
430+
t.Errorf("unexpected body: got %q, want %q", labels, expectedBody)
431+
}
432+
},
433+
issueNum: 7,
434+
labels: []string{"new-label", "another-label"},
435+
},
436+
{
437+
name: "GitHub API error",
438+
handler: func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) },
439+
wantErr: true,
440+
wantErrSubstr: "500",
441+
},
442+
} {
443+
t.Run(test.name, func(t *testing.T) {
444+
t.Parallel()
445+
server := httptest.NewServer(test.handler)
446+
defer server.Close()
447+
448+
repo := &Repository{Owner: "owner", Name: "repo"}
449+
client, err := newClientWithHTTP("fake-token", repo, server.Client())
450+
if err != nil {
451+
t.Fatalf("newClientWithHTTP() error = %v", err)
452+
}
453+
client.BaseURL, _ = url.Parse(server.URL + "/")
454+
455+
err = client.AddLabelsToIssue(context.Background(), repo, test.issueNum, test.labels)
456+
457+
if test.wantErr {
458+
if err == nil {
459+
t.Errorf("AddLabelsToIssue() should return an error")
460+
}
461+
if !strings.Contains(err.Error(), test.wantErrSubstr) {
462+
t.Errorf("AddLabelsToIssue() err = %v, want error containing %q", err, test.wantErrSubstr)
463+
}
464+
465+
return
466+
}
467+
468+
if err != nil {
469+
t.Errorf("AddLabelsToIssue() err = %v, want nil", err)
470+
}
471+
})
472+
}
473+
}

internal/librarian/librarian.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ func Run(ctx context.Context, arg ...string) error {
7979
type GitHubClient interface {
8080
GetRawContent(ctx context.Context, path, ref string) ([]byte, error)
8181
CreatePullRequest(ctx context.Context, repo *github.Repository, remoteBranch, title, body string) (*github.PullRequestMetadata, error)
82+
AddLabelsToIssue(ctx context.Context, repo *github.Repository, number int, labels []string) error
8283
}
8384

8485
// ContainerClient is an abstraction over the Docker client.

internal/librarian/mocks_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ type mockGitHubClient struct {
3333
rawContent []byte
3434
rawErr error
3535
createPullRequestCalls int
36+
addLabelsToIssuesCalls int
3637
createPullRequestErr error
38+
addLabelsToIssuesErr error
3739
createdPR *github.PullRequestMetadata
3840
}
3941

@@ -49,6 +51,11 @@ func (m *mockGitHubClient) CreatePullRequest(ctx context.Context, repo *github.R
4951
return m.createdPR, nil
5052
}
5153

54+
func (m *mockGitHubClient) AddLabelsToIssue(ctx context.Context, repo *github.Repository, number int, labels []string) error {
55+
m.addLabelsToIssuesCalls++
56+
return m.addLabelsToIssuesErr
57+
}
58+
5259
// mockContainerClient is a mock implementation of the ContainerClient interface for testing.
5360
type mockContainerClient struct {
5461
ContainerClient

0 commit comments

Comments
 (0)