@sentry/junior-github 0.249.0 → 0.251.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/SETUP.md +3 -5
- package/dist/{chunk-EYBKPQTJ.js → chunk-D75O3CY3.js} +15 -0
- package/dist/events/pull-request.d.ts +6 -0
- package/dist/index.js +157 -51
- package/dist/testing.js +1 -1
- package/dist/webhooks/handler.d.ts +4 -1
- package/dist/webhooks/pull-request-people.d.ts +31 -0
- package/package.json +2 -2
- package/skills/github-code/SKILL.md +8 -8
- package/skills/github-code/references/api-surface.md +3 -3
- package/skills/github-code/references/troubleshooting-workarounds.md +0 -1
- package/skills/github-code/references/workflow.md +1 -1
package/SETUP.md
CHANGED
|
@@ -164,11 +164,9 @@ Use `additionalUserScopes` only when a human-identity integration flow requires
|
|
|
164
164
|
|
|
165
165
|
Run as a regular sandbox `bash` command while the relevant skill is active:
|
|
166
166
|
|
|
167
|
-
Clone a repository with
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
gh repo clone owner/repo -- --depth=1
|
|
171
|
-
```
|
|
167
|
+
Clone a repository with the `github_cloneRepository` tool. It makes a shallow
|
|
168
|
+
checkout. It first checks for a matching Workspace. Do not use `git clone` or
|
|
169
|
+
`gh repo clone`, because they skip that check.
|
|
172
170
|
|
|
173
171
|
Deepen later only if the task needs more history:
|
|
174
172
|
|
|
@@ -104,6 +104,20 @@ function gitHubPullRequestEventGuidance(guidance) {
|
|
|
104
104
|
}
|
|
105
105
|
return merged;
|
|
106
106
|
}
|
|
107
|
+
var GITHUB_PULL_REQUEST_OUTCOME_GUIDANCE = {
|
|
108
|
+
"pull_request.review.approved": "Mention each person in People involved, or the person who asked for the pull request if that line is missing. Say who approved it and link it. Do not merge it unless asked.",
|
|
109
|
+
"pull_request.merged": "Mention each person in People involved, or the person who asked for the pull request if that line is missing. Say that it merged and link it."
|
|
110
|
+
};
|
|
111
|
+
function gitHubPullRequestCreateWatch(config) {
|
|
112
|
+
const outcomeEvents = Object.keys(
|
|
113
|
+
GITHUB_PULL_REQUEST_OUTCOME_GUIDANCE
|
|
114
|
+
);
|
|
115
|
+
return {
|
|
116
|
+
events: [.../* @__PURE__ */ new Set([...config.events, ...outcomeEvents])],
|
|
117
|
+
guidance: GITHUB_PULL_REQUEST_OUTCOME_GUIDANCE,
|
|
118
|
+
intent: config.intent
|
|
119
|
+
};
|
|
120
|
+
}
|
|
107
121
|
var GITHUB_PULL_REQUEST_SUGGESTED_EVENTS = [
|
|
108
122
|
"pull_request.checks.failed",
|
|
109
123
|
"pull_request.comment.created",
|
|
@@ -1910,6 +1924,7 @@ export {
|
|
|
1910
1924
|
gitHubDeploymentSourceSubscribable,
|
|
1911
1925
|
GITHUB_PULL_REQUEST_EVENTS,
|
|
1912
1926
|
gitHubPullRequestEventGuidance,
|
|
1927
|
+
gitHubPullRequestCreateWatch,
|
|
1913
1928
|
GITHUB_PULL_REQUEST_SUGGESTED_EVENTS,
|
|
1914
1929
|
GITHUB_PULL_REQUEST_MATCH_FIELDS,
|
|
1915
1930
|
GITHUB_PULL_REQUEST_IDENTIFIER,
|
|
@@ -15,6 +15,12 @@ export interface GitHubPullRequestSubscriptionConfig {
|
|
|
15
15
|
events: GitHubPullRequestEvent[];
|
|
16
16
|
intent: string;
|
|
17
17
|
}
|
|
18
|
+
/** Build the Watch created after Junior creates a pull request. */
|
|
19
|
+
export declare function gitHubPullRequestCreateWatch(config: GitHubPullRequestSubscriptionConfig): {
|
|
20
|
+
events: GitHubPullRequestEvent[];
|
|
21
|
+
guidance: Partial<Record<GitHubPullRequestEvent, string>>;
|
|
22
|
+
intent: string;
|
|
23
|
+
};
|
|
18
24
|
export declare const GITHUB_PULL_REQUEST_SUGGESTED_EVENTS: readonly ["pull_request.checks.failed", "pull_request.comment.created", "pull_request.ready_for_review", "pull_request.review.changes_requested", "pull_request.review.commented", "pull_request.review_comment.created", "pull_request.merged", "pull_request.closed_unmerged"];
|
|
19
25
|
/** Exact values GitHub may set on pull request event data. Missing keys do not match. */
|
|
20
26
|
export declare const GITHUB_PULL_REQUEST_MATCH_FIELDS: Record<string, {
|
package/dist/index.js
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
credentialUnavailable,
|
|
31
31
|
gitHubDeploymentSourceSubscribable,
|
|
32
32
|
gitHubIssueSubscribable,
|
|
33
|
+
gitHubPullRequestCreateWatch,
|
|
33
34
|
gitHubPullRequestEventGuidance,
|
|
34
35
|
gitHubPullRequestSubscribable,
|
|
35
36
|
gitHubReleaseSourceSubscribable,
|
|
@@ -51,7 +52,7 @@ import {
|
|
|
51
52
|
readGrantPermissions,
|
|
52
53
|
requireEnv,
|
|
53
54
|
resolveUserAccount
|
|
54
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-D75O3CY3.js";
|
|
55
56
|
|
|
56
57
|
// src/plugin.ts
|
|
57
58
|
import {
|
|
@@ -1447,17 +1448,17 @@ async function gitHubPullRequestStructuredResult(ctx, input, result, subscriptio
|
|
|
1447
1448
|
...base
|
|
1448
1449
|
};
|
|
1449
1450
|
}
|
|
1451
|
+
const watch = gitHubPullRequestCreateWatch(subscriptionConfig);
|
|
1450
1452
|
try {
|
|
1451
1453
|
const subscription = await ctx.events.subscribe({
|
|
1452
|
-
|
|
1453
|
-
intent: subscriptionConfig.intent,
|
|
1454
|
+
...watch,
|
|
1454
1455
|
resource: base.subscribable
|
|
1455
1456
|
});
|
|
1456
1457
|
const data = gitHubPullRequestToolResult(
|
|
1457
1458
|
input,
|
|
1458
1459
|
result,
|
|
1459
1460
|
ctx.events.canSubscribe,
|
|
1460
|
-
|
|
1461
|
+
watch.events
|
|
1461
1462
|
);
|
|
1462
1463
|
return {
|
|
1463
1464
|
objectAnnotations,
|
|
@@ -3518,6 +3519,102 @@ function normalizeGitHubPullRequestLinkedIssues(args) {
|
|
|
3518
3519
|
} : void 0;
|
|
3519
3520
|
}
|
|
3520
3521
|
|
|
3522
|
+
// src/webhooks/pull-request-people.ts
|
|
3523
|
+
import { z as z19 } from "zod";
|
|
3524
|
+
var PEOPLE_EVENTS = /* @__PURE__ */ new Set([
|
|
3525
|
+
"pull_request.review.approved",
|
|
3526
|
+
"pull_request.merged"
|
|
3527
|
+
]);
|
|
3528
|
+
var MAX_PEOPLE = 20;
|
|
3529
|
+
var PAGE_SIZE = 100;
|
|
3530
|
+
var MAX_PAGES = 3;
|
|
3531
|
+
var CO_AUTHOR_TRAILER = /^co-authored-by:[^<\n]*<([^>\s]+@[^>\s]+)>\s*$/gim;
|
|
3532
|
+
var GITHUB_NOREPLY_EMAIL = /^(?:\d+\+)?(?<login>[^@+]+)@users\.noreply\.github\.com$/i;
|
|
3533
|
+
var webhookSchema = z19.object({
|
|
3534
|
+
pull_request: z19.object({
|
|
3535
|
+
assignees: z19.array(z19.object({ login: z19.string().min(1) })).nullish(),
|
|
3536
|
+
number: z19.number(),
|
|
3537
|
+
user: z19.object({ login: z19.string().min(1) }).nullish()
|
|
3538
|
+
}),
|
|
3539
|
+
repository: z19.object({ full_name: z19.string().min(1) })
|
|
3540
|
+
});
|
|
3541
|
+
var commitPageSchema = z19.array(
|
|
3542
|
+
z19.object({
|
|
3543
|
+
author: z19.object({ login: z19.string().min(1) }).nullish(),
|
|
3544
|
+
commit: z19.object({ message: z19.string() })
|
|
3545
|
+
})
|
|
3546
|
+
);
|
|
3547
|
+
async function loadGitHubPullRequestPeople(args) {
|
|
3548
|
+
const people = /* @__PURE__ */ new Map();
|
|
3549
|
+
const addLogin = (login) => {
|
|
3550
|
+
if (/\[bot\]$/i.test(login)) return;
|
|
3551
|
+
const key = `github:${login.toLowerCase()}`;
|
|
3552
|
+
if (!people.has(key))
|
|
3553
|
+
people.set(key, { provider: "github", handle: login });
|
|
3554
|
+
};
|
|
3555
|
+
const addEmail = (value) => {
|
|
3556
|
+
const email = value.toLowerCase();
|
|
3557
|
+
const login = GITHUB_NOREPLY_EMAIL.exec(email)?.groups?.login;
|
|
3558
|
+
if (login) addLogin(login);
|
|
3559
|
+
else if (!people.has(`email:${email}`)) {
|
|
3560
|
+
people.set(`email:${email}`, { email });
|
|
3561
|
+
}
|
|
3562
|
+
};
|
|
3563
|
+
args.logins.forEach(addLogin);
|
|
3564
|
+
for (let page = 1; page <= MAX_PAGES; page += 1) {
|
|
3565
|
+
const commits = commitPageSchema.parse(
|
|
3566
|
+
await args.loadPage(page, PAGE_SIZE)
|
|
3567
|
+
);
|
|
3568
|
+
for (const commit of commits) {
|
|
3569
|
+
if (commit.author) addLogin(commit.author.login);
|
|
3570
|
+
for (const match of commit.commit.message.matchAll(CO_AUTHOR_TRAILER)) {
|
|
3571
|
+
addEmail(match[1]);
|
|
3572
|
+
}
|
|
3573
|
+
}
|
|
3574
|
+
if (commits.length < PAGE_SIZE) break;
|
|
3575
|
+
}
|
|
3576
|
+
return [...people.values()].slice(0, MAX_PEOPLE);
|
|
3577
|
+
}
|
|
3578
|
+
async function withGitHubPullRequestPeople(args) {
|
|
3579
|
+
const { hasMatch } = args;
|
|
3580
|
+
const peopleEvents = args.events.filter(
|
|
3581
|
+
(event) => PEOPLE_EVENTS.has(event.eventType)
|
|
3582
|
+
);
|
|
3583
|
+
const parsed = webhookSchema.safeParse(args.body);
|
|
3584
|
+
if (peopleEvents.length === 0 || !hasMatch || !args.load || !parsed.success) {
|
|
3585
|
+
return args.events;
|
|
3586
|
+
}
|
|
3587
|
+
const matches = await Promise.all(
|
|
3588
|
+
peopleEvents.map((event) => hasMatch(event))
|
|
3589
|
+
);
|
|
3590
|
+
if (!matches.some(Boolean)) return args.events;
|
|
3591
|
+
const { pull_request: pullRequest, repository } = parsed.data;
|
|
3592
|
+
const target = {
|
|
3593
|
+
logins: [
|
|
3594
|
+
...pullRequest.user ? [pullRequest.user.login] : [],
|
|
3595
|
+
...(pullRequest.assignees ?? []).map((assignee) => assignee.login)
|
|
3596
|
+
],
|
|
3597
|
+
number: pullRequest.number,
|
|
3598
|
+
repositoryFullName: repository.full_name
|
|
3599
|
+
};
|
|
3600
|
+
let people;
|
|
3601
|
+
try {
|
|
3602
|
+
people = await args.load(target);
|
|
3603
|
+
} catch (error) {
|
|
3604
|
+
args.log?.error("GitHub pull request people lookup failed", {
|
|
3605
|
+
deliveryId: args.deliveryId,
|
|
3606
|
+
errorType: error instanceof Error ? error.name : "UnknownError",
|
|
3607
|
+
number: target.number,
|
|
3608
|
+
repository: target.repositoryFullName
|
|
3609
|
+
});
|
|
3610
|
+
return args.events;
|
|
3611
|
+
}
|
|
3612
|
+
if (people.length === 0) return args.events;
|
|
3613
|
+
return args.events.map(
|
|
3614
|
+
(event) => PEOPLE_EVENTS.has(event.eventType) ? { ...event, people } : event
|
|
3615
|
+
);
|
|
3616
|
+
}
|
|
3617
|
+
|
|
3521
3618
|
// src/webhooks/handler.ts
|
|
3522
3619
|
function verifyGitHubSignature(body, signature, secret) {
|
|
3523
3620
|
if (!secret || !signature.startsWith("sha256=")) return false;
|
|
@@ -3710,15 +3807,22 @@ function createGitHubWebhookRoute(args) {
|
|
|
3710
3807
|
log: args.log,
|
|
3711
3808
|
privateKeyEnv: args.privateKeyEnv
|
|
3712
3809
|
}) : void 0;
|
|
3713
|
-
const
|
|
3810
|
+
const hasMatch = args.events.hasMatch;
|
|
3811
|
+
const events = await withGitHubPullRequestPeople({
|
|
3714
3812
|
body,
|
|
3715
|
-
botEmail,
|
|
3716
|
-
...checkSuiteFacts ? { checkSuiteFacts } : void 0,
|
|
3717
3813
|
deliveryId,
|
|
3718
|
-
|
|
3814
|
+
events: normalizeGitHubEvents({
|
|
3815
|
+
body,
|
|
3816
|
+
botEmail,
|
|
3817
|
+
...checkSuiteFacts ? { checkSuiteFacts } : void 0,
|
|
3818
|
+
deliveryId,
|
|
3819
|
+
eventName
|
|
3820
|
+
}),
|
|
3821
|
+
hasMatch,
|
|
3822
|
+
load: args.loadPullRequestPeople,
|
|
3823
|
+
log: args.log
|
|
3719
3824
|
});
|
|
3720
3825
|
const feedbackTarget = pullRequestFeedbackTarget(events);
|
|
3721
|
-
const hasMatch = args.events.hasMatch;
|
|
3722
3826
|
const feedbackHasMatch = feedbackTarget && hasMatch ? (await Promise.all(events.map((event) => hasMatch(event)))).some(
|
|
3723
3827
|
Boolean
|
|
3724
3828
|
) : false;
|
|
@@ -3749,18 +3853,18 @@ function createGitHubWebhookRoute(args) {
|
|
|
3749
3853
|
}
|
|
3750
3854
|
|
|
3751
3855
|
// src/pull-request-outcomes/commit-composition.ts
|
|
3752
|
-
import { z as
|
|
3753
|
-
var canonicalCommitSchema =
|
|
3754
|
-
authorEmail:
|
|
3755
|
-
authorLogin:
|
|
3856
|
+
import { z as z20 } from "zod";
|
|
3857
|
+
var canonicalCommitSchema = z20.object({
|
|
3858
|
+
authorEmail: z20.string().nullable(),
|
|
3859
|
+
authorLogin: z20.string().nullable()
|
|
3756
3860
|
}).strict();
|
|
3757
|
-
var providerCommitSchema =
|
|
3758
|
-
author:
|
|
3759
|
-
commit:
|
|
3760
|
-
author:
|
|
3861
|
+
var providerCommitSchema = z20.object({
|
|
3862
|
+
author: z20.object({ login: z20.string() }).passthrough().nullable(),
|
|
3863
|
+
commit: z20.object({
|
|
3864
|
+
author: z20.object({ email: z20.string() }).passthrough().nullable()
|
|
3761
3865
|
}).passthrough()
|
|
3762
3866
|
}).passthrough();
|
|
3763
|
-
var
|
|
3867
|
+
var commitPageSchema2 = z20.array(providerCommitSchema).transform(
|
|
3764
3868
|
(commits) => commits.map(
|
|
3765
3869
|
(commit) => canonicalCommitSchema.parse({
|
|
3766
3870
|
authorEmail: commit.commit.author?.email ?? null,
|
|
@@ -3768,8 +3872,8 @@ var commitPageSchema = z19.array(providerCommitSchema).transform(
|
|
|
3768
3872
|
})
|
|
3769
3873
|
)
|
|
3770
3874
|
);
|
|
3771
|
-
var
|
|
3772
|
-
var
|
|
3875
|
+
var PAGE_SIZE2 = 100;
|
|
3876
|
+
var MAX_PAGES2 = 3;
|
|
3773
3877
|
var MAX_LISTED_COMMITS = 250;
|
|
3774
3878
|
async function classifyGitHubPullRequestCommitComposition(args) {
|
|
3775
3879
|
const botEmail = args.botEmail.trim().toLowerCase();
|
|
@@ -3780,9 +3884,9 @@ async function classifyGitHubPullRequestCommitComposition(args) {
|
|
|
3780
3884
|
}
|
|
3781
3885
|
let foundCommit = false;
|
|
3782
3886
|
let inspectedCommits = 0;
|
|
3783
|
-
for (let page = 1; page <=
|
|
3784
|
-
const commits =
|
|
3785
|
-
await args.loadPage(page,
|
|
3887
|
+
for (let page = 1; page <= MAX_PAGES2; page += 1) {
|
|
3888
|
+
const commits = commitPageSchema2.parse(
|
|
3889
|
+
await args.loadPage(page, PAGE_SIZE2)
|
|
3786
3890
|
);
|
|
3787
3891
|
inspectedCommits += commits.length;
|
|
3788
3892
|
for (const commit of commits) {
|
|
@@ -3793,8 +3897,8 @@ async function classifyGitHubPullRequestCommitComposition(args) {
|
|
|
3793
3897
|
}
|
|
3794
3898
|
}
|
|
3795
3899
|
if (inspectedCommits >= MAX_LISTED_COMMITS) return void 0;
|
|
3796
|
-
if (commits.length <
|
|
3797
|
-
if (page ===
|
|
3900
|
+
if (commits.length < PAGE_SIZE2) break;
|
|
3901
|
+
if (page === MAX_PAGES2) return void 0;
|
|
3798
3902
|
}
|
|
3799
3903
|
return foundCommit ? "junior_only" : void 0;
|
|
3800
3904
|
}
|
|
@@ -4918,41 +5022,43 @@ function githubPlugin(options = {}) {
|
|
|
4918
5022
|
};
|
|
4919
5023
|
},
|
|
4920
5024
|
routes(ctx) {
|
|
5025
|
+
const loadPullRequestCommitPage = async (pullRequest) => {
|
|
5026
|
+
const [owner, name, ...extra] = pullRequest.repositoryFullName.split("/");
|
|
5027
|
+
if (!owner || !name || extra.length > 0) {
|
|
5028
|
+
throw new Error(
|
|
5029
|
+
"GitHub pull request commits received an invalid repository name"
|
|
5030
|
+
);
|
|
5031
|
+
}
|
|
5032
|
+
const token = await issueInstallationToken({
|
|
5033
|
+
appIdEnv,
|
|
5034
|
+
privateKeyEnv,
|
|
5035
|
+
installationIdEnv,
|
|
5036
|
+
permissions: { pull_requests: "read" },
|
|
5037
|
+
repositories: [name]
|
|
5038
|
+
});
|
|
5039
|
+
return async (page, perPage) => await githubRequest(
|
|
5040
|
+
"https://api.github.com",
|
|
5041
|
+
`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/pulls/${pullRequest.number}/commits?per_page=${perPage}&page=${page}`,
|
|
5042
|
+
{ token: token.token }
|
|
5043
|
+
);
|
|
5044
|
+
};
|
|
4921
5045
|
return [
|
|
4922
5046
|
createGitHubWebhookRoute({
|
|
4923
5047
|
annotations: ctx.annotations,
|
|
4924
5048
|
appIdEnv,
|
|
4925
5049
|
botEmail: () => readEnv(botEmailEnv),
|
|
4926
|
-
classifyPullRequestCommits: async ({
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
})
|
|
4930
|
-
const [owner, name, ...extra] = repositoryFullName.split("/");
|
|
4931
|
-
if (!owner || !name || extra.length > 0) {
|
|
4932
|
-
throw new Error(
|
|
4933
|
-
"GitHub pull request commit classification received an invalid repository name"
|
|
4934
|
-
);
|
|
4935
|
-
}
|
|
4936
|
-
const token = await issueInstallationToken({
|
|
4937
|
-
appIdEnv,
|
|
4938
|
-
privateKeyEnv,
|
|
4939
|
-
installationIdEnv,
|
|
4940
|
-
permissions: { pull_requests: "read" },
|
|
4941
|
-
repositories: [name]
|
|
4942
|
-
});
|
|
4943
|
-
return await classifyGitHubPullRequestCommitComposition({
|
|
4944
|
-
botEmail: requireEnv(botEmailEnv),
|
|
4945
|
-
loadPage: async (page, perPage) => await githubRequest(
|
|
4946
|
-
"https://api.github.com",
|
|
4947
|
-
`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/pulls/${number}/commits?per_page=${perPage}&page=${page}`,
|
|
4948
|
-
{ token: token.token }
|
|
4949
|
-
)
|
|
4950
|
-
});
|
|
4951
|
-
},
|
|
5050
|
+
classifyPullRequestCommits: async (pullRequest) => await classifyGitHubPullRequestCommitComposition({
|
|
5051
|
+
botEmail: requireEnv(botEmailEnv),
|
|
5052
|
+
loadPage: await loadPullRequestCommitPage(pullRequest)
|
|
5053
|
+
}),
|
|
4952
5054
|
codeChanges: ctx.codeChanges,
|
|
4953
5055
|
db: ctx.db,
|
|
4954
5056
|
installationId: () => readEnv(installationIdEnv),
|
|
4955
5057
|
installationIdEnv,
|
|
5058
|
+
loadPullRequestPeople: async (pullRequest) => await loadGitHubPullRequestPeople({
|
|
5059
|
+
logins: pullRequest.logins,
|
|
5060
|
+
loadPage: await loadPullRequestCommitPage(pullRequest)
|
|
5061
|
+
}),
|
|
4956
5062
|
log: ctx.log,
|
|
4957
5063
|
markFeedbackReviewing: async ({ commentId, commentKind, repo }) => {
|
|
4958
5064
|
const [owner, name, ...extra] = repo.split("/");
|
package/dist/testing.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { CodeChangePublisher, PluginConversationAnnotations, PluginLogger, PluginRoute, EventPublisher } from "@sentry/junior-plugin-api";
|
|
1
|
+
import type { CodeChangePublisher, PluginConversationAnnotations, PluginLogger, PluginRoute, EventPerson, EventPublisher } from "@sentry/junior-plugin-api";
|
|
2
2
|
import type { GitHubDb } from "../db/database.js";
|
|
3
3
|
import type { GitHubPullRequestCommitComposition } from "../db/schema.js";
|
|
4
|
+
import { type GitHubPullRequestPeopleTarget } from "./pull-request-people.js";
|
|
4
5
|
interface FeedbackTarget {
|
|
5
6
|
commentId: number;
|
|
6
7
|
commentKind: "conversation" | "review";
|
|
@@ -19,6 +20,8 @@ export declare function createGitHubWebhookRoute(args: {
|
|
|
19
20
|
db: GitHubDb;
|
|
20
21
|
installationId(): string | undefined;
|
|
21
22
|
installationIdEnv: string;
|
|
23
|
+
/** Load the people involved in a pull request for approvals and merges. */
|
|
24
|
+
loadPullRequestPeople?(target: GitHubPullRequestPeopleTarget): Promise<EventPerson[]>;
|
|
22
25
|
log?: Pick<PluginLogger, "error">;
|
|
23
26
|
markFeedbackReviewing(input: FeedbackTarget): Promise<void>;
|
|
24
27
|
privateKeyEnv: string;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { EventInput, EventPerson, EventPublisher, PluginLogger } from "@sentry/junior-plugin-api";
|
|
2
|
+
/** The pull request that a people lookup reads. */
|
|
3
|
+
export interface GitHubPullRequestPeopleTarget {
|
|
4
|
+
/** Pull request author and assignees from the webhook. */
|
|
5
|
+
logins: string[];
|
|
6
|
+
number: number;
|
|
7
|
+
repositoryFullName: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Collect the people involved in a pull request: the webhook logins, commit
|
|
11
|
+
* authors, and `Co-authored-by` trailers. Junior adds a trailer for each
|
|
12
|
+
* person whose instructions a run accepted, so people who steer later are
|
|
13
|
+
* included. Noreply emails become logins. `[bot]` accounts are skipped.
|
|
14
|
+
*/
|
|
15
|
+
export declare function loadGitHubPullRequestPeople(args: {
|
|
16
|
+
logins: readonly string[];
|
|
17
|
+
loadPage(page: number, perPage: number): Promise<unknown>;
|
|
18
|
+
}): Promise<EventPerson[]>;
|
|
19
|
+
/**
|
|
20
|
+
* Add the people involved to approval and merge events. It loads them only
|
|
21
|
+
* when a Watch or event automation matches, so other deliveries make no extra
|
|
22
|
+
* GitHub calls. People only add context, so a failed load keeps the events.
|
|
23
|
+
*/
|
|
24
|
+
export declare function withGitHubPullRequestPeople(args: {
|
|
25
|
+
body: unknown;
|
|
26
|
+
deliveryId: string;
|
|
27
|
+
events: EventInput[];
|
|
28
|
+
hasMatch: EventPublisher["hasMatch"];
|
|
29
|
+
load?(target: GitHubPullRequestPeopleTarget): Promise<EventPerson[]>;
|
|
30
|
+
log?: Pick<PluginLogger, "error">;
|
|
31
|
+
}): Promise<EventInput[]>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior-github",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.251.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@sinclair/typebox": "^0.34.49",
|
|
32
32
|
"drizzle-orm": "^0.45.2",
|
|
33
33
|
"zod": "^4.5.4",
|
|
34
|
-
"@sentry/junior-plugin-api": "0.
|
|
34
|
+
"@sentry/junior-plugin-api": "0.251.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@oxlint/plugins": "1.79.0",
|
|
@@ -7,14 +7,14 @@ description: Work with GitHub repositories, source code, branches, commits, pull
|
|
|
7
7
|
|
|
8
8
|
Use `git` and `gh` for repository work.
|
|
9
9
|
|
|
10
|
-
| Action | Tool / command
|
|
11
|
-
| ------------------------------- |
|
|
12
|
-
| Create PR | `github_createPullRequest` (not `gh pr create`)
|
|
13
|
-
| Update PR title/body/base/state | `github_updatePullRequest`
|
|
14
|
-
| Mark PR ready / draft | `github_updatePullRequest` with `draft: false` / `draft: true`
|
|
15
|
-
| Submit PR review | `github_submitPullRequestReview`
|
|
16
|
-
| Resolve review thread | `github_resolvePullRequestReviewThread`
|
|
17
|
-
| Clone missing repo | `github_cloneRepository
|
|
10
|
+
| Action | Tool / command |
|
|
11
|
+
| ------------------------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
12
|
+
| Create PR | `github_createPullRequest` (not `gh pr create`) |
|
|
13
|
+
| Update PR title/body/base/state | `github_updatePullRequest` |
|
|
14
|
+
| Mark PR ready / draft | `github_updatePullRequest` with `draft: false` / `draft: true` |
|
|
15
|
+
| Submit PR review | `github_submitPullRequestReview` |
|
|
16
|
+
| Resolve review thread | `github_resolvePullRequestReviewThread` |
|
|
17
|
+
| Clone missing repo | `github_cloneRepository` (not `git clone` or `gh repo clone`); on Workspace match error use `switchWorkspace` |
|
|
18
18
|
|
|
19
19
|
## Open when needed
|
|
20
20
|
|
|
@@ -12,7 +12,7 @@ If the user omits `owner/repo`, run `jr-rpc config get github.repo`. Then pass `
|
|
|
12
12
|
| ---------------------------- | ------------------------------------------------------------------------------------ |
|
|
13
13
|
| `github.actions.read` | `gh run list`, `gh run view`, `gh run watch`, `gh workflow list`, `gh workflow view` |
|
|
14
14
|
| `github.actions.write` | `gh workflow run`, `gh run rerun`, `gh run cancel` |
|
|
15
|
-
| `github.contents.read` | `
|
|
15
|
+
| `github.contents.read` | `github_cloneRepository`, `git fetch` |
|
|
16
16
|
| `github.contents.write` | Git smart-HTTP `git push` only |
|
|
17
17
|
| `github.workflows.write` | Workflow-file changes on push |
|
|
18
18
|
| `github.pull-requests.read` | `gh pr view`, `gh pr list`, `gh pr diff`, `gh pr checks` |
|
|
@@ -22,7 +22,7 @@ If the user omits `owner/repo`, run `jr-rpc config get github.repo`. Then pass `
|
|
|
22
22
|
|
|
23
23
|
| Operation | Command |
|
|
24
24
|
| ----------------------- | ----------------------------------------------------------------------------------------------- |
|
|
25
|
-
| Clone (default shallow) | `
|
|
25
|
+
| Clone (default shallow) | `github_cloneRepository`. Do not use `git clone` or `gh repo clone`. |
|
|
26
26
|
| Fetch bounded base | `git -C DIR fetch --depth=N origin BASE:refs/remotes/origin/BASE` |
|
|
27
27
|
| Deepen base | `git -C DIR fetch --deepen=N origin BASE:refs/remotes/origin/BASE` |
|
|
28
28
|
| Unshallow | `git -C DIR fetch --unshallow origin` |
|
|
@@ -51,7 +51,7 @@ If the user omits `owner/repo`, run `jr-rpc config get github.repo`. Then pass `
|
|
|
51
51
|
|
|
52
52
|
## Notes
|
|
53
53
|
|
|
54
|
-
- Prefer `--json` where available.
|
|
54
|
+
- Prefer `--json` where available.
|
|
55
55
|
- A local commit does not call GitHub. A push uses installation credentials. Workflow changes also need `workflows.write`.
|
|
56
56
|
- Deepen a shallow clone before work that needs old history. Do not force-push to work around missing history.
|
|
57
57
|
- Push `head` and read the default `base` before `github_createPullRequest`.
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
| Problem | Action |
|
|
4
4
|
| ---------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
|
|
5
5
|
| `gh` reports an unknown command | Report that the GitHub plugin is unavailable. Do not install it. |
|
|
6
|
-
| Clone rejects `--depth` | Put clone flags after `--`: `gh repo clone owner/repo -- --depth=1`. |
|
|
7
6
|
| Repository is missing or wrong | Read `github.repo`, then pass `--repo owner/repo`. |
|
|
8
7
|
| GraphQL cannot find the repository | Check `owner/repo` and the GitHub App installation. |
|
|
9
8
|
| `junior-auth-required` with `user-write` | Follow the private OAuth prompt. Never ask for a pasted token. |
|
|
@@ -5,7 +5,7 @@ Open this when making repository edits, not for read-only inspection.
|
|
|
5
5
|
## Resolve and inspect
|
|
6
6
|
|
|
7
7
|
1. Identify repo, checkout, branches, worktree, package manager, and relevant checks.
|
|
8
|
-
2. Prefer an existing checkout or matching Workspace; otherwise clone
|
|
8
|
+
2. Prefer an existing checkout or matching Workspace; otherwise clone with `github_cloneRepository`.
|
|
9
9
|
3. If clone returns a Workspace match error, switch Workspace or pass `allowAdHoc=true`.
|
|
10
10
|
4. Shallow clones are for fast inspection. Before rebase, merge-base, blame, or base comparison, fetch/deepen the needed refs. Never force-push around missing history.
|
|
11
11
|
5. For edits, pick the smallest credible validation path. Capture a baseline when a failure may be pre-existing.
|