@goodfellow-cms/gitlab 0.9.0 → 0.10.0

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/dist/index.js CHANGED
@@ -1368,27 +1368,77 @@ function gitlabSetup(options = {}) {
1368
1368
  async function isAvailable(owner, name) {
1369
1369
  return (await gitlabRequest(api, `/projects/${encodeURIComponent(`${owner.path}/${name}`)}`, { allow: [404] })).status === 404;
1370
1370
  }
1371
+ const projectPath = (path) => `/projects/${encodeURIComponent(path)}`;
1372
+ /**
1373
+ * Makes one connection between a site's projects, giving what's left to do
1374
+ * by hand where GitLab refuses it, with what it answered.
1375
+ */
1376
+ async function attempt(unmade, request, left) {
1377
+ try {
1378
+ await request();
1379
+ } catch (error) {
1380
+ if (!(error instanceof GitApiError)) throw error;
1381
+ left.push({
1382
+ ...unmade,
1383
+ reason: error.message
1384
+ });
1385
+ }
1386
+ }
1371
1387
  /**
1372
1388
  * Lets each project's pipelines reach the other, as their setup files say:
1373
1389
  * a job token is refused by a project whose job token allowlist doesn't
1374
1390
  * name its project, even a public one. Also lets the code project's job
1375
- * token publish updates, and runs pipelines each night where they need it.
1376
- * Returns whether it worked.
1391
+ * token publish updates. Runs before the project's files are written,
1392
+ * since their first pipeline reads the other project. Each is tried on its
1393
+ * own, so one GitLab refuses doesn't keep the others from being made.
1377
1394
  */
1378
- async function connectProjects(link) {
1379
- const project = (path) => `/projects/${encodeURIComponent(path)}`;
1380
- try {
1381
- const ids = /* @__PURE__ */ new Map();
1382
- for (const which of ["code", "content"]) ids.set(which, (await gitlabJson(api, project(link[which]))).id);
1383
- for (const { reader, read } of link.links.reads) await gitlabRequest(api, `${project(link[read])}/job_token_scope/allowlist`, {
1384
- method: "POST",
1385
- body: { target_project_id: ids.get(reader) }
1386
- });
1387
- if (link.links.updates) await gitlabRequest(api, project(link[link.links.updates]), {
1395
+ async function connectProjects(link, left) {
1396
+ const ids = /* @__PURE__ */ new Map();
1397
+ for (const which of ["code", "content"]) try {
1398
+ ids.set(which, (await gitlabJson(api, projectPath(link[which]))).id);
1399
+ } catch (error) {
1400
+ if (!(error instanceof GitApiError)) throw error;
1401
+ }
1402
+ for (const { reader, read } of link.links.reads) {
1403
+ const target = ids.get(reader);
1404
+ await attempt({
1405
+ kind: "read",
1406
+ reader: link[reader],
1407
+ read: link[read],
1408
+ reason: ""
1409
+ }, async () => {
1410
+ if (target === void 0) throw new GitApiError(404, `GitLab couldn't find ${link[reader]}.`);
1411
+ await gitlabRequest(api, `${projectPath(link[read])}/job_token_scope/allowlist`, {
1412
+ method: "POST",
1413
+ body: { target_project_id: target }
1414
+ });
1415
+ }, left);
1416
+ }
1417
+ if (link.links.updates) {
1418
+ const repo = link[link.links.updates];
1419
+ await attempt({
1420
+ kind: "push",
1421
+ repo,
1422
+ reason: ""
1423
+ }, () => gitlabRequest(api, projectPath(repo), {
1388
1424
  method: "PUT",
1389
1425
  body: { ci_push_repository_for_job_token_allowed: true }
1390
- });
1391
- for (const which of link.links.nightly) await gitlabRequest(api, `${project(link[which])}/pipeline_schedules`, {
1426
+ }), left);
1427
+ }
1428
+ }
1429
+ /**
1430
+ * Runs the pipelines that need it each night. After the project's files
1431
+ * are written, since a schedule names the main branch, which an empty
1432
+ * project doesn't have yet.
1433
+ */
1434
+ async function scheduleNightly(link, left) {
1435
+ for (const which of link.links.nightly) {
1436
+ const repo = link[which];
1437
+ await attempt({
1438
+ kind: "nightly",
1439
+ repo,
1440
+ reason: ""
1441
+ }, () => gitlabRequest(api, `${projectPath(repo)}/pipeline_schedules`, {
1392
1442
  method: "POST",
1393
1443
  body: {
1394
1444
  description: which === link.links.updates ? "Rebuild the site and install Goodfellow's fixes each night" : "Rebuild the site each night",
@@ -1397,15 +1447,7 @@ function gitlabSetup(options = {}) {
1397
1447
  cron_timezone: "UTC",
1398
1448
  active: true
1399
1449
  }
1400
- });
1401
- return true;
1402
- } catch (error) {
1403
- if (error instanceof GitApiError && [
1404
- 400,
1405
- 403,
1406
- 404
1407
- ].includes(error.status)) return false;
1408
- throw error;
1450
+ }), left);
1409
1451
  }
1410
1452
  }
1411
1453
  return {
@@ -1446,9 +1488,10 @@ function gitlabSetup(options = {}) {
1446
1488
  throw error;
1447
1489
  }
1448
1490
  const warnings = [];
1491
+ const unconnected = [];
1449
1492
  if (site.link) {
1450
1493
  onStep?.("connect");
1451
- if (!await connectProjects(site.link)) warnings.push("connect-unavailable");
1494
+ await connectProjects(site.link, unconnected);
1452
1495
  }
1453
1496
  let siteUrl;
1454
1497
  if (site.pages && site.domain) {
@@ -1491,13 +1534,16 @@ function gitlabSetup(options = {}) {
1491
1534
  }])
1492
1535
  }
1493
1536
  });
1537
+ if (site.link) await scheduleNightly(site.link, unconnected);
1538
+ if (unconnected.length) warnings.push("connect-unavailable");
1494
1539
  return {
1495
1540
  repo: project.path_with_namespace,
1496
1541
  webUrl: project.web_url,
1497
1542
  ...siteUrl && { siteUrl },
1498
1543
  buildsUrl: `${project.web_url}/-/pipelines`,
1499
1544
  pagesUrl: site.pages ? `${project.web_url}/pages` : void 0,
1500
- warnings
1545
+ warnings,
1546
+ ...unconnected.length && { unconnected }
1501
1547
  };
1502
1548
  }
1503
1549
  };
package/dist/testing.d.ts CHANGED
@@ -40,6 +40,8 @@ export interface FakeGitLabOptions {
40
40
  };
41
41
  /** Domains another GitLab Pages site already uses. */
42
42
  takenDomains?: string[];
43
+ /** What connecting created projects GitLab refuses, as a plan or a sign-in might: job token allowlists, job token pushes or pipeline schedules. */
44
+ refuseConnections?: Array<"allowlist" | "push" | "schedule">;
43
45
  /** Other GitLab accounts that exist, which can be added to the project. Every token's user exists and is a member. */
44
46
  accounts?: string[];
45
47
  /** Who can see the project. Private by default. */
package/dist/testing.js CHANGED
@@ -375,6 +375,7 @@ function fakeGitLab(options) {
375
375
  });
376
376
  if (rest === "" && request.method === "PUT") {
377
377
  const body = await request.json();
378
+ if (options.refuseConnections?.includes("push")) return json({ message: "403 Forbidden" }, 403);
378
379
  if (body.ci_push_repository_for_job_token_allowed !== void 0) project.jobTokenPush = body.ci_push_repository_for_job_token_allowed;
379
380
  return json({
380
381
  id: project.id,
@@ -383,6 +384,7 @@ function fakeGitLab(options) {
383
384
  }
384
385
  if (rest === "/job_token_scope/allowlist" && request.method === "POST") {
385
386
  const body = await request.json();
387
+ if (options.refuseConnections?.includes("allowlist")) return json({ message: "403 Forbidden" }, 403);
386
388
  if (![...created.values()].some((candidate) => candidate.id === body.target_project_id)) return json({ message: "404 Project Not Found" }, 404);
387
389
  if (!project.jobTokenAllowlist.includes(body.target_project_id)) project.jobTokenAllowlist.push(body.target_project_id);
388
390
  return json({
@@ -401,6 +403,8 @@ function fakeGitLab(options) {
401
403
  }
402
404
  if (rest === "/pipeline_schedules" && request.method === "POST") {
403
405
  const body = await request.json();
406
+ if (options.refuseConnections?.includes("schedule")) return json({ message: { ref: ["is ambiguous"] } }, 400);
407
+ if (project.repo.files().size === 0) return json({ message: { ref: ["is ambiguous"] } }, 400);
404
408
  project.schedules.push({
405
409
  ...body,
406
410
  active: body.active !== false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodfellow-cms/gitlab",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Edit a Goodfellow CMS site stored on GitLab, straight from the browser.",
5
5
  "keywords": [
6
6
  "goodfellow",
@@ -39,7 +39,7 @@
39
39
  "dist"
40
40
  ],
41
41
  "dependencies": {
42
- "@goodfellow-cms/core": "0.9.0"
42
+ "@goodfellow-cms/core": "0.10.0"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@puckeditor/core": "0.23.0"