@goodfellow-cms/gitlab 0.2.0 → 0.3.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.d.ts +9 -1
- package/dist/index.js +128 -2
- package/dist/testing.d.ts +3 -1
- package/dist/testing.js +80 -4
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ConnectDomainOptions, DeployStatus, DnsRecord, FileChange, FileVersion, GitBackend, GitHost, GitUser, HistoryOptions, PagesDomains, SetupHost, SiteEditors, SiteUpdates, StorageLike, WriteOptions } from "@goodfellow-cms/core";
|
|
1
|
+
import { ConnectDomainOptions, DeployStatus, DnsRecord, FileChange, FileVersion, GitBackend, GitHost, GitUser, HistoryOptions, PagesDomains, SetupHost, SiteDrafts, SiteEditors, SiteUpdates, StorageLike, WriteOptions } from "@goodfellow-cms/core";
|
|
2
2
|
//#region src/api.d.ts
|
|
3
3
|
interface ApiOptions {
|
|
4
4
|
/** The API root, such as `https://gitlab.com/api/v4`. */
|
|
@@ -30,6 +30,8 @@ export declare class GitLabBackend implements GitBackend {
|
|
|
30
30
|
readonly pages: PagesDomains;
|
|
31
31
|
readonly editors: SiteEditors;
|
|
32
32
|
readonly updates: SiteUpdates;
|
|
33
|
+
readonly drafts: SiteDrafts;
|
|
34
|
+
private readonly options;
|
|
33
35
|
private readonly api;
|
|
34
36
|
private readonly project;
|
|
35
37
|
private readonly branch;
|
|
@@ -38,6 +40,8 @@ export declare class GitLabBackend implements GitBackend {
|
|
|
38
40
|
private readonly trees;
|
|
39
41
|
private readonly blobs;
|
|
40
42
|
constructor(options: GitLabBackendOptions);
|
|
43
|
+
/** Drafts on branches of the project, with merge requests for review. */
|
|
44
|
+
private draftHost;
|
|
41
45
|
private repo;
|
|
42
46
|
revision(): Promise<string>;
|
|
43
47
|
/** Every file in one folder at a commit, page by page. */
|
|
@@ -46,8 +50,12 @@ export declare class GitLabBackend implements GitBackend {
|
|
|
46
50
|
private tree;
|
|
47
51
|
private currentTree;
|
|
48
52
|
read(path: string): Promise<string | undefined>;
|
|
53
|
+
/** Reads many text files in a few GraphQL requests rather than one request each. */
|
|
54
|
+
readFiles(paths: string[]): Promise<Map<string, string>>;
|
|
49
55
|
readBytes(path: string): Promise<Uint8Array | undefined>;
|
|
50
56
|
list(dir: string): Promise<string[]>;
|
|
57
|
+
/** GitLab's tree doesn't say how large files are, so they're asked for with GraphQL, in batches. */
|
|
58
|
+
fileSizes(dir: string): Promise<Map<string, number>>;
|
|
51
59
|
private lastCommitFor;
|
|
52
60
|
write(changes: FileChange[], { message, expectedRevision }: WriteOptions): Promise<{
|
|
53
61
|
revision: string;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ConflictError, DomainError, EDITABLE_FOLDERS, EditorsError, GitApiError, SetupError, SignInError, UpdatesError, buildStepFor, credentialStorage, encodeBase64Bytes, findBuildCause, findUpdateResult } from "@goodfellow-cms/core";
|
|
1
|
+
import { ConflictError, DomainError, DraftError, EDITABLE_FOLDERS, EditorsError, GitApiError, SetupError, SignInError, UpdatesError, branchDrafts, buildStepFor, credentialStorage, encodeBase64Bytes, findBuildCause, findUpdateResult } from "@goodfellow-cms/core";
|
|
2
2
|
//#region src/api.ts
|
|
3
3
|
/** Calls the GitLab API. A 401 refreshes the token once; after that it means the sign-in has expired. */
|
|
4
4
|
async function gitlabRequest(api, path, init = {}) {
|
|
@@ -427,6 +427,8 @@ function gitlabUpdates(api, project, branch, readAt) {
|
|
|
427
427
|
//#endregion
|
|
428
428
|
//#region src/backend.ts
|
|
429
429
|
const PAGE_SIZE = 100;
|
|
430
|
+
/** How many files one GraphQL request reads. */
|
|
431
|
+
const GRAPHQL_BATCH = 50;
|
|
430
432
|
/** The color and section codes in GitLab's job logs. */
|
|
431
433
|
const ANSI_CODES = new RegExp(`${String.fromCharCode(27)}\\[[\\d;]*[A-Za-z]`, "g");
|
|
432
434
|
/**
|
|
@@ -434,11 +436,13 @@ const ANSI_CODES = new RegExp(`${String.fromCharCode(27)}\\[[\\d;]*[A-Za-z]`, "g
|
|
|
434
436
|
* returned. Saves are single commits; each changed file carries the commit it
|
|
435
437
|
* was last changed in, so GitLab refuses to overwrite anyone else's edits.
|
|
436
438
|
*/
|
|
437
|
-
var GitLabBackend = class {
|
|
439
|
+
var GitLabBackend = class GitLabBackend {
|
|
438
440
|
user;
|
|
439
441
|
pages;
|
|
440
442
|
editors;
|
|
441
443
|
updates;
|
|
444
|
+
drafts;
|
|
445
|
+
options;
|
|
442
446
|
api;
|
|
443
447
|
project;
|
|
444
448
|
branch;
|
|
@@ -447,6 +451,7 @@ var GitLabBackend = class {
|
|
|
447
451
|
trees = /* @__PURE__ */ new Map();
|
|
448
452
|
blobs = /* @__PURE__ */ new Map();
|
|
449
453
|
constructor(options) {
|
|
454
|
+
this.options = options;
|
|
450
455
|
this.api = options.api;
|
|
451
456
|
this.project = encodeURIComponent(options.project);
|
|
452
457
|
this.branch = options.branch;
|
|
@@ -455,6 +460,67 @@ var GitLabBackend = class {
|
|
|
455
460
|
this.pages = gitlabPages(this.api, options.project);
|
|
456
461
|
this.editors = gitlabEditors(this.api, options.project, this.branch, this.user.login);
|
|
457
462
|
this.updates = gitlabUpdates(this.api, options.project, this.branch, async (path) => this.readAt(path, this.pinned ?? await this.revision()));
|
|
463
|
+
this.drafts = branchDrafts(this, this.draftHost());
|
|
464
|
+
}
|
|
465
|
+
/** Drafts on branches of the project, with merge requests for review. */
|
|
466
|
+
draftHost() {
|
|
467
|
+
const project = `/projects/${this.project}`;
|
|
468
|
+
return {
|
|
469
|
+
liveBranch: this.branch,
|
|
470
|
+
branchStore: (branch) => new GitLabBackend({
|
|
471
|
+
...this.options,
|
|
472
|
+
branch
|
|
473
|
+
}),
|
|
474
|
+
createBranch: async (branch, revision) => {
|
|
475
|
+
if ((await gitlabRequest(this.api, this.repo(`/branches?${new URLSearchParams({
|
|
476
|
+
branch,
|
|
477
|
+
ref: revision
|
|
478
|
+
})}`), {
|
|
479
|
+
method: "POST",
|
|
480
|
+
allow: [400]
|
|
481
|
+
})).status === 400) throw new DraftError("exists", `There's already a draft called ${branch}.`);
|
|
482
|
+
},
|
|
483
|
+
deleteBranch: async (branch) => {
|
|
484
|
+
await gitlabRequest(this.api, this.repo(`/branches/${encodeURIComponent(branch)}`), {
|
|
485
|
+
method: "DELETE",
|
|
486
|
+
allow: [404]
|
|
487
|
+
});
|
|
488
|
+
},
|
|
489
|
+
mergeBase: async (branch) => {
|
|
490
|
+
const query = new URLSearchParams();
|
|
491
|
+
query.append("refs[]", this.branch);
|
|
492
|
+
query.append("refs[]", branch);
|
|
493
|
+
const response = await gitlabRequest(this.api, this.repo(`/merge_base?${query}`), { allow: [400, 404] });
|
|
494
|
+
if (!response.ok) throw new DraftError("not-found", `There's no draft ${branch}.`);
|
|
495
|
+
return (await response.json()).id;
|
|
496
|
+
},
|
|
497
|
+
openReview: async ({ branch, title, description }) => {
|
|
498
|
+
const response = await gitlabRequest(this.api, `${project}/merge_requests`, {
|
|
499
|
+
method: "POST",
|
|
500
|
+
body: {
|
|
501
|
+
source_branch: branch,
|
|
502
|
+
target_branch: this.branch,
|
|
503
|
+
title,
|
|
504
|
+
description,
|
|
505
|
+
remove_source_branch: true
|
|
506
|
+
},
|
|
507
|
+
allow: [
|
|
508
|
+
403,
|
|
509
|
+
404,
|
|
510
|
+
409
|
|
511
|
+
]
|
|
512
|
+
});
|
|
513
|
+
if (response.ok) return { url: (await response.json()).web_url };
|
|
514
|
+
if (response.status !== 409) throw new DraftError("no-reviews", "This sign-in can't open merge requests on the site's project.");
|
|
515
|
+
const query = new URLSearchParams({
|
|
516
|
+
source_branch: branch,
|
|
517
|
+
state: "opened"
|
|
518
|
+
});
|
|
519
|
+
const [open] = await gitlabJson(this.api, `${project}/merge_requests?${query}`);
|
|
520
|
+
if (!open) throw new GitApiError(409, "GitLab couldn't open the merge request.");
|
|
521
|
+
return { url: open.web_url };
|
|
522
|
+
}
|
|
523
|
+
};
|
|
458
524
|
}
|
|
459
525
|
repo(path) {
|
|
460
526
|
return `/projects/${this.project}/repository${path}`;
|
|
@@ -506,6 +572,39 @@ var GitLabBackend = class {
|
|
|
506
572
|
}
|
|
507
573
|
return blob;
|
|
508
574
|
}
|
|
575
|
+
/** Reads many text files in a few GraphQL requests rather than one request each. */
|
|
576
|
+
async readFiles(paths) {
|
|
577
|
+
const tree = await this.currentTree();
|
|
578
|
+
const wanted = paths.filter((path) => tree.has(path));
|
|
579
|
+
const fetch = wanted.filter((path) => !this.blobs.has(tree.get(path) ?? ""));
|
|
580
|
+
const ref = this.pinned ?? await this.revision();
|
|
581
|
+
for (let start = 0; start < fetch.length; start += GRAPHQL_BATCH) {
|
|
582
|
+
const batch = fetch.slice(start, start + GRAPHQL_BATCH);
|
|
583
|
+
const response = await gitlabJson(this.api, "/../graphql", {
|
|
584
|
+
method: "POST",
|
|
585
|
+
body: {
|
|
586
|
+
query: `query ($project: ID!, $ref: String!, $paths: [String!]!) {
|
|
587
|
+
project(fullPath: $project) { repository { blobs(ref: $ref, paths: $paths) { nodes { path rawTextBlob } } } }
|
|
588
|
+
}`,
|
|
589
|
+
variables: {
|
|
590
|
+
project: decodeURIComponent(this.project),
|
|
591
|
+
ref,
|
|
592
|
+
paths: batch
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
for (const node of response.data?.project?.repository?.blobs?.nodes ?? []) {
|
|
597
|
+
const sha = tree.get(node.path);
|
|
598
|
+
if (sha && typeof node.rawTextBlob === "string") this.blobs.set(sha, Promise.resolve(node.rawTextBlob));
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
const result = /* @__PURE__ */ new Map();
|
|
602
|
+
for (const path of wanted) {
|
|
603
|
+
const text = await this.read(path);
|
|
604
|
+
if (text !== void 0) result.set(path, text);
|
|
605
|
+
}
|
|
606
|
+
return result;
|
|
607
|
+
}
|
|
509
608
|
async readBytes(path) {
|
|
510
609
|
const sha = (await this.currentTree()).get(path);
|
|
511
610
|
if (!sha) return void 0;
|
|
@@ -516,6 +615,32 @@ var GitLabBackend = class {
|
|
|
516
615
|
const prefix = `${dir}/`;
|
|
517
616
|
return [...(await this.currentTree()).keys()].filter((path) => path.startsWith(prefix)).sort();
|
|
518
617
|
}
|
|
618
|
+
/** GitLab's tree doesn't say how large files are, so they're asked for with GraphQL, in batches. */
|
|
619
|
+
async fileSizes(dir) {
|
|
620
|
+
const paths = await this.list(dir);
|
|
621
|
+
const ref = this.pinned ?? await this.revision();
|
|
622
|
+
const sizes = /* @__PURE__ */ new Map();
|
|
623
|
+
for (let start = 0; start < paths.length; start += GRAPHQL_BATCH) {
|
|
624
|
+
const response = await gitlabJson(this.api, "/../graphql", {
|
|
625
|
+
method: "POST",
|
|
626
|
+
body: {
|
|
627
|
+
query: `query ($project: ID!, $ref: String!, $paths: [String!]!) {
|
|
628
|
+
project(fullPath: $project) { repository { blobs(ref: $ref, paths: $paths) { nodes { path size } } } }
|
|
629
|
+
}`,
|
|
630
|
+
variables: {
|
|
631
|
+
project: decodeURIComponent(this.project),
|
|
632
|
+
ref,
|
|
633
|
+
paths: paths.slice(start, start + GRAPHQL_BATCH)
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
for (const node of response.data?.project?.repository?.blobs?.nodes ?? []) {
|
|
638
|
+
const size = Number(node.size);
|
|
639
|
+
if (Number.isFinite(size)) sizes.set(node.path, size);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
return sizes;
|
|
643
|
+
}
|
|
519
644
|
async lastCommitFor(path, ref) {
|
|
520
645
|
return (await gitlabJson(this.api, this.repo(`/files/${encodeURIComponent(path)}?ref=${ref}`))).last_commit_id;
|
|
521
646
|
}
|
|
@@ -970,6 +1095,7 @@ function gitlab(options) {
|
|
|
970
1095
|
return {
|
|
971
1096
|
name: "GitLab",
|
|
972
1097
|
canRedirect: Boolean(options.clientId),
|
|
1098
|
+
redirectAddressesListed: Boolean(options.clientId),
|
|
973
1099
|
tokenLinks: [{
|
|
974
1100
|
url: `${webUrl}/-/user_settings/personal_access_tokens?${new URLSearchParams({
|
|
975
1101
|
name: "Goodfellow admin",
|
package/dist/testing.d.ts
CHANGED
|
@@ -65,6 +65,8 @@ export interface FakeCreatedProject {
|
|
|
65
65
|
repo: FakeRepo;
|
|
66
66
|
}
|
|
67
67
|
export declare function fakeGitLab(options: FakeGitLabOptions): {
|
|
68
|
+
/** How many GraphQL requests read files, as `readFiles()` makes. */
|
|
69
|
+
readonly graphqlReads: number;
|
|
68
70
|
/** The project's members by user id, invitations by email, and protected branches, which tests can change. */
|
|
69
71
|
members: Map<number, FakeMember>;
|
|
70
72
|
invitations: Map<string, number>;
|
|
@@ -122,7 +124,7 @@ export declare function fakeGitLab(options: FakeGitLabOptions): {
|
|
|
122
124
|
/** The site's GitLab Pages, if it has them, which tests can change. */
|
|
123
125
|
pages: FakeGitLabPages | undefined;
|
|
124
126
|
requests: Request[];
|
|
125
|
-
commit: (changes: FileChange[], message: string, author?: string) => string;
|
|
127
|
+
commit: (changes: FileChange[], message: string, author?: string, branch?: string) => string;
|
|
126
128
|
authorize: (authorizeUrl: string) => string;
|
|
127
129
|
handle: (request: Request) => Promise<Response>;
|
|
128
130
|
fetch: typeof fetch;
|
package/dist/testing.js
CHANGED
|
@@ -187,6 +187,7 @@ function fakeGitLab(options) {
|
|
|
187
187
|
const pipelineId = (sha) => [...pipelines.keys()].indexOf(sha) + 1;
|
|
188
188
|
const requests = [];
|
|
189
189
|
let counter = 0;
|
|
190
|
+
let graphqlReads = 0;
|
|
190
191
|
const created = /* @__PURE__ */ new Map();
|
|
191
192
|
const pages = options.pages ? {
|
|
192
193
|
uniqueDomain: options.pages.uniqueDomain ?? true,
|
|
@@ -330,9 +331,12 @@ function fakeGitLab(options) {
|
|
|
330
331
|
}
|
|
331
332
|
return json({ message: "404 Not Found" }, 404);
|
|
332
333
|
}
|
|
333
|
-
function commit(changes, message, author) {
|
|
334
|
-
const sha = repo.commit(changes, message, {
|
|
335
|
-
|
|
334
|
+
function commit(changes, message, author, branch = repo.defaultBranch) {
|
|
335
|
+
const sha = repo.commit(changes, message, {
|
|
336
|
+
author,
|
|
337
|
+
branch
|
|
338
|
+
});
|
|
339
|
+
if (options.deployAfterChecks !== void 0 && branch === repo.defaultBranch) pipelines.set(sha, { checks: 0 });
|
|
336
340
|
return sha;
|
|
337
341
|
}
|
|
338
342
|
function issueTokens(user) {
|
|
@@ -390,6 +394,22 @@ function fakeGitLab(options) {
|
|
|
390
394
|
}
|
|
391
395
|
return json({ error: "unsupported_grant_type" }, 400);
|
|
392
396
|
}
|
|
397
|
+
if (url.pathname === "/api/graphql" && request.method === "POST") {
|
|
398
|
+
if (!tokens.has(request.headers.get("authorization")?.replace(/^Bearer /, "") ?? "")) return json({ message: "401 Unauthorized" }, 401);
|
|
399
|
+
const { variables } = await request.json();
|
|
400
|
+
if (variables.project !== options.project) return json({ data: { project: null } });
|
|
401
|
+
graphqlReads += 1;
|
|
402
|
+
const files = repo.files(variables.ref);
|
|
403
|
+
return json({ data: { project: { repository: { blobs: { nodes: variables.paths.flatMap((path) => {
|
|
404
|
+
const content = files.get(path);
|
|
405
|
+
if (content === void 0) return [];
|
|
406
|
+
return [{
|
|
407
|
+
path,
|
|
408
|
+
rawTextBlob: typeof content === "string" ? content : null,
|
|
409
|
+
size: String(fakeFileBytes(content).length)
|
|
410
|
+
}];
|
|
411
|
+
}) } } } } });
|
|
412
|
+
}
|
|
393
413
|
if (!url.pathname.startsWith("/api/v4/")) return json({ message: "404 Not Found" }, 404);
|
|
394
414
|
const user = tokens.get(request.headers.get("authorization")?.replace(/^Bearer /, "") ?? "");
|
|
395
415
|
if (!user) return json({ message: "401 Unauthorized" }, 401);
|
|
@@ -401,6 +421,14 @@ function fakeGitLab(options) {
|
|
|
401
421
|
});
|
|
402
422
|
const setup = await handleCreated(request, path, user);
|
|
403
423
|
if (setup) return setup;
|
|
424
|
+
if (path === "/projects" && request.method === "GET") {
|
|
425
|
+
const level = members.get(accounts.get(user.username) ?? 0)?.accessLevel ?? 0;
|
|
426
|
+
const min = Number(url.searchParams.get("min_access_level") ?? "0");
|
|
427
|
+
return json(level > 0 && level >= min ? [{
|
|
428
|
+
path_with_namespace: options.project,
|
|
429
|
+
default_branch: repo.defaultBranch
|
|
430
|
+
}] : []);
|
|
431
|
+
}
|
|
404
432
|
if (path === "/users") {
|
|
405
433
|
const name = url.searchParams.get("username") ?? "";
|
|
406
434
|
const id = [...accounts].find(([username]) => username.toLowerCase() === name.toLowerCase())?.[1];
|
|
@@ -425,11 +453,55 @@ function fakeGitLab(options) {
|
|
|
425
453
|
},
|
|
426
454
|
ci_push_repository_for_job_token_allowed: updateSettings.jobTokenPush
|
|
427
455
|
});
|
|
456
|
+
if (rest === "/repository/branches" && request.method === "POST") {
|
|
457
|
+
if (accessLevel < 30) return json({ message: "403 Forbidden" }, 403);
|
|
458
|
+
const name = url.searchParams.get("branch") ?? "";
|
|
459
|
+
const ref = url.searchParams.get("ref") ?? "";
|
|
460
|
+
if (repo.branches.has(name)) return json({ message: "Branch already exists" }, 400);
|
|
461
|
+
const sha = repo.branches.get(ref) ?? (repo.commitAt(ref) ? ref : void 0);
|
|
462
|
+
if (!sha) return json({ message: "Invalid reference name" }, 400);
|
|
463
|
+
repo.branches.set(name, sha);
|
|
464
|
+
return json({
|
|
465
|
+
name,
|
|
466
|
+
commit: { id: sha }
|
|
467
|
+
}, 201);
|
|
468
|
+
}
|
|
428
469
|
const branch = rest.match(/^\/repository\/branches\/(.+)$/);
|
|
470
|
+
if (branch && request.method === "DELETE") {
|
|
471
|
+
if (accessLevel < 30) return json({ message: "403 Forbidden" }, 403);
|
|
472
|
+
const name = decodeURIComponent(branch[1] ?? "");
|
|
473
|
+
if (!repo.branches.has(name)) return json({ message: "404 Branch Not Found" }, 404);
|
|
474
|
+
repo.branches.delete(name);
|
|
475
|
+
return new Response(null, { status: 204 });
|
|
476
|
+
}
|
|
429
477
|
if (branch) {
|
|
430
478
|
const sha = repo.branches.get(decodeURIComponent(branch[1] ?? ""));
|
|
431
479
|
return sha ? json({ commit: { id: sha } }) : json({ message: "404 Branch Not Found" }, 404);
|
|
432
480
|
}
|
|
481
|
+
if (rest === "/repository/merge_base") {
|
|
482
|
+
const refs = url.searchParams.getAll("refs[]").map((ref) => repo.branches.get(ref) ?? ref);
|
|
483
|
+
if (refs.length !== 2 || refs.some((ref) => !repo.commitAt(ref))) return json({ message: "Could not find ref" }, 400);
|
|
484
|
+
const base = repo.mergeBase(refs[0] ?? "", refs[1] ?? "");
|
|
485
|
+
return base ? json({ id: base }) : json({ message: "404 Not Found" }, 404);
|
|
486
|
+
}
|
|
487
|
+
if (rest === "/merge_requests" && request.method === "POST") {
|
|
488
|
+
if (accessLevel < 30) return json({ message: "403 Forbidden" }, 403);
|
|
489
|
+
const body = await request.json();
|
|
490
|
+
if (!repo.branches.has(body.source_branch) || !repo.branches.has(body.target_branch)) return json({ message: ["Source branch does not exist"] }, 422);
|
|
491
|
+
if (repo.reviews.some((review) => review.branch === body.source_branch && review.state === "open")) return json({ message: ["Another open merge request already exists for this source branch"] }, 409);
|
|
492
|
+
const review = repo.openReview(body.source_branch, body.title, body.description ?? "", body.target_branch);
|
|
493
|
+
return json({
|
|
494
|
+
iid: review.number,
|
|
495
|
+
web_url: `${webUrl}/${options.project}/-/merge_requests/${review.number}`
|
|
496
|
+
}, 201);
|
|
497
|
+
}
|
|
498
|
+
if (rest === "/merge_requests" && request.method === "GET") {
|
|
499
|
+
const source = url.searchParams.get("source_branch");
|
|
500
|
+
return json(repo.reviews.filter((review) => review.state === "open" && (!source || review.branch === source)).map((review) => ({
|
|
501
|
+
iid: review.number,
|
|
502
|
+
web_url: `${webUrl}/${options.project}/-/merge_requests/${review.number}`
|
|
503
|
+
})));
|
|
504
|
+
}
|
|
433
505
|
if (rest === "/repository/tree") {
|
|
434
506
|
const ref = url.searchParams.get("ref") ?? repo.defaultBranch;
|
|
435
507
|
const dir = url.searchParams.get("path") ?? "";
|
|
@@ -487,7 +559,7 @@ function fakeGitLab(options) {
|
|
|
487
559
|
} : {
|
|
488
560
|
path: action.file_path,
|
|
489
561
|
content: action.content ?? ""
|
|
490
|
-
}), body.commit_message, user.name ?? user.username),
|
|
562
|
+
}), body.commit_message, user.name ?? user.username, body.branch),
|
|
491
563
|
message: body.commit_message
|
|
492
564
|
}, 201);
|
|
493
565
|
}
|
|
@@ -617,6 +689,10 @@ function fakeGitLab(options) {
|
|
|
617
689
|
return json({ message: "404 Not Found" }, 404);
|
|
618
690
|
}
|
|
619
691
|
return {
|
|
692
|
+
/** How many GraphQL requests read files, as `readFiles()` makes. */
|
|
693
|
+
get graphqlReads() {
|
|
694
|
+
return graphqlReads;
|
|
695
|
+
},
|
|
620
696
|
/** The project's members by user id, invitations by email, and protected branches, which tests can change. */
|
|
621
697
|
members,
|
|
622
698
|
invitations,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodfellow-cms/gitlab",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Edit a Goodfellow site stored on GitLab, straight from the browser.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"goodfellow",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"dist"
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@goodfellow-cms/core": "0.
|
|
38
|
+
"@goodfellow-cms/core": "0.3.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@puckeditor/core": "0.23.0"
|