@remogram/provider-gitea-api 0.1.0-beta.2 → 0.1.0-beta.4
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/index.js +416 -37
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
fetchJson,
|
|
3
|
+
fetchJsonWithMeta,
|
|
3
4
|
sanitizeField,
|
|
4
5
|
sanitizeUrl,
|
|
5
6
|
assertGitRef,
|
|
@@ -7,23 +8,57 @@ import {
|
|
|
7
8
|
gitRevParse,
|
|
8
9
|
gitCurrentBranch,
|
|
9
10
|
gitAheadBehind,
|
|
11
|
+
refsInventory,
|
|
12
|
+
crInventory,
|
|
13
|
+
buildMergePlanBodyFromFacts,
|
|
14
|
+
buildChangeRequestOpenedBody,
|
|
10
15
|
ERROR_CODES,
|
|
11
16
|
forgeError,
|
|
12
17
|
forgeIngestCapabilityFacts,
|
|
18
|
+
checkPaginationCapabilityFacts,
|
|
19
|
+
idempotencyScanCapabilityFacts,
|
|
20
|
+
openPullListCapabilityFacts,
|
|
21
|
+
DEFAULT_CHECK_STATUS_PAGE_SIZE,
|
|
22
|
+
MAX_CHECK_STATUS_PAGES,
|
|
23
|
+
DEFAULT_OPEN_PULL_LIST_PAGE_SIZE,
|
|
24
|
+
MAX_OPEN_PULL_IDEMPOTENCY_PAGES,
|
|
25
|
+
paginateCheckStatusPages,
|
|
26
|
+
paginateOffsetListPages,
|
|
27
|
+
fetchWithIngestPageBackoff,
|
|
28
|
+
fetchPageWithIngestBackoff,
|
|
29
|
+
withPerPageParam,
|
|
13
30
|
apiProviderCommands,
|
|
31
|
+
normalizeCrInventorySort,
|
|
32
|
+
DEFAULT_CR_INVENTORY_SLICE_SORT,
|
|
33
|
+
parseTotalCountHeader,
|
|
34
|
+
isCrInventoryFastPathEligible,
|
|
35
|
+
validateFastPathPageLength,
|
|
36
|
+
isNumberSortFastPathEligible,
|
|
37
|
+
isRecentCreatedFastPathEligible,
|
|
38
|
+
giteaRecentCreatedTailPage,
|
|
39
|
+
isNumberSortFullCollectRequired,
|
|
40
|
+
resolveListTruncatedWithTrustedTotal,
|
|
41
|
+
prepareGiteaOpenPullPageItems,
|
|
42
|
+
orderOpenPullNumbers,
|
|
43
|
+
buildOpenPullListMeta,
|
|
44
|
+
giteaOpenPullSortQuery,
|
|
45
|
+
appendSortQuery,
|
|
14
46
|
} from '@remogram/core';
|
|
15
47
|
const PUBLIC_GITEA_HOST = 'gitea.com';
|
|
16
48
|
const PUBLIC_GITEA_API = 'https://gitea.com/api/v1';
|
|
17
49
|
const AUTH_CAPABILITIES = [
|
|
18
50
|
'repo_status',
|
|
19
51
|
'ref_compare',
|
|
52
|
+
'ref_inventory',
|
|
53
|
+
'cr_inventory',
|
|
20
54
|
'pr_status',
|
|
21
55
|
'pr_checks',
|
|
22
56
|
'merge_plan',
|
|
23
57
|
'sync_plan',
|
|
58
|
+
'cr_open',
|
|
24
59
|
];
|
|
25
60
|
|
|
26
|
-
const STRUCTURED_COMMANDS = apiProviderCommands();
|
|
61
|
+
const STRUCTURED_COMMANDS = apiProviderCommands({ writeCommandsImplemented: true });
|
|
27
62
|
|
|
28
63
|
export function giteaToken() {
|
|
29
64
|
return process.env.GITEA_TOKEN || null;
|
|
@@ -119,23 +154,45 @@ export async function giteaFetch(config, parsed, path, options = {}) {
|
|
|
119
154
|
});
|
|
120
155
|
}
|
|
121
156
|
|
|
122
|
-
|
|
123
|
-
const
|
|
157
|
+
export async function giteaFetchWithMeta(config, parsed, path, options = {}) {
|
|
158
|
+
const token = requireToken();
|
|
159
|
+
const url = `${apiBase(config, parsed)}${path}`;
|
|
160
|
+
return fetchJsonWithMeta(url, {
|
|
161
|
+
...options,
|
|
162
|
+
headers: { ...authHeaders(token), ...(options.headers || {}) },
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const MAX_CHECK_PAGES = MAX_CHECK_STATUS_PAGES;
|
|
167
|
+
const GITEA_PAGE_SIZE = DEFAULT_OPEN_PULL_LIST_PAGE_SIZE;
|
|
168
|
+
|
|
169
|
+
function idempotencyScanIncompleteError(pagesScanned, pageSizeUsed) {
|
|
170
|
+
return forgeError(
|
|
171
|
+
ERROR_CODES.IDEMPOTENCY_SCAN_INCOMPLETE,
|
|
172
|
+
'Cannot prove no open pull exists for head+base within scan limit; use cr inventory or open manually',
|
|
173
|
+
null,
|
|
174
|
+
{
|
|
175
|
+
idempotency_scan: {
|
|
176
|
+
pages: pagesScanned,
|
|
177
|
+
max_pages: MAX_OPEN_PULL_IDEMPOTENCY_PAGES,
|
|
178
|
+
page_size: pageSizeUsed,
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
);
|
|
182
|
+
}
|
|
124
183
|
|
|
125
184
|
export async function giteaFetchPaginated(config, parsed, path) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
return all;
|
|
185
|
+
return paginateCheckStatusPages({
|
|
186
|
+
fetchPage: async ({ page, limit }) => {
|
|
187
|
+
const separator = path.includes('?') ? '&' : '?';
|
|
188
|
+
const body = await giteaFetch(
|
|
189
|
+
config,
|
|
190
|
+
parsed,
|
|
191
|
+
`${path}${separator}limit=${limit}&page=${page}`,
|
|
192
|
+
);
|
|
193
|
+
return Array.isArray(body) ? body : [];
|
|
194
|
+
},
|
|
195
|
+
});
|
|
139
196
|
}
|
|
140
197
|
|
|
141
198
|
export function normalizeGiteaStatusState(state) {
|
|
@@ -150,6 +207,36 @@ export function normalizeGiteaStatusState(state) {
|
|
|
150
207
|
return 'unknown';
|
|
151
208
|
}
|
|
152
209
|
|
|
210
|
+
function giteaStatusRecordOrder(a, b) {
|
|
211
|
+
const aUpdated = Date.parse(a.updated_at ?? '') || 0;
|
|
212
|
+
const bUpdated = Date.parse(b.updated_at ?? '') || 0;
|
|
213
|
+
if (aUpdated !== bUpdated) return aUpdated - bUpdated;
|
|
214
|
+
const aId = Number(a.id) || 0;
|
|
215
|
+
const bId = Number(b.id) || 0;
|
|
216
|
+
return aId - bId;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function dedupeGiteaStatusRecords(records) {
|
|
220
|
+
const latestByContext = new Map();
|
|
221
|
+
for (const record of records) {
|
|
222
|
+
const context = record?.context;
|
|
223
|
+
if (context == null || context === '') continue;
|
|
224
|
+
const existing = latestByContext.get(context);
|
|
225
|
+
if (!existing || giteaStatusRecordOrder(record, existing) > 0) {
|
|
226
|
+
latestByContext.set(context, record);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return Array.from(latestByContext.values());
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function mapGiteaCommitStatuses(records) {
|
|
233
|
+
return dedupeGiteaStatusRecords(records).map((s) => ({
|
|
234
|
+
context: sanitizeField(s.context),
|
|
235
|
+
state: normalizeGiteaStatusState(s.status ?? s.state),
|
|
236
|
+
description: sanitizeField(s.description),
|
|
237
|
+
}));
|
|
238
|
+
}
|
|
239
|
+
|
|
153
240
|
export function normalizeGiteaPrState(state) {
|
|
154
241
|
const normalized = String(state ?? '').toLowerCase();
|
|
155
242
|
if (normalized === 'open') return 'open';
|
|
@@ -173,15 +260,35 @@ export async function repoStatus(ctx) {
|
|
|
173
260
|
}
|
|
174
261
|
|
|
175
262
|
export function providerCapabilities() {
|
|
263
|
+
const check_sources = ['commit_statuses'];
|
|
176
264
|
return {
|
|
177
265
|
commands: STRUCTURED_COMMANDS,
|
|
178
266
|
auth_envs: ['GITEA_TOKEN'],
|
|
179
|
-
check_sources
|
|
267
|
+
check_sources,
|
|
180
268
|
mergeability_confidence: 'direct',
|
|
181
269
|
host_binding: 'verified_remote_host',
|
|
182
270
|
pagination: 'supported',
|
|
183
|
-
write_support:
|
|
271
|
+
write_support: true,
|
|
272
|
+
write_commands: ['cr_open'],
|
|
184
273
|
...forgeIngestCapabilityFacts(),
|
|
274
|
+
...checkPaginationCapabilityFacts({
|
|
275
|
+
strategy: 'offset_limit',
|
|
276
|
+
pageSizeParam: 'limit',
|
|
277
|
+
sourceCount: check_sources.length,
|
|
278
|
+
}),
|
|
279
|
+
...idempotencyScanCapabilityFacts(),
|
|
280
|
+
...openPullListCapabilityFacts({
|
|
281
|
+
totalCountSource: 'response_header',
|
|
282
|
+
totalCountHeader: 'X-Total-Count',
|
|
283
|
+
sliceSortNotes: {
|
|
284
|
+
recent_created:
|
|
285
|
+
'sort=oldest; fetches tail page when total exceeds limit; page reversed for newest-first',
|
|
286
|
+
number_asc:
|
|
287
|
+
'full-list collect within compliant_max when total exceeds limit, then client sort',
|
|
288
|
+
number_desc:
|
|
289
|
+
'full-list collect within compliant_max when total exceeds limit, then client sort',
|
|
290
|
+
},
|
|
291
|
+
}),
|
|
185
292
|
};
|
|
186
293
|
}
|
|
187
294
|
|
|
@@ -214,6 +321,284 @@ export async function getPull(ctx, { number }) {
|
|
|
214
321
|
return giteaFetch(ctx.config, ctx.parsed, repoApiPath(ctx.config, 'pulls', number));
|
|
215
322
|
}
|
|
216
323
|
|
|
324
|
+
/** Paginated open-pull scan for idempotent cr open; fail-closed when scan cap prevents proof of absence. */
|
|
325
|
+
export async function findOpenPullByHeadBase(ctx, head, base) {
|
|
326
|
+
requireToken();
|
|
327
|
+
const path = `${repoApiPath(ctx.config, 'pulls')}?state=open`;
|
|
328
|
+
const pageSep = path.includes('?') ? '&' : '?';
|
|
329
|
+
let activeLimit = GITEA_PAGE_SIZE;
|
|
330
|
+
|
|
331
|
+
for (let page = 1; page <= MAX_OPEN_PULL_IDEMPOTENCY_PAGES; page += 1) {
|
|
332
|
+
const { items, usedLimit } = await fetchPageWithIngestBackoff(
|
|
333
|
+
async ({ page: pageNum, limit }) => {
|
|
334
|
+
const body = await giteaFetch(
|
|
335
|
+
ctx.config,
|
|
336
|
+
ctx.parsed,
|
|
337
|
+
`${path}${pageSep}limit=${limit}&page=${pageNum}`,
|
|
338
|
+
);
|
|
339
|
+
if (!Array.isArray(body)) {
|
|
340
|
+
throw Object.assign(new Error('Provider returned non-array open pull list'), {
|
|
341
|
+
forgeError: forgeError(
|
|
342
|
+
ERROR_CODES.UNPARSEABLE_PROVIDER_OUTPUT,
|
|
343
|
+
'Provider returned non-array open pull list',
|
|
344
|
+
),
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
return body;
|
|
348
|
+
},
|
|
349
|
+
page,
|
|
350
|
+
activeLimit,
|
|
351
|
+
);
|
|
352
|
+
activeLimit = usedLimit;
|
|
353
|
+
|
|
354
|
+
const match =
|
|
355
|
+
items.find(
|
|
356
|
+
(pr) =>
|
|
357
|
+
String(pr?.state ?? '').toLowerCase() === 'open' &&
|
|
358
|
+
pr?.head?.ref === head &&
|
|
359
|
+
pr?.base?.ref === base,
|
|
360
|
+
) ?? null;
|
|
361
|
+
if (match) return match;
|
|
362
|
+
if (items.length < usedLimit) return null;
|
|
363
|
+
if (page === MAX_OPEN_PULL_IDEMPOTENCY_PAGES) {
|
|
364
|
+
throw Object.assign(new Error('Open pull idempotency scan incomplete'), {
|
|
365
|
+
forgeError: idempotencyScanIncompleteError(page, usedLimit),
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
export async function crOpen(ctx, { head, base, title, body: prBody }) {
|
|
373
|
+
assertGitRef(head, 'head');
|
|
374
|
+
assertGitRef(base, 'base');
|
|
375
|
+
if (!title || typeof title !== 'string' || !title.trim()) {
|
|
376
|
+
throw Object.assign(new Error('--title required'), {
|
|
377
|
+
forgeError: forgeError(ERROR_CODES.INVALID_ARGS, '--title required for cr open'),
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
const payload = {
|
|
381
|
+
title: sanitizeField(title),
|
|
382
|
+
head: sanitizeField(head),
|
|
383
|
+
base: sanitizeField(base),
|
|
384
|
+
};
|
|
385
|
+
if (prBody != null && String(prBody).trim() !== '') {
|
|
386
|
+
payload.body = sanitizeField(String(prBody));
|
|
387
|
+
}
|
|
388
|
+
const existing = await findOpenPullByHeadBase(ctx, payload.head, payload.base);
|
|
389
|
+
if (existing) {
|
|
390
|
+
return buildChangeRequestOpenedBody(existing, { head, base, title }, { reusedExisting: true });
|
|
391
|
+
}
|
|
392
|
+
const pull = await giteaFetch(ctx.config, ctx.parsed, repoApiPath(ctx.config, 'pulls'), {
|
|
393
|
+
method: 'POST',
|
|
394
|
+
headers: { 'Content-Type': 'application/json' },
|
|
395
|
+
body: JSON.stringify(payload),
|
|
396
|
+
});
|
|
397
|
+
return buildChangeRequestOpenedBody(pull, { head, base, title });
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const GITEA_OPEN_PULL_COMPLIANT_MAX =
|
|
401
|
+
DEFAULT_OPEN_PULL_LIST_PAGE_SIZE * MAX_CHECK_STATUS_PAGES;
|
|
402
|
+
|
|
403
|
+
async function probeGiteaOpenPullPageOne(ctx, retainMax, sliceSort) {
|
|
404
|
+
const maxTrusted = GITEA_OPEN_PULL_COMPLIANT_MAX * 2;
|
|
405
|
+
let path = `${repoApiPath(ctx.config, 'pulls')}?state=open`;
|
|
406
|
+
path = appendSortQuery(path, giteaOpenPullSortQuery(sliceSort));
|
|
407
|
+
const pageSep = path.includes('?') ? '&' : '?';
|
|
408
|
+
const requestLimit = Math.min(retainMax, GITEA_PAGE_SIZE);
|
|
409
|
+
try {
|
|
410
|
+
const { body, headers } = await giteaFetchWithMeta(
|
|
411
|
+
ctx.config,
|
|
412
|
+
ctx.parsed,
|
|
413
|
+
`${path}${pageSep}limit=${requestLimit}&page=1`,
|
|
414
|
+
);
|
|
415
|
+
if (!Array.isArray(body)) return null;
|
|
416
|
+
const totalCount = parseTotalCountHeader(headers, 'X-Total-Count', { maxTrusted });
|
|
417
|
+
if (totalCount == null) return null;
|
|
418
|
+
const listTruncated = totalCount > GITEA_OPEN_PULL_COMPLIANT_MAX;
|
|
419
|
+
return { body, totalCount, listTruncated, requestLimit };
|
|
420
|
+
} catch {
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function buildGiteaOpenPullMetaFromPage(body, retainMax, sliceSort, totalCount, listTruncated) {
|
|
426
|
+
const pageItems = prepareGiteaOpenPullPageItems(body, sliceSort);
|
|
427
|
+
let numbers = orderOpenPullNumbers(pageItems, (pr) => pr?.number, sliceSort);
|
|
428
|
+
if (numbers.length > retainMax) numbers = numbers.slice(0, retainMax);
|
|
429
|
+
return buildOpenPullListMeta({
|
|
430
|
+
totalCount,
|
|
431
|
+
numbers,
|
|
432
|
+
listTruncated,
|
|
433
|
+
sliceSort,
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
async function fetchGiteaRecentCreatedTailSlice(ctx, retainMax, sliceSort, totalCount) {
|
|
438
|
+
const tailPage = giteaRecentCreatedTailPage(totalCount, GITEA_PAGE_SIZE);
|
|
439
|
+
let path = `${repoApiPath(ctx.config, 'pulls')}?state=open`;
|
|
440
|
+
path = appendSortQuery(path, giteaOpenPullSortQuery(sliceSort));
|
|
441
|
+
const pageSep = path.includes('?') ? '&' : '?';
|
|
442
|
+
let body;
|
|
443
|
+
try {
|
|
444
|
+
body = await giteaFetch(
|
|
445
|
+
ctx.config,
|
|
446
|
+
ctx.parsed,
|
|
447
|
+
`${path}${pageSep}limit=${GITEA_PAGE_SIZE}&page=${tailPage}`,
|
|
448
|
+
);
|
|
449
|
+
} catch {
|
|
450
|
+
return null;
|
|
451
|
+
}
|
|
452
|
+
if (!Array.isArray(body) || body.length === 0) return null;
|
|
453
|
+
return buildGiteaOpenPullMetaFromPage(body, retainMax, sliceSort, totalCount, false);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function giteaProbePaginationOpts(probe, extra = {}) {
|
|
457
|
+
const { body, totalCount, requestLimit } = probe;
|
|
458
|
+
return {
|
|
459
|
+
trustedTotalCount: totalCount,
|
|
460
|
+
seededFirstPage: { items: body, usedLimit: requestLimit },
|
|
461
|
+
...extra,
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
async function paginateGiteaOpenPullList(ctx, opts, sliceSort, paginationOpts = {}) {
|
|
466
|
+
const {
|
|
467
|
+
trustedTotalCount = null,
|
|
468
|
+
numberSortFullCollect = false,
|
|
469
|
+
seededFirstPage = null,
|
|
470
|
+
startPage = 1,
|
|
471
|
+
maxPages = MAX_CHECK_STATUS_PAGES,
|
|
472
|
+
suppressFinalPageProbe = false,
|
|
473
|
+
} = paginationOpts;
|
|
474
|
+
const listLimit =
|
|
475
|
+
opts.limit != null && Number.isInteger(Number(opts.limit)) && Number(opts.limit) > 0
|
|
476
|
+
? Number(opts.limit)
|
|
477
|
+
: null;
|
|
478
|
+
const retainMax =
|
|
479
|
+
listLimit == null &&
|
|
480
|
+
opts.retain_max != null &&
|
|
481
|
+
Number.isInteger(Number(opts.retain_max)) &&
|
|
482
|
+
Number(opts.retain_max) > 0
|
|
483
|
+
? Number(opts.retain_max)
|
|
484
|
+
: null;
|
|
485
|
+
const pageSize =
|
|
486
|
+
listLimit != null ? Math.min(listLimit, GITEA_PAGE_SIZE) : GITEA_PAGE_SIZE;
|
|
487
|
+
let path = `${repoApiPath(ctx.config, 'pulls')}?state=open`;
|
|
488
|
+
path = appendSortQuery(path, giteaOpenPullSortQuery(sliceSort));
|
|
489
|
+
const pageSep = path.includes('?') ? '&' : '?';
|
|
490
|
+
const effectiveRetainMax = numberSortFullCollect ? null : retainMax;
|
|
491
|
+
const {
|
|
492
|
+
items: all,
|
|
493
|
+
list_truncated: listTruncated,
|
|
494
|
+
entry_count: entryCount,
|
|
495
|
+
walked_count: walkedCount,
|
|
496
|
+
} = await paginateOffsetListPages({
|
|
497
|
+
pageSize,
|
|
498
|
+
listLimit,
|
|
499
|
+
retainMax: effectiveRetainMax,
|
|
500
|
+
trustedEntryCount: trustedTotalCount,
|
|
501
|
+
seededFirstPage,
|
|
502
|
+
startPage,
|
|
503
|
+
maxPages,
|
|
504
|
+
suppressFinalPageProbe,
|
|
505
|
+
...(listLimit != null && pageSize < listLimit ? { maxPagesTruncatesWithLimit: true } : {}),
|
|
506
|
+
fetchPage: async ({ page, limit }) => {
|
|
507
|
+
const body = await giteaFetch(
|
|
508
|
+
ctx.config,
|
|
509
|
+
ctx.parsed,
|
|
510
|
+
`${path}${pageSep}limit=${limit}&page=${page}`,
|
|
511
|
+
);
|
|
512
|
+
return Array.isArray(body) ? body : [];
|
|
513
|
+
},
|
|
514
|
+
});
|
|
515
|
+
let numbers = orderOpenPullNumbers(
|
|
516
|
+
prepareGiteaOpenPullPageItems(all, sliceSort),
|
|
517
|
+
(pr) => pr?.number,
|
|
518
|
+
sliceSort,
|
|
519
|
+
);
|
|
520
|
+
const outputCap = listLimit ?? retainMax;
|
|
521
|
+
if (outputCap != null && numbers.length > outputCap) {
|
|
522
|
+
numbers = numbers.slice(0, outputCap);
|
|
523
|
+
}
|
|
524
|
+
return {
|
|
525
|
+
numbers,
|
|
526
|
+
list_truncated: resolveListTruncatedWithTrustedTotal({
|
|
527
|
+
listTruncated,
|
|
528
|
+
trustedTotalCount,
|
|
529
|
+
walkedCount,
|
|
530
|
+
fullCollect: numberSortFullCollect,
|
|
531
|
+
}),
|
|
532
|
+
...(entryCount != null ? { entry_count: entryCount } : {}),
|
|
533
|
+
slice_sort: sliceSort,
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
export async function listOpenPullsWithMeta(ctx, opts = {}) {
|
|
538
|
+
requireToken();
|
|
539
|
+
const sliceSort = normalizeCrInventorySort(opts.sort ?? DEFAULT_CR_INVENTORY_SLICE_SORT);
|
|
540
|
+
if (!isCrInventoryFastPathEligible(opts)) {
|
|
541
|
+
return paginateGiteaOpenPullList(ctx, opts, sliceSort);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
const retainMax = Number(opts.retain_max);
|
|
545
|
+
const probe = await probeGiteaOpenPullPageOne(ctx, retainMax, sliceSort);
|
|
546
|
+
if (!probe) {
|
|
547
|
+
return paginateGiteaOpenPullList(ctx, opts, sliceSort);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
const { body, totalCount, listTruncated, requestLimit } = probe;
|
|
551
|
+
|
|
552
|
+
if (listTruncated) {
|
|
553
|
+
if (body.length === 0) {
|
|
554
|
+
return paginateGiteaOpenPullList(ctx, opts, sliceSort, giteaProbePaginationOpts(probe));
|
|
555
|
+
}
|
|
556
|
+
return buildGiteaOpenPullMetaFromPage(body, retainMax, sliceSort, totalCount, true);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
if (
|
|
560
|
+
sliceSort === 'recent_created' &&
|
|
561
|
+
!isRecentCreatedFastPathEligible(totalCount, retainMax, sliceSort, 'gitea-api')
|
|
562
|
+
) {
|
|
563
|
+
const tail = await fetchGiteaRecentCreatedTailSlice(ctx, retainMax, sliceSort, totalCount);
|
|
564
|
+
if (tail) return tail;
|
|
565
|
+
const tailRetry = await fetchGiteaRecentCreatedTailSlice(ctx, retainMax, sliceSort, totalCount);
|
|
566
|
+
if (tailRetry) return tailRetry;
|
|
567
|
+
const tailPage = giteaRecentCreatedTailPage(totalCount, GITEA_PAGE_SIZE);
|
|
568
|
+
return paginateGiteaOpenPullList(ctx, opts, sliceSort, {
|
|
569
|
+
trustedTotalCount: totalCount,
|
|
570
|
+
startPage: tailPage,
|
|
571
|
+
maxPages: tailPage,
|
|
572
|
+
suppressFinalPageProbe: true,
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (
|
|
577
|
+
isRecentCreatedFastPathEligible(totalCount, retainMax, sliceSort, 'gitea-api') &&
|
|
578
|
+
isNumberSortFastPathEligible(totalCount, retainMax, sliceSort) &&
|
|
579
|
+
validateFastPathPageLength(totalCount, requestLimit, body.length)
|
|
580
|
+
) {
|
|
581
|
+
return buildGiteaOpenPullMetaFromPage(body, retainMax, sliceSort, totalCount, false);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
const numberSortFullCollect = isNumberSortFullCollectRequired(totalCount, retainMax, sliceSort);
|
|
585
|
+
return paginateGiteaOpenPullList(
|
|
586
|
+
ctx,
|
|
587
|
+
opts,
|
|
588
|
+
sliceSort,
|
|
589
|
+
giteaProbePaginationOpts(probe, { numberSortFullCollect }),
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
export async function listOpenPulls(ctx, opts = {}) {
|
|
594
|
+
const meta = await listOpenPullsWithMeta(ctx, opts);
|
|
595
|
+
return meta.numbers;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
export async function crInventorySlice(ctx, opts = {}) {
|
|
599
|
+
return crInventory(ctx, { listOpenPulls, listOpenPullsWithMeta, prView, prChecks }, opts);
|
|
600
|
+
}
|
|
601
|
+
|
|
217
602
|
function mergeability(pr) {
|
|
218
603
|
if (pr.mergeable === true) return 'clean';
|
|
219
604
|
if (pr.mergeable === false) return 'conflicted';
|
|
@@ -255,18 +640,19 @@ export async function prChecks(ctx, opts) {
|
|
|
255
640
|
forgeError: forgeError(ERROR_CODES.MISSING_REF, 'Could not determine head SHA for checks'),
|
|
256
641
|
});
|
|
257
642
|
}
|
|
258
|
-
const statusRecords = await giteaFetchPaginated(
|
|
643
|
+
const { items: statusRecords, truncated: checks_truncated } = await giteaFetchPaginated(
|
|
259
644
|
ctx.config,
|
|
260
645
|
ctx.parsed,
|
|
261
646
|
repoApiPath(ctx.config, 'commits', sha, 'statuses'),
|
|
262
647
|
);
|
|
263
|
-
const mapped = statusRecords
|
|
264
|
-
context: sanitizeField(s.context),
|
|
265
|
-
state: normalizeGiteaStatusState(s.state),
|
|
266
|
-
description: sanitizeField(s.description),
|
|
267
|
-
}));
|
|
648
|
+
const mapped = mapGiteaCommitStatuses(statusRecords);
|
|
268
649
|
const conclusion = summarizeChecks(mapped);
|
|
269
|
-
return {
|
|
650
|
+
return {
|
|
651
|
+
head_sha: sha,
|
|
652
|
+
check_conclusion: conclusion,
|
|
653
|
+
checks_truncated,
|
|
654
|
+
statuses: mapped,
|
|
655
|
+
};
|
|
270
656
|
}
|
|
271
657
|
|
|
272
658
|
export function summarizeChecks(statuses) {
|
|
@@ -280,18 +666,7 @@ export function summarizeChecks(statuses) {
|
|
|
280
666
|
export async function mergePlan(ctx, opts) {
|
|
281
667
|
const view = await prView(ctx, opts);
|
|
282
668
|
const checks = await prChecks(ctx, { number: view.pr_number });
|
|
283
|
-
|
|
284
|
-
if (view.mergeability === 'conflicted') blockers.push('merge_conflict');
|
|
285
|
-
if (view.state !== 'open') blockers.push('pr_not_open');
|
|
286
|
-
if (checks.check_conclusion === 'failure') blockers.push('checks_failed');
|
|
287
|
-
if (checks.check_conclusion === 'missing') blockers.push('checks_missing');
|
|
288
|
-
if (checks.check_conclusion === 'pending') blockers.push('checks_pending');
|
|
289
|
-
return {
|
|
290
|
-
pr_number: view.pr_number,
|
|
291
|
-
mergeability: view.mergeability,
|
|
292
|
-
checks_conclusion: checks.check_conclusion,
|
|
293
|
-
blockers,
|
|
294
|
-
};
|
|
669
|
+
return buildMergePlanBodyFromFacts(ctx, view, checks, opts);
|
|
295
670
|
}
|
|
296
671
|
|
|
297
672
|
export async function syncPlan(ctx, remoteName = 'origin') {
|
|
@@ -326,8 +701,12 @@ export const provider = {
|
|
|
326
701
|
providerCapabilities,
|
|
327
702
|
repoStatus,
|
|
328
703
|
refsCompare,
|
|
704
|
+
refsInventory,
|
|
705
|
+
listOpenPulls,
|
|
706
|
+
crInventory: crInventorySlice,
|
|
329
707
|
prView,
|
|
330
708
|
prChecks,
|
|
331
709
|
mergePlan,
|
|
332
710
|
syncPlan,
|
|
711
|
+
crOpen,
|
|
333
712
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remogram/provider-gitea-api",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.4",
|
|
4
4
|
"description": "Gitea REST API forge provider for remogram",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,6 +22,6 @@
|
|
|
22
22
|
"node": ">=20"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@remogram/core": "0.1.0-beta.
|
|
25
|
+
"@remogram/core": "0.1.0-beta.4"
|
|
26
26
|
}
|
|
27
27
|
}
|