@remogram/provider-gitlab-api 0.1.0-beta.1 → 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 +89 -24
  2. package/package.json +2 -2
package/index.js CHANGED
@@ -7,9 +7,21 @@ 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,
24
+ apiProviderCommands,
13
25
  } from '@remogram/core';
14
26
 
15
27
  const PUBLIC_GITLAB_HOST = 'gitlab.com';
@@ -17,12 +29,14 @@ const PUBLIC_GITLAB_API = 'https://gitlab.com/api/v4';
17
29
  const AUTH_CAPABILITIES = [
18
30
  'repo_status',
19
31
  'ref_compare',
32
+ 'ref_inventory',
33
+ 'cr_inventory',
20
34
  'pr_status',
21
35
  'pr_checks',
22
36
  'merge_plan',
23
37
  'sync_plan',
24
38
  ];
25
- const STRUCTURED_COMMANDS = AUTH_CAPABILITIES.map((name) => ({ name, implemented: true }));
39
+ const STRUCTURED_COMMANDS = apiProviderCommands();
26
40
 
27
41
  export function gitlabToken() {
28
42
  return process.env.GITLAB_TOKEN || null;
@@ -115,35 +129,39 @@ export async function gitlabFetch(config, parsed, path, options = {}) {
115
129
  });
116
130
  }
117
131
 
118
- const MAX_CHECK_PAGES = 50;
132
+ const MAX_CHECK_PAGES = MAX_CHECK_STATUS_PAGES;
119
133
  const GITLAB_PAGE_SIZE = 100;
120
134
 
121
135
  export async function gitlabFetchPaginated(config, parsed, path) {
122
- const all = [];
123
- for (let page = 1; page <= MAX_CHECK_PAGES; page += 1) {
124
- const separator = path.includes('?') ? '&' : '?';
125
- const body = await gitlabFetch(
126
- config,
127
- parsed,
128
- `${path}${separator}per_page=${GITLAB_PAGE_SIZE}&page=${page}`,
129
- );
130
- const items = Array.isArray(body) ? body : [];
131
- all.push(...items);
132
- if (items.length < GITLAB_PAGE_SIZE) break;
133
- }
134
- 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
+ });
135
147
  }
136
148
 
137
149
  export function providerCapabilities() {
150
+ const check_sources = ['commit_statuses', 'pipelines'];
138
151
  return {
139
152
  commands: STRUCTURED_COMMANDS,
140
153
  auth_envs: ['GITLAB_TOKEN'],
141
- check_sources: ['commit_statuses', 'pipelines'],
154
+ check_sources,
142
155
  mergeability_confidence: 'derived',
143
156
  host_binding: 'verified_remote_host',
144
157
  pagination: 'supported',
145
158
  write_support: false,
146
159
  ...forgeIngestCapabilityFacts(),
160
+ ...checkPaginationCapabilityFacts({
161
+ strategy: 'offset_limit',
162
+ pageSizeParam: 'per_page',
163
+ sourceCount: check_sources.length,
164
+ }),
147
165
  };
148
166
  }
149
167
 
@@ -259,7 +277,7 @@ export async function prChecks(ctx, opts) {
259
277
  });
260
278
  }
261
279
 
262
- const [statusRecords, pipelineRecords] = await Promise.all([
280
+ const [statusResult, pipelineResult] = await Promise.all([
263
281
  gitlabFetchPaginated(
264
282
  ctx.config,
265
283
  ctx.parsed,
@@ -271,6 +289,8 @@ export async function prChecks(ctx, opts) {
271
289
  `${projectApiPath(ctx.config, 'pipelines')}?sha=${encodeURIComponent(sha)}`,
272
290
  ),
273
291
  ]);
292
+ const statusRecords = statusResult.items;
293
+ const pipelineRecords = pipelineResult.items;
274
294
  const mappedStatuses = statusRecords.map((status) => ({
275
295
  context: sanitizeField(status.name || status.context),
276
296
  state: normalizeStatusState(status.status),
@@ -282,18 +302,19 @@ export async function prChecks(ctx, opts) {
282
302
  description: sanitizeField(pipeline.status),
283
303
  }));
284
304
  const mapped = [...mappedStatuses, ...mappedPipelines];
285
- 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
+ };
286
312
  }
287
313
 
288
314
  export async function mergePlan(ctx, opts) {
289
315
  const view = await prView(ctx, opts);
290
316
  const checks = await prChecks(ctx, { number: view.pr_number });
291
- const blockers = [];
292
- if (view.mergeability === 'conflicted') blockers.push('merge_conflict');
293
- if (view.state !== 'open') blockers.push('pr_not_open');
294
- if (checks.check_conclusion === 'failure') blockers.push('checks_failed');
295
- if (checks.check_conclusion === 'missing') blockers.push('checks_missing');
296
- if (checks.check_conclusion === 'pending') blockers.push('checks_pending');
317
+ const blockers = mergeBlockersFromFacts(view, checks);
297
318
  return {
298
319
  pr_number: view.pr_number,
299
320
  mergeability: view.mergeability,
@@ -302,6 +323,47 @@ export async function mergePlan(ctx, opts) {
302
323
  };
303
324
  }
304
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
+
305
367
  export async function syncPlan(ctx, remoteName = 'origin') {
306
368
  assertGitRemote(remoteName, 'remote');
307
369
  apiBase(ctx.config, ctx.parsed);
@@ -335,6 +397,9 @@ export const provider = {
335
397
  providerCapabilities,
336
398
  repoStatus,
337
399
  refsCompare,
400
+ refsInventory,
401
+ listOpenPulls,
402
+ crInventory: crInventorySlice,
338
403
  prView,
339
404
  prChecks,
340
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.1",
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.1"
25
+ "@remogram/core": "0.1.0-beta.3"
26
26
  }
27
27
  }