Skip to content
Merged
Show file tree
Hide file tree
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
16 changes: 13 additions & 3 deletions devtools/gh-nudge/internal/github/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func NewClientWithExecutor(executor CommandExecutor) *Client {
const pullRequestListLimit = 100

// GetPendingPullRequests fetches pending pull requests using the gh CLI.
// It fetches open PRs created by the current user.
// It fetches open PRs created by the current user, excluding drafts.
func (c *Client) GetPendingPullRequests() ([]models.PullRequest, error) {
// Construct the gh command to get PR information.
// This fetches open PRs authored by the current user with their title, URL,
Expand All @@ -71,7 +71,7 @@ func (c *Client) GetPendingPullRequests() ([]models.PullRequest, error) {
output, err := c.executor.Execute("gh", "pr", "list",
"--author", "@me",
"--limit", fmt.Sprintf("%d", pullRequestListLimit),
"--json", "url,title,reviewRequests,files,mergeable,headRefName,statusCheckRollup")
"--json", "url,title,reviewRequests,files,mergeable,headRefName,statusCheckRollup,isDraft")
if err != nil {
return nil, fmt.Errorf("failed to execute gh command: %w", err)
}
Expand All @@ -82,7 +82,17 @@ func (c *Client) GetPendingPullRequests() ([]models.PullRequest, error) {
return nil, fmt.Errorf("failed to parse gh command output: %w", err)
}

return prs, nil
// Skip draft PRs: reviewers are not expected to act on them yet, so
// nudging about them would be noise.
pending := make([]models.PullRequest, 0, len(prs))
for _, pr := range prs {
if pr.IsDraft {
continue
}
pending = append(pending, pr)
}

return pending, nil
}

// GetMergeablePullRequests fetches pull requests with no review requests.
Expand Down
56 changes: 56 additions & 0 deletions devtools/gh-nudge/internal/github/github_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,62 @@ func TestGetPendingPullRequests(t *testing.T) {
}
})

t.Run("skips draft PRs", func(t *testing.T) {
sampleJSON := `[
{
"title": "Draft PR",
"url": "https://github.com/org/repo/pull/1",
"files": [],
"reviewRequests": [{"__typename": "User", "login": "reviewer"}],
"isDraft": true
},
{
"title": "Ready PR",
"url": "https://github.com/org/repo/pull/2",
"files": [],
"reviewRequests": [{"__typename": "User", "login": "reviewer"}],
"isDraft": false
}
]`

client := NewClientWithExecutor(&mockExecutor{output: sampleJSON})
prs, err := client.GetPendingPullRequests()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

if len(prs) != 1 {
t.Fatalf("expected 1 non-draft PR, got %d", len(prs))
}
if prs[0].Title != "Ready PR" {
t.Errorf("expected non-draft PR to be returned, got %q", prs[0].Title)
}
})

t.Run("treats PRs without isDraft field as non-draft", func(t *testing.T) {
sampleJSON := `[
{
"title": "PR without isDraft",
"url": "https://github.com/org/repo/pull/1",
"files": [],
"reviewRequests": []
}
]`

client := NewClientWithExecutor(&mockExecutor{output: sampleJSON})
prs, err := client.GetPendingPullRequests()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

if len(prs) != 1 {
t.Fatalf("expected 1 PR, got %d", len(prs))
}
if prs[0].IsDraft {
t.Error("expected PR without isDraft field to default to non-draft")
}
})

t.Run("parses PR with multiple files", func(t *testing.T) {
sampleJSON := `[
{
Expand Down
1 change: 1 addition & 0 deletions devtools/gh-nudge/internal/models/pr.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type PullRequest struct {
Mergeable string `json:"mergeable,omitempty"`
HeadRefName string `json:"headRefName,omitempty"`
StatusCheckRollup []StatusCheck `json:"statusCheckRollup,omitempty"`
IsDraft bool `json:"isDraft,omitempty"`
}

// StatusCheck represents a single CI check or status from GitHub's statusCheckRollup.
Expand Down
Loading