@remogram/provider-gitea-api 0.1.0-beta.2 → 0.1.0-beta.3
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 +116 -28
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -7,9 +7,20 @@ import {
|
|
|
7
7
|
gitRevParse,
|
|
8
8
|
gitCurrentBranch,
|
|
9
9
|
gitAheadBehind,
|
|
10
|
+
refsInventory,
|
|
11
|
+
crInventory,
|
|
12
|
+
mergeBlockersFromFacts,
|
|
10
13
|
ERROR_CODES,
|
|
11
14
|
forgeError,
|
|
12
15
|
forgeIngestCapabilityFacts,
|
|
16
|
+
checkPaginationCapabilityFacts,
|
|
17
|
+
DEFAULT_CHECK_STATUS_PAGE_SIZE,
|
|
18
|
+
MAX_CHECK_STATUS_PAGES,
|
|
19
|
+
paginateCheckStatusPages,
|
|
20
|
+
paginateOffsetListPages,
|
|
21
|
+
fetchWithIngestPageBackoff,
|
|
22
|
+
fetchPageWithIngestBackoff,
|
|
23
|
+
withPerPageParam,
|
|
13
24
|
apiProviderCommands,
|
|
14
25
|
} from '@remogram/core';
|
|
15
26
|
const PUBLIC_GITEA_HOST = 'gitea.com';
|
|
@@ -17,6 +28,8 @@ const PUBLIC_GITEA_API = 'https://gitea.com/api/v1';
|
|
|
17
28
|
const AUTH_CAPABILITIES = [
|
|
18
29
|
'repo_status',
|
|
19
30
|
'ref_compare',
|
|
31
|
+
'ref_inventory',
|
|
32
|
+
'cr_inventory',
|
|
20
33
|
'pr_status',
|
|
21
34
|
'pr_checks',
|
|
22
35
|
'merge_plan',
|
|
@@ -119,23 +132,21 @@ export async function giteaFetch(config, parsed, path, options = {}) {
|
|
|
119
132
|
});
|
|
120
133
|
}
|
|
121
134
|
|
|
122
|
-
const MAX_CHECK_PAGES =
|
|
135
|
+
const MAX_CHECK_PAGES = MAX_CHECK_STATUS_PAGES;
|
|
123
136
|
const GITEA_PAGE_SIZE = 100;
|
|
124
137
|
|
|
125
138
|
export async function giteaFetchPaginated(config, parsed, path) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
return all;
|
|
139
|
+
return paginateCheckStatusPages({
|
|
140
|
+
fetchPage: async ({ page, limit }) => {
|
|
141
|
+
const separator = path.includes('?') ? '&' : '?';
|
|
142
|
+
const body = await giteaFetch(
|
|
143
|
+
config,
|
|
144
|
+
parsed,
|
|
145
|
+
`${path}${separator}limit=${limit}&page=${page}`,
|
|
146
|
+
);
|
|
147
|
+
return Array.isArray(body) ? body : [];
|
|
148
|
+
},
|
|
149
|
+
});
|
|
139
150
|
}
|
|
140
151
|
|
|
141
152
|
export function normalizeGiteaStatusState(state) {
|
|
@@ -150,6 +161,36 @@ export function normalizeGiteaStatusState(state) {
|
|
|
150
161
|
return 'unknown';
|
|
151
162
|
}
|
|
152
163
|
|
|
164
|
+
function giteaStatusRecordOrder(a, b) {
|
|
165
|
+
const aUpdated = Date.parse(a.updated_at ?? '') || 0;
|
|
166
|
+
const bUpdated = Date.parse(b.updated_at ?? '') || 0;
|
|
167
|
+
if (aUpdated !== bUpdated) return aUpdated - bUpdated;
|
|
168
|
+
const aId = Number(a.id) || 0;
|
|
169
|
+
const bId = Number(b.id) || 0;
|
|
170
|
+
return aId - bId;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function dedupeGiteaStatusRecords(records) {
|
|
174
|
+
const latestByContext = new Map();
|
|
175
|
+
for (const record of records) {
|
|
176
|
+
const context = record?.context;
|
|
177
|
+
if (context == null || context === '') continue;
|
|
178
|
+
const existing = latestByContext.get(context);
|
|
179
|
+
if (!existing || giteaStatusRecordOrder(record, existing) > 0) {
|
|
180
|
+
latestByContext.set(context, record);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return Array.from(latestByContext.values());
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function mapGiteaCommitStatuses(records) {
|
|
187
|
+
return dedupeGiteaStatusRecords(records).map((s) => ({
|
|
188
|
+
context: sanitizeField(s.context),
|
|
189
|
+
state: normalizeGiteaStatusState(s.status ?? s.state),
|
|
190
|
+
description: sanitizeField(s.description),
|
|
191
|
+
}));
|
|
192
|
+
}
|
|
193
|
+
|
|
153
194
|
export function normalizeGiteaPrState(state) {
|
|
154
195
|
const normalized = String(state ?? '').toLowerCase();
|
|
155
196
|
if (normalized === 'open') return 'open';
|
|
@@ -173,15 +214,21 @@ export async function repoStatus(ctx) {
|
|
|
173
214
|
}
|
|
174
215
|
|
|
175
216
|
export function providerCapabilities() {
|
|
217
|
+
const check_sources = ['commit_statuses'];
|
|
176
218
|
return {
|
|
177
219
|
commands: STRUCTURED_COMMANDS,
|
|
178
220
|
auth_envs: ['GITEA_TOKEN'],
|
|
179
|
-
check_sources
|
|
221
|
+
check_sources,
|
|
180
222
|
mergeability_confidence: 'direct',
|
|
181
223
|
host_binding: 'verified_remote_host',
|
|
182
224
|
pagination: 'supported',
|
|
183
225
|
write_support: false,
|
|
184
226
|
...forgeIngestCapabilityFacts(),
|
|
227
|
+
...checkPaginationCapabilityFacts({
|
|
228
|
+
strategy: 'offset_limit',
|
|
229
|
+
pageSizeParam: 'limit',
|
|
230
|
+
sourceCount: check_sources.length,
|
|
231
|
+
}),
|
|
185
232
|
};
|
|
186
233
|
}
|
|
187
234
|
|
|
@@ -214,6 +261,48 @@ export async function getPull(ctx, { number }) {
|
|
|
214
261
|
return giteaFetch(ctx.config, ctx.parsed, repoApiPath(ctx.config, 'pulls', number));
|
|
215
262
|
}
|
|
216
263
|
|
|
264
|
+
export async function listOpenPullsWithMeta(ctx, opts = {}) {
|
|
265
|
+
requireToken();
|
|
266
|
+
const listLimit =
|
|
267
|
+
opts.limit != null && Number.isInteger(Number(opts.limit)) && Number(opts.limit) > 0
|
|
268
|
+
? Number(opts.limit)
|
|
269
|
+
: null;
|
|
270
|
+
const pageSize =
|
|
271
|
+
listLimit != null ? Math.min(listLimit, GITEA_PAGE_SIZE) : GITEA_PAGE_SIZE;
|
|
272
|
+
const path = `${repoApiPath(ctx.config, 'pulls')}?state=open`;
|
|
273
|
+
const pageSep = path.includes('?') ? '&' : '?';
|
|
274
|
+
const { items: all, list_truncated: listTruncated } = await paginateOffsetListPages({
|
|
275
|
+
pageSize,
|
|
276
|
+
listLimit,
|
|
277
|
+
...(listLimit != null && pageSize < listLimit ? { maxPagesTruncatesWithLimit: true } : {}),
|
|
278
|
+
fetchPage: async ({ page, limit }) => {
|
|
279
|
+
const body = await giteaFetch(
|
|
280
|
+
ctx.config,
|
|
281
|
+
ctx.parsed,
|
|
282
|
+
`${path}${pageSep}limit=${limit}&page=${page}`,
|
|
283
|
+
);
|
|
284
|
+
return Array.isArray(body) ? body : [];
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
let numbers = all
|
|
288
|
+
.map((pr) => pr.number)
|
|
289
|
+
.filter((number) => Number.isInteger(number))
|
|
290
|
+
.sort((a, b) => a - b);
|
|
291
|
+
if (listLimit != null && numbers.length > listLimit) {
|
|
292
|
+
numbers = numbers.slice(0, listLimit);
|
|
293
|
+
}
|
|
294
|
+
return { numbers, list_truncated: listTruncated };
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export async function listOpenPulls(ctx, opts = {}) {
|
|
298
|
+
const meta = await listOpenPullsWithMeta(ctx, opts);
|
|
299
|
+
return meta.numbers;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export async function crInventorySlice(ctx, opts = {}) {
|
|
303
|
+
return crInventory(ctx, { listOpenPulls, listOpenPullsWithMeta, prView, prChecks }, opts);
|
|
304
|
+
}
|
|
305
|
+
|
|
217
306
|
function mergeability(pr) {
|
|
218
307
|
if (pr.mergeable === true) return 'clean';
|
|
219
308
|
if (pr.mergeable === false) return 'conflicted';
|
|
@@ -255,18 +344,19 @@ export async function prChecks(ctx, opts) {
|
|
|
255
344
|
forgeError: forgeError(ERROR_CODES.MISSING_REF, 'Could not determine head SHA for checks'),
|
|
256
345
|
});
|
|
257
346
|
}
|
|
258
|
-
const statusRecords = await giteaFetchPaginated(
|
|
347
|
+
const { items: statusRecords, truncated: checks_truncated } = await giteaFetchPaginated(
|
|
259
348
|
ctx.config,
|
|
260
349
|
ctx.parsed,
|
|
261
350
|
repoApiPath(ctx.config, 'commits', sha, 'statuses'),
|
|
262
351
|
);
|
|
263
|
-
const mapped = statusRecords
|
|
264
|
-
context: sanitizeField(s.context),
|
|
265
|
-
state: normalizeGiteaStatusState(s.state),
|
|
266
|
-
description: sanitizeField(s.description),
|
|
267
|
-
}));
|
|
352
|
+
const mapped = mapGiteaCommitStatuses(statusRecords);
|
|
268
353
|
const conclusion = summarizeChecks(mapped);
|
|
269
|
-
return {
|
|
354
|
+
return {
|
|
355
|
+
head_sha: sha,
|
|
356
|
+
check_conclusion: conclusion,
|
|
357
|
+
checks_truncated,
|
|
358
|
+
statuses: mapped,
|
|
359
|
+
};
|
|
270
360
|
}
|
|
271
361
|
|
|
272
362
|
export function summarizeChecks(statuses) {
|
|
@@ -280,12 +370,7 @@ export function summarizeChecks(statuses) {
|
|
|
280
370
|
export async function mergePlan(ctx, opts) {
|
|
281
371
|
const view = await prView(ctx, opts);
|
|
282
372
|
const checks = await prChecks(ctx, { number: view.pr_number });
|
|
283
|
-
const blockers =
|
|
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');
|
|
373
|
+
const blockers = mergeBlockersFromFacts(view, checks);
|
|
289
374
|
return {
|
|
290
375
|
pr_number: view.pr_number,
|
|
291
376
|
mergeability: view.mergeability,
|
|
@@ -326,6 +411,9 @@ export const provider = {
|
|
|
326
411
|
providerCapabilities,
|
|
327
412
|
repoStatus,
|
|
328
413
|
refsCompare,
|
|
414
|
+
refsInventory,
|
|
415
|
+
listOpenPulls,
|
|
416
|
+
crInventory: crInventorySlice,
|
|
329
417
|
prView,
|
|
330
418
|
prChecks,
|
|
331
419
|
mergePlan,
|
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.3",
|
|
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.3"
|
|
26
26
|
}
|
|
27
27
|
}
|