github-issue-tower-defence-management 1.116.2 → 1.116.5
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/.github/workflows/commit-lint.yml +1 -1
- package/.github/workflows/configs/commitlint.config.cjs +32 -0
- package/.github/workflows/umino-project.yml +9 -3
- package/CHANGELOG.md +14 -0
- package/README.md +2 -2
- package/bin/adapter/repositories/ProcClaudeLiveSessionRepository.js +18 -6
- package/bin/adapter/repositories/ProcClaudeLiveSessionRepository.js.map +1 -1
- package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js +74 -49
- package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js.map +1 -1
- package/bin/adapter/repositories/issue/githubRateLimitRetry.js +77 -0
- package/bin/adapter/repositories/issue/githubRateLimitRetry.js.map +1 -0
- package/bin/domain/usecases/LiveSessionOauthTokenSelectUseCase.js +6 -6
- package/bin/domain/usecases/LiveSessionOauthTokenSelectUseCase.js.map +1 -1
- package/bin/domain/usecases/OauthTokenSelectUseCase.js +1 -1
- package/bin/domain/usecases/OauthTokenSelectUseCase.js.map +1 -1
- package/package.json +1 -1
- package/renovate.json +1 -0
- package/src/adapter/entry-points/handlers/LiveSessionOauthTokenSelectHandler.test.ts +11 -11
- package/src/adapter/repositories/ProcClaudeLiveSessionRepository.test.ts +69 -7
- package/src/adapter/repositories/ProcClaudeLiveSessionRepository.ts +19 -8
- package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.test.ts +111 -0
- package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.ts +243 -193
- package/src/adapter/repositories/issue/githubRateLimitRetry.test.ts +167 -0
- package/src/adapter/repositories/issue/githubRateLimitRetry.ts +105 -0
- package/src/domain/usecases/LiveSessionOauthTokenSelectUseCase.test.ts +25 -3
- package/src/domain/usecases/LiveSessionOauthTokenSelectUseCase.ts +7 -7
- package/src/domain/usecases/OauthTokenSelectUseCase.test.ts +13 -4
- package/src/domain/usecases/OauthTokenSelectUseCase.ts +1 -1
- package/src/domain/usecases/adapter-interfaces/ClaudeLiveSessionRepository.ts +1 -1
- package/types/adapter/repositories/ProcClaudeLiveSessionRepository.d.ts +1 -0
- package/types/adapter/repositories/ProcClaudeLiveSessionRepository.d.ts.map +1 -1
- package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts +4 -1
- package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts.map +1 -1
- package/types/adapter/repositories/issue/githubRateLimitRetry.d.ts +10 -0
- package/types/adapter/repositories/issue/githubRateLimitRetry.d.ts.map +1 -0
- package/types/domain/usecases/OauthTokenSelectUseCase.d.ts +1 -1
- package/types/domain/usecases/OauthTokenSelectUseCase.d.ts.map +1 -1
- package/types/domain/usecases/adapter-interfaces/ClaudeLiveSessionRepository.d.ts +1 -1
- package/types/domain/usecases/adapter-interfaces/ClaudeLiveSessionRepository.d.ts.map +1 -1
|
@@ -22,6 +22,13 @@ import { BaseGitHubRepository } from '../BaseGitHubRepository';
|
|
|
22
22
|
import { normalizeFieldName } from '../utils';
|
|
23
23
|
import { LocalStorageRepository } from '../LocalStorageRepository';
|
|
24
24
|
import { Member } from '../../../domain/entities/Member';
|
|
25
|
+
import {
|
|
26
|
+
Sleep,
|
|
27
|
+
realSleep,
|
|
28
|
+
fetchWithGitHubRateLimitRetry,
|
|
29
|
+
computeRateLimitResetIso,
|
|
30
|
+
hasRateLimitSignals,
|
|
31
|
+
} from './githubRateLimitRetry';
|
|
25
32
|
|
|
26
33
|
type TimelineItem = {
|
|
27
34
|
__typename: string;
|
|
@@ -471,10 +478,15 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
471
478
|
>,
|
|
472
479
|
readonly localStorageRepository: LocalStorageRepository,
|
|
473
480
|
readonly ghToken: string = process.env.GH_TOKEN || 'dummy',
|
|
481
|
+
readonly sleep: Sleep = realSleep,
|
|
474
482
|
) {
|
|
475
483
|
super(localStorageRepository, ghToken);
|
|
476
484
|
}
|
|
477
485
|
|
|
486
|
+
private fetchWithRateLimitRetry = (
|
|
487
|
+
request: () => Promise<Response>,
|
|
488
|
+
): Promise<Response> => fetchWithGitHubRateLimitRetry(request, this.sleep);
|
|
489
|
+
|
|
478
490
|
updateStatus: (
|
|
479
491
|
project: Project,
|
|
480
492
|
issue: Issue,
|
|
@@ -1106,17 +1118,19 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
1106
1118
|
let hasNextPage = true;
|
|
1107
1119
|
|
|
1108
1120
|
while (hasNextPage) {
|
|
1109
|
-
const response = await
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1121
|
+
const response = await this.fetchWithRateLimitRetry(() =>
|
|
1122
|
+
fetch('https://api.github.com/graphql', {
|
|
1123
|
+
method: 'POST',
|
|
1124
|
+
headers: {
|
|
1125
|
+
Authorization: `Bearer ${this.ghToken}`,
|
|
1126
|
+
'Content-Type': 'application/json',
|
|
1127
|
+
},
|
|
1128
|
+
body: JSON.stringify({
|
|
1129
|
+
query,
|
|
1130
|
+
variables: { owner, repo, issueNumber, after },
|
|
1131
|
+
}),
|
|
1118
1132
|
}),
|
|
1119
|
-
|
|
1133
|
+
);
|
|
1120
1134
|
|
|
1121
1135
|
if (!response.ok) {
|
|
1122
1136
|
throw new Error(
|
|
@@ -1285,17 +1299,19 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
1285
1299
|
}
|
|
1286
1300
|
`;
|
|
1287
1301
|
|
|
1288
|
-
const response = await
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1302
|
+
const response = await this.fetchWithRateLimitRetry(() =>
|
|
1303
|
+
fetch('https://api.github.com/graphql', {
|
|
1304
|
+
method: 'POST',
|
|
1305
|
+
headers: {
|
|
1306
|
+
Authorization: `Bearer ${this.ghToken}`,
|
|
1307
|
+
'Content-Type': 'application/json',
|
|
1308
|
+
},
|
|
1309
|
+
body: JSON.stringify({
|
|
1310
|
+
query,
|
|
1311
|
+
variables: { owner, repo, prNumber },
|
|
1312
|
+
}),
|
|
1297
1313
|
}),
|
|
1298
|
-
|
|
1314
|
+
);
|
|
1299
1315
|
|
|
1300
1316
|
if (!response.ok) {
|
|
1301
1317
|
throw new Error(
|
|
@@ -1322,16 +1338,18 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
1322
1338
|
|
|
1323
1339
|
closePullRequest = async (prUrl: string): Promise<void> => {
|
|
1324
1340
|
const { owner, repo, issueNumber: prNumber } = this.parseIssueUrl(prUrl);
|
|
1325
|
-
const response = await
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1341
|
+
const response = await this.fetchWithRateLimitRetry(() =>
|
|
1342
|
+
fetch(
|
|
1343
|
+
`https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}`,
|
|
1344
|
+
{
|
|
1345
|
+
method: 'PATCH',
|
|
1346
|
+
headers: {
|
|
1347
|
+
Authorization: `Bearer ${this.ghToken}`,
|
|
1348
|
+
'Content-Type': 'application/json',
|
|
1349
|
+
},
|
|
1350
|
+
body: JSON.stringify({ state: 'closed' }),
|
|
1332
1351
|
},
|
|
1333
|
-
|
|
1334
|
-
},
|
|
1352
|
+
),
|
|
1335
1353
|
);
|
|
1336
1354
|
if (!response.ok) {
|
|
1337
1355
|
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
@@ -1346,21 +1364,22 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
1346
1364
|
const { owner, repo, issueNumber } = this.parseIssueUrl(issueUrl);
|
|
1347
1365
|
const ownerSegment = encodeURIComponent(owner);
|
|
1348
1366
|
const repoSegment = encodeURIComponent(repo);
|
|
1349
|
-
const response = await
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1367
|
+
const response = await this.fetchWithRateLimitRetry(() =>
|
|
1368
|
+
fetch(
|
|
1369
|
+
`https://api.github.com/repos/${ownerSegment}/${repoSegment}/issues/${issueNumber}`,
|
|
1370
|
+
{
|
|
1371
|
+
method: 'PATCH',
|
|
1372
|
+
headers: {
|
|
1373
|
+
Authorization: `Bearer ${this.ghToken}`,
|
|
1374
|
+
'Content-Type': 'application/json',
|
|
1375
|
+
},
|
|
1376
|
+
body: JSON.stringify({ state: 'closed', state_reason: stateReason }),
|
|
1356
1377
|
},
|
|
1357
|
-
|
|
1358
|
-
},
|
|
1378
|
+
),
|
|
1359
1379
|
);
|
|
1360
1380
|
if (!response.ok) {
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
);
|
|
1381
|
+
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
1382
|
+
throw new Error(`Failed to close issue ${issueUrl}: ${reason}`);
|
|
1364
1383
|
}
|
|
1365
1384
|
};
|
|
1366
1385
|
|
|
@@ -1371,19 +1390,22 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
1371
1390
|
let page = 1;
|
|
1372
1391
|
let hasMore = true;
|
|
1373
1392
|
while (hasMore) {
|
|
1374
|
-
const response = await
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1393
|
+
const response = await this.fetchWithRateLimitRetry(() =>
|
|
1394
|
+
fetch(
|
|
1395
|
+
`https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}/files?per_page=${perPage}&page=${page}`,
|
|
1396
|
+
{
|
|
1397
|
+
method: 'GET',
|
|
1398
|
+
headers: {
|
|
1399
|
+
Authorization: `Bearer ${this.ghToken}`,
|
|
1400
|
+
Accept: 'application/vnd.github+json',
|
|
1401
|
+
},
|
|
1381
1402
|
},
|
|
1382
|
-
|
|
1403
|
+
),
|
|
1383
1404
|
);
|
|
1384
1405
|
if (!response.ok) {
|
|
1406
|
+
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
1385
1407
|
throw new Error(
|
|
1386
|
-
`Failed to fetch changed files for PR ${prUrl}:
|
|
1408
|
+
`Failed to fetch changed files for PR ${prUrl}: ${reason}`,
|
|
1387
1409
|
);
|
|
1388
1410
|
}
|
|
1389
1411
|
const body: unknown = await response.json();
|
|
@@ -1406,17 +1428,19 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
1406
1428
|
|
|
1407
1429
|
approvePullRequest = async (prUrl: string): Promise<void> => {
|
|
1408
1430
|
const { owner, repo, issueNumber: prNumber } = this.parseIssueUrl(prUrl);
|
|
1409
|
-
const response = await
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1431
|
+
const response = await this.fetchWithRateLimitRetry(() =>
|
|
1432
|
+
fetch(
|
|
1433
|
+
`https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}/reviews`,
|
|
1434
|
+
{
|
|
1435
|
+
method: 'POST',
|
|
1436
|
+
headers: {
|
|
1437
|
+
Authorization: `Bearer ${this.ghToken}`,
|
|
1438
|
+
'Content-Type': 'application/json',
|
|
1439
|
+
Accept: 'application/vnd.github+json',
|
|
1440
|
+
},
|
|
1441
|
+
body: JSON.stringify({ event: 'APPROVE' }),
|
|
1417
1442
|
},
|
|
1418
|
-
|
|
1419
|
-
},
|
|
1443
|
+
),
|
|
1420
1444
|
);
|
|
1421
1445
|
if (!response.ok) {
|
|
1422
1446
|
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
@@ -1444,17 +1468,19 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
1444
1468
|
},
|
|
1445
1469
|
],
|
|
1446
1470
|
};
|
|
1447
|
-
const response = await
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1471
|
+
const response = await this.fetchWithRateLimitRetry(() =>
|
|
1472
|
+
fetch(
|
|
1473
|
+
`https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}/reviews`,
|
|
1474
|
+
{
|
|
1475
|
+
method: 'POST',
|
|
1476
|
+
headers: {
|
|
1477
|
+
Authorization: `Bearer ${this.ghToken}`,
|
|
1478
|
+
'Content-Type': 'application/json',
|
|
1479
|
+
Accept: 'application/vnd.github+json',
|
|
1480
|
+
},
|
|
1481
|
+
body: JSON.stringify(reviewBody),
|
|
1455
1482
|
},
|
|
1456
|
-
|
|
1457
|
-
},
|
|
1483
|
+
),
|
|
1458
1484
|
);
|
|
1459
1485
|
if (!response.ok) {
|
|
1460
1486
|
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
@@ -1470,20 +1496,21 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
1470
1496
|
): Promise<string> => {
|
|
1471
1497
|
const ownerSegment = encodeURIComponent(owner);
|
|
1472
1498
|
const repoSegment = encodeURIComponent(repo);
|
|
1473
|
-
const response = await
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1499
|
+
const response = await this.fetchWithRateLimitRetry(() =>
|
|
1500
|
+
fetch(
|
|
1501
|
+
`https://api.github.com/repos/${ownerSegment}/${repoSegment}/pulls/${prNumber}`,
|
|
1502
|
+
{
|
|
1503
|
+
method: 'GET',
|
|
1504
|
+
headers: {
|
|
1505
|
+
Authorization: `Bearer ${this.ghToken}`,
|
|
1506
|
+
Accept: 'application/vnd.github+json',
|
|
1507
|
+
},
|
|
1480
1508
|
},
|
|
1481
|
-
|
|
1509
|
+
),
|
|
1482
1510
|
);
|
|
1483
1511
|
if (!response.ok) {
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
);
|
|
1512
|
+
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
1513
|
+
throw new Error(`Failed to fetch head commit for PR ${prUrl}: ${reason}`);
|
|
1487
1514
|
}
|
|
1488
1515
|
const body: unknown = await response.json();
|
|
1489
1516
|
if (
|
|
@@ -1514,23 +1541,25 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
1514
1541
|
);
|
|
1515
1542
|
const ownerSegment = encodeURIComponent(owner);
|
|
1516
1543
|
const repoSegment = encodeURIComponent(repo);
|
|
1517
|
-
const response = await
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1544
|
+
const response = await this.fetchWithRateLimitRetry(() =>
|
|
1545
|
+
fetch(
|
|
1546
|
+
`https://api.github.com/repos/${ownerSegment}/${repoSegment}/pulls/${prNumber}/comments`,
|
|
1547
|
+
{
|
|
1548
|
+
method: 'POST',
|
|
1549
|
+
headers: {
|
|
1550
|
+
Authorization: `Bearer ${this.ghToken}`,
|
|
1551
|
+
'Content-Type': 'application/json',
|
|
1552
|
+
Accept: 'application/vnd.github+json',
|
|
1553
|
+
},
|
|
1554
|
+
body: JSON.stringify({
|
|
1555
|
+
body: commentBody,
|
|
1556
|
+
commit_id: commitId,
|
|
1557
|
+
path,
|
|
1558
|
+
line,
|
|
1559
|
+
side,
|
|
1560
|
+
}),
|
|
1525
1561
|
},
|
|
1526
|
-
|
|
1527
|
-
body: commentBody,
|
|
1528
|
-
commit_id: commitId,
|
|
1529
|
-
path,
|
|
1530
|
-
line,
|
|
1531
|
-
side,
|
|
1532
|
-
}),
|
|
1533
|
-
},
|
|
1562
|
+
),
|
|
1534
1563
|
);
|
|
1535
1564
|
if (!response.ok) {
|
|
1536
1565
|
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
@@ -1576,7 +1605,17 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
1576
1605
|
response: Response,
|
|
1577
1606
|
): Promise<string> => {
|
|
1578
1607
|
const status = `HTTP ${response.status}`;
|
|
1608
|
+
const bodyText = await response.clone().text();
|
|
1579
1609
|
const reason = await this.readGitHubErrorReason(response);
|
|
1610
|
+
if (hasRateLimitSignals(response.status, response.headers, bodyText)) {
|
|
1611
|
+
const resetIso = computeRateLimitResetIso(response.headers);
|
|
1612
|
+
const resetSuffix = resetIso === null ? '' : ` (resets at ${resetIso})`;
|
|
1613
|
+
return `${status} GitHub rate limit exceeded, please retry shortly${resetSuffix}`;
|
|
1614
|
+
}
|
|
1615
|
+
if (response.status === 403) {
|
|
1616
|
+
const permissionSuffix = reason === null ? '' : ` ${reason}`;
|
|
1617
|
+
return `${status} permission denied, the token cannot perform this operation${permissionSuffix}`;
|
|
1618
|
+
}
|
|
1580
1619
|
if (reason === null) {
|
|
1581
1620
|
return status;
|
|
1582
1621
|
}
|
|
@@ -1588,18 +1627,21 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
1588
1627
|
branchName: string,
|
|
1589
1628
|
): Promise<void> => {
|
|
1590
1629
|
const { owner, repo } = this.parseIssueUrl(prUrl);
|
|
1591
|
-
const response = await
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1630
|
+
const response = await this.fetchWithRateLimitRetry(() =>
|
|
1631
|
+
fetch(
|
|
1632
|
+
`https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/git/refs/heads/${encodeURIComponent(branchName)}`,
|
|
1633
|
+
{
|
|
1634
|
+
method: 'DELETE',
|
|
1635
|
+
headers: {
|
|
1636
|
+
Authorization: `Bearer ${this.ghToken}`,
|
|
1637
|
+
},
|
|
1597
1638
|
},
|
|
1598
|
-
|
|
1639
|
+
),
|
|
1599
1640
|
);
|
|
1600
1641
|
if (!response.ok && response.status !== 422) {
|
|
1642
|
+
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
1601
1643
|
throw new Error(
|
|
1602
|
-
`Failed to delete branch ${branchName} for PR ${prUrl}:
|
|
1644
|
+
`Failed to delete branch ${branchName} for PR ${prUrl}: ${reason}`,
|
|
1603
1645
|
);
|
|
1604
1646
|
}
|
|
1605
1647
|
};
|
|
@@ -1613,20 +1655,21 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
1613
1655
|
|
|
1614
1656
|
getIssueOrPullRequestBody = async (url: string): Promise<string> => {
|
|
1615
1657
|
const { owner, repo, issueNumber } = this.parseIssueUrl(url);
|
|
1616
|
-
const response = await
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1658
|
+
const response = await this.fetchWithRateLimitRetry(() =>
|
|
1659
|
+
fetch(
|
|
1660
|
+
`https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues/${issueNumber}`,
|
|
1661
|
+
{
|
|
1662
|
+
method: 'GET',
|
|
1663
|
+
headers: {
|
|
1664
|
+
Authorization: `Bearer ${this.ghToken}`,
|
|
1665
|
+
Accept: 'application/vnd.github+json',
|
|
1666
|
+
},
|
|
1623
1667
|
},
|
|
1624
|
-
|
|
1668
|
+
),
|
|
1625
1669
|
);
|
|
1626
1670
|
if (!response.ok) {
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
);
|
|
1671
|
+
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
1672
|
+
throw new Error(`Failed to fetch body for ${url}: ${reason}`);
|
|
1630
1673
|
}
|
|
1631
1674
|
const body: unknown = await response.json();
|
|
1632
1675
|
if (!isIssueOrPullRequestBodyResponse(body)) {
|
|
@@ -1646,20 +1689,21 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
1646
1689
|
let page = 1;
|
|
1647
1690
|
let hasMore = true;
|
|
1648
1691
|
while (hasMore) {
|
|
1649
|
-
const response = await
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1692
|
+
const response = await this.fetchWithRateLimitRetry(() =>
|
|
1693
|
+
fetch(
|
|
1694
|
+
`https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues/${issueNumber}/comments?per_page=${perPage}&page=${page}`,
|
|
1695
|
+
{
|
|
1696
|
+
method: 'GET',
|
|
1697
|
+
headers: {
|
|
1698
|
+
Authorization: `Bearer ${this.ghToken}`,
|
|
1699
|
+
Accept: 'application/vnd.github+json',
|
|
1700
|
+
},
|
|
1656
1701
|
},
|
|
1657
|
-
|
|
1702
|
+
),
|
|
1658
1703
|
);
|
|
1659
1704
|
if (!response.ok) {
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
);
|
|
1705
|
+
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
1706
|
+
throw new Error(`Failed to fetch comments for ${url}: ${reason}`);
|
|
1663
1707
|
}
|
|
1664
1708
|
const body: unknown = await response.json();
|
|
1665
1709
|
if (!isIssueCommentsResponse(body)) {
|
|
@@ -1695,20 +1739,21 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
1695
1739
|
if (!isPr) {
|
|
1696
1740
|
return null;
|
|
1697
1741
|
}
|
|
1698
|
-
const detailResponse = await
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1742
|
+
const detailResponse = await this.fetchWithRateLimitRetry(() =>
|
|
1743
|
+
fetch(
|
|
1744
|
+
`https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}`,
|
|
1745
|
+
{
|
|
1746
|
+
method: 'GET',
|
|
1747
|
+
headers: {
|
|
1748
|
+
Authorization: `Bearer ${this.ghToken}`,
|
|
1749
|
+
Accept: 'application/vnd.github+json',
|
|
1750
|
+
},
|
|
1705
1751
|
},
|
|
1706
|
-
|
|
1752
|
+
),
|
|
1707
1753
|
);
|
|
1708
1754
|
if (!detailResponse.ok) {
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
);
|
|
1755
|
+
const reason = await this.formatGitHubErrorWithStatus(detailResponse);
|
|
1756
|
+
throw new Error(`Failed to fetch detail for PR ${prUrl}: ${reason}`);
|
|
1712
1757
|
}
|
|
1713
1758
|
const detailBody: unknown = await detailResponse.json();
|
|
1714
1759
|
if (!isPullRequestDetailResponse(detailBody)) {
|
|
@@ -1748,20 +1793,21 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
1748
1793
|
let page = 1;
|
|
1749
1794
|
let hasMore = true;
|
|
1750
1795
|
while (hasMore) {
|
|
1751
|
-
const response = await
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1796
|
+
const response = await this.fetchWithRateLimitRetry(() =>
|
|
1797
|
+
fetch(
|
|
1798
|
+
`https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}/files?per_page=${perPage}&page=${page}`,
|
|
1799
|
+
{
|
|
1800
|
+
method: 'GET',
|
|
1801
|
+
headers: {
|
|
1802
|
+
Authorization: `Bearer ${this.ghToken}`,
|
|
1803
|
+
Accept: 'application/vnd.github+json',
|
|
1804
|
+
},
|
|
1758
1805
|
},
|
|
1759
|
-
|
|
1806
|
+
),
|
|
1760
1807
|
);
|
|
1761
1808
|
if (!response.ok) {
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
);
|
|
1809
|
+
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
1810
|
+
throw new Error(`Failed to fetch files for PR ${prUrl}: ${reason}`);
|
|
1765
1811
|
}
|
|
1766
1812
|
const body: unknown = await response.json();
|
|
1767
1813
|
if (!isPullRequestDetailFilesResponse(body)) {
|
|
@@ -1804,20 +1850,21 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
1804
1850
|
let page = 1;
|
|
1805
1851
|
let hasMore = true;
|
|
1806
1852
|
while (hasMore) {
|
|
1807
|
-
const response = await
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1853
|
+
const response = await this.fetchWithRateLimitRetry(() =>
|
|
1854
|
+
fetch(
|
|
1855
|
+
`https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}/commits?per_page=${perPage}&page=${page}`,
|
|
1856
|
+
{
|
|
1857
|
+
method: 'GET',
|
|
1858
|
+
headers: {
|
|
1859
|
+
Authorization: `Bearer ${this.ghToken}`,
|
|
1860
|
+
Accept: 'application/vnd.github+json',
|
|
1861
|
+
},
|
|
1814
1862
|
},
|
|
1815
|
-
|
|
1863
|
+
),
|
|
1816
1864
|
);
|
|
1817
1865
|
if (!response.ok) {
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
);
|
|
1866
|
+
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
1867
|
+
throw new Error(`Failed to fetch commits for PR ${prUrl}: ${reason}`);
|
|
1821
1868
|
}
|
|
1822
1869
|
const body: unknown = await response.json();
|
|
1823
1870
|
if (!isPullRequestCommitsResponse(body)) {
|
|
@@ -1847,20 +1894,21 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
1847
1894
|
): Promise<{ state: string; merged: boolean; isPullRequest: boolean }> => {
|
|
1848
1895
|
const { owner, repo, issueNumber, isPr } = this.parseIssueUrl(url);
|
|
1849
1896
|
if (isPr) {
|
|
1850
|
-
const response = await
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1897
|
+
const response = await this.fetchWithRateLimitRetry(() =>
|
|
1898
|
+
fetch(
|
|
1899
|
+
`https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${issueNumber}`,
|
|
1900
|
+
{
|
|
1901
|
+
method: 'GET',
|
|
1902
|
+
headers: {
|
|
1903
|
+
Authorization: `Bearer ${this.ghToken}`,
|
|
1904
|
+
Accept: 'application/vnd.github+json',
|
|
1905
|
+
},
|
|
1857
1906
|
},
|
|
1858
|
-
|
|
1907
|
+
),
|
|
1859
1908
|
);
|
|
1860
1909
|
if (!response.ok) {
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
);
|
|
1910
|
+
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
1911
|
+
throw new Error(`Failed to fetch state for ${url}: ${reason}`);
|
|
1864
1912
|
}
|
|
1865
1913
|
const body: unknown = await response.json();
|
|
1866
1914
|
if (!isPullRequestDetailResponse(body)) {
|
|
@@ -1870,20 +1918,21 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
1870
1918
|
}
|
|
1871
1919
|
return { state: body.state, merged: body.merged, isPullRequest: true };
|
|
1872
1920
|
}
|
|
1873
|
-
const response = await
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1921
|
+
const response = await this.fetchWithRateLimitRetry(() =>
|
|
1922
|
+
fetch(
|
|
1923
|
+
`https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues/${issueNumber}`,
|
|
1924
|
+
{
|
|
1925
|
+
method: 'GET',
|
|
1926
|
+
headers: {
|
|
1927
|
+
Authorization: `Bearer ${this.ghToken}`,
|
|
1928
|
+
Accept: 'application/vnd.github+json',
|
|
1929
|
+
},
|
|
1880
1930
|
},
|
|
1881
|
-
|
|
1931
|
+
),
|
|
1882
1932
|
);
|
|
1883
1933
|
if (!response.ok) {
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
);
|
|
1934
|
+
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
1935
|
+
throw new Error(`Failed to fetch state for ${url}: ${reason}`);
|
|
1887
1936
|
}
|
|
1888
1937
|
const body: unknown = await response.json();
|
|
1889
1938
|
if (!isIssueOrPullRequestStateResponse(body)) {
|
|
@@ -1912,20 +1961,21 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
1912
1961
|
if (!isPr) {
|
|
1913
1962
|
return null;
|
|
1914
1963
|
}
|
|
1915
|
-
const response = await
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1964
|
+
const response = await this.fetchWithRateLimitRetry(() =>
|
|
1965
|
+
fetch(
|
|
1966
|
+
`https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}`,
|
|
1967
|
+
{
|
|
1968
|
+
method: 'GET',
|
|
1969
|
+
headers: {
|
|
1970
|
+
Authorization: `Bearer ${this.ghToken}`,
|
|
1971
|
+
Accept: 'application/vnd.github+json',
|
|
1972
|
+
},
|
|
1922
1973
|
},
|
|
1923
|
-
|
|
1974
|
+
),
|
|
1924
1975
|
);
|
|
1925
1976
|
if (!response.ok) {
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
);
|
|
1977
|
+
const reason = await this.formatGitHubErrorWithStatus(response);
|
|
1978
|
+
throw new Error(`Failed to fetch summary for PR ${prUrl}: ${reason}`);
|
|
1929
1979
|
}
|
|
1930
1980
|
const body: unknown = await response.json();
|
|
1931
1981
|
if (!isPullRequestDetailResponse(body)) {
|