@forge-glance/sdk 0.1.1 → 0.2.1
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/dist/GitHubProvider.d.ts +14 -3
- package/dist/GitHubProvider.js +100 -3
- package/dist/GitLabProvider.d.ts +21 -4
- package/dist/GitLabProvider.js +194 -11
- package/dist/GitProvider.d.ts +64 -1
- package/dist/index.d.ts +14 -14
- package/dist/index.js +294 -14
- package/dist/providers.js +294 -14
- package/dist/types.d.ts +64 -1
- package/package.json +1 -1
- package/src/GitHubProvider.ts +431 -153
- package/src/GitLabProvider.ts +437 -87
- package/src/GitProvider.ts +113 -21
- package/src/index.ts +22 -15
- package/src/types.ts +70 -1
package/dist/index.js
CHANGED
|
@@ -101,6 +101,10 @@ var MR_DASHBOARD_FRAGMENT = `
|
|
|
101
101
|
approvalsLeft
|
|
102
102
|
resolvableDiscussionsCount
|
|
103
103
|
resolvedDiscussionsCount
|
|
104
|
+
autoMergeEnabled
|
|
105
|
+
autoMergeStrategy
|
|
106
|
+
mergeUser { id username name avatarUrl }
|
|
107
|
+
mergeAfter
|
|
104
108
|
headPipeline {
|
|
105
109
|
id iid status
|
|
106
110
|
createdAt
|
|
@@ -221,7 +225,11 @@ function toMR(gql, role, baseURL) {
|
|
|
221
225
|
approved: gql.approved ?? false,
|
|
222
226
|
approvedBy: gql.approvedBy.nodes.map(toUserRef),
|
|
223
227
|
diffStats,
|
|
224
|
-
detailedMergeStatus: gql.detailedMergeStatus ?? null
|
|
228
|
+
detailedMergeStatus: gql.detailedMergeStatus ?? null,
|
|
229
|
+
autoMergeEnabled: gql.autoMergeEnabled ?? false,
|
|
230
|
+
autoMergeStrategy: gql.autoMergeStrategy ?? null,
|
|
231
|
+
mergeUser: gql.mergeUser ? toUserRef(gql.mergeUser) : null,
|
|
232
|
+
mergeAfter: gql.mergeAfter ?? null
|
|
225
233
|
};
|
|
226
234
|
}
|
|
227
235
|
var MR_DETAIL_QUERY = `
|
|
@@ -245,8 +253,20 @@ class GitLabProvider {
|
|
|
245
253
|
this.baseURL = baseURL.replace(/\/$/, "");
|
|
246
254
|
this.token = token;
|
|
247
255
|
this.log = options.logger ?? noopLogger;
|
|
248
|
-
this.mrDetailFetcher = new MRDetailFetcher(this.baseURL, token, {
|
|
256
|
+
this.mrDetailFetcher = new MRDetailFetcher(this.baseURL, token, {
|
|
257
|
+
logger: this.log
|
|
258
|
+
});
|
|
249
259
|
}
|
|
260
|
+
capabilities = {
|
|
261
|
+
canMerge: true,
|
|
262
|
+
canApprove: true,
|
|
263
|
+
canUnapprove: true,
|
|
264
|
+
canRebase: true,
|
|
265
|
+
canAutoMerge: true,
|
|
266
|
+
canResolveDiscussions: true,
|
|
267
|
+
canRetryPipeline: true,
|
|
268
|
+
canRequestReReview: true
|
|
269
|
+
};
|
|
250
270
|
async validateToken() {
|
|
251
271
|
const url = `${this.baseURL}/api/v4/user`;
|
|
252
272
|
const res = await fetch(url, {
|
|
@@ -352,7 +372,11 @@ class GitLabProvider {
|
|
|
352
372
|
headers: { "PRIVATE-TOKEN": this.token }
|
|
353
373
|
});
|
|
354
374
|
if (!res.ok) {
|
|
355
|
-
this.log.warn("fetchPullRequestByBranch failed", {
|
|
375
|
+
this.log.warn("fetchPullRequestByBranch failed", {
|
|
376
|
+
projectPath,
|
|
377
|
+
sourceBranch,
|
|
378
|
+
status: res.status
|
|
379
|
+
});
|
|
356
380
|
return null;
|
|
357
381
|
}
|
|
358
382
|
const mrs = await res.json();
|
|
@@ -390,10 +414,7 @@ class GitLabProvider {
|
|
|
390
414
|
throw new Error(`createPullRequest failed: ${res.status} ${text}`);
|
|
391
415
|
}
|
|
392
416
|
const created = await res.json();
|
|
393
|
-
|
|
394
|
-
if (!pr)
|
|
395
|
-
throw new Error("Created MR but failed to fetch it back");
|
|
396
|
-
return pr;
|
|
417
|
+
return this.fetchSingleMRWithRetry(input.projectPath, created.iid, "Created MR but failed to fetch it back");
|
|
397
418
|
}
|
|
398
419
|
async updatePullRequest(projectPath, mrIid, input) {
|
|
399
420
|
const encoded = encodeURIComponent(projectPath);
|
|
@@ -426,10 +447,7 @@ class GitLabProvider {
|
|
|
426
447
|
const text = await res.text();
|
|
427
448
|
throw new Error(`updatePullRequest failed: ${res.status} ${text}`);
|
|
428
449
|
}
|
|
429
|
-
|
|
430
|
-
if (!pr)
|
|
431
|
-
throw new Error("Updated MR but failed to fetch it back");
|
|
432
|
-
return pr;
|
|
450
|
+
return this.fetchSingleMRWithRetry(projectPath, mrIid, "Updated MR but failed to fetch it back");
|
|
433
451
|
}
|
|
434
452
|
async restRequest(method, path, body) {
|
|
435
453
|
const url = `${this.baseURL}${path}`;
|
|
@@ -445,6 +463,171 @@ class GitLabProvider {
|
|
|
445
463
|
body: body !== undefined ? JSON.stringify(body) : undefined
|
|
446
464
|
});
|
|
447
465
|
}
|
|
466
|
+
async mergePullRequest(projectPath, mrIid, input) {
|
|
467
|
+
const encoded = encodeURIComponent(projectPath);
|
|
468
|
+
const body = {};
|
|
469
|
+
if (input?.commitMessage != null)
|
|
470
|
+
body.merge_commit_message = input.commitMessage;
|
|
471
|
+
if (input?.squashCommitMessage != null)
|
|
472
|
+
body.squash_commit_message = input.squashCommitMessage;
|
|
473
|
+
if (input?.squash != null)
|
|
474
|
+
body.squash = input.squash;
|
|
475
|
+
if (input?.shouldRemoveSourceBranch != null)
|
|
476
|
+
body.should_remove_source_branch = input.shouldRemoveSourceBranch;
|
|
477
|
+
if (input?.sha != null)
|
|
478
|
+
body.sha = input.sha;
|
|
479
|
+
if (input?.mergeMethod === "squash" && input?.squash == null)
|
|
480
|
+
body.squash = true;
|
|
481
|
+
const res = await fetch(`${this.baseURL}/api/v4/projects/${encoded}/merge_requests/${mrIid}/merge`, {
|
|
482
|
+
method: "PUT",
|
|
483
|
+
headers: {
|
|
484
|
+
"PRIVATE-TOKEN": this.token,
|
|
485
|
+
"Content-Type": "application/json"
|
|
486
|
+
},
|
|
487
|
+
body: JSON.stringify(body)
|
|
488
|
+
});
|
|
489
|
+
if (!res.ok) {
|
|
490
|
+
const text = await res.text();
|
|
491
|
+
throw new Error(`mergePullRequest failed: ${res.status} ${text}`);
|
|
492
|
+
}
|
|
493
|
+
return this.fetchSingleMRWithRetry(projectPath, mrIid, "Merged MR but failed to fetch it back");
|
|
494
|
+
}
|
|
495
|
+
async approvePullRequest(projectPath, mrIid) {
|
|
496
|
+
const encoded = encodeURIComponent(projectPath);
|
|
497
|
+
const res = await fetch(`${this.baseURL}/api/v4/projects/${encoded}/merge_requests/${mrIid}/approve`, {
|
|
498
|
+
method: "POST",
|
|
499
|
+
headers: { "PRIVATE-TOKEN": this.token }
|
|
500
|
+
});
|
|
501
|
+
if (!res.ok) {
|
|
502
|
+
const text = await res.text().catch(() => "");
|
|
503
|
+
throw new Error(`approvePullRequest failed: ${res.status} ${res.statusText}${text ? ` — ${text}` : ""}`);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
async unapprovePullRequest(projectPath, mrIid) {
|
|
507
|
+
const encoded = encodeURIComponent(projectPath);
|
|
508
|
+
const res = await fetch(`${this.baseURL}/api/v4/projects/${encoded}/merge_requests/${mrIid}/unapprove`, {
|
|
509
|
+
method: "POST",
|
|
510
|
+
headers: { "PRIVATE-TOKEN": this.token }
|
|
511
|
+
});
|
|
512
|
+
if (!res.ok) {
|
|
513
|
+
const text = await res.text().catch(() => "");
|
|
514
|
+
throw new Error(`unapprovePullRequest failed: ${res.status} ${res.statusText}${text ? ` — ${text}` : ""}`);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
async rebasePullRequest(projectPath, mrIid) {
|
|
518
|
+
const encoded = encodeURIComponent(projectPath);
|
|
519
|
+
const res = await fetch(`${this.baseURL}/api/v4/projects/${encoded}/merge_requests/${mrIid}/rebase`, {
|
|
520
|
+
method: "PUT",
|
|
521
|
+
headers: { "PRIVATE-TOKEN": this.token }
|
|
522
|
+
});
|
|
523
|
+
if (!res.ok) {
|
|
524
|
+
const text = await res.text().catch(() => "");
|
|
525
|
+
throw new Error(`rebasePullRequest failed: ${res.status} ${res.statusText}${text ? ` — ${text}` : ""}`);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
async setAutoMerge(projectPath, mrIid) {
|
|
529
|
+
const encoded = encodeURIComponent(projectPath);
|
|
530
|
+
const res = await fetch(`${this.baseURL}/api/v4/projects/${encoded}/merge_requests/${mrIid}/merge`, {
|
|
531
|
+
method: "PUT",
|
|
532
|
+
headers: {
|
|
533
|
+
"PRIVATE-TOKEN": this.token,
|
|
534
|
+
"Content-Type": "application/json"
|
|
535
|
+
},
|
|
536
|
+
body: JSON.stringify({ merge_when_pipeline_succeeds: true })
|
|
537
|
+
});
|
|
538
|
+
if (!res.ok) {
|
|
539
|
+
const text = await res.text().catch(() => "");
|
|
540
|
+
throw new Error(`setAutoMerge failed: ${res.status} ${res.statusText}${text ? ` — ${text}` : ""}`);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
async cancelAutoMerge(projectPath, mrIid) {
|
|
544
|
+
const encoded = encodeURIComponent(projectPath);
|
|
545
|
+
const res = await fetch(`${this.baseURL}/api/v4/projects/${encoded}/merge_requests/${mrIid}/cancel_merge_when_pipeline_succeeds`, {
|
|
546
|
+
method: "POST",
|
|
547
|
+
headers: { "PRIVATE-TOKEN": this.token }
|
|
548
|
+
});
|
|
549
|
+
if (!res.ok) {
|
|
550
|
+
const text = await res.text().catch(() => "");
|
|
551
|
+
throw new Error(`cancelAutoMerge failed: ${res.status} ${res.statusText}${text ? ` — ${text}` : ""}`);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
async resolveDiscussion(projectPath, mrIid, discussionId) {
|
|
555
|
+
const encoded = encodeURIComponent(projectPath);
|
|
556
|
+
const res = await fetch(`${this.baseURL}/api/v4/projects/${encoded}/merge_requests/${mrIid}/discussions/${discussionId}`, {
|
|
557
|
+
method: "PUT",
|
|
558
|
+
headers: {
|
|
559
|
+
"PRIVATE-TOKEN": this.token,
|
|
560
|
+
"Content-Type": "application/json"
|
|
561
|
+
},
|
|
562
|
+
body: JSON.stringify({ resolved: true })
|
|
563
|
+
});
|
|
564
|
+
if (!res.ok) {
|
|
565
|
+
const text = await res.text().catch(() => "");
|
|
566
|
+
throw new Error(`resolveDiscussion failed: ${res.status} ${res.statusText}${text ? ` — ${text}` : ""}`);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
async unresolveDiscussion(projectPath, mrIid, discussionId) {
|
|
570
|
+
const encoded = encodeURIComponent(projectPath);
|
|
571
|
+
const res = await fetch(`${this.baseURL}/api/v4/projects/${encoded}/merge_requests/${mrIid}/discussions/${discussionId}`, {
|
|
572
|
+
method: "PUT",
|
|
573
|
+
headers: {
|
|
574
|
+
"PRIVATE-TOKEN": this.token,
|
|
575
|
+
"Content-Type": "application/json"
|
|
576
|
+
},
|
|
577
|
+
body: JSON.stringify({ resolved: false })
|
|
578
|
+
});
|
|
579
|
+
if (!res.ok) {
|
|
580
|
+
const text = await res.text().catch(() => "");
|
|
581
|
+
throw new Error(`unresolveDiscussion failed: ${res.status} ${res.statusText}${text ? ` — ${text}` : ""}`);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
async retryPipeline(projectPath, pipelineId) {
|
|
585
|
+
const encoded = encodeURIComponent(projectPath);
|
|
586
|
+
const res = await fetch(`${this.baseURL}/api/v4/projects/${encoded}/pipelines/${pipelineId}/retry`, {
|
|
587
|
+
method: "POST",
|
|
588
|
+
headers: { "PRIVATE-TOKEN": this.token }
|
|
589
|
+
});
|
|
590
|
+
if (!res.ok) {
|
|
591
|
+
const text = await res.text().catch(() => "");
|
|
592
|
+
throw new Error(`retryPipeline failed: ${res.status} ${res.statusText}${text ? ` — ${text}` : ""}`);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
async requestReReview(projectPath, mrIid, _reviewerUsernames) {
|
|
596
|
+
const encoded = encodeURIComponent(projectPath);
|
|
597
|
+
const mrRes = await fetch(`${this.baseURL}/api/v4/projects/${encoded}/merge_requests/${mrIid}`, { headers: { "PRIVATE-TOKEN": this.token } });
|
|
598
|
+
if (!mrRes.ok) {
|
|
599
|
+
const text = await mrRes.text().catch(() => "");
|
|
600
|
+
throw new Error(`requestReReview: failed to fetch MR: ${mrRes.status}${text ? ` — ${text}` : ""}`);
|
|
601
|
+
}
|
|
602
|
+
const mr = await mrRes.json();
|
|
603
|
+
const reviewerIds = mr.reviewers?.map((r) => r.id) ?? [];
|
|
604
|
+
if (reviewerIds.length === 0) {
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
const res = await fetch(`${this.baseURL}/api/v4/projects/${encoded}/merge_requests/${mrIid}`, {
|
|
608
|
+
method: "PUT",
|
|
609
|
+
headers: {
|
|
610
|
+
"PRIVATE-TOKEN": this.token,
|
|
611
|
+
"Content-Type": "application/json"
|
|
612
|
+
},
|
|
613
|
+
body: JSON.stringify({ reviewer_ids: reviewerIds })
|
|
614
|
+
});
|
|
615
|
+
if (!res.ok) {
|
|
616
|
+
const text = await res.text().catch(() => "");
|
|
617
|
+
throw new Error(`requestReReview failed: ${res.status} ${res.statusText}${text ? ` — ${text}` : ""}`);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
async fetchSingleMRWithRetry(projectPath, mrIid, errorMessage) {
|
|
621
|
+
for (let attempt = 0;attempt < 3; attempt++) {
|
|
622
|
+
if (attempt > 0) {
|
|
623
|
+
await new Promise((r) => setTimeout(r, attempt * 300));
|
|
624
|
+
}
|
|
625
|
+
const pr = await this.fetchSingleMR(projectPath, mrIid, null);
|
|
626
|
+
if (pr)
|
|
627
|
+
return pr;
|
|
628
|
+
}
|
|
629
|
+
throw new Error(errorMessage);
|
|
630
|
+
}
|
|
448
631
|
async runQuery(query, variables) {
|
|
449
632
|
const url = `${this.baseURL}/api/graphql`;
|
|
450
633
|
const body = JSON.stringify({ query, variables: variables ?? {} });
|
|
@@ -560,6 +743,16 @@ class GitHubProvider {
|
|
|
560
743
|
this.apiBase = `${this.baseURL}/api/v3`;
|
|
561
744
|
}
|
|
562
745
|
}
|
|
746
|
+
capabilities = {
|
|
747
|
+
canMerge: true,
|
|
748
|
+
canApprove: true,
|
|
749
|
+
canUnapprove: false,
|
|
750
|
+
canRebase: false,
|
|
751
|
+
canAutoMerge: false,
|
|
752
|
+
canResolveDiscussions: false,
|
|
753
|
+
canRetryPipeline: true,
|
|
754
|
+
canRequestReReview: true
|
|
755
|
+
};
|
|
563
756
|
async validateToken() {
|
|
564
757
|
const res = await this.api("GET", "/user");
|
|
565
758
|
if (!res.ok) {
|
|
@@ -601,7 +794,9 @@ class GitHubProvider {
|
|
|
601
794
|
]);
|
|
602
795
|
return this.toPullRequest(pr, prRoles, reviews, checkRuns);
|
|
603
796
|
}));
|
|
604
|
-
this.log.debug("GitHubProvider.fetchPullRequests", {
|
|
797
|
+
this.log.debug("GitHubProvider.fetchPullRequests", {
|
|
798
|
+
count: results.length
|
|
799
|
+
});
|
|
605
800
|
return results;
|
|
606
801
|
}
|
|
607
802
|
async fetchSingleMR(projectPath, mrIid, _currentUserNumericId) {
|
|
@@ -714,7 +909,11 @@ class GitHubProvider {
|
|
|
714
909
|
async fetchPullRequestByBranch(projectPath, sourceBranch) {
|
|
715
910
|
const res = await this.api("GET", `/repos/${projectPath}/pulls?head=${projectPath.split("/")[0]}:${encodeURIComponent(sourceBranch)}&state=open&per_page=1`);
|
|
716
911
|
if (!res.ok) {
|
|
717
|
-
this.log.warn("fetchPullRequestByBranch failed", {
|
|
912
|
+
this.log.warn("fetchPullRequestByBranch failed", {
|
|
913
|
+
projectPath,
|
|
914
|
+
sourceBranch,
|
|
915
|
+
status: res.status
|
|
916
|
+
});
|
|
718
917
|
return null;
|
|
719
918
|
}
|
|
720
919
|
const prs = await res.json();
|
|
@@ -798,6 +997,83 @@ class GitHubProvider {
|
|
|
798
997
|
async restRequest(method, path, body) {
|
|
799
998
|
return this.api(method, path, body);
|
|
800
999
|
}
|
|
1000
|
+
async mergePullRequest(projectPath, mrIid, input) {
|
|
1001
|
+
const body = {};
|
|
1002
|
+
if (input?.commitMessage != null)
|
|
1003
|
+
body.commit_title = input.commitMessage;
|
|
1004
|
+
if (input?.squashCommitMessage != null)
|
|
1005
|
+
body.commit_title = input.squashCommitMessage;
|
|
1006
|
+
if (input?.shouldRemoveSourceBranch != null)
|
|
1007
|
+
body.delete_branch = input.shouldRemoveSourceBranch;
|
|
1008
|
+
if (input?.sha != null)
|
|
1009
|
+
body.sha = input.sha;
|
|
1010
|
+
if (input?.mergeMethod) {
|
|
1011
|
+
body.merge_method = input.mergeMethod;
|
|
1012
|
+
} else if (input?.squash) {
|
|
1013
|
+
body.merge_method = "squash";
|
|
1014
|
+
}
|
|
1015
|
+
const res = await this.api("PUT", `/repos/${projectPath}/pulls/${mrIid}/merge`, body);
|
|
1016
|
+
if (!res.ok) {
|
|
1017
|
+
const text = await res.text();
|
|
1018
|
+
throw new Error(`mergePullRequest failed: ${res.status} ${text}`);
|
|
1019
|
+
}
|
|
1020
|
+
const pr = await this.fetchSingleMR(projectPath, mrIid, null);
|
|
1021
|
+
if (!pr)
|
|
1022
|
+
throw new Error("Merged PR but failed to fetch it back");
|
|
1023
|
+
return pr;
|
|
1024
|
+
}
|
|
1025
|
+
async approvePullRequest(projectPath, mrIid) {
|
|
1026
|
+
const res = await this.api("POST", `/repos/${projectPath}/pulls/${mrIid}/reviews`, {
|
|
1027
|
+
event: "APPROVE"
|
|
1028
|
+
});
|
|
1029
|
+
if (!res.ok) {
|
|
1030
|
+
const text = await res.text().catch(() => "");
|
|
1031
|
+
throw new Error(`approvePullRequest failed: ${res.status} ${res.statusText}${text ? ` — ${text}` : ""}`);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
async unapprovePullRequest(_projectPath, _mrIid) {
|
|
1035
|
+
throw new Error("unapprovePullRequest is not supported by GitHub. " + "Check provider.capabilities.canUnapprove before calling.");
|
|
1036
|
+
}
|
|
1037
|
+
async rebasePullRequest(_projectPath, _mrIid) {
|
|
1038
|
+
throw new Error("rebasePullRequest is not supported by GitHub. " + "Check provider.capabilities.canRebase before calling.");
|
|
1039
|
+
}
|
|
1040
|
+
async setAutoMerge(_projectPath, _mrIid) {
|
|
1041
|
+
throw new Error("setAutoMerge is not supported by the GitHub REST API. " + "Check provider.capabilities.canAutoMerge before calling.");
|
|
1042
|
+
}
|
|
1043
|
+
async cancelAutoMerge(_projectPath, _mrIid) {
|
|
1044
|
+
throw new Error("cancelAutoMerge is not supported by the GitHub REST API. " + "Check provider.capabilities.canAutoMerge before calling.");
|
|
1045
|
+
}
|
|
1046
|
+
async resolveDiscussion(_projectPath, _mrIid, _discussionId) {
|
|
1047
|
+
throw new Error("resolveDiscussion is not supported by the GitHub REST API. " + "Check provider.capabilities.canResolveDiscussions before calling.");
|
|
1048
|
+
}
|
|
1049
|
+
async unresolveDiscussion(_projectPath, _mrIid, _discussionId) {
|
|
1050
|
+
throw new Error("unresolveDiscussion is not supported by the GitHub REST API. " + "Check provider.capabilities.canResolveDiscussions before calling.");
|
|
1051
|
+
}
|
|
1052
|
+
async retryPipeline(projectPath, pipelineId) {
|
|
1053
|
+
const res = await this.api("POST", `/repos/${projectPath}/actions/runs/${pipelineId}/rerun`);
|
|
1054
|
+
if (!res.ok) {
|
|
1055
|
+
const text = await res.text().catch(() => "");
|
|
1056
|
+
throw new Error(`retryPipeline failed: ${res.status} ${res.statusText}${text ? ` — ${text}` : ""}`);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
async requestReReview(projectPath, mrIid, reviewerUsernames) {
|
|
1060
|
+
if (!reviewerUsernames?.length) {
|
|
1061
|
+
const prRes = await this.api("GET", `/repos/${projectPath}/pulls/${mrIid}`);
|
|
1062
|
+
if (!prRes.ok) {
|
|
1063
|
+
throw new Error(`requestReReview: failed to fetch PR: ${prRes.status}`);
|
|
1064
|
+
}
|
|
1065
|
+
const pr = await prRes.json();
|
|
1066
|
+
reviewerUsernames = pr.requested_reviewers.map((r) => r.login);
|
|
1067
|
+
if (!reviewerUsernames.length) {
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
const res = await this.api("POST", `/repos/${projectPath}/pulls/${mrIid}/requested_reviewers`, { reviewers: reviewerUsernames });
|
|
1072
|
+
if (!res.ok) {
|
|
1073
|
+
const text = await res.text().catch(() => "");
|
|
1074
|
+
throw new Error(`requestReReview failed: ${res.status} ${res.statusText}${text ? ` — ${text}` : ""}`);
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
801
1077
|
async api(method, path, body) {
|
|
802
1078
|
const url = `${this.apiBase}${path}`;
|
|
803
1079
|
const headers = {
|
|
@@ -912,7 +1188,11 @@ class GitHubProvider {
|
|
|
912
1188
|
approved: approvedBy.length > 0 && changesRequested === 0,
|
|
913
1189
|
approvedBy,
|
|
914
1190
|
diffStats,
|
|
915
|
-
detailedMergeStatus: null
|
|
1191
|
+
detailedMergeStatus: null,
|
|
1192
|
+
autoMergeEnabled: pr.auto_merge != null,
|
|
1193
|
+
autoMergeStrategy: pr.auto_merge?.merge_method ?? null,
|
|
1194
|
+
mergeUser: pr.auto_merge ? toUserRef2(pr.auto_merge.enabled_by) : null,
|
|
1195
|
+
mergeAfter: null
|
|
916
1196
|
};
|
|
917
1197
|
}
|
|
918
1198
|
}
|