better-review 0.2.0 → 0.3.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
|
@@ -108,6 +108,11 @@ packages/shared schémas zod, parseur de diff unifié (empreinte de hunk partag
|
|
|
108
108
|
```
|
|
109
109
|
|
|
110
110
|
- Les diffs sont demandés entre deux SHA (immuables) et mis en cache définitivement.
|
|
111
|
+
- La coloration syntaxique porte sur le fichier entier (blobs désignés par les lignes `index` du diff, mis en cache
|
|
112
|
+
définitivement) : un commentaire ou une chaîne ouvert hors d'un hunk est coloré correctement. Fichier trop gros ou
|
|
113
|
+
indisponible : seules les lignes du diff sont colorées.
|
|
114
|
+
- Un commentaire, une réponse ou un nouveau thread s'affiche dès la réponse de GitHub ; la PR est rechargée ensuite en
|
|
115
|
+
arrière-plan.
|
|
111
116
|
- L'inbox fait une recherche « ids seulement » par section puis charge chaque PR une seule fois
|
|
112
117
|
(≈ 1 point de quota GraphQL par PR).
|
|
113
118
|
- Une PR ouverte est surveillée toutes les 30 s, l'inbox toutes les 2 min ; un changement côté GitHub
|
package/dist/cli.js
CHANGED
|
@@ -5852,6 +5852,8 @@ function parseUnifiedDiff(text) {
|
|
|
5852
5852
|
status: "modified",
|
|
5853
5853
|
binary: false,
|
|
5854
5854
|
truncated: false,
|
|
5855
|
+
oldBlob: null,
|
|
5856
|
+
newBlob: null,
|
|
5855
5857
|
additions: 0,
|
|
5856
5858
|
deletions: 0,
|
|
5857
5859
|
hunks: []
|
|
@@ -5887,7 +5889,12 @@ function parseUnifiedDiff(text) {
|
|
|
5887
5889
|
f.path = unquote(line.slice("rename to ".length));
|
|
5888
5890
|
f.status = "renamed";
|
|
5889
5891
|
} else if (line.startsWith("Binary files ") || line === "GIT binary patch") f.binary = true;
|
|
5890
|
-
else if (line.startsWith("
|
|
5892
|
+
else if (line.startsWith("index ")) {
|
|
5893
|
+
const ids = /^index ([0-9a-f]+)\.\.([0-9a-f]+)/.exec(line);
|
|
5894
|
+
const blob = (id) => id === void 0 || /^0+$/.test(id) ? null : id;
|
|
5895
|
+
f.oldBlob = blob(ids?.[1]);
|
|
5896
|
+
f.newBlob = blob(ids?.[2]);
|
|
5897
|
+
} else if (line.startsWith("--- ")) {
|
|
5891
5898
|
const p = line.slice(4);
|
|
5892
5899
|
if (p !== "/dev/null") f.oldPath = stripPrefix(p);
|
|
5893
5900
|
} else if (line.startsWith("+++ ")) {
|
|
@@ -25774,153 +25781,6 @@ var GitHub = class {
|
|
|
25774
25781
|
}
|
|
25775
25782
|
};
|
|
25776
25783
|
|
|
25777
|
-
// ../server/src/actions.ts
|
|
25778
|
-
async function setFileViewed(gh, prId, path, viewed) {
|
|
25779
|
-
const mutation = viewed ? "markFileAsViewed" : "unmarkFileAsViewed";
|
|
25780
|
-
await gh.graphql(
|
|
25781
|
-
`mutation($id: ID!, $path: String!) { ${mutation}(input: { pullRequestId: $id, path: $path }) { clientMutationId } }`,
|
|
25782
|
-
{ id: prId, path }
|
|
25783
|
-
);
|
|
25784
|
-
}
|
|
25785
|
-
async function replyToThread(gh, threadId, body, pendingReviewId) {
|
|
25786
|
-
const data = await gh.graphql(
|
|
25787
|
-
`mutation($thread: ID!, $body: String!, $review: ID) {
|
|
25788
|
-
addPullRequestReviewThreadReply(input: { pullRequestReviewThreadId: $thread, body: $body, pullRequestReviewId: $review }) {
|
|
25789
|
-
comment { id }
|
|
25790
|
-
}
|
|
25791
|
-
}`,
|
|
25792
|
-
{ thread: threadId, body, review: pendingReviewId }
|
|
25793
|
-
);
|
|
25794
|
-
return data.addPullRequestReviewThreadReply.comment.id;
|
|
25795
|
-
}
|
|
25796
|
-
async function setThreadResolved(gh, threadId, resolved) {
|
|
25797
|
-
const mutation = resolved ? "resolveReviewThread" : "unresolveReviewThread";
|
|
25798
|
-
await gh.graphql(`mutation($id: ID!) { ${mutation}(input: { threadId: $id }) { thread { id } } }`, {
|
|
25799
|
-
id: threadId
|
|
25800
|
-
});
|
|
25801
|
-
}
|
|
25802
|
-
function threadInput(t2) {
|
|
25803
|
-
if (t2.subjectType === "FILE") return { path: t2.path, body: t2.body, subjectType: "FILE" };
|
|
25804
|
-
return {
|
|
25805
|
-
path: t2.path,
|
|
25806
|
-
body: t2.body,
|
|
25807
|
-
subjectType: "LINE",
|
|
25808
|
-
line: t2.line,
|
|
25809
|
-
side: t2.side,
|
|
25810
|
-
...t2.startLine !== null && t2.startLine !== t2.line ? { startLine: t2.startLine, startSide: t2.startSide ?? t2.side } : {}
|
|
25811
|
-
};
|
|
25812
|
-
}
|
|
25813
|
-
async function addThread(gh, prId, pendingReviewId, thread, mode) {
|
|
25814
|
-
if (mode === "single" && pendingReviewId) {
|
|
25815
|
-
throw new GitHubError(
|
|
25816
|
-
"Une review est en attente : ajoutez le commentaire \xE0 la review ou soumettez-la d\u2019abord.",
|
|
25817
|
-
409
|
|
25818
|
-
);
|
|
25819
|
-
}
|
|
25820
|
-
let reviewId = pendingReviewId;
|
|
25821
|
-
if (!reviewId) {
|
|
25822
|
-
const created = await gh.graphql(
|
|
25823
|
-
`mutation($id: ID!) { addPullRequestReview(input: { pullRequestId: $id }) { pullRequestReview { id } } }`,
|
|
25824
|
-
{ id: prId }
|
|
25825
|
-
);
|
|
25826
|
-
reviewId = created.addPullRequestReview.pullRequestReview.id;
|
|
25827
|
-
}
|
|
25828
|
-
try {
|
|
25829
|
-
await gh.graphql(
|
|
25830
|
-
`mutation($input: AddPullRequestReviewThreadInput!) { addPullRequestReviewThread(input: $input) { thread { id } } }`,
|
|
25831
|
-
{ input: { pullRequestReviewId: reviewId, ...threadInput(thread) } }
|
|
25832
|
-
);
|
|
25833
|
-
} catch (error62) {
|
|
25834
|
-
if (!pendingReviewId) await discardReview(gh, reviewId).catch(() => void 0);
|
|
25835
|
-
throw error62;
|
|
25836
|
-
}
|
|
25837
|
-
if (mode === "single") await submitReview(gh, prId, reviewId, "COMMENT", "");
|
|
25838
|
-
}
|
|
25839
|
-
async function submitReview(gh, prId, pendingReviewId, event, body) {
|
|
25840
|
-
if (pendingReviewId) {
|
|
25841
|
-
await gh.graphql(
|
|
25842
|
-
`mutation($id: ID!, $event: PullRequestReviewEvent!, $body: String) {
|
|
25843
|
-
submitPullRequestReview(input: { pullRequestReviewId: $id, event: $event, body: $body }) { pullRequestReview { id } }
|
|
25844
|
-
}`,
|
|
25845
|
-
{ id: pendingReviewId, event, body: body || null }
|
|
25846
|
-
);
|
|
25847
|
-
return;
|
|
25848
|
-
}
|
|
25849
|
-
await gh.graphql(
|
|
25850
|
-
`mutation($input: AddPullRequestReviewInput!) { addPullRequestReview(input: $input) { pullRequestReview { id } } }`,
|
|
25851
|
-
{ input: { pullRequestId: prId, event, ...body ? { body } : {} } }
|
|
25852
|
-
);
|
|
25853
|
-
}
|
|
25854
|
-
async function discardReview(gh, reviewId) {
|
|
25855
|
-
await gh.graphql(
|
|
25856
|
-
`mutation($id: ID!) { deletePullRequestReview(input: { pullRequestReviewId: $id }) { pullRequestReview { id } } }`,
|
|
25857
|
-
{ id: reviewId }
|
|
25858
|
-
);
|
|
25859
|
-
}
|
|
25860
|
-
async function addIssueComment(gh, subjectId, body) {
|
|
25861
|
-
await gh.graphql(
|
|
25862
|
-
`mutation($id: ID!, $body: String!) { addComment(input: { subjectId: $id, body: $body }) { clientMutationId } }`,
|
|
25863
|
-
{
|
|
25864
|
-
id: subjectId,
|
|
25865
|
-
body
|
|
25866
|
-
}
|
|
25867
|
-
);
|
|
25868
|
-
}
|
|
25869
|
-
async function deleteReviewComment(gh, commentId) {
|
|
25870
|
-
await gh.graphql(
|
|
25871
|
-
`mutation($id: ID!) { deletePullRequestReviewComment(input: { id: $id }) { clientMutationId } }`,
|
|
25872
|
-
{ id: commentId }
|
|
25873
|
-
);
|
|
25874
|
-
}
|
|
25875
|
-
async function updateReviewComment(gh, commentId, body) {
|
|
25876
|
-
await gh.graphql(
|
|
25877
|
-
`mutation($id: ID!, $body: String!) {
|
|
25878
|
-
updatePullRequestReviewComment(input: { pullRequestReviewCommentId: $id, body: $body }) { clientMutationId }
|
|
25879
|
-
}`,
|
|
25880
|
-
{ id: commentId, body }
|
|
25881
|
-
);
|
|
25882
|
-
}
|
|
25883
|
-
async function applySuggestion(gh, ref, input2) {
|
|
25884
|
-
const branch = await gh.rest(
|
|
25885
|
-
`/repos/${ref.owner}/${ref.repo}/branches/${encodeURIComponent(input2.branch)}`
|
|
25886
|
-
);
|
|
25887
|
-
if (branch.commit.sha !== input2.expectedHeadOid) {
|
|
25888
|
-
throw new GitHubError(
|
|
25889
|
-
"La branche a boug\xE9 depuis le chargement de la PR : rechargez avant d\u2019appliquer.",
|
|
25890
|
-
409
|
|
25891
|
-
);
|
|
25892
|
-
}
|
|
25893
|
-
const encodedPath = input2.path.split("/").map(encodeURIComponent).join("/");
|
|
25894
|
-
const file2 = await gh.rest(
|
|
25895
|
-
`/repos/${ref.owner}/${ref.repo}/contents/${encodedPath}?ref=${input2.expectedHeadOid}`
|
|
25896
|
-
);
|
|
25897
|
-
const text = Buffer.from(file2.content, "base64").toString("utf8");
|
|
25898
|
-
const eol = text.includes("\r\n") ? "\r\n" : "\n";
|
|
25899
|
-
const lines = text.split(eol);
|
|
25900
|
-
if (input2.endLine > lines.length || input2.startLine < 1 || input2.startLine > input2.endLine) {
|
|
25901
|
-
throw new GitHubError("Lignes de la suggestion hors du fichier.", 422);
|
|
25902
|
-
}
|
|
25903
|
-
const replacement = input2.replacement.replace(/\r?\n$/, "").split(/\r?\n/);
|
|
25904
|
-
lines.splice(
|
|
25905
|
-
input2.startLine - 1,
|
|
25906
|
-
input2.endLine - input2.startLine + 1,
|
|
25907
|
-
...input2.replacement === "" ? [] : replacement
|
|
25908
|
-
);
|
|
25909
|
-
const res = await gh.rest(
|
|
25910
|
-
`/repos/${ref.owner}/${ref.repo}/contents/${encodedPath}`,
|
|
25911
|
-
{
|
|
25912
|
-
method: "PUT",
|
|
25913
|
-
body: {
|
|
25914
|
-
message: input2.message,
|
|
25915
|
-
content: Buffer.from(lines.join(eol), "utf8").toString("base64"),
|
|
25916
|
-
sha: file2.sha,
|
|
25917
|
-
branch: input2.branch
|
|
25918
|
-
}
|
|
25919
|
-
}
|
|
25920
|
-
);
|
|
25921
|
-
return res.commit.sha;
|
|
25922
|
-
}
|
|
25923
|
-
|
|
25924
25784
|
// ../server/src/github/queries.ts
|
|
25925
25785
|
var PR_SUMMARY = (
|
|
25926
25786
|
/* GraphQL */
|
|
@@ -26069,6 +25929,34 @@ var INBOX_FINGERPRINT_QUERY = (
|
|
|
26069
25929
|
}
|
|
26070
25930
|
`
|
|
26071
25931
|
);
|
|
25932
|
+
var REVIEW_COMMENT_FIELDS = (
|
|
25933
|
+
/* GraphQL */
|
|
25934
|
+
`
|
|
25935
|
+
fragment ReviewCommentFields on PullRequestReviewComment {
|
|
25936
|
+
id
|
|
25937
|
+
databaseId
|
|
25938
|
+
url
|
|
25939
|
+
body
|
|
25940
|
+
bodyHTML
|
|
25941
|
+
createdAt
|
|
25942
|
+
state
|
|
25943
|
+
diffHunk
|
|
25944
|
+
author {
|
|
25945
|
+
login
|
|
25946
|
+
avatarUrl
|
|
25947
|
+
}
|
|
25948
|
+
originalCommit {
|
|
25949
|
+
oid
|
|
25950
|
+
}
|
|
25951
|
+
pullRequestReview {
|
|
25952
|
+
id
|
|
25953
|
+
state
|
|
25954
|
+
}
|
|
25955
|
+
viewerCanUpdate
|
|
25956
|
+
viewerCanDelete
|
|
25957
|
+
}
|
|
25958
|
+
`
|
|
25959
|
+
);
|
|
26072
25960
|
var THREAD_FIELDS = (
|
|
26073
25961
|
/* GraphQL */
|
|
26074
25962
|
`
|
|
@@ -26092,30 +25980,11 @@ var THREAD_FIELDS = (
|
|
|
26092
25980
|
viewerCanReply
|
|
26093
25981
|
comments(first: 100) {
|
|
26094
25982
|
nodes {
|
|
26095
|
-
|
|
26096
|
-
databaseId
|
|
26097
|
-
url
|
|
26098
|
-
body
|
|
26099
|
-
bodyHTML
|
|
26100
|
-
createdAt
|
|
26101
|
-
state
|
|
26102
|
-
diffHunk
|
|
26103
|
-
author {
|
|
26104
|
-
login
|
|
26105
|
-
avatarUrl
|
|
26106
|
-
}
|
|
26107
|
-
originalCommit {
|
|
26108
|
-
oid
|
|
26109
|
-
}
|
|
26110
|
-
pullRequestReview {
|
|
26111
|
-
id
|
|
26112
|
-
state
|
|
26113
|
-
}
|
|
26114
|
-
viewerCanUpdate
|
|
26115
|
-
viewerCanDelete
|
|
25983
|
+
...ReviewCommentFields
|
|
26116
25984
|
}
|
|
26117
25985
|
}
|
|
26118
25986
|
}
|
|
25987
|
+
${REVIEW_COMMENT_FIELDS}
|
|
26119
25988
|
`
|
|
26120
25989
|
);
|
|
26121
25990
|
var PR_QUERY = (
|
|
@@ -26380,6 +26249,28 @@ var PR_FINGERPRINT_QUERY = (
|
|
|
26380
26249
|
`
|
|
26381
26250
|
);
|
|
26382
26251
|
|
|
26252
|
+
// ../server/src/github/blobs.ts
|
|
26253
|
+
var BATCH = 50;
|
|
26254
|
+
async function fetchTexts(gh, ref, expressions) {
|
|
26255
|
+
const texts = /* @__PURE__ */ new Map();
|
|
26256
|
+
for (let i = 0; i < expressions.length; i += BATCH) {
|
|
26257
|
+
const batch = expressions.slice(i, i + BATCH);
|
|
26258
|
+
const vars = { owner: ref.owner, repo: ref.repo };
|
|
26259
|
+
batch.forEach((e, k) => vars[`e${k}`] = e);
|
|
26260
|
+
const query = `query(${["$owner: String!", "$repo: String!", ...batch.map((_, k) => `$e${k}: String!`)].join(", ")}) {
|
|
26261
|
+
repository(owner: $owner, name: $repo) {
|
|
26262
|
+
${batch.map((_, k) => `f${k}: object(expression: $e${k}) { ... on Blob { text isBinary isTruncated } }`).join("\n")}
|
|
26263
|
+
}
|
|
26264
|
+
}`;
|
|
26265
|
+
const data = await gh.graphql(query, vars);
|
|
26266
|
+
batch.forEach((e, k) => {
|
|
26267
|
+
const blob = data.repository[`f${k}`];
|
|
26268
|
+
texts.set(e, blob && !blob.isBinary && !blob.isTruncated ? blob.text : null);
|
|
26269
|
+
});
|
|
26270
|
+
}
|
|
26271
|
+
return texts;
|
|
26272
|
+
}
|
|
26273
|
+
|
|
26383
26274
|
// ../server/src/inbox.ts
|
|
26384
26275
|
var SCOPE_CHUNK = 20;
|
|
26385
26276
|
function scopeChunks(config2) {
|
|
@@ -26695,8 +26586,8 @@ async function loadPr(gh, store, ref) {
|
|
|
26695
26586
|
seenHunks: store.seenHunks(key)
|
|
26696
26587
|
};
|
|
26697
26588
|
}
|
|
26698
|
-
function
|
|
26699
|
-
|
|
26589
|
+
function toComment(c) {
|
|
26590
|
+
return {
|
|
26700
26591
|
id: c.id,
|
|
26701
26592
|
databaseId: c.databaseId,
|
|
26702
26593
|
url: c.url,
|
|
@@ -26708,7 +26599,10 @@ function toThread(t2, me, lastReadId) {
|
|
|
26708
26599
|
originalCommitOid: c.originalCommit?.oid ?? null,
|
|
26709
26600
|
canUpdate: c.viewerCanUpdate,
|
|
26710
26601
|
canDelete: c.viewerCanDelete
|
|
26711
|
-
}
|
|
26602
|
+
};
|
|
26603
|
+
}
|
|
26604
|
+
function toThread(t2, me, lastReadId) {
|
|
26605
|
+
const comments = t2.comments.nodes.map(toComment);
|
|
26712
26606
|
const readIdx = lastReadId ? comments.findIndex((c) => c.id === lastReadId) : -1;
|
|
26713
26607
|
const unread = comments.slice(readIdx + 1).filter((c) => c.author?.login !== me).length;
|
|
26714
26608
|
return {
|
|
@@ -26748,6 +26642,7 @@ function restFilesToDiff(files) {
|
|
|
26748
26642
|
if (f.status === "added") header.push("new file mode 100644");
|
|
26749
26643
|
if (f.status === "removed") header.push("deleted file mode 100644");
|
|
26750
26644
|
if (f.status === "renamed") header.push(`rename from ${oldPath}`, `rename to ${f.filename}`);
|
|
26645
|
+
if (f.sha && f.status !== "removed") header.push(`index 0000000..${f.sha}`);
|
|
26751
26646
|
if (f.patch !== void 0) {
|
|
26752
26647
|
header.push(
|
|
26753
26648
|
f.status === "added" ? "--- /dev/null" : `--- a/${oldPath}`,
|
|
@@ -26797,6 +26692,186 @@ async function loadDiff(gh, store, ref, base, head, isFullPr) {
|
|
|
26797
26692
|
const files = compare.files ?? [];
|
|
26798
26693
|
return { base, head, files: restFilesToDiff(files), missingFiles: files.length >= 300 ? 1 : 0 };
|
|
26799
26694
|
}
|
|
26695
|
+
async function loadBlobs(gh, store, ref, oids) {
|
|
26696
|
+
const key = (oid) => `blob:${ref.owner}/${ref.repo}:${oid}`.toLowerCase();
|
|
26697
|
+
const out = {};
|
|
26698
|
+
const missing = [];
|
|
26699
|
+
for (const oid of oids) {
|
|
26700
|
+
const cached2 = store.getDiff(key(oid));
|
|
26701
|
+
if (cached2 === void 0) missing.push(oid);
|
|
26702
|
+
else out[oid] = JSON.parse(cached2);
|
|
26703
|
+
}
|
|
26704
|
+
if (missing.length > 0) {
|
|
26705
|
+
const texts = await fetchTexts(gh, ref, missing);
|
|
26706
|
+
for (const oid of missing) {
|
|
26707
|
+
const text = texts.get(oid) ?? null;
|
|
26708
|
+
store.putDiff(key(oid), JSON.stringify(text));
|
|
26709
|
+
out[oid] = text;
|
|
26710
|
+
}
|
|
26711
|
+
}
|
|
26712
|
+
return out;
|
|
26713
|
+
}
|
|
26714
|
+
|
|
26715
|
+
// ../server/src/actions.ts
|
|
26716
|
+
async function setFileViewed(gh, prId, path, viewed) {
|
|
26717
|
+
const mutation = viewed ? "markFileAsViewed" : "unmarkFileAsViewed";
|
|
26718
|
+
await gh.graphql(
|
|
26719
|
+
`mutation($id: ID!, $path: String!) { ${mutation}(input: { pullRequestId: $id, path: $path }) { clientMutationId } }`,
|
|
26720
|
+
{ id: prId, path }
|
|
26721
|
+
);
|
|
26722
|
+
}
|
|
26723
|
+
async function replyToThread(gh, threadId, body, pendingReviewId) {
|
|
26724
|
+
const data = await gh.graphql(
|
|
26725
|
+
`mutation($thread: ID!, $body: String!, $review: ID) {
|
|
26726
|
+
addPullRequestReviewThreadReply(input: { pullRequestReviewThreadId: $thread, body: $body, pullRequestReviewId: $review }) {
|
|
26727
|
+
comment { ...ReviewCommentFields }
|
|
26728
|
+
}
|
|
26729
|
+
}
|
|
26730
|
+
${REVIEW_COMMENT_FIELDS}`,
|
|
26731
|
+
{ thread: threadId, body, review: pendingReviewId }
|
|
26732
|
+
);
|
|
26733
|
+
return toComment(data.addPullRequestReviewThreadReply.comment);
|
|
26734
|
+
}
|
|
26735
|
+
async function setThreadResolved(gh, threadId, resolved) {
|
|
26736
|
+
const mutation = resolved ? "resolveReviewThread" : "unresolveReviewThread";
|
|
26737
|
+
await gh.graphql(`mutation($id: ID!) { ${mutation}(input: { threadId: $id }) { thread { id } } }`, {
|
|
26738
|
+
id: threadId
|
|
26739
|
+
});
|
|
26740
|
+
}
|
|
26741
|
+
function threadInput(t2) {
|
|
26742
|
+
if (t2.subjectType === "FILE") return { path: t2.path, body: t2.body, subjectType: "FILE" };
|
|
26743
|
+
return {
|
|
26744
|
+
path: t2.path,
|
|
26745
|
+
body: t2.body,
|
|
26746
|
+
subjectType: "LINE",
|
|
26747
|
+
line: t2.line,
|
|
26748
|
+
side: t2.side,
|
|
26749
|
+
...t2.startLine !== null && t2.startLine !== t2.line ? { startLine: t2.startLine, startSide: t2.startSide ?? t2.side } : {}
|
|
26750
|
+
};
|
|
26751
|
+
}
|
|
26752
|
+
async function addThread(gh, prId, pendingReviewId, thread, mode) {
|
|
26753
|
+
if (mode === "single" && pendingReviewId) {
|
|
26754
|
+
throw new GitHubError(
|
|
26755
|
+
"Une review est en attente : ajoutez le commentaire \xE0 la review ou soumettez-la d\u2019abord.",
|
|
26756
|
+
409
|
|
26757
|
+
);
|
|
26758
|
+
}
|
|
26759
|
+
let reviewId = pendingReviewId;
|
|
26760
|
+
if (!reviewId) {
|
|
26761
|
+
const created2 = await gh.graphql(
|
|
26762
|
+
`mutation($id: ID!) { addPullRequestReview(input: { pullRequestId: $id }) { pullRequestReview { id } } }`,
|
|
26763
|
+
{ id: prId }
|
|
26764
|
+
);
|
|
26765
|
+
reviewId = created2.addPullRequestReview.pullRequestReview.id;
|
|
26766
|
+
}
|
|
26767
|
+
let node2;
|
|
26768
|
+
try {
|
|
26769
|
+
const data = await gh.graphql(
|
|
26770
|
+
`mutation($input: AddPullRequestReviewThreadInput!) {
|
|
26771
|
+
addPullRequestReviewThread(input: $input) { thread { ...ThreadFields } }
|
|
26772
|
+
}
|
|
26773
|
+
${THREAD_FIELDS}`,
|
|
26774
|
+
{ input: { pullRequestReviewId: reviewId, ...threadInput(thread) } }
|
|
26775
|
+
);
|
|
26776
|
+
node2 = data.addPullRequestReviewThread.thread;
|
|
26777
|
+
} catch (error62) {
|
|
26778
|
+
if (!pendingReviewId) await discardReview(gh, reviewId).catch(() => void 0);
|
|
26779
|
+
throw error62;
|
|
26780
|
+
}
|
|
26781
|
+
const created = toThread(node2, "", node2.comments.nodes.at(-1)?.id);
|
|
26782
|
+
if (mode === "review") return { thread: created, pendingReviewId: reviewId };
|
|
26783
|
+
await submitReview(gh, prId, reviewId, "COMMENT", "");
|
|
26784
|
+
return {
|
|
26785
|
+
thread: { ...created, pending: false, comments: created.comments.map((c) => ({ ...c, pending: false })) },
|
|
26786
|
+
pendingReviewId: null
|
|
26787
|
+
};
|
|
26788
|
+
}
|
|
26789
|
+
async function submitReview(gh, prId, pendingReviewId, event, body) {
|
|
26790
|
+
if (pendingReviewId) {
|
|
26791
|
+
await gh.graphql(
|
|
26792
|
+
`mutation($id: ID!, $event: PullRequestReviewEvent!, $body: String) {
|
|
26793
|
+
submitPullRequestReview(input: { pullRequestReviewId: $id, event: $event, body: $body }) { pullRequestReview { id } }
|
|
26794
|
+
}`,
|
|
26795
|
+
{ id: pendingReviewId, event, body: body || null }
|
|
26796
|
+
);
|
|
26797
|
+
return;
|
|
26798
|
+
}
|
|
26799
|
+
await gh.graphql(
|
|
26800
|
+
`mutation($input: AddPullRequestReviewInput!) { addPullRequestReview(input: $input) { pullRequestReview { id } } }`,
|
|
26801
|
+
{ input: { pullRequestId: prId, event, ...body ? { body } : {} } }
|
|
26802
|
+
);
|
|
26803
|
+
}
|
|
26804
|
+
async function discardReview(gh, reviewId) {
|
|
26805
|
+
await gh.graphql(
|
|
26806
|
+
`mutation($id: ID!) { deletePullRequestReview(input: { pullRequestReviewId: $id }) { pullRequestReview { id } } }`,
|
|
26807
|
+
{ id: reviewId }
|
|
26808
|
+
);
|
|
26809
|
+
}
|
|
26810
|
+
async function addIssueComment(gh, subjectId, body) {
|
|
26811
|
+
const data = await gh.graphql(
|
|
26812
|
+
`mutation($id: ID!, $body: String!) {
|
|
26813
|
+
addComment(input: { subjectId: $id, body: $body }) {
|
|
26814
|
+
commentEdge { node { id url body bodyHTML createdAt author { login avatarUrl } } }
|
|
26815
|
+
}
|
|
26816
|
+
}`,
|
|
26817
|
+
{ id: subjectId, body }
|
|
26818
|
+
);
|
|
26819
|
+
return { kind: "comment", ...data.addComment.commentEdge.node, unread: false };
|
|
26820
|
+
}
|
|
26821
|
+
async function deleteReviewComment(gh, commentId) {
|
|
26822
|
+
await gh.graphql(
|
|
26823
|
+
`mutation($id: ID!) { deletePullRequestReviewComment(input: { id: $id }) { clientMutationId } }`,
|
|
26824
|
+
{ id: commentId }
|
|
26825
|
+
);
|
|
26826
|
+
}
|
|
26827
|
+
async function updateReviewComment(gh, commentId, body) {
|
|
26828
|
+
await gh.graphql(
|
|
26829
|
+
`mutation($id: ID!, $body: String!) {
|
|
26830
|
+
updatePullRequestReviewComment(input: { pullRequestReviewCommentId: $id, body: $body }) { clientMutationId }
|
|
26831
|
+
}`,
|
|
26832
|
+
{ id: commentId, body }
|
|
26833
|
+
);
|
|
26834
|
+
}
|
|
26835
|
+
async function applySuggestion(gh, ref, input2) {
|
|
26836
|
+
const branch = await gh.rest(
|
|
26837
|
+
`/repos/${ref.owner}/${ref.repo}/branches/${encodeURIComponent(input2.branch)}`
|
|
26838
|
+
);
|
|
26839
|
+
if (branch.commit.sha !== input2.expectedHeadOid) {
|
|
26840
|
+
throw new GitHubError(
|
|
26841
|
+
"La branche a boug\xE9 depuis le chargement de la PR : rechargez avant d\u2019appliquer.",
|
|
26842
|
+
409
|
|
26843
|
+
);
|
|
26844
|
+
}
|
|
26845
|
+
const encodedPath = input2.path.split("/").map(encodeURIComponent).join("/");
|
|
26846
|
+
const file2 = await gh.rest(
|
|
26847
|
+
`/repos/${ref.owner}/${ref.repo}/contents/${encodedPath}?ref=${input2.expectedHeadOid}`
|
|
26848
|
+
);
|
|
26849
|
+
const text = Buffer.from(file2.content, "base64").toString("utf8");
|
|
26850
|
+
const eol = text.includes("\r\n") ? "\r\n" : "\n";
|
|
26851
|
+
const lines = text.split(eol);
|
|
26852
|
+
if (input2.endLine > lines.length || input2.startLine < 1 || input2.startLine > input2.endLine) {
|
|
26853
|
+
throw new GitHubError("Lignes de la suggestion hors du fichier.", 422);
|
|
26854
|
+
}
|
|
26855
|
+
const replacement = input2.replacement.replace(/\r?\n$/, "").split(/\r?\n/);
|
|
26856
|
+
lines.splice(
|
|
26857
|
+
input2.startLine - 1,
|
|
26858
|
+
input2.endLine - input2.startLine + 1,
|
|
26859
|
+
...input2.replacement === "" ? [] : replacement
|
|
26860
|
+
);
|
|
26861
|
+
const res = await gh.rest(
|
|
26862
|
+
`/repos/${ref.owner}/${ref.repo}/contents/${encodedPath}`,
|
|
26863
|
+
{
|
|
26864
|
+
method: "PUT",
|
|
26865
|
+
body: {
|
|
26866
|
+
message: input2.message,
|
|
26867
|
+
content: Buffer.from(lines.join(eol), "utf8").toString("base64"),
|
|
26868
|
+
sha: file2.sha,
|
|
26869
|
+
branch: input2.branch
|
|
26870
|
+
}
|
|
26871
|
+
}
|
|
26872
|
+
);
|
|
26873
|
+
return res.commit.sha;
|
|
26874
|
+
}
|
|
26800
26875
|
|
|
26801
26876
|
// ../server/src/links/extract.ts
|
|
26802
26877
|
import ts from "typescript";
|
|
@@ -28195,7 +28270,6 @@ function resolveGradleProject(projectPath, from, gradleRoots) {
|
|
|
28195
28270
|
|
|
28196
28271
|
// ../server/src/prLinks.ts
|
|
28197
28272
|
var LINKS_VERSION = 6;
|
|
28198
|
-
var BATCH = 50;
|
|
28199
28273
|
var languageSchema = external_exports.enum([
|
|
28200
28274
|
"typescript",
|
|
28201
28275
|
"javascript",
|
|
@@ -28306,13 +28380,14 @@ async function loadWorkspace(gh, store, ref, head) {
|
|
|
28306
28380
|
const texts = await fetchTexts(
|
|
28307
28381
|
gh,
|
|
28308
28382
|
ref,
|
|
28309
|
-
manifests.map((path) =>
|
|
28383
|
+
manifests.map((path) => `${head}:${path}`)
|
|
28310
28384
|
);
|
|
28311
|
-
const
|
|
28385
|
+
const textOf = (path) => texts.get(`${head}:${path}`) ?? null;
|
|
28386
|
+
const packages = parseWorkspace(manifests.map((path) => ({ path, text: textOf(path) })));
|
|
28312
28387
|
const refs = packages.map((p) => ({
|
|
28313
28388
|
name: p.name,
|
|
28314
28389
|
root: p.root,
|
|
28315
|
-
entries: entryCandidates(p,
|
|
28390
|
+
entries: entryCandidates(p, textOf(p.manifest))
|
|
28316
28391
|
}));
|
|
28317
28392
|
return { packages, refs };
|
|
28318
28393
|
}
|
|
@@ -28363,25 +28438,6 @@ ${to}`;
|
|
|
28363
28438
|
packageEdges
|
|
28364
28439
|
};
|
|
28365
28440
|
}
|
|
28366
|
-
async function fetchTexts(gh, ref, wanted) {
|
|
28367
|
-
const texts = /* @__PURE__ */ new Map();
|
|
28368
|
-
for (let i = 0; i < wanted.length; i += BATCH) {
|
|
28369
|
-
const batch = wanted.slice(i, i + BATCH);
|
|
28370
|
-
const vars = { owner: ref.owner, repo: ref.repo };
|
|
28371
|
-
batch.forEach((w, k) => vars[`e${k}`] = `${w.oid}:${w.path}`);
|
|
28372
|
-
const query = `query(${["$owner: String!", "$repo: String!", ...batch.map((_, k) => `$e${k}: String!`)].join(", ")}) {
|
|
28373
|
-
repository(owner: $owner, name: $repo) {
|
|
28374
|
-
${batch.map((_, k) => `f${k}: object(expression: $e${k}) { ... on Blob { text isBinary isTruncated } }`).join("\n")}
|
|
28375
|
-
}
|
|
28376
|
-
}`;
|
|
28377
|
-
const data = await gh.graphql(query, vars);
|
|
28378
|
-
batch.forEach((w, k) => {
|
|
28379
|
-
const blob = data.repository[`f${k}`];
|
|
28380
|
-
texts.set(w.path, blob && !blob.isBinary && !blob.isTruncated ? blob.text : null);
|
|
28381
|
-
});
|
|
28382
|
-
}
|
|
28383
|
-
return texts;
|
|
28384
|
-
}
|
|
28385
28441
|
async function loadLinks(gh, store, ref, base, head, fullPr) {
|
|
28386
28442
|
const cacheKey2 = `links:v${LINKS_VERSION}:${ref.owner}/${ref.repo}:${base}...${head}`.toLowerCase();
|
|
28387
28443
|
const cached2 = store.getDiff(cacheKey2);
|
|
@@ -28391,16 +28447,11 @@ async function loadLinks(gh, store, ref, base, head, fullPr) {
|
|
|
28391
28447
|
}
|
|
28392
28448
|
const diff = await loadDiff(gh, store, ref, base, head, fullPr);
|
|
28393
28449
|
const readable = diff.files.filter((f) => !f.binary);
|
|
28394
|
-
const
|
|
28395
|
-
|
|
28396
|
-
ref,
|
|
28397
|
-
readable.map(
|
|
28398
|
-
(f) => f.status === "deleted" ? { path: f.oldPath, oid: base } : { path: f.path, oid: head }
|
|
28399
|
-
)
|
|
28400
|
-
);
|
|
28450
|
+
const expression = (f) => f.status === "deleted" ? `${base}:${f.oldPath}` : `${head}:${f.path}`;
|
|
28451
|
+
const texts = await fetchTexts(gh, ref, readable.map(expression));
|
|
28401
28452
|
const sources = diff.files.map((f) => ({
|
|
28402
28453
|
path: f.path,
|
|
28403
|
-
text: f.binary ? null : texts.get(f
|
|
28454
|
+
text: f.binary ? null : texts.get(expression(f)) ?? null
|
|
28404
28455
|
}));
|
|
28405
28456
|
const workspace = await loadWorkspace(gh, store, ref, head);
|
|
28406
28457
|
const graph = buildLinkGraph(sources, workspace ? { packages: workspace.refs } : void 0);
|
|
@@ -28836,6 +28887,7 @@ var prRouter = router({
|
|
|
28836
28887
|
get: procedure.input(prRefSchema).query(({ ctx, input: input2 }) => loadPr(ctx.gh, ctx.store, input2)),
|
|
28837
28888
|
diff: procedure.input(external_exports.object({ ref: prRefSchema, base: shaSchema, head: shaSchema, fullPr: external_exports.boolean() })).query(({ ctx, input: input2 }) => loadDiff(ctx.gh, ctx.store, input2.ref, input2.base, input2.head, input2.fullPr)),
|
|
28838
28889
|
links: procedure.input(external_exports.object({ ref: prRefSchema, base: shaSchema, head: shaSchema, fullPr: external_exports.boolean() })).query(({ ctx, input: input2 }) => loadLinks(ctx.gh, ctx.store, input2.ref, input2.base, input2.head, input2.fullPr)),
|
|
28890
|
+
blobs: procedure.input(external_exports.object({ ref: prRefSchema, oids: external_exports.array(shaSchema).min(1).max(2) })).query(({ ctx, input: input2 }) => loadBlobs(ctx.gh, ctx.store, input2.ref, input2.oids)),
|
|
28839
28891
|
onChange: procedure.input(prRefSchema).subscription(async function* ({ ctx, input: input2, signal }) {
|
|
28840
28892
|
yield* pollChanges(PR_POLL_MS, signal, () => prFingerprint(ctx.gh, input2, signal));
|
|
28841
28893
|
}),
|