@remogram/provider-gitlab-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.
Files changed (2) hide show
  1. package/index.js +87 -23
  2. 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
 
@@ -18,6 +29,8 @@ const PUBLIC_GITLAB_API = 'https://gitlab.com/api/v4';
18
29
  const AUTH_CAPABILITIES = [
19
30
  'repo_status',
20
31
  'ref_compare',
32
+ 'ref_inventory',
33
+ 'cr_inventory',
21
34
  'pr_status',
22
35
  'pr_checks',
23
36
  'merge_plan',
@@ -116,35 +129,39 @@ export async function gitlabFetch(config, parsed, path, options = {}) {
116
129
  });
117
130
  }
118
131
 
119
- const MAX_CHECK_PAGES = 50;
132
+ const MAX_CHECK_PAGES = MAX_CHECK_STATUS_PAGES;
120
133
  const GITLAB_PAGE_SIZE = 100;
121
134
 
122
135
  export async function gitlabFetchPaginated(config, parsed, path) {
123
- const all = [];
124
- for (let page = 1; page <= MAX_CHECK_PAGES; page += 1) {
125
- const separator = path.includes('?') ? '&' : '?';
126
- const body = await gitlabFetch(
127
- config,
128
- parsed,
129
- `${path}${separator}per_page=${GITLAB_PAGE_SIZE}&page=${page}`,
130
- );
131
- const items = Array.isArray(body) ? body : [];
132
- all.push(...items);
133
- if (items.length < GITLAB_PAGE_SIZE) break;
134
- }
135
- return all;
136
+ return paginateCheckStatusPages({
137
+ fetchPage: async ({ page, limit }) => {
138
+ const separator = path.includes('?') ? '&' : '?';
139
+ const body = await gitlabFetch(
140
+ config,
141
+ parsed,
142
+ `${path}${separator}per_page=${limit}&page=${page}`,
143
+ );
144
+ return Array.isArray(body) ? body : [];
145
+ },
146
+ });
136
147
  }
137
148
 
138
149
  export function providerCapabilities() {
150
+ const check_sources = ['commit_statuses', 'pipelines'];
139
151
  return {
140
152
  commands: STRUCTURED_COMMANDS,
141
153
  auth_envs: ['GITLAB_TOKEN'],
142
- check_sources: ['commit_statuses', 'pipelines'],
154
+ check_sources,
143
155
  mergeability_confidence: 'derived',
144
156
  host_binding: 'verified_remote_host',
145
157
  pagination: 'supported',
146
158
  write_support: false,
147
159
  ...forgeIngestCapabilityFacts(),
160
+ ...checkPaginationCapabilityFacts({
161
+ strategy: 'offset_limit',
162
+ pageSizeParam: 'per_page',
163
+ sourceCount: check_sources.length,
164
+ }),
148
165
  };
149
166
  }
150
167
 
@@ -260,7 +277,7 @@ export async function prChecks(ctx, opts) {
260
277
  });
261
278
  }
262
279
 
263
- const [statusRecords, pipelineRecords] = await Promise.all([
280
+ const [statusResult, pipelineResult] = await Promise.all([
264
281
  gitlabFetchPaginated(
265
282
  ctx.config,
266
283
  ctx.parsed,
@@ -272,6 +289,8 @@ export async function prChecks(ctx, opts) {
272
289
  `${projectApiPath(ctx.config, 'pipelines')}?sha=${encodeURIComponent(sha)}`,
273
290
  ),
274
291
  ]);
292
+ const statusRecords = statusResult.items;
293
+ const pipelineRecords = pipelineResult.items;
275
294
  const mappedStatuses = statusRecords.map((status) => ({
276
295
  context: sanitizeField(status.name || status.context),
277
296
  state: normalizeStatusState(status.status),
@@ -283,18 +302,19 @@ export async function prChecks(ctx, opts) {
283
302
  description: sanitizeField(pipeline.status),
284
303
  }));
285
304
  const mapped = [...mappedStatuses, ...mappedPipelines];
286
- return { head_sha: sha, check_conclusion: summarizeChecks(mapped), statuses: mapped };
305
+ const checks_truncated = statusResult.truncated || pipelineResult.truncated;
306
+ return {
307
+ head_sha: sha,
308
+ check_conclusion: summarizeChecks(mapped),
309
+ checks_truncated,
310
+ statuses: mapped,
311
+ };
287
312
  }
288
313
 
289
314
  export async function mergePlan(ctx, opts) {
290
315
  const view = await prView(ctx, opts);
291
316
  const checks = await prChecks(ctx, { number: view.pr_number });
292
- const blockers = [];
293
- if (view.mergeability === 'conflicted') blockers.push('merge_conflict');
294
- if (view.state !== 'open') blockers.push('pr_not_open');
295
- if (checks.check_conclusion === 'failure') blockers.push('checks_failed');
296
- if (checks.check_conclusion === 'missing') blockers.push('checks_missing');
297
- if (checks.check_conclusion === 'pending') blockers.push('checks_pending');
317
+ const blockers = mergeBlockersFromFacts(view, checks);
298
318
  return {
299
319
  pr_number: view.pr_number,
300
320
  mergeability: view.mergeability,
@@ -303,6 +323,47 @@ export async function mergePlan(ctx, opts) {
303
323
  };
304
324
  }
305
325
 
326
+ export async function listOpenPullsWithMeta(ctx, opts = {}) {
327
+ apiBase(ctx.config, ctx.parsed);
328
+ requireToken();
329
+ const listLimit =
330
+ opts.limit != null && Number.isInteger(Number(opts.limit)) && Number(opts.limit) > 0
331
+ ? Number(opts.limit)
332
+ : null;
333
+ const path = `${projectApiPath(ctx.config, 'merge_requests')}?state=opened`;
334
+ const separator = path.includes('?') ? '&' : '?';
335
+ const { items: all, list_truncated: listTruncated } = await paginateOffsetListPages({
336
+ pageSize: GITLAB_PAGE_SIZE,
337
+ listLimit,
338
+ ...(listLimit != null ? { maxPagesTruncatesWithLimit: true } : {}),
339
+ fetchPage: async ({ page, limit }) => {
340
+ const body = await gitlabFetch(
341
+ ctx.config,
342
+ ctx.parsed,
343
+ `${path}${separator}per_page=${limit}&page=${page}`,
344
+ );
345
+ return Array.isArray(body) ? body : [];
346
+ },
347
+ });
348
+ let numbers = all
349
+ .map((mr) => mr.iid)
350
+ .filter((number) => Number.isInteger(number))
351
+ .sort((a, b) => a - b);
352
+ if (listLimit != null && numbers.length > listLimit) {
353
+ numbers = numbers.slice(0, listLimit);
354
+ }
355
+ return { numbers, list_truncated: listTruncated };
356
+ }
357
+
358
+ export async function listOpenPulls(ctx, opts = {}) {
359
+ const meta = await listOpenPullsWithMeta(ctx, opts);
360
+ return meta.numbers;
361
+ }
362
+
363
+ export async function crInventorySlice(ctx, opts = {}) {
364
+ return crInventory(ctx, { listOpenPulls, listOpenPullsWithMeta, prView, prChecks }, opts);
365
+ }
366
+
306
367
  export async function syncPlan(ctx, remoteName = 'origin') {
307
368
  assertGitRemote(remoteName, 'remote');
308
369
  apiBase(ctx.config, ctx.parsed);
@@ -336,6 +397,9 @@ export const provider = {
336
397
  providerCapabilities,
337
398
  repoStatus,
338
399
  refsCompare,
400
+ refsInventory,
401
+ listOpenPulls,
402
+ crInventory: crInventorySlice,
339
403
  prView,
340
404
  prChecks,
341
405
  mergePlan,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remogram/provider-gitlab-api",
3
- "version": "0.1.0-beta.2",
3
+ "version": "0.1.0-beta.3",
4
4
  "description": "GitLab API 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.2"
25
+ "@remogram/core": "0.1.0-beta.3"
26
26
  }
27
27
  }