Skip to content
This repository was archived by the owner on Jun 25, 2026. It is now read-only.

Commit 94eb095

Browse files
committed
fix(cli): improve PR review comment webhook diagnostics
1 parent b38ef19 commit 94eb095

4 files changed

Lines changed: 135 additions & 5 deletions

File tree

cmd/rascal/repo.go

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,22 @@ package main
33
import (
44
"context"
55
"os"
6+
"slices"
67
"strings"
78
"time"
89

910
ghapi "github.com/rtzll/rascal/internal/github"
1011
"github.com/spf13/cobra"
1112
)
1213

14+
var requiredWebhookEvents = []string{
15+
"issues",
16+
"issue_comment",
17+
"pull_request_review",
18+
"pull_request_review_comment",
19+
"pull_request",
20+
}
21+
1322
func (a *app) newRepoCmd() *cobra.Command {
1423
cmd := &cobra.Command{
1524
Use: "repo",
@@ -174,11 +183,18 @@ func (a *app) newRepoStatusCmd() *cobra.Command {
174183
if err != nil {
175184
return &cliError{Code: exitRuntime, Message: "failed to check webhook", Cause: err}
176185
}
186+
missingEvents := []string{}
187+
if hook != nil {
188+
missingEvents = missingRequiredWebhookEvents(hook.Events)
189+
}
177190
out := map[string]any{
178-
"repo": repo,
179-
"label_exists": labelExists,
180-
"webhook_url": webhookURL,
181-
"webhook": hook,
191+
"repo": repo,
192+
"label_exists": labelExists,
193+
"webhook_url": webhookURL,
194+
"webhook": hook,
195+
"required_events": requiredWebhookEvents,
196+
"missing_events": missingEvents,
197+
"webhook_events_healthy": hook != nil && len(missingEvents) == 0,
182198
}
183199
return a.emit(out, func() error {
184200
a.println("repo: %s", repo)
@@ -191,6 +207,9 @@ func (a *app) newRepoStatusCmd() *cobra.Command {
191207
if len(hook.Events) > 0 {
192208
a.println("events: %s", strings.Join(hook.Events, ","))
193209
}
210+
if len(missingEvents) > 0 {
211+
a.println("warning: webhook missing required events: %s", strings.Join(missingEvents, ","))
212+
}
194213
return nil
195214
})
196215
},
@@ -207,3 +226,22 @@ func resolveRepoArg(args []string, def string) string {
207226
}
208227
return strings.TrimSpace(def)
209228
}
229+
230+
func missingRequiredWebhookEvents(events []string) []string {
231+
normalized := make([]string, 0, len(events))
232+
for _, event := range events {
233+
event = strings.ToLower(strings.TrimSpace(event))
234+
if event == "" || slices.Contains(normalized, event) {
235+
continue
236+
}
237+
normalized = append(normalized, event)
238+
}
239+
240+
missing := make([]string, 0)
241+
for _, want := range requiredWebhookEvents {
242+
if !slices.Contains(normalized, want) {
243+
missing = append(missing, want)
244+
}
245+
}
246+
return missing
247+
}

cmd/rascal/repo_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package main
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func TestMissingRequiredWebhookEvents(t *testing.T) {
9+
t.Run("all required events present", func(t *testing.T) {
10+
events := []string{
11+
"pull_request_review",
12+
"pull_request_review_comment",
13+
"issue_comment",
14+
"issues",
15+
"pull_request",
16+
}
17+
if missing := missingRequiredWebhookEvents(events); len(missing) != 0 {
18+
t.Fatalf("expected no missing events, got %v", missing)
19+
}
20+
})
21+
22+
t.Run("normalizes case and trims", func(t *testing.T) {
23+
events := []string{
24+
" Issues ",
25+
"ISSUE_COMMENT",
26+
"pull_request_review",
27+
"pull_request_review_comment",
28+
"pull_request",
29+
"pull_request",
30+
}
31+
if missing := missingRequiredWebhookEvents(events); len(missing) != 0 {
32+
t.Fatalf("expected no missing events, got %v", missing)
33+
}
34+
})
35+
36+
t.Run("returns missing events in required order", func(t *testing.T) {
37+
events := []string{"issues", "issue_comment", "pull_request"}
38+
want := []string{"pull_request_review", "pull_request_review_comment"}
39+
got := missingRequiredWebhookEvents(events)
40+
if !reflect.DeepEqual(got, want) {
41+
t.Fatalf("missing = %v, want %v", got, want)
42+
}
43+
})
44+
}

cmd/rascal/webhook.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ var webhookTestEvents = []string{
2626
"issues",
2727
"issue_comment",
2828
"pull_request_review",
29+
"pull_request_review_comment",
2930
"pull_request",
3031
}
3132

@@ -212,7 +213,7 @@ func (a *app) newWebhookTestCmd() *cobra.Command {
212213

213214
cmd.Flags().StringVar(&webhookSecret, "webhook-secret", "", "GitHub webhook secret (or RASCAL_GITHUB_WEBHOOK_SECRET)")
214215
cmd.Flags().StringVar(&repo, "repo", "", "repository in OWNER/REPO form")
215-
cmd.Flags().StringVar(&event, "event", webhookTestDefaultEvent, "event template: issues|issue_comment|pull_request_review|pull_request")
216+
cmd.Flags().StringVar(&event, "event", webhookTestDefaultEvent, "event template: issues|issue_comment|pull_request_review|pull_request_review_comment|pull_request")
216217
cmd.Flags().BoolVar(&dryRun, "dry-run", false, "print payload/signature without sending")
217218
cmd.Flags().BoolVar(&verbose, "verbose", false, "print full request/response")
218219
return cmd
@@ -372,6 +373,26 @@ func buildWebhookTestEvent(event, repo string) (any, error) {
372373
Repository: ghapi.Repository{FullName: repo},
373374
Sender: ghapi.User{Login: actorLogin},
374375
}, nil
376+
case "pull_request_review_comment":
377+
pr := ghapi.PullRequest{Number: prNumber, Merged: false}
378+
pr.Base.Ref = baseBranch
379+
pr.Head.Ref = headBranch
380+
line := 42
381+
startLine := 40
382+
return ghapi.PullRequestReviewCommentEvent{
383+
Action: "created",
384+
Comment: ghapi.ReviewComment{
385+
ID: commentID,
386+
Body: "Synthetic inline review comment from rascal webhook test.",
387+
Path: "cmd/rascald/main.go",
388+
Line: &line,
389+
StartLine: &startLine,
390+
User: ghapi.User{Login: actorLogin},
391+
},
392+
PullRequest: pr,
393+
Repository: ghapi.Repository{FullName: repo},
394+
Sender: ghapi.User{Login: actorLogin},
395+
}, nil
375396
case "pull_request":
376397
pr := ghapi.PullRequest{Number: prNumber, Merged: false}
377398
pr.Base.Ref = baseBranch

cmd/rascal/webhook_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,33 @@ func TestBuildWebhookTestPayloadTemplates(t *testing.T) {
116116
}
117117
},
118118
},
119+
{
120+
event: "pull_request_review_comment",
121+
check: func(t *testing.T, payload []byte) {
122+
var ev ghapi.PullRequestReviewCommentEvent
123+
if err := json.Unmarshal(payload, &ev); err != nil {
124+
t.Fatalf("unmarshal pull_request_review_comment: %v", err)
125+
}
126+
if ev.Action != "created" {
127+
t.Fatalf("unexpected action: %q", ev.Action)
128+
}
129+
if ev.Comment.ID == 0 {
130+
t.Fatal("expected review comment id")
131+
}
132+
if ev.Comment.Path == "" {
133+
t.Fatal("expected review comment path")
134+
}
135+
if ev.Comment.Line == nil || *ev.Comment.Line <= 0 {
136+
t.Fatal("expected review comment line")
137+
}
138+
if ev.PullRequest.Number == 0 {
139+
t.Fatal("expected pull request number")
140+
}
141+
if ev.Repository.FullName != repo {
142+
t.Fatalf("unexpected repo: %q", ev.Repository.FullName)
143+
}
144+
},
145+
},
119146
{
120147
event: "pull_request",
121148
check: func(t *testing.T, payload []byte) {

0 commit comments

Comments
 (0)