@goodfellow-cms/gitlab 0.8.0 → 0.9.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
@@ -143,6 +143,11 @@ function gitlabEditors(api, project, branch, username) {
143
143
  await gitlabRequest(api, editor.id.startsWith(INVITATION) ? `${base}/invitations/${encodeURIComponent(editor.id.slice(11))}` : `${base}/members/${editor.id.slice(7)}`, { method: "DELETE" }).catch(notAllowed$2);
144
144
  },
145
145
  async letEditorsPublish() {
146
+ const read = async () => {
147
+ const answer = await gitlabRequest(api, protectedPath, { allow: [404] }).catch(notAllowed$2);
148
+ return answer.ok ? await answer.json() : void 0;
149
+ };
150
+ const developersMay = (protection) => !protection || ["push_access_levels", "merge_access_levels"].every((kind) => (protection[kind] ?? []).some((level) => level.access_level !== null && level.access_level <= DEVELOPER));
146
151
  const protect = (level) => gitlabRequest(api, `${base}/protected_branches`, {
147
152
  method: "POST",
148
153
  body: {
@@ -152,6 +157,30 @@ function gitlabEditors(api, project, branch, username) {
152
157
  allow_force_push: false
153
158
  }
154
159
  });
160
+ const current = await read();
161
+ if (!current) {
162
+ await protect(DEVELOPER).catch(notAllowed$2);
163
+ return;
164
+ }
165
+ if (developersMay(current)) return;
166
+ const swap = (levels = []) => [{ access_level: DEVELOPER }, ...levels.flatMap((level) => level.id === void 0 ? [] : [{
167
+ id: level.id,
168
+ _destroy: true
169
+ }])];
170
+ if ((await gitlabRequest(api, protectedPath, {
171
+ method: "PATCH",
172
+ body: {
173
+ allow_force_push: false,
174
+ allowed_to_push: swap(current.push_access_levels),
175
+ allowed_to_merge: swap(current.merge_access_levels)
176
+ },
177
+ allow: [
178
+ 400,
179
+ 404,
180
+ 405,
181
+ 422
182
+ ]
183
+ }).catch(notAllowed$2)).ok && developersMay(await read())) return;
155
184
  await gitlabRequest(api, protectedPath, {
156
185
  method: "DELETE",
157
186
  allow: [404]
@@ -162,6 +191,11 @@ function gitlabEditors(api, project, branch, username) {
162
191
  await protect(MAINTAINER).catch(() => void 0);
163
192
  notAllowed$2(error);
164
193
  }
194
+ const after = await read();
195
+ if (!after || !developersMay(after)) {
196
+ if (!after) await protect(MAINTAINER).catch(() => void 0);
197
+ throw new Error("GitLab didn't keep the main branch's new protection. Try again.");
198
+ }
165
199
  }
166
200
  };
167
201
  }
package/dist/testing.d.ts CHANGED
@@ -50,6 +50,8 @@ export interface FakeGitLabOptions {
50
50
  * artifact, or nothing for a job that fails. Without it, task pipelines fail.
51
51
  */
52
52
  tasks?: (variables: Record<string, string>) => string | undefined | Promise<string | undefined>;
53
+ /** GitLab before 15.6, which can't change a protected branch's access levels, only remove and add it. */
54
+ noProtectionChanges?: boolean;
53
55
  }
54
56
  /** A pipeline schedule, such as the nightly one or one the admin panel adds for a scheduled change. */
55
57
  export interface FakeSchedule {
@@ -120,6 +122,7 @@ export declare function fakeGitLab(options: FakeGitLabOptions): {
120
122
  merge: number;
121
123
  forcePush: boolean;
122
124
  }>;
125
+ protectionChanges: string[];
123
126
  failBuild: (sha: string, failure?: {
124
127
  job?: string;
125
128
  failureReason?: string;
package/dist/testing.js CHANGED
@@ -53,6 +53,8 @@ function fakeGitLab(options) {
53
53
  merge: 40,
54
54
  forcePush: false
55
55
  }]]);
56
+ /** Each change to a protected branch, in order: `DELETE main`, `POST main`, `PATCH main`. */
57
+ const protectionChanges = [];
56
58
  /** The project's members and invitations, and its protected branches. */
57
59
  async function handleMembers(request, url, rest, accessLevel) {
58
60
  const forbidden = () => json({ message: "403 Forbidden" }, 403);
@@ -134,20 +136,48 @@ function fakeGitLab(options) {
134
136
  if (!found) return json({ message: "404 Not found" }, 404);
135
137
  if (request.method === "GET") return json({
136
138
  name,
137
- push_access_levels: [{ access_level: found.push }],
138
- merge_access_levels: [{ access_level: found.merge }],
139
+ push_access_levels: [{
140
+ id: 1,
141
+ access_level: found.push
142
+ }],
143
+ merge_access_levels: [{
144
+ id: 2,
145
+ access_level: found.merge
146
+ }],
139
147
  allow_force_push: found.forcePush
140
148
  });
141
149
  if (request.method === "DELETE") {
142
150
  if (accessLevel < 40) return forbidden();
143
151
  protectedBranches.delete(name);
152
+ protectionChanges.push(`DELETE ${name}`);
144
153
  return new Response(null, { status: 204 });
145
154
  }
155
+ if (request.method === "PATCH" && !options.noProtectionChanges) {
156
+ if (accessLevel < 40) return forbidden();
157
+ const body = await request.json();
158
+ const levelFrom = (levels, id, now) => {
159
+ if (!levels) return now;
160
+ const kept = levels.some((level) => level.id === id && level._destroy) ? [] : [now];
161
+ const added = levels.flatMap((level) => level.access_level === void 0 ? [] : [level.access_level]);
162
+ return kept.length + added.length === 1 ? kept[0] ?? added[0] ?? now : void 0;
163
+ };
164
+ const push = levelFrom(body.allowed_to_push, 1, found.push);
165
+ const merge = levelFrom(body.allowed_to_merge, 2, found.merge);
166
+ if (push === void 0 || merge === void 0) return json({ message: "Only one access level allowed" }, 422);
167
+ protectedBranches.set(name, {
168
+ push,
169
+ merge,
170
+ forcePush: body.allow_force_push ?? found.forcePush
171
+ });
172
+ protectionChanges.push(`PATCH ${name}`);
173
+ return json({ name });
174
+ }
146
175
  }
147
176
  if (rest === "/protected_branches" && request.method === "POST") {
148
177
  if (accessLevel < 40) return forbidden();
149
178
  const body = await request.json();
150
179
  if (protectedBranches.has(body.name)) return json({ message: "Protected branch already exists" }, 409);
180
+ protectionChanges.push(`POST ${body.name}`);
151
181
  protectedBranches.set(body.name, {
152
182
  push: body.push_access_level ?? 40,
153
183
  merge: body.merge_access_level ?? 40,
@@ -898,6 +928,7 @@ function fakeGitLab(options) {
898
928
  members,
899
929
  invitations,
900
930
  protectedBranches,
931
+ protectionChanges,
901
932
  failBuild,
902
933
  cancelBuild,
903
934
  /** The project's update settings, and the pipelines the update ran in, which tests add with `addUpdateRun()`. */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@goodfellow-cms/gitlab",
3
- "version": "0.8.0",
4
- "description": "Edit a Goodfellow site stored on GitLab, straight from the browser.",
3
+ "version": "0.9.0",
4
+ "description": "Edit a Goodfellow CMS site stored on GitLab, straight from the browser.",
5
5
  "keywords": [
6
6
  "goodfellow",
7
7
  "cms",
@@ -12,6 +12,10 @@
12
12
  ],
13
13
  "homepage": "https://goodfellow-cms.github.io/goodfellow-cms/docs/hosts",
14
14
  "bugs": "https://github.com/goodfellow-cms/goodfellow-cms/issues",
15
+ "funding": {
16
+ "type": "github",
17
+ "url": "https://github.com/sponsors/goodfellow-cms"
18
+ },
15
19
  "license": "MIT",
16
20
  "repository": {
17
21
  "type": "git",
@@ -35,7 +39,7 @@
35
39
  "dist"
36
40
  ],
37
41
  "dependencies": {
38
- "@goodfellow-cms/core": "0.8.0"
42
+ "@goodfellow-cms/core": "0.9.0"
39
43
  },
40
44
  "devDependencies": {
41
45
  "@puckeditor/core": "0.23.0"