github-issue-tower-defence-management 2.66.0 → 2.67.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/bin/adapter/repositories/GitHubIssueCommentRepository.js +9 -2
- package/bin/adapter/repositories/GitHubIssueCommentRepository.js.map +1 -1
- package/bin/adapter/repositories/NodeTmuxSessionRepository.js +72 -0
- package/bin/adapter/repositories/NodeTmuxSessionRepository.js.map +1 -1
- package/bin/adapter/repositories/githubRestClient.js +87 -2
- package/bin/adapter/repositories/githubRestClient.js.map +1 -1
- package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js +89 -59
- package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js.map +1 -1
- package/bin/adapter/repositories/issue/githubRateLimitRetry.js +8 -2
- package/bin/adapter/repositories/issue/githubRateLimitRetry.js.map +1 -1
- package/bin/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.js +3 -1
- package/bin/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.js.map +1 -1
- package/package.json +1 -1
- package/types/adapter/repositories/GitHubIssueCommentRepository.d.ts.map +1 -1
- package/types/adapter/repositories/NodeTmuxSessionRepository.d.ts +1 -0
- package/types/adapter/repositories/NodeTmuxSessionRepository.d.ts.map +1 -1
- package/types/adapter/repositories/githubRestClient.d.ts +6 -0
- package/types/adapter/repositories/githubRestClient.d.ts.map +1 -1
- package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts.map +1 -1
- package/types/adapter/repositories/issue/githubRateLimitRetry.d.ts +5 -1
- package/types/adapter/repositories/issue/githubRateLimitRetry.d.ts.map +1 -1
- package/types/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.d.ts.map +1 -1
|
@@ -8,6 +8,8 @@ const githubGraphqlClient_1 = require("../githubGraphqlClient");
|
|
|
8
8
|
const utils_1 = require("../utils");
|
|
9
9
|
const localStorageCacheDirectory_1 = require("../localStorageCacheDirectory");
|
|
10
10
|
const githubRateLimitRetry_1 = require("./githubRateLimitRetry");
|
|
11
|
+
const githubRestClient_1 = require("../githubRestClient");
|
|
12
|
+
const githubSecondaryRateLimitBreaker_1 = require("./githubSecondaryRateLimitBreaker");
|
|
11
13
|
exports.FULL_ISSUE_FETCH_INTERVAL_MS = 60 * 60 * 1000;
|
|
12
14
|
exports.INCREMENTAL_FETCH_SKEW_BUFFER_MS = 5 * 60 * 1000;
|
|
13
15
|
exports.REQUIRED_CHECKS_CACHE_TTL_MS = 10 * 60 * 1000;
|
|
@@ -238,14 +240,14 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
238
240
|
this.getAllIssuesRefreshMemo = new Map();
|
|
239
241
|
this.lastIssuesFetchedAtByProjectId = new Map();
|
|
240
242
|
this.getLastIssuesFetchedAt = (projectId) => this.lastIssuesFetchedAtByProjectId.get(projectId) ?? null;
|
|
241
|
-
this.fetchWithRateLimitRetry = (request) => (0, githubRateLimitRetry_1.fetchWithGitHubRateLimitRetry)(request, this.sleep);
|
|
243
|
+
this.fetchWithRateLimitRetry = (request, requestInfo = {}) => (0, githubRateLimitRetry_1.fetchWithGitHubRateLimitRetry)(request, this.sleep, Date.now, false, false, (0, githubSecondaryRateLimitBreaker_1.secondaryRateLimitStateFilePath)(), requestInfo);
|
|
242
244
|
/**
|
|
243
245
|
* Variant for content-creating requests (POST / PUT / PATCH / DELETE).
|
|
244
246
|
* Checks the shared secondary rate limit circuit breaker before issuing
|
|
245
247
|
* the request so that a block discovered by one process immediately
|
|
246
248
|
* silences all other processes on the host.
|
|
247
249
|
*/
|
|
248
|
-
this.fetchWithRateLimitRetryForWrite = (request) => (0, githubRateLimitRetry_1.fetchWithGitHubRateLimitRetry)(request, this.sleep, Date.now, false, true);
|
|
250
|
+
this.fetchWithRateLimitRetryForWrite = (request, requestInfo = {}) => (0, githubRateLimitRetry_1.fetchWithGitHubRateLimitRetry)(request, this.sleep, Date.now, false, true, (0, githubSecondaryRateLimitBreaker_1.secondaryRateLimitStateFilePath)(), requestInfo);
|
|
249
251
|
this.throwGitHubError = async (prefix, response) => {
|
|
250
252
|
const bodyText = await response.clone().text();
|
|
251
253
|
const isRateLimit = (0, githubRateLimitRetry_1.hasRateLimitSignals)(response.status, response.headers, bodyText);
|
|
@@ -769,13 +771,14 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
769
771
|
const repoSegment = encodeURIComponent(repo);
|
|
770
772
|
const branchSegment = encodeURIComponent(branch);
|
|
771
773
|
const requiredCheckNamesSet = new Set();
|
|
772
|
-
const
|
|
774
|
+
const rulesUrl = `https://api.github.com/repos/${ownerSegment}/${repoSegment}/rules/branches/${branchSegment}?per_page=100`;
|
|
775
|
+
const rulesResponse = await this.fetchWithRateLimitRetry(() => fetch(rulesUrl, {
|
|
773
776
|
method: 'GET',
|
|
774
777
|
headers: {
|
|
775
778
|
Authorization: `Bearer ${this.ghToken}`,
|
|
776
779
|
Accept: 'application/vnd.github+json',
|
|
777
780
|
},
|
|
778
|
-
}));
|
|
781
|
+
}), { method: 'GET', path: (0, githubRestClient_1.sanitizeRestPath)(rulesUrl) });
|
|
779
782
|
if (rulesResponse.ok) {
|
|
780
783
|
const rulesBody = await rulesResponse.json();
|
|
781
784
|
if (!isBranchRulesResponse(rulesBody)) {
|
|
@@ -797,13 +800,14 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
797
800
|
const reason = await this.formatGitHubErrorWithStatus(rulesResponse);
|
|
798
801
|
throw new Error(`Failed to fetch branch rules for ${owner}/${repo}/${branch}: ${reason}`);
|
|
799
802
|
}
|
|
800
|
-
const
|
|
803
|
+
const branchUrl = `https://api.github.com/repos/${ownerSegment}/${repoSegment}/branches/${branchSegment}`;
|
|
804
|
+
const branchResponse = await this.fetchWithRateLimitRetry(() => fetch(branchUrl, {
|
|
801
805
|
method: 'GET',
|
|
802
806
|
headers: {
|
|
803
807
|
Authorization: `Bearer ${this.ghToken}`,
|
|
804
808
|
Accept: 'application/vnd.github+json',
|
|
805
809
|
},
|
|
806
|
-
}));
|
|
810
|
+
}), { method: 'GET', path: (0, githubRestClient_1.sanitizeRestPath)(branchUrl) });
|
|
807
811
|
if (branchResponse.ok) {
|
|
808
812
|
const branchBody = await branchResponse.json();
|
|
809
813
|
if (!isBranchDetailResponse(branchBody)) {
|
|
@@ -852,13 +856,14 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
852
856
|
const ownerSegment = encodeURIComponent(owner);
|
|
853
857
|
const repoSegment = encodeURIComponent(repo);
|
|
854
858
|
const shaSegment = encodeURIComponent(commitSha);
|
|
855
|
-
const
|
|
859
|
+
const checkSuitesUrl = `https://api.github.com/repos/${ownerSegment}/${repoSegment}/commits/${shaSegment}/check-suites`;
|
|
860
|
+
const checkSuitesResponse = await this.fetchWithRateLimitRetry(() => fetch(checkSuitesUrl, {
|
|
856
861
|
method: 'GET',
|
|
857
862
|
headers: {
|
|
858
863
|
Authorization: `Bearer ${this.ghToken}`,
|
|
859
864
|
Accept: 'application/vnd.github+json',
|
|
860
865
|
},
|
|
861
|
-
}));
|
|
866
|
+
}), { method: 'GET', path: (0, githubRestClient_1.sanitizeRestPath)(checkSuitesUrl) });
|
|
862
867
|
if (!checkSuitesResponse.ok) {
|
|
863
868
|
if (checkSuitesResponse.status === 404) {
|
|
864
869
|
console.warn(`ApiV3CheerioRestIssueRepository: commits/${commitSha}/check-suites returned 404 for ${owner}/${repo}, treating as no check runs.`);
|
|
@@ -877,13 +882,14 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
877
882
|
let page = 1;
|
|
878
883
|
let hasMore = true;
|
|
879
884
|
while (hasMore) {
|
|
880
|
-
const
|
|
885
|
+
const runsUrl = `https://api.github.com/repos/${ownerSegment}/${repoSegment}/check-suites/${suite.id}/check-runs?per_page=${perPage}&page=${page}`;
|
|
886
|
+
const runsResponse = await this.fetchWithRateLimitRetry(() => fetch(runsUrl, {
|
|
881
887
|
method: 'GET',
|
|
882
888
|
headers: {
|
|
883
889
|
Authorization: `Bearer ${this.ghToken}`,
|
|
884
890
|
Accept: 'application/vnd.github+json',
|
|
885
891
|
},
|
|
886
|
-
}));
|
|
892
|
+
}), { method: 'GET', path: (0, githubRestClient_1.sanitizeRestPath)(runsUrl) });
|
|
887
893
|
if (!runsResponse.ok) {
|
|
888
894
|
const reason = await this.formatGitHubErrorWithStatus(runsResponse);
|
|
889
895
|
throw new Error(`Failed to fetch check runs for suite ${suite.id} in ${owner}/${repo}: ${reason}`);
|
|
@@ -936,7 +942,8 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
936
942
|
if (diskCache?.etag) {
|
|
937
943
|
page1Headers['If-None-Match'] = diskCache.etag;
|
|
938
944
|
}
|
|
939
|
-
const
|
|
945
|
+
const page1Url = `https://api.github.com/repos/${ownerSegment}/${repoSegment}/commits/${shaSegment}/check-runs?per_page=100&page=1`;
|
|
946
|
+
const page1Response = await this.fetchWithRateLimitRetry(() => fetch(page1Url, { method: 'GET', headers: page1Headers }), { method: 'GET', path: (0, githubRestClient_1.sanitizeRestPath)(page1Url) });
|
|
940
947
|
if (!page1Response.ok &&
|
|
941
948
|
(0, githubRateLimitRetry_1.hasRateLimitSignals)(page1Response.status, page1Response.headers, await page1Response.clone().text())) {
|
|
942
949
|
if (diskCache) {
|
|
@@ -982,13 +989,14 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
982
989
|
let page = 2;
|
|
983
990
|
let hasMore = page1Body.check_runs.length >= perPage && perPage < page1Body.total_count;
|
|
984
991
|
while (hasMore) {
|
|
985
|
-
const
|
|
992
|
+
const pageUrl = `https://api.github.com/repos/${ownerSegment}/${repoSegment}/commits/${shaSegment}/check-runs?per_page=${perPage}&page=${page}`;
|
|
993
|
+
const pageResponse = await this.fetchWithRateLimitRetry(() => fetch(pageUrl, {
|
|
986
994
|
method: 'GET',
|
|
987
995
|
headers: {
|
|
988
996
|
Authorization: `Bearer ${this.ghToken}`,
|
|
989
997
|
Accept: 'application/vnd.github+json',
|
|
990
998
|
},
|
|
991
|
-
}));
|
|
999
|
+
}), { method: 'GET', path: (0, githubRestClient_1.sanitizeRestPath)(pageUrl) });
|
|
992
1000
|
if (!pageResponse.ok &&
|
|
993
1001
|
(0, githubRateLimitRetry_1.hasRateLimitSignals)(pageResponse.status, pageResponse.headers, await pageResponse.clone().text())) {
|
|
994
1002
|
if (diskCache) {
|
|
@@ -1033,10 +1041,11 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
1033
1041
|
if (diskCache?.combinedStatusEtag) {
|
|
1034
1042
|
combinedStatusHeaders['If-None-Match'] = diskCache.combinedStatusEtag;
|
|
1035
1043
|
}
|
|
1036
|
-
const
|
|
1044
|
+
const combinedStatusUrl = `https://api.github.com/repos/${ownerSegment}/${repoSegment}/commits/${shaSegment}/status?per_page=100`;
|
|
1045
|
+
const combinedStatusResponse = await this.fetchWithRateLimitRetry(() => fetch(combinedStatusUrl, {
|
|
1037
1046
|
method: 'GET',
|
|
1038
1047
|
headers: combinedStatusHeaders,
|
|
1039
|
-
}));
|
|
1048
|
+
}), { method: 'GET', path: (0, githubRestClient_1.sanitizeRestPath)(combinedStatusUrl) });
|
|
1040
1049
|
if (!combinedStatusResponse.ok &&
|
|
1041
1050
|
(0, githubRateLimitRetry_1.hasRateLimitSignals)(combinedStatusResponse.status, combinedStatusResponse.headers, await combinedStatusResponse.clone().text())) {
|
|
1042
1051
|
if (diskCache) {
|
|
@@ -1536,13 +1545,14 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
1536
1545
|
if (attempt > 0) {
|
|
1537
1546
|
await this.sleep(retryDelayMilliseconds);
|
|
1538
1547
|
}
|
|
1539
|
-
const
|
|
1548
|
+
const prStatusUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}`;
|
|
1549
|
+
const response = await this.fetchWithRateLimitRetry(() => fetch(prStatusUrl, {
|
|
1540
1550
|
method: 'GET',
|
|
1541
1551
|
headers: {
|
|
1542
1552
|
Authorization: `Bearer ${this.ghToken}`,
|
|
1543
1553
|
Accept: 'application/vnd.github+json',
|
|
1544
1554
|
},
|
|
1545
|
-
}));
|
|
1555
|
+
}), { method: 'GET', path: (0, githubRestClient_1.sanitizeRestPath)(prStatusUrl) });
|
|
1546
1556
|
if (response.status === 404) {
|
|
1547
1557
|
return null;
|
|
1548
1558
|
}
|
|
@@ -1747,14 +1757,15 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
1747
1757
|
};
|
|
1748
1758
|
this.closePullRequest = async (prUrl) => {
|
|
1749
1759
|
const { owner, repo, issueNumber: prNumber } = this.parseIssueUrl(prUrl);
|
|
1750
|
-
const
|
|
1760
|
+
const closePrUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}`;
|
|
1761
|
+
const response = await this.fetchWithRateLimitRetryForWrite(() => fetch(closePrUrl, {
|
|
1751
1762
|
method: 'PATCH',
|
|
1752
1763
|
headers: {
|
|
1753
1764
|
Authorization: `Bearer ${this.ghToken}`,
|
|
1754
1765
|
'Content-Type': 'application/json',
|
|
1755
1766
|
},
|
|
1756
1767
|
body: JSON.stringify({ state: 'closed' }),
|
|
1757
|
-
}));
|
|
1768
|
+
}), { method: 'PATCH', path: (0, githubRestClient_1.sanitizeRestPath)(closePrUrl) });
|
|
1758
1769
|
if (!response.ok) {
|
|
1759
1770
|
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
1760
1771
|
throw new Error(`Failed to close PR ${prUrl}: ${reason}`);
|
|
@@ -1764,14 +1775,15 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
1764
1775
|
const { owner, repo, issueNumber } = this.parseIssueUrl(issueUrl);
|
|
1765
1776
|
const ownerSegment = encodeURIComponent(owner);
|
|
1766
1777
|
const repoSegment = encodeURIComponent(repo);
|
|
1767
|
-
const
|
|
1778
|
+
const closeIssueUrl = `https://api.github.com/repos/${ownerSegment}/${repoSegment}/issues/${issueNumber}`;
|
|
1779
|
+
const response = await this.fetchWithRateLimitRetryForWrite(() => fetch(closeIssueUrl, {
|
|
1768
1780
|
method: 'PATCH',
|
|
1769
1781
|
headers: {
|
|
1770
1782
|
Authorization: `Bearer ${this.ghToken}`,
|
|
1771
1783
|
'Content-Type': 'application/json',
|
|
1772
1784
|
},
|
|
1773
1785
|
body: JSON.stringify({ state: 'closed', state_reason: stateReason }),
|
|
1774
|
-
}));
|
|
1786
|
+
}), { method: 'PATCH', path: (0, githubRestClient_1.sanitizeRestPath)(closeIssueUrl) });
|
|
1775
1787
|
if (!response.ok) {
|
|
1776
1788
|
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
1777
1789
|
throw new Error(`Failed to close issue ${issueUrl}: ${reason}`);
|
|
@@ -1784,13 +1796,14 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
1784
1796
|
let page = 1;
|
|
1785
1797
|
let hasMore = true;
|
|
1786
1798
|
while (hasMore) {
|
|
1787
|
-
const
|
|
1799
|
+
const prFilesUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}/files?per_page=${perPage}&page=${page}`;
|
|
1800
|
+
const response = await this.fetchWithRateLimitRetry(() => fetch(prFilesUrl, {
|
|
1788
1801
|
method: 'GET',
|
|
1789
1802
|
headers: {
|
|
1790
1803
|
Authorization: `Bearer ${this.ghToken}`,
|
|
1791
1804
|
Accept: 'application/vnd.github+json',
|
|
1792
1805
|
},
|
|
1793
|
-
}));
|
|
1806
|
+
}), { method: 'GET', path: (0, githubRestClient_1.sanitizeRestPath)(prFilesUrl) });
|
|
1794
1807
|
if (!response.ok) {
|
|
1795
1808
|
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
1796
1809
|
throw new Error(`Failed to fetch changed files for PR ${prUrl}: ${reason}`);
|
|
@@ -1818,7 +1831,7 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
1818
1831
|
Authorization: `Bearer ${this.ghToken}`,
|
|
1819
1832
|
Accept: 'application/vnd.github+json',
|
|
1820
1833
|
},
|
|
1821
|
-
}));
|
|
1834
|
+
}), { method: 'GET', path: '/user' });
|
|
1822
1835
|
if (!response.ok) {
|
|
1823
1836
|
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
1824
1837
|
throw new Error(`Failed to fetch authenticated user: ${reason}`);
|
|
@@ -1831,7 +1844,8 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
1831
1844
|
};
|
|
1832
1845
|
this.approvePullRequest = async (prUrl) => {
|
|
1833
1846
|
const { owner, repo, issueNumber: prNumber } = this.parseIssueUrl(prUrl);
|
|
1834
|
-
const
|
|
1847
|
+
const approveUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}/reviews`;
|
|
1848
|
+
const response = await this.fetchWithRateLimitRetryForWrite(() => fetch(approveUrl, {
|
|
1835
1849
|
method: 'POST',
|
|
1836
1850
|
headers: {
|
|
1837
1851
|
Authorization: `Bearer ${this.ghToken}`,
|
|
@@ -1839,7 +1853,7 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
1839
1853
|
Accept: 'application/vnd.github+json',
|
|
1840
1854
|
},
|
|
1841
1855
|
body: JSON.stringify({ event: 'APPROVE' }),
|
|
1842
|
-
}));
|
|
1856
|
+
}), { method: 'POST', path: (0, githubRestClient_1.sanitizeRestPath)(approveUrl) });
|
|
1843
1857
|
if (!response.ok) {
|
|
1844
1858
|
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
1845
1859
|
throw new Error(`Failed to approve PR ${prUrl}: ${reason}`);
|
|
@@ -1857,7 +1871,7 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
1857
1871
|
method: 'PUT',
|
|
1858
1872
|
headers,
|
|
1859
1873
|
body: JSON.stringify(mergeMethod ? { merge_method: mergeMethod } : {}),
|
|
1860
|
-
}));
|
|
1874
|
+
}), { method: 'PUT', path: (0, githubRestClient_1.sanitizeRestPath)(mergeUrl) });
|
|
1861
1875
|
const response = await mergeWith();
|
|
1862
1876
|
if (response.ok) {
|
|
1863
1877
|
return;
|
|
@@ -1877,12 +1891,13 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
1877
1891
|
throw new Error(`Failed to merge PR ${prUrl}: ${reason}`);
|
|
1878
1892
|
};
|
|
1879
1893
|
this.resolveAllowedMergeMethod = async (owner, repo) => {
|
|
1880
|
-
const
|
|
1894
|
+
const repoUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`;
|
|
1895
|
+
const response = await this.fetchWithRateLimitRetry(() => fetch(repoUrl, {
|
|
1881
1896
|
headers: {
|
|
1882
1897
|
Authorization: `Bearer ${this.ghToken}`,
|
|
1883
1898
|
Accept: 'application/vnd.github+json',
|
|
1884
1899
|
},
|
|
1885
|
-
}));
|
|
1900
|
+
}), { method: 'GET', path: (0, githubRestClient_1.sanitizeRestPath)(repoUrl) });
|
|
1886
1901
|
if (!response.ok) {
|
|
1887
1902
|
return null;
|
|
1888
1903
|
}
|
|
@@ -1917,7 +1932,8 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
1917
1932
|
body: commentBody,
|
|
1918
1933
|
comments: [inlineComment],
|
|
1919
1934
|
};
|
|
1920
|
-
const
|
|
1935
|
+
const reviewUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}/reviews`;
|
|
1936
|
+
const response = await this.fetchWithRateLimitRetryForWrite(() => fetch(reviewUrl, {
|
|
1921
1937
|
method: 'POST',
|
|
1922
1938
|
headers: {
|
|
1923
1939
|
Authorization: `Bearer ${this.ghToken}`,
|
|
@@ -1925,7 +1941,7 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
1925
1941
|
Accept: 'application/vnd.github+json',
|
|
1926
1942
|
},
|
|
1927
1943
|
body: JSON.stringify(reviewBody),
|
|
1928
|
-
}));
|
|
1944
|
+
}), { method: 'POST', path: (0, githubRestClient_1.sanitizeRestPath)(reviewUrl) });
|
|
1929
1945
|
if (!response.ok) {
|
|
1930
1946
|
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
1931
1947
|
if (response.status === 422 &&
|
|
@@ -1946,13 +1962,14 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
1946
1962
|
this.fetchPullRequestHeadSha = async (owner, repo, prNumber, prUrl) => {
|
|
1947
1963
|
const ownerSegment = encodeURIComponent(owner);
|
|
1948
1964
|
const repoSegment = encodeURIComponent(repo);
|
|
1949
|
-
const
|
|
1965
|
+
const headShaUrl = `https://api.github.com/repos/${ownerSegment}/${repoSegment}/pulls/${prNumber}`;
|
|
1966
|
+
const response = await this.fetchWithRateLimitRetry(() => fetch(headShaUrl, {
|
|
1950
1967
|
method: 'GET',
|
|
1951
1968
|
headers: {
|
|
1952
1969
|
Authorization: `Bearer ${this.ghToken}`,
|
|
1953
1970
|
Accept: 'application/vnd.github+json',
|
|
1954
1971
|
},
|
|
1955
|
-
}));
|
|
1972
|
+
}), { method: 'GET', path: (0, githubRestClient_1.sanitizeRestPath)(headShaUrl) });
|
|
1956
1973
|
if (!response.ok) {
|
|
1957
1974
|
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
1958
1975
|
throw new Error(`Failed to fetch head commit for PR ${prUrl}: ${reason}`);
|
|
@@ -1970,7 +1987,8 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
1970
1987
|
const commitId = await this.fetchPullRequestHeadSha(owner, repo, prNumber, prUrl);
|
|
1971
1988
|
const ownerSegment = encodeURIComponent(owner);
|
|
1972
1989
|
const repoSegment = encodeURIComponent(repo);
|
|
1973
|
-
const
|
|
1990
|
+
const reviewCommentUrl = `https://api.github.com/repos/${ownerSegment}/${repoSegment}/pulls/${prNumber}/comments`;
|
|
1991
|
+
const response = await this.fetchWithRateLimitRetryForWrite(() => fetch(reviewCommentUrl, {
|
|
1974
1992
|
method: 'POST',
|
|
1975
1993
|
headers: {
|
|
1976
1994
|
Authorization: `Bearer ${this.ghToken}`,
|
|
@@ -1984,7 +2002,7 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
1984
2002
|
line,
|
|
1985
2003
|
side,
|
|
1986
2004
|
}),
|
|
1987
|
-
}));
|
|
2005
|
+
}), { method: 'POST', path: (0, githubRestClient_1.sanitizeRestPath)(reviewCommentUrl) });
|
|
1988
2006
|
if (!response.ok) {
|
|
1989
2007
|
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
1990
2008
|
throw new Error(`Failed to create review comment on PR ${prUrl}: ${reason}`);
|
|
@@ -2040,13 +2058,14 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
2040
2058
|
};
|
|
2041
2059
|
this.updateBranch = async (prUrl) => {
|
|
2042
2060
|
const { owner, repo, issueNumber: prNumber } = this.parseIssueUrl(prUrl);
|
|
2043
|
-
const
|
|
2061
|
+
const updateBranchUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}/update-branch`;
|
|
2062
|
+
const response = await this.fetchWithRateLimitRetryForWrite(() => fetch(updateBranchUrl, {
|
|
2044
2063
|
method: 'PUT',
|
|
2045
2064
|
headers: {
|
|
2046
2065
|
Authorization: `Bearer ${this.ghToken}`,
|
|
2047
2066
|
Accept: 'application/vnd.github+json',
|
|
2048
2067
|
},
|
|
2049
|
-
}));
|
|
2068
|
+
}), { method: 'PUT', path: (0, githubRestClient_1.sanitizeRestPath)(updateBranchUrl) });
|
|
2050
2069
|
if (response.ok) {
|
|
2051
2070
|
return true;
|
|
2052
2071
|
}
|
|
@@ -2058,12 +2077,13 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
2058
2077
|
};
|
|
2059
2078
|
this.deletePullRequestBranch = async (prUrl, branchName) => {
|
|
2060
2079
|
const { owner, repo } = this.parseIssueUrl(prUrl);
|
|
2061
|
-
const
|
|
2080
|
+
const deleteBranchUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/git/refs/heads/${encodeURIComponent(branchName)}`;
|
|
2081
|
+
const response = await this.fetchWithRateLimitRetryForWrite(() => fetch(deleteBranchUrl, {
|
|
2062
2082
|
method: 'DELETE',
|
|
2063
2083
|
headers: {
|
|
2064
2084
|
Authorization: `Bearer ${this.ghToken}`,
|
|
2065
2085
|
},
|
|
2066
|
-
}));
|
|
2086
|
+
}), { method: 'DELETE', path: (0, githubRestClient_1.sanitizeRestPath)(deleteBranchUrl) });
|
|
2067
2087
|
if (!response.ok && response.status !== 422) {
|
|
2068
2088
|
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
2069
2089
|
throw new Error(`Failed to delete branch ${branchName} for PR ${prUrl}: ${reason}`);
|
|
@@ -2074,13 +2094,14 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
2074
2094
|
};
|
|
2075
2095
|
this.fetchIssueBodyResponse = (url) => {
|
|
2076
2096
|
const { owner, repo, issueNumber } = this.parseIssueUrl(url);
|
|
2077
|
-
|
|
2097
|
+
const issueBodyUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues/${issueNumber}`;
|
|
2098
|
+
return this.fetchWithRateLimitRetry(() => fetch(issueBodyUrl, {
|
|
2078
2099
|
method: 'GET',
|
|
2079
2100
|
headers: {
|
|
2080
2101
|
Authorization: `Bearer ${this.ghToken}`,
|
|
2081
2102
|
Accept: 'application/vnd.github+json',
|
|
2082
2103
|
},
|
|
2083
|
-
}));
|
|
2104
|
+
}), { method: 'GET', path: (0, githubRestClient_1.sanitizeRestPath)(issueBodyUrl) });
|
|
2084
2105
|
};
|
|
2085
2106
|
this.parseIssueBodyResponse = async (url, response) => {
|
|
2086
2107
|
const body = await response.json();
|
|
@@ -2114,13 +2135,14 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
2114
2135
|
let page = 1;
|
|
2115
2136
|
let hasMore = true;
|
|
2116
2137
|
while (hasMore) {
|
|
2117
|
-
const
|
|
2138
|
+
const commentsUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues/${issueNumber}/comments?per_page=${perPage}&page=${page}`;
|
|
2139
|
+
const response = await this.fetchWithRateLimitRetry(() => fetch(commentsUrl, {
|
|
2118
2140
|
method: 'GET',
|
|
2119
2141
|
headers: {
|
|
2120
2142
|
Authorization: `Bearer ${this.ghToken}`,
|
|
2121
2143
|
Accept: 'application/vnd.github+json',
|
|
2122
2144
|
},
|
|
2123
|
-
}));
|
|
2145
|
+
}), { method: 'GET', path: (0, githubRestClient_1.sanitizeRestPath)(commentsUrl) });
|
|
2124
2146
|
if (!response.ok) {
|
|
2125
2147
|
await this.throwGitHubError(`Failed to fetch comments for ${url}`, response);
|
|
2126
2148
|
}
|
|
@@ -2150,13 +2172,14 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
2150
2172
|
if (!isPr) {
|
|
2151
2173
|
return null;
|
|
2152
2174
|
}
|
|
2153
|
-
const
|
|
2175
|
+
const prDetailUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}`;
|
|
2176
|
+
const detailResponse = await this.fetchWithRateLimitRetry(() => fetch(prDetailUrl, {
|
|
2154
2177
|
method: 'GET',
|
|
2155
2178
|
headers: {
|
|
2156
2179
|
Authorization: `Bearer ${this.ghToken}`,
|
|
2157
2180
|
Accept: 'application/vnd.github+json',
|
|
2158
2181
|
},
|
|
2159
|
-
}));
|
|
2182
|
+
}), { method: 'GET', path: (0, githubRestClient_1.sanitizeRestPath)(prDetailUrl) });
|
|
2160
2183
|
if (!detailResponse.ok) {
|
|
2161
2184
|
await this.throwGitHubError(`Failed to fetch detail for PR ${prUrl}`, detailResponse);
|
|
2162
2185
|
}
|
|
@@ -2185,13 +2208,14 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
2185
2208
|
let page = 1;
|
|
2186
2209
|
let hasMore = true;
|
|
2187
2210
|
while (hasMore) {
|
|
2188
|
-
const
|
|
2211
|
+
const prDetailFilesUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}/files?per_page=${perPage}&page=${page}`;
|
|
2212
|
+
const response = await this.fetchWithRateLimitRetry(() => fetch(prDetailFilesUrl, {
|
|
2189
2213
|
method: 'GET',
|
|
2190
2214
|
headers: {
|
|
2191
2215
|
Authorization: `Bearer ${this.ghToken}`,
|
|
2192
2216
|
Accept: 'application/vnd.github+json',
|
|
2193
2217
|
},
|
|
2194
|
-
}));
|
|
2218
|
+
}), { method: 'GET', path: (0, githubRestClient_1.sanitizeRestPath)(prDetailFilesUrl) });
|
|
2195
2219
|
if (!response.ok) {
|
|
2196
2220
|
await this.throwGitHubError(`Failed to fetch files for PR ${prUrl}`, response);
|
|
2197
2221
|
}
|
|
@@ -2230,13 +2254,14 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
2230
2254
|
let page = 1;
|
|
2231
2255
|
let hasMore = true;
|
|
2232
2256
|
while (hasMore) {
|
|
2233
|
-
const
|
|
2257
|
+
const prCommitsUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}/commits?per_page=${perPage}&page=${page}`;
|
|
2258
|
+
const response = await this.fetchWithRateLimitRetry(() => fetch(prCommitsUrl, {
|
|
2234
2259
|
method: 'GET',
|
|
2235
2260
|
headers: {
|
|
2236
2261
|
Authorization: `Bearer ${this.ghToken}`,
|
|
2237
2262
|
Accept: 'application/vnd.github+json',
|
|
2238
2263
|
},
|
|
2239
|
-
}));
|
|
2264
|
+
}), { method: 'GET', path: (0, githubRestClient_1.sanitizeRestPath)(prCommitsUrl) });
|
|
2240
2265
|
if (!response.ok) {
|
|
2241
2266
|
await this.throwGitHubError(`Failed to fetch commits for PR ${prUrl}`, response);
|
|
2242
2267
|
}
|
|
@@ -2264,13 +2289,14 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
2264
2289
|
this.getIssueOrPullRequestState = async (url) => {
|
|
2265
2290
|
const { owner, repo, issueNumber, isPr } = this.parseIssueUrl(url);
|
|
2266
2291
|
if (isPr) {
|
|
2267
|
-
const
|
|
2292
|
+
const prStateUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${issueNumber}`;
|
|
2293
|
+
const response = await this.fetchWithRateLimitRetry(() => fetch(prStateUrl, {
|
|
2268
2294
|
method: 'GET',
|
|
2269
2295
|
headers: {
|
|
2270
2296
|
Authorization: `Bearer ${this.ghToken}`,
|
|
2271
2297
|
Accept: 'application/vnd.github+json',
|
|
2272
2298
|
},
|
|
2273
|
-
}));
|
|
2299
|
+
}), { method: 'GET', path: (0, githubRestClient_1.sanitizeRestPath)(prStateUrl) });
|
|
2274
2300
|
if (!response.ok) {
|
|
2275
2301
|
await this.throwGitHubError(`Failed to fetch state for ${url}`, response);
|
|
2276
2302
|
}
|
|
@@ -2285,13 +2311,14 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
2285
2311
|
title: body.title,
|
|
2286
2312
|
};
|
|
2287
2313
|
}
|
|
2288
|
-
const
|
|
2314
|
+
const issueStateUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues/${issueNumber}`;
|
|
2315
|
+
const response = await this.fetchWithRateLimitRetry(() => fetch(issueStateUrl, {
|
|
2289
2316
|
method: 'GET',
|
|
2290
2317
|
headers: {
|
|
2291
2318
|
Authorization: `Bearer ${this.ghToken}`,
|
|
2292
2319
|
Accept: 'application/vnd.github+json',
|
|
2293
2320
|
},
|
|
2294
|
-
}));
|
|
2321
|
+
}), { method: 'GET', path: (0, githubRestClient_1.sanitizeRestPath)(issueStateUrl) });
|
|
2295
2322
|
if (!response.ok) {
|
|
2296
2323
|
await this.throwGitHubError(`Failed to fetch state for ${url}`, response);
|
|
2297
2324
|
}
|
|
@@ -2311,13 +2338,14 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
2311
2338
|
if (!isPr) {
|
|
2312
2339
|
return null;
|
|
2313
2340
|
}
|
|
2314
|
-
const
|
|
2341
|
+
const prSummaryUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}`;
|
|
2342
|
+
const response = await this.fetchWithRateLimitRetry(() => fetch(prSummaryUrl, {
|
|
2315
2343
|
method: 'GET',
|
|
2316
2344
|
headers: {
|
|
2317
2345
|
Authorization: `Bearer ${this.ghToken}`,
|
|
2318
2346
|
Accept: 'application/vnd.github+json',
|
|
2319
2347
|
},
|
|
2320
|
-
}));
|
|
2348
|
+
}), { method: 'GET', path: (0, githubRestClient_1.sanitizeRestPath)(prSummaryUrl) });
|
|
2321
2349
|
if (!response.ok) {
|
|
2322
2350
|
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
2323
2351
|
throw new Error(`Failed to fetch summary for PR ${prUrl}: ${reason}`);
|
|
@@ -2338,13 +2366,14 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
2338
2366
|
const { owner, repo, issueNumber } = this.parseIssueUrl(issueOrPrUrl);
|
|
2339
2367
|
const perPage = 100;
|
|
2340
2368
|
while (true) {
|
|
2341
|
-
const
|
|
2369
|
+
const listCommentsUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues/${issueNumber}/comments?per_page=${perPage}&page=1`;
|
|
2370
|
+
const response = await this.fetchWithRateLimitRetry(() => fetch(listCommentsUrl, {
|
|
2342
2371
|
method: 'GET',
|
|
2343
2372
|
headers: {
|
|
2344
2373
|
Authorization: `Bearer ${this.ghToken}`,
|
|
2345
2374
|
Accept: 'application/vnd.github+json',
|
|
2346
2375
|
},
|
|
2347
|
-
}));
|
|
2376
|
+
}), { method: 'GET', path: (0, githubRestClient_1.sanitizeRestPath)(listCommentsUrl) });
|
|
2348
2377
|
if (!response.ok) {
|
|
2349
2378
|
await this.throwGitHubError(`Failed to fetch comments for ${issueOrPrUrl}`, response);
|
|
2350
2379
|
}
|
|
@@ -2353,13 +2382,14 @@ class ApiV3CheerioRestIssueRepository extends BaseGitHubRepository_1.BaseGitHubR
|
|
|
2353
2382
|
throw new Error(`Unexpected response shape when fetching comments for ${issueOrPrUrl}`);
|
|
2354
2383
|
}
|
|
2355
2384
|
for (const comment of body) {
|
|
2356
|
-
const
|
|
2385
|
+
const deleteCommentUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues/comments/${comment.id}`;
|
|
2386
|
+
const deleteResponse = await this.fetchWithRateLimitRetryForWrite(() => fetch(deleteCommentUrl, {
|
|
2357
2387
|
method: 'DELETE',
|
|
2358
2388
|
headers: {
|
|
2359
2389
|
Authorization: `Bearer ${this.ghToken}`,
|
|
2360
2390
|
Accept: 'application/vnd.github+json',
|
|
2361
2391
|
},
|
|
2362
|
-
}));
|
|
2392
|
+
}), { method: 'DELETE', path: (0, githubRestClient_1.sanitizeRestPath)(deleteCommentUrl) });
|
|
2363
2393
|
if (!deleteResponse.ok) {
|
|
2364
2394
|
await this.throwGitHubError(`Failed to delete comment ${comment.id} on ${issueOrPrUrl}`, deleteResponse);
|
|
2365
2395
|
}
|