@remogram/provider-github-api 0.1.0-beta.6 → 0.1.0-beta.9
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/branch-protection-internal.js +13 -0
- package/index.js +114 -36
- package/package.json +4 -3
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** @type {((ctx: object, opts: { branchRef: string }) => Promise<object>) | null} */
|
|
2
|
+
let branchProtectionImpl = null;
|
|
3
|
+
|
|
4
|
+
export function setBranchProtectionImpl(fn) {
|
|
5
|
+
branchProtectionImpl = fn;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function resolveBranchProtection(ctx, opts) {
|
|
9
|
+
if (typeof branchProtectionImpl !== 'function') {
|
|
10
|
+
throw new Error('branch protection impl not registered');
|
|
11
|
+
}
|
|
12
|
+
return branchProtectionImpl(ctx, opts);
|
|
13
|
+
}
|
package/index.js
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
buildMergePlanFromProviderFacts,
|
|
16
16
|
ERROR_CODES,
|
|
17
17
|
forgeError,
|
|
18
|
+
LIVE_REACHABILITY_TIMEOUT_MS,
|
|
18
19
|
forgeIngestCapabilityFacts,
|
|
19
20
|
checkPaginationCapabilityFacts,
|
|
20
21
|
openPullListCapabilityFacts,
|
|
@@ -39,6 +40,7 @@ import {
|
|
|
39
40
|
githubOpenPullSortQuery,
|
|
40
41
|
buildProviderIdentityFromGitHubUser,
|
|
41
42
|
buildBranchProtectionFromGitHubProtection,
|
|
43
|
+
buildPrChecksBody,
|
|
42
44
|
buildCrFilesBody,
|
|
43
45
|
buildCrFilesFromGiteaFiles,
|
|
44
46
|
buildCrCommentsBody,
|
|
@@ -48,12 +50,17 @@ import {
|
|
|
48
50
|
buildChecksConclusionObservedEvent,
|
|
49
51
|
appendForgeChangeEvents,
|
|
50
52
|
buildCommitStatusSetBody,
|
|
53
|
+
idempotencyPacketFields,
|
|
51
54
|
parseStatusSetArgs,
|
|
52
55
|
normalizeStatusSetState,
|
|
53
56
|
statusSetIdempotencyScanCapabilityFacts,
|
|
54
57
|
MAX_OPEN_PULL_IDEMPOTENCY_PAGES,
|
|
55
58
|
assertWriteCommandConfigured,
|
|
56
59
|
} from '@remogram/core';
|
|
60
|
+
import {
|
|
61
|
+
resolveBranchProtection,
|
|
62
|
+
setBranchProtectionImpl,
|
|
63
|
+
} from './branch-protection-internal.js';
|
|
57
64
|
|
|
58
65
|
const PUBLIC_GITHUB_HOST = 'github.com';
|
|
59
66
|
const PUBLIC_GITHUB_API = 'https://api.github.com';
|
|
@@ -207,6 +214,25 @@ export async function githubFetch(config, parsed, path, options = {}) {
|
|
|
207
214
|
});
|
|
208
215
|
}
|
|
209
216
|
|
|
217
|
+
export async function apiReachability(ctx) {
|
|
218
|
+
if (!githubToken()) {
|
|
219
|
+
throw Object.assign(new Error('GitHub token not set'), {
|
|
220
|
+
forgeError: forgeError(
|
|
221
|
+
ERROR_CODES.UNAUTHENTICATED_PROVIDER,
|
|
222
|
+
'GITHUB_TOKEN or GH_TOKEN not set',
|
|
223
|
+
),
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
const { token } = requireToken();
|
|
227
|
+
const url = `${apiBase(ctx.config, ctx.parsed)}${repoApiPath(ctx.config)}`;
|
|
228
|
+
await fetchJson(
|
|
229
|
+
url,
|
|
230
|
+
{ headers: authHeaders(token) },
|
|
231
|
+
LIVE_REACHABILITY_TIMEOUT_MS,
|
|
232
|
+
);
|
|
233
|
+
return { repo_accessible: true };
|
|
234
|
+
}
|
|
235
|
+
|
|
210
236
|
const MAX_CHECK_PAGES = MAX_CHECK_STATUS_PAGES;
|
|
211
237
|
|
|
212
238
|
export function resolveGitHubLinkNextPage({ trustedOrigin, currentUrl, linkHeader, pageIndex, maxPages }) {
|
|
@@ -435,10 +461,10 @@ export async function refsCompare(ctx, baseRef, headRef) {
|
|
|
435
461
|
}
|
|
436
462
|
const counts = gitAheadBehind(ctx.cwd, baseSha, headSha);
|
|
437
463
|
return {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
464
|
+
compare_base_ref: sanitizeField(baseRef),
|
|
465
|
+
compare_base_sha: baseSha,
|
|
466
|
+
compare_head_ref: sanitizeField(headRef),
|
|
467
|
+
compare_head_sha: headSha,
|
|
442
468
|
...counts,
|
|
443
469
|
};
|
|
444
470
|
}
|
|
@@ -460,10 +486,10 @@ export async function prView(ctx, opts) {
|
|
|
460
486
|
url: sanitizeUrl(pr.html_url ?? pr.url),
|
|
461
487
|
title: sanitizeField(pr.title),
|
|
462
488
|
state: sanitizeField(pr.state),
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
489
|
+
forge_target_branch_ref: sanitizeField(pr.base?.ref),
|
|
490
|
+
forge_target_sha: sanitizeField(pr.base?.sha),
|
|
491
|
+
forge_source_branch_ref: sanitizeField(pr.head?.ref),
|
|
492
|
+
forge_source_sha: sanitizeField(pr.head?.sha),
|
|
467
493
|
mergeability: mergeability(pr),
|
|
468
494
|
};
|
|
469
495
|
}
|
|
@@ -509,6 +535,7 @@ export async function prChecks(ctx, opts) {
|
|
|
509
535
|
apiBase(ctx.config, ctx.parsed);
|
|
510
536
|
requireToken();
|
|
511
537
|
let sha;
|
|
538
|
+
let requiredContexts = [];
|
|
512
539
|
if (opts.ref) {
|
|
513
540
|
assertGitRef(opts.ref, 'ref');
|
|
514
541
|
sha = gitRevParse(ctx.cwd, opts.ref);
|
|
@@ -520,6 +547,11 @@ export async function prChecks(ctx, opts) {
|
|
|
520
547
|
} else {
|
|
521
548
|
const pr = await fetchPullGraphql(ctx.config, ctx.parsed, opts.number);
|
|
522
549
|
sha = pr.head?.sha;
|
|
550
|
+
const targetBranch = pr.base?.ref;
|
|
551
|
+
if (targetBranch) {
|
|
552
|
+
const protection = await resolveBranchProtection(ctx, { branchRef: targetBranch });
|
|
553
|
+
requiredContexts = protection.required_status_contexts ?? [];
|
|
554
|
+
}
|
|
523
555
|
}
|
|
524
556
|
if (!sha) {
|
|
525
557
|
throw Object.assign(new Error('No SHA'), {
|
|
@@ -541,20 +573,27 @@ export async function prChecks(ctx, opts) {
|
|
|
541
573
|
context: sanitizeField(s.context),
|
|
542
574
|
state: normalizeCommitStatusState(s.state),
|
|
543
575
|
description: sanitizeField(s.description),
|
|
576
|
+
...(s.target_url ? { target_url: sanitizeField(s.target_url) } : {}),
|
|
577
|
+
sha,
|
|
578
|
+
source: 'commit_status',
|
|
544
579
|
}));
|
|
545
580
|
const mappedCheckRuns = checkRunRecords.map((run) => ({
|
|
546
581
|
context: sanitizeField(run.name),
|
|
547
582
|
state: normalizeCheckRunState(run),
|
|
548
583
|
description: sanitizeField(checkRunDescription(run)),
|
|
584
|
+
...(run.details_url ? { target_url: sanitizeField(run.details_url) } : {}),
|
|
585
|
+
sha: sanitizeField(run.head_sha) || sha,
|
|
586
|
+
source: 'check_run',
|
|
549
587
|
}));
|
|
550
588
|
const mapped = [...mappedStatuses, ...mappedCheckRuns];
|
|
551
589
|
const checks_truncated = statusResult.truncated || checkRunResult.truncated;
|
|
552
|
-
return {
|
|
553
|
-
|
|
590
|
+
return buildPrChecksBody({
|
|
591
|
+
forge_source_sha: sha,
|
|
554
592
|
check_conclusion: summarizeChecks(mapped),
|
|
555
593
|
checks_truncated,
|
|
556
594
|
statuses: mapped,
|
|
557
|
-
|
|
595
|
+
required_contexts: requiredContexts,
|
|
596
|
+
});
|
|
558
597
|
}
|
|
559
598
|
|
|
560
599
|
export async function mergePlan(ctx, opts) {
|
|
@@ -743,28 +782,54 @@ async function paginateGitHubOpenPullList(ctx, opts, sliceSort, paginationOpts =
|
|
|
743
782
|
: null;
|
|
744
783
|
|
|
745
784
|
if (listLimit == null && numberSortFullCollect) {
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
:
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
785
|
+
if (seededFirstPage?.linkHeader) {
|
|
786
|
+
const trustedOrigin = new URL(base).origin;
|
|
787
|
+
const startUrl = seededFirstPage.listUrl ?? `${base}${listPath}`;
|
|
788
|
+
const linkSeed = {
|
|
789
|
+
items: seededFirstPage.items,
|
|
790
|
+
linkHeader: seededFirstPage.linkHeader,
|
|
791
|
+
currentUrl: startUrl,
|
|
792
|
+
usedLimit: seededFirstPage.usedLimit,
|
|
793
|
+
};
|
|
794
|
+
const {
|
|
795
|
+
items: linkItems,
|
|
796
|
+
truncated: linkTruncated,
|
|
797
|
+
walked_count: linkWalkedCount,
|
|
798
|
+
} = await paginateGitHubLinkPages({
|
|
799
|
+
trustedOrigin,
|
|
800
|
+
startUrl,
|
|
801
|
+
token,
|
|
802
|
+
initialLimit: seededFirstPage.usedLimit ?? DEFAULT_CHECK_STATUS_PAGE_SIZE,
|
|
803
|
+
mapPageItems: (body) => (Array.isArray(body) ? body : []),
|
|
804
|
+
seededFirstPage: linkSeed,
|
|
805
|
+
});
|
|
806
|
+
all.push(...linkItems);
|
|
807
|
+
listTruncated = linkTruncated;
|
|
808
|
+
walkedCount = linkWalkedCount;
|
|
809
|
+
} else {
|
|
810
|
+
const {
|
|
811
|
+
items: offsetItems,
|
|
812
|
+
list_truncated: offsetTruncated,
|
|
813
|
+
walked_count: offsetWalkedCount,
|
|
814
|
+
} = await paginateOffsetListPages({
|
|
815
|
+
pageSize: GITHUB_PAGE_SIZE,
|
|
816
|
+
retainMax: null,
|
|
817
|
+
trustedEntryCount: trustedTotalCount,
|
|
818
|
+
seededFirstPage: seededFirstPage
|
|
819
|
+
? { items: seededFirstPage.items, usedLimit: seededFirstPage.usedLimit }
|
|
820
|
+
: null,
|
|
821
|
+
fetchPage: async ({ page, limit }) => {
|
|
822
|
+
const pageUrl = `${base}${listPath}&page=${page}`;
|
|
823
|
+
const { body } = await fetchJsonWithMeta(withPerPageParam(pageUrl, limit), {
|
|
824
|
+
headers: authHeaders(token),
|
|
825
|
+
});
|
|
826
|
+
return Array.isArray(body) ? body : [];
|
|
827
|
+
},
|
|
828
|
+
});
|
|
829
|
+
all.push(...offsetItems);
|
|
830
|
+
listTruncated = offsetTruncated;
|
|
831
|
+
walkedCount = offsetWalkedCount;
|
|
832
|
+
}
|
|
768
833
|
} else if (listLimit == null) {
|
|
769
834
|
const trustedOrigin = new URL(base).origin;
|
|
770
835
|
const startUrl = `${base}${listPath}`;
|
|
@@ -1000,12 +1065,18 @@ export async function findCommitStatusByContext(ctx, sha, context) {
|
|
|
1000
1065
|
|
|
1001
1066
|
export async function statusSet(ctx, args) {
|
|
1002
1067
|
assertWriteCommandConfigured(ctx.config, 'status_set');
|
|
1003
|
-
const
|
|
1068
|
+
const { idempotencyFingerprint = null, ...rest } = args;
|
|
1069
|
+
const parsed = parseStatusSetArgs(rest);
|
|
1004
1070
|
const existing = await findCommitStatusByContext(ctx, parsed.sha, parsed.context);
|
|
1005
1071
|
if (existing) {
|
|
1006
1072
|
const existingState = normalizeStatusSetState(existing.state ?? existing.status);
|
|
1007
1073
|
if (existingState === parsed.state) {
|
|
1008
|
-
return buildCommitStatusSetBody(existing, parsed, {
|
|
1074
|
+
return buildCommitStatusSetBody(existing, parsed, {
|
|
1075
|
+
reusedExisting: true,
|
|
1076
|
+
idempotencyFields: idempotencyFingerprint
|
|
1077
|
+
? idempotencyPacketFields(idempotencyFingerprint, { reusedExisting: true })
|
|
1078
|
+
: null,
|
|
1079
|
+
});
|
|
1009
1080
|
}
|
|
1010
1081
|
}
|
|
1011
1082
|
const payload = {
|
|
@@ -1019,7 +1090,11 @@ export async function statusSet(ctx, args) {
|
|
|
1019
1090
|
headers: { 'Content-Type': 'application/json' },
|
|
1020
1091
|
body: JSON.stringify(payload),
|
|
1021
1092
|
});
|
|
1022
|
-
return buildCommitStatusSetBody(response, parsed
|
|
1093
|
+
return buildCommitStatusSetBody(response, parsed, {
|
|
1094
|
+
idempotencyFields: idempotencyFingerprint
|
|
1095
|
+
? idempotencyPacketFields(idempotencyFingerprint, { reusedExisting: false })
|
|
1096
|
+
: null,
|
|
1097
|
+
});
|
|
1023
1098
|
}
|
|
1024
1099
|
|
|
1025
1100
|
export async function branchProtection(ctx, { branchRef }) {
|
|
@@ -1043,6 +1118,7 @@ export async function branchProtection(ctx, { branchRef }) {
|
|
|
1043
1118
|
export const provider = {
|
|
1044
1119
|
id: 'github-api',
|
|
1045
1120
|
providerCapabilities,
|
|
1121
|
+
apiReachability,
|
|
1046
1122
|
repoStatus,
|
|
1047
1123
|
refsCompare,
|
|
1048
1124
|
refsInventory,
|
|
@@ -1059,3 +1135,5 @@ export const provider = {
|
|
|
1059
1135
|
forgeChanges,
|
|
1060
1136
|
statusSet,
|
|
1061
1137
|
};
|
|
1138
|
+
|
|
1139
|
+
setBranchProtectionImpl(branchProtection);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remogram/provider-github-api",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.9",
|
|
4
4
|
"description": "GitHub API provider for remogram",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -16,12 +16,13 @@
|
|
|
16
16
|
"*.js"
|
|
17
17
|
],
|
|
18
18
|
"exports": {
|
|
19
|
-
".": "./index.js"
|
|
19
|
+
".": "./index.js",
|
|
20
|
+
"./branch-protection-internal.js": "./branch-protection-internal.js"
|
|
20
21
|
},
|
|
21
22
|
"engines": {
|
|
22
23
|
"node": ">=20"
|
|
23
24
|
},
|
|
24
25
|
"dependencies": {
|
|
25
|
-
"@remogram/core": "0.1.0-beta.
|
|
26
|
+
"@remogram/core": "0.1.0-beta.9"
|
|
26
27
|
}
|
|
27
28
|
}
|