@sentry/junior-github 0.129.0 → 0.130.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @sentry/junior-github
2
2
 
3
- `@sentry/junior-github` adds GitHub deployment, issue, pull request,
3
+ `@sentry/junior-github` adds GitHub deployment, issue, pull request, release,
4
4
  repository, and user-attachment workflows to Junior using a GitHub App.
5
5
 
6
6
  Install it alongside `@sentry/junior`:
@@ -25,7 +25,7 @@ export const plugins = defineJuniorPlugins([
25
25
 
26
26
  Full setup guide: https://junior.sentry.dev/extend/github-plugin/
27
27
 
28
- The plugin owns its signed webhook route, deployment and pull request resource
29
- events, normalized pull request and issue outcome projections, and dashboard
30
- operational report. Core only owns delivery from plugin-published resource
31
- events into matching conversation subscriptions.
28
+ The plugin owns its signed webhook route, deployment, pull request, and release
29
+ resource events, normalized pull request and issue outcome projections, and
30
+ dashboard operational report. Core only owns delivery from plugin-published
31
+ resource events into matching conversation subscriptions.
package/SETUP.md CHANGED
@@ -79,8 +79,8 @@ the App bot login from that email. Set the GitHub App webhook URL to
79
79
  and Issues events. The secret in GitHub must match the deployment environment.
80
80
 
81
81
  Conversation watches additionally use Check suite, Deployment, Deployment
82
- status, Issue comment, Pull request review, and Pull request review comment
83
- events.
82
+ status, Issue comment, Pull request review, Pull request review comment, and
83
+ Release events.
84
84
 
85
85
  `github_getDeployment` reads the latest deployment and status for an exact
86
86
  repository and commit SHA, optionally limited to one environment. When
@@ -91,6 +91,13 @@ subscription remains available when GitHub has not created the deployment yet.
91
91
  Commit-wide watches remain active across terminal outcomes so later
92
92
  environments for the same commit can also notify the conversation.
93
93
 
94
+ `github_getRelease` reads a release for an exact repository, optionally limited
95
+ to one tag. When webhooks are configured, its result can subscribe the current
96
+ conversation to `release.published`. Omitting the tag watches every published
97
+ release in the repository. The subscription remains available when GitHub has
98
+ not published the release yet. Tag-specific watches complete after publish;
99
+ repository-wide release watches remain active across later tags.
100
+
94
101
  ```bash
95
102
  vercel env add GITHUB_WEBHOOK_SECRET production
96
103
  ```
@@ -103,6 +103,28 @@ function gitHubPullRequestSubscribable(input) {
103
103
  };
104
104
  }
105
105
 
106
+ // src/resource-events/release.ts
107
+ var GITHUB_RELEASE_EVENTS = ["release.published"];
108
+ var GITHUB_RELEASE_SUGGESTED_EVENTS = ["release.published"];
109
+ function gitHubReleaseSourceResource(input) {
110
+ const repo = input.repo.toLowerCase();
111
+ const tag = input.tag?.trim();
112
+ return {
113
+ label: `GitHub release for ${repo}`,
114
+ namespace: "github",
115
+ identifier: tag ? `release-source:${repo}:${encodeURIComponent(tag)}` : `release-source:${repo}`
116
+ };
117
+ }
118
+ function gitHubReleaseSourceSubscribable(input) {
119
+ if (!process.env.GITHUB_WEBHOOK_SECRET?.trim()) return void 0;
120
+ return {
121
+ ...gitHubReleaseSourceResource(input),
122
+ suggestedEvents: GITHUB_RELEASE_SUGGESTED_EVENTS,
123
+ supportedEvents: [...GITHUB_RELEASE_EVENTS],
124
+ type: "release_source"
125
+ };
126
+ }
127
+
106
128
  // src/resource-events/repository.ts
107
129
  function gitHubRepositoryResource(input) {
108
130
  return {
@@ -115,8 +137,12 @@ function gitHubRepositorySubscribable(input) {
115
137
  if (!process.env.GITHUB_WEBHOOK_SECRET?.trim()) return void 0;
116
138
  return {
117
139
  ...gitHubRepositoryResource(input),
118
- suggestedEvents: ["issue.opened", ...GITHUB_ISSUE_SUGGESTED_EVENTS],
119
- supportedEvents: GITHUB_ISSUE_EVENTS,
140
+ suggestedEvents: [
141
+ "issue.opened",
142
+ ...GITHUB_ISSUE_SUGGESTED_EVENTS,
143
+ ...GITHUB_PULL_REQUEST_SUGGESTED_EVENTS
144
+ ],
145
+ supportedEvents: [...GITHUB_ISSUE_EVENTS, ...GITHUB_PULL_REQUEST_EVENTS],
120
146
  type: "repository"
121
147
  };
122
148
  }
@@ -125,6 +151,16 @@ function gitHubRepositorySubscribable(input) {
125
151
  function gitHubEventKey(deliveryId, eventType) {
126
152
  return `github:${deliveryId}:${eventType}`;
127
153
  }
154
+ function pullRequestTargets(event, repo) {
155
+ const { terminal: _terminal, ...repositoryEvent } = event;
156
+ return [
157
+ event,
158
+ {
159
+ ...repositoryEvent,
160
+ identifier: gitHubRepositoryResource({ repo }).identifier
161
+ }
162
+ ];
163
+ }
128
164
  var repositorySchema = z.object({ full_name: z.string().min(1) }).passthrough();
129
165
  var deploymentSchema = z.object({
130
166
  created_at: z.string().optional(),
@@ -265,21 +301,25 @@ function normalizeCheckSuiteEvents(deliveryId, body) {
265
301
  const eventType = conclusion === "failure" || conclusion === "timed_out" ? "pull_request.checks.failed" : conclusion === "success" ? "pull_request.checks.recovered" : void 0;
266
302
  if (!eventType) return [];
267
303
  const sha = parsed.data.check_suite.head_sha?.slice(0, 12);
268
- return parsed.data.check_suite.pull_requests.map((pullRequest) => {
304
+ return parsed.data.check_suite.pull_requests.flatMap((pullRequest) => {
305
+ const repo = parsed.data.repository.full_name;
269
306
  const resource = gitHubPullRequestResource({
270
307
  number: pullRequest.number,
271
- repo: parsed.data.repository.full_name
308
+ repo
272
309
  });
273
- return {
274
- eventKey: gitHubEventKey(
275
- deliveryId,
276
- `${eventType}:${pullRequest.number}`
277
- ),
278
- eventType,
279
- occurredAtMs: Date.now(),
280
- identifier: resource.identifier,
281
- trustedSummary: eventType === "pull_request.checks.failed" ? `${resource.label} checks failed${sha ? ` for ${sha}` : ""}.` : `${resource.label} checks recovered${sha ? ` for ${sha}` : ""}.`
282
- };
310
+ return pullRequestTargets(
311
+ {
312
+ eventKey: gitHubEventKey(
313
+ deliveryId,
314
+ `${eventType}:${pullRequest.number}`
315
+ ),
316
+ eventType,
317
+ occurredAtMs: Date.now(),
318
+ identifier: resource.identifier,
319
+ trustedSummary: eventType === "pull_request.checks.failed" ? `${resource.label} checks failed${sha ? ` for ${sha}` : ""}.` : `${resource.label} checks recovered${sha ? ` for ${sha}` : ""}.`
320
+ },
321
+ repo
322
+ );
283
323
  });
284
324
  }
285
325
  var issueCommentWebhookSchema = z.object({
@@ -305,7 +345,7 @@ function normalizeIssueCommentEvents(deliveryId, body) {
305
345
  if (parsed.data.issue.pull_request) {
306
346
  const eventType = "pull_request.comment.created";
307
347
  const resource = gitHubPullRequestResource(input);
308
- return [
348
+ return pullRequestTargets(
309
349
  {
310
350
  eventKey: gitHubEventKey(deliveryId, eventType),
311
351
  eventType,
@@ -313,8 +353,9 @@ function normalizeIssueCommentEvents(deliveryId, body) {
313
353
  identifier: resource.identifier,
314
354
  trustedSummary: `${resource.label} received a comment${author ? ` from ${author}` : ""}.`,
315
355
  untrustedText: parsed.data.comment.body
316
- }
317
- ];
356
+ },
357
+ input.repo
358
+ );
318
359
  }
319
360
  const issue = gitHubIssueResource(input);
320
361
  const repository = gitHubRepositoryResource(input);
@@ -401,21 +442,25 @@ var pullRequestReviewCommentWebhookSchema = z.object({
401
442
  });
402
443
  function normalizePullRequestReviewCommentEvent(deliveryId, body) {
403
444
  const parsed = pullRequestReviewCommentWebhookSchema.safeParse(body);
404
- if (!parsed.success || parsed.data.action !== "created") return void 0;
445
+ if (!parsed.success || parsed.data.action !== "created") return [];
405
446
  const eventType = "pull_request.review_comment.created";
447
+ const repo = parsed.data.repository.full_name;
406
448
  const resource = gitHubPullRequestResource({
407
449
  number: parsed.data.pull_request.number,
408
- repo: parsed.data.repository.full_name
450
+ repo
409
451
  });
410
452
  const author = parsed.data.comment.user?.login;
411
- return {
412
- eventKey: gitHubEventKey(deliveryId, eventType),
413
- eventType,
414
- occurredAtMs: Date.now(),
415
- identifier: resource.identifier,
416
- trustedSummary: `${resource.label} received an inline review comment${author ? ` from ${author}` : ""}.`,
417
- untrustedText: parsed.data.comment.body
418
- };
453
+ return pullRequestTargets(
454
+ {
455
+ eventKey: gitHubEventKey(deliveryId, eventType),
456
+ eventType,
457
+ occurredAtMs: Date.now(),
458
+ identifier: resource.identifier,
459
+ trustedSummary: `${resource.label} received an inline review comment${author ? ` from ${author}` : ""}.`,
460
+ untrustedText: parsed.data.comment.body
461
+ },
462
+ repo
463
+ );
419
464
  }
420
465
  var pullRequestReviewWebhookSchema = z.object({
421
466
  action: z.string(),
@@ -429,23 +474,27 @@ var pullRequestReviewWebhookSchema = z.object({
429
474
  });
430
475
  function normalizePullRequestReviewEvent(deliveryId, body) {
431
476
  const parsed = pullRequestReviewWebhookSchema.safeParse(body);
432
- if (!parsed.success || parsed.data.action !== "submitted") return void 0;
477
+ if (!parsed.success || parsed.data.action !== "submitted") return [];
433
478
  const reviewState = parsed.data.review.state.toUpperCase();
434
479
  const eventType = reviewState === "APPROVED" ? "pull_request.review.approved" : reviewState === "CHANGES_REQUESTED" ? "pull_request.review.changes_requested" : reviewState === "COMMENTED" ? "pull_request.review.commented" : void 0;
435
- if (!eventType) return void 0;
480
+ if (!eventType) return [];
481
+ const repo = parsed.data.repository.full_name;
436
482
  const resource = gitHubPullRequestResource({
437
483
  number: parsed.data.pull_request.number,
438
- repo: parsed.data.repository.full_name
484
+ repo
439
485
  });
440
486
  const reviewer = parsed.data.review.user?.login;
441
- return {
442
- eventKey: gitHubEventKey(deliveryId, eventType),
443
- eventType,
444
- occurredAtMs: Date.now(),
445
- identifier: resource.identifier,
446
- trustedSummary: eventType === "pull_request.review.approved" ? `${resource.label} was approved${reviewer ? ` by ${reviewer}` : ""}.` : eventType === "pull_request.review.changes_requested" ? `${resource.label} received requested changes${reviewer ? ` from ${reviewer}` : ""}.` : `${resource.label} received a review comment${reviewer ? ` from ${reviewer}` : ""}.`,
447
- untrustedText: parsed.data.review.body ?? void 0
448
- };
487
+ return pullRequestTargets(
488
+ {
489
+ eventKey: gitHubEventKey(deliveryId, eventType),
490
+ eventType,
491
+ occurredAtMs: Date.now(),
492
+ identifier: resource.identifier,
493
+ trustedSummary: eventType === "pull_request.review.approved" ? `${resource.label} was approved${reviewer ? ` by ${reviewer}` : ""}.` : eventType === "pull_request.review.changes_requested" ? `${resource.label} received requested changes${reviewer ? ` from ${reviewer}` : ""}.` : `${resource.label} received a review comment${reviewer ? ` from ${reviewer}` : ""}.`,
494
+ untrustedText: parsed.data.review.body ?? void 0
495
+ },
496
+ repo
497
+ );
449
498
  }
450
499
  var pullRequestWebhookSchema = z.object({
451
500
  action: z.string(),
@@ -464,22 +513,76 @@ function providerTime(value) {
464
513
  }
465
514
  function normalizePullRequestEvent(deliveryId, body) {
466
515
  const parsed = pullRequestWebhookSchema.safeParse(body);
467
- if (!parsed.success || parsed.data.action !== "closed") return void 0;
516
+ if (!parsed.success || parsed.data.action !== "closed") return [];
468
517
  const eventType = parsed.data.pull_request.merged ? "pull_request.merged" : "pull_request.closed_unmerged";
518
+ const repo = parsed.data.repository.full_name;
469
519
  const resource = gitHubPullRequestResource({
470
520
  number: parsed.data.pull_request.number,
471
- repo: parsed.data.repository.full_name
521
+ repo
472
522
  });
473
- return {
474
- eventKey: gitHubEventKey(deliveryId, eventType),
475
- eventType,
476
- occurredAtMs: providerTime(
477
- parsed.data.pull_request.merged ? parsed.data.pull_request.merged_at : parsed.data.pull_request.closed_at
478
- ) ?? Date.now(),
479
- identifier: resource.identifier,
480
- terminal: true,
481
- trustedSummary: eventType === "pull_request.merged" ? `${resource.label} was merged.` : `${resource.label} was closed without being merged.`
482
- };
523
+ return pullRequestTargets(
524
+ {
525
+ eventKey: gitHubEventKey(deliveryId, eventType),
526
+ eventType,
527
+ occurredAtMs: providerTime(
528
+ parsed.data.pull_request.merged ? parsed.data.pull_request.merged_at : parsed.data.pull_request.closed_at
529
+ ) ?? Date.now(),
530
+ identifier: resource.identifier,
531
+ terminal: true,
532
+ trustedSummary: eventType === "pull_request.merged" ? `${resource.label} was merged.` : `${resource.label} was closed without being merged.`
533
+ },
534
+ repo
535
+ );
536
+ }
537
+ var releaseWebhookSchema = z.object({
538
+ action: z.string(),
539
+ release: z.object({
540
+ body: z.string().optional().nullable(),
541
+ draft: z.boolean().optional(),
542
+ id: z.number(),
543
+ name: z.string().optional().nullable(),
544
+ prerelease: z.boolean().optional(),
545
+ published_at: z.string().optional().nullable(),
546
+ tag_name: z.string().min(1)
547
+ }).passthrough(),
548
+ repository: repositorySchema
549
+ }).passthrough();
550
+ function releaseSourceTargets(input) {
551
+ return [
552
+ {
553
+ completeOnTerminalEvent: true,
554
+ resource: gitHubReleaseSourceResource(input)
555
+ },
556
+ {
557
+ completeOnTerminalEvent: false,
558
+ resource: gitHubReleaseSourceResource({ repo: input.repo })
559
+ }
560
+ ];
561
+ }
562
+ function normalizeReleaseEvent(deliveryId, body) {
563
+ const parsed = releaseWebhookSchema.safeParse(body);
564
+ if (!parsed.success || parsed.data.action !== "published") return [];
565
+ if (parsed.data.release.draft) return [];
566
+ const eventType = "release.published";
567
+ const repo = parsed.data.repository.full_name;
568
+ const tag = parsed.data.release.tag_name;
569
+ const untrustedParts = [
570
+ tag ? `Tag: ${tag}` : void 0,
571
+ parsed.data.release.name?.trim() ? `Name: ${parsed.data.release.name.trim()}` : void 0,
572
+ parsed.data.release.body?.trim() || void 0
573
+ ].filter((part) => part !== void 0);
574
+ const untrustedText = untrustedParts.length > 0 ? untrustedParts.join("\n\n") : void 0;
575
+ return releaseSourceTargets({ repo, tag }).map(
576
+ ({ completeOnTerminalEvent, resource }) => ({
577
+ eventKey: gitHubEventKey(deliveryId, eventType),
578
+ eventType,
579
+ occurredAtMs: providerTime(parsed.data.release.published_at) ?? Date.now(),
580
+ identifier: resource.identifier,
581
+ ...completeOnTerminalEvent ? { terminal: true } : {},
582
+ trustedSummary: `${resource.label} was published (release ${parsed.data.release.id}).`,
583
+ ...untrustedText ? { untrustedText } : {}
584
+ })
585
+ );
483
586
  }
484
587
  function normalizeGitHubResourceEvents(args) {
485
588
  switch (args.eventName) {
@@ -490,28 +593,24 @@ function normalizeGitHubResourceEvents(args) {
490
593
  return normalizeDeploymentStatusEvent(args.deliveryId, args.body);
491
594
  }
492
595
  case "pull_request": {
493
- const event = normalizePullRequestEvent(args.deliveryId, args.body);
494
- return event ? [event] : [];
596
+ return normalizePullRequestEvent(args.deliveryId, args.body);
495
597
  }
496
598
  case "issues": {
497
599
  return normalizeIssueEvents(args.deliveryId, args.body);
498
600
  }
499
601
  case "pull_request_review": {
500
- const event = normalizePullRequestReviewEvent(args.deliveryId, args.body);
501
- return event ? [event] : [];
602
+ return normalizePullRequestReviewEvent(args.deliveryId, args.body);
502
603
  }
503
604
  case "issue_comment": {
504
605
  return normalizeIssueCommentEvents(args.deliveryId, args.body);
505
606
  }
506
607
  case "pull_request_review_comment": {
507
- const event = normalizePullRequestReviewCommentEvent(
508
- args.deliveryId,
509
- args.body
510
- );
511
- return event ? [event] : [];
608
+ return normalizePullRequestReviewCommentEvent(args.deliveryId, args.body);
512
609
  }
513
610
  case "check_suite":
514
611
  return normalizeCheckSuiteEvents(args.deliveryId, args.body);
612
+ case "release":
613
+ return normalizeReleaseEvent(args.deliveryId, args.body);
515
614
  default:
516
615
  return [];
517
616
  }
@@ -527,6 +626,9 @@ export {
527
626
  GITHUB_PULL_REQUEST_EVENTS,
528
627
  GITHUB_PULL_REQUEST_SUGGESTED_EVENTS,
529
628
  gitHubPullRequestSubscribable,
629
+ GITHUB_RELEASE_EVENTS,
630
+ GITHUB_RELEASE_SUGGESTED_EVENTS,
631
+ gitHubReleaseSourceSubscribable,
530
632
  gitHubRepositorySubscribable,
531
633
  normalizeGitHubResourceEvents
532
634
  };