@procedures/git 0.8.1 → 0.9.1

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.
@@ -0,0 +1,443 @@
1
+ /**
2
+ * Git under the root of the files of a host (roadmap R33, tracker item O35).
3
+ *
4
+ * A host with a root (`confineToRoot()` of `@procedures/fs`) examines the path inputs of each
5
+ * `git.*` procedure, and it gives the handler the root in `ctx.fsRoot`. The handler then runs in
6
+ * `underGitRoot()`, and each `git()` call of the handler gets these changes:
7
+ *
8
+ * - The working folder must be in the root. `GIT_CEILING_DIRECTORIES` is the parent of the root, so
9
+ * git does not look for a repository above the root.
10
+ * - The configuration of the system does not apply (`GIT_CONFIG_NOSYSTEM`). The global
11
+ * configuration is a file of this process with only the identity of the user of the host
12
+ * (`GIT_CONFIG_GLOBAL`). The variables that move the repository (`GIT_DIR`, `GIT_WORK_TREE` and
13
+ * the others) do not apply.
14
+ * - The options of `SAFE_CONFIG` turn off the hooks, `core.fsmonitor`, the transports `file` and
15
+ * `ext` (and each transport other than https, http, ssh and git), and the recursion into
16
+ * submodules.
17
+ * - Before the first git command in a folder, `examineRepository()` examines the repository: its
18
+ * folders must be in the root, and its configuration must not start a program
19
+ * (`refusedConfigKeys()`). A repository with submodules is refused.
20
+ *
21
+ * A refusal is `FORBIDDEN` with the reason `outside-root`. Without a root, nothing changes.
22
+ */
23
+ import { AsyncLocalStorage } from "node:async_hooks";
24
+ import { rmSync } from "node:fs";
25
+ import { access, mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
26
+ import { tmpdir } from "node:os";
27
+ import { delimiter, dirname, join, resolve } from "node:path";
28
+ import { inRoot, isInside, outsideRoot, realPathOf, resolveRoot } from "@procedures/fs/root";
29
+ import { runCommand } from "@procedures/shell/command";
30
+ const roots = new AsyncLocalStorage();
31
+ /**
32
+ * This function runs the handler of a git procedure. With a root, each `git()` call of the handler
33
+ * stays in the root. Without a root, it only runs the handler.
34
+ */
35
+ export async function underGitRoot(folder, procedure, handler) {
36
+ if (folder === undefined)
37
+ return handler();
38
+ const root = await resolveRoot(folder, "the git procedures");
39
+ return roots.run({ root, procedure, examined: new Map() }, handler);
40
+ }
41
+ /** This function gives the root of the current call, or undefined without a root. */
42
+ export function currentGitRoot() {
43
+ return roots.getStore();
44
+ }
45
+ /**
46
+ * This function gives the absolute path of a path that a git procedure makes from its input, for
47
+ * example the folder of a clone. Under a root, a path outside the root is refused.
48
+ */
49
+ export async function pathInGitRoot(path) {
50
+ const current = currentGitRoot();
51
+ if (current === undefined)
52
+ return resolve(path);
53
+ return inRoot(current.root, path, current.procedure);
54
+ }
55
+ /**
56
+ * This function refuses, under a root, the URL of a remote that starts a program: the form
57
+ * `<transport>::<address>` starts the remote helper `git-remote-<transport>`, and `ext::` starts
58
+ * a command.
59
+ */
60
+ export function remoteUrlInGitRoot(url) {
61
+ const current = currentGitRoot();
62
+ if (current !== undefined && url.includes("::")) {
63
+ throw outsideRoot(current.procedure, `under the root ${current.root.folder}, a remote URL with "::" is refused, because it starts a program: ${url}`);
64
+ }
65
+ return url;
66
+ }
67
+ // =============================================================================
68
+ // The command line and the environment of git under a root
69
+ // =============================================================================
70
+ /**
71
+ * The configuration of each git command under a root, as `-c` options. A `-c` option overrides each
72
+ * configuration file. Git gives these options to the git commands that it starts.
73
+ */
74
+ export function safeConfig(hooks) {
75
+ return [
76
+ // No hook runs: the folder is empty
77
+ `core.hooksPath=${hooks}`,
78
+ "core.fsmonitor=false",
79
+ // Only the network transports: no local path, no file://, no ext:: and no remote helper. The
80
+ // rule of a transport overrides protocol.allow, so file and ext get their own rule.
81
+ "protocol.allow=never",
82
+ "protocol.file.allow=never",
83
+ "protocol.ext.allow=never",
84
+ "protocol.https.allow=always",
85
+ "protocol.http.allow=always",
86
+ "protocol.ssh.allow=always",
87
+ "protocol.git.allow=always",
88
+ // Git does not start git in a submodule
89
+ "diff.ignoreSubmodules=all",
90
+ "submodule.recurse=false",
91
+ "fetch.recurseSubmodules=false",
92
+ "push.recurseSubmodules=no",
93
+ "status.submoduleSummary=false",
94
+ ].flatMap((option) => ["-c", option]);
95
+ }
96
+ /**
97
+ * The variables of the environment that move the repository, add configuration or allow a
98
+ * transport. Under a root, git does not get them.
99
+ */
100
+ const REMOVED_VARIABLES = [
101
+ "GIT_DIR",
102
+ "GIT_WORK_TREE",
103
+ "GIT_COMMON_DIR",
104
+ "GIT_INDEX_FILE",
105
+ "GIT_OBJECT_DIRECTORY",
106
+ "GIT_ALTERNATE_OBJECT_DIRECTORIES",
107
+ "GIT_NAMESPACE",
108
+ "GIT_IMPLICIT_WORK_TREE",
109
+ "GIT_PREFIX",
110
+ "GIT_GRAFT_FILE",
111
+ "GIT_SHALLOW_FILE",
112
+ "GIT_CONFIG",
113
+ "GIT_CONFIG_SYSTEM",
114
+ "GIT_CONFIG_PARAMETERS",
115
+ "GIT_CONFIG_COUNT",
116
+ "GIT_EXTERNAL_DIFF",
117
+ "GIT_TEMPLATE_DIR",
118
+ "GIT_ATTR_SOURCE",
119
+ "GIT_ALLOW_PROTOCOL",
120
+ ];
121
+ /** The folders of `GIT_CEILING_DIRECTORIES` for a root: the parent of the root, as given and as its real path. */
122
+ export function ceilingOf(root) {
123
+ const parents = new Set();
124
+ for (const folder of [root.folder, root.real]) {
125
+ const parent = dirname(folder);
126
+ // A root at the top of a drive has no parent
127
+ if (parent !== folder)
128
+ parents.add(parent);
129
+ }
130
+ return [...parents].join(delimiter);
131
+ }
132
+ /** This function gives the environment of git under a root. */
133
+ export function rootEnvironment(root, setup) {
134
+ const env = {};
135
+ for (const name of REMOVED_VARIABLES)
136
+ env[name] = undefined;
137
+ env["GIT_CEILING_DIRECTORIES"] = ceilingOf(root);
138
+ env["GIT_CONFIG_NOSYSTEM"] = "1";
139
+ env["GIT_CONFIG_GLOBAL"] = setup.globalConfig;
140
+ // No prompt for a password: the call has no terminal
141
+ env["GIT_TERMINAL_PROMPT"] = "0";
142
+ return env;
143
+ }
144
+ /** The keys of the global configuration that git under a root keeps. They start no program. */
145
+ const KEPT_GLOBAL_KEYS = ["user.name", "user.email", "init.defaultBranch"];
146
+ let setup;
147
+ /** The folder of the files of git under a root, when they exist. */
148
+ let setupFolder;
149
+ /**
150
+ * This function makes the files of git under a root one time for each process: a new folder in
151
+ * the temporary folder, with an empty hooks folder and a global configuration. The configuration
152
+ * has only the identity of the user from the global configuration of the host, so a commit works.
153
+ * The process deletes the folder at its end (`removeGitRootSetup()`).
154
+ */
155
+ export function gitRootSetup() {
156
+ setup ??= makeSetup();
157
+ return setup;
158
+ }
159
+ /**
160
+ * This function deletes the files of git under a root. The process starts it at its end. The next
161
+ * git command under a root makes the files again.
162
+ */
163
+ export function removeGitRootSetup() {
164
+ if (setupFolder !== undefined)
165
+ rmSync(setupFolder, { recursive: true, force: true });
166
+ setupFolder = undefined;
167
+ setup = undefined;
168
+ }
169
+ async function makeSetup() {
170
+ try {
171
+ const folder = await mkdtemp(join(tmpdir(), "procedures-git-root-"));
172
+ setupFolder = folder;
173
+ process.once("exit", removeGitRootSetup);
174
+ const hooks = join(folder, "hooks");
175
+ await mkdir(hooks);
176
+ const globalConfig = join(folder, "global.gitconfig");
177
+ await writeFile(globalConfig, "");
178
+ for (const key of KEPT_GLOBAL_KEYS) {
179
+ const value = await runCommand("git", { args: ["config", "--global", "--get", key], cwd: folder });
180
+ if (!value.success)
181
+ continue;
182
+ // Git writes the value, so a special character of the value stays correct
183
+ await runCommand("git", { args: ["config", "--file", globalConfig, key, value.stdout.replace(/\r?\n$/, "")], cwd: folder });
184
+ }
185
+ return { hooks, globalConfig };
186
+ }
187
+ catch (error) {
188
+ // The next call tries again
189
+ setup = undefined;
190
+ throw error;
191
+ }
192
+ }
193
+ // =============================================================================
194
+ // The configuration of a repository
195
+ // =============================================================================
196
+ /** The keys (section.name, or section.subsection.name as section.*.name) that start a program or name a file outside the repository. */
197
+ const REFUSED_KEYS = new Set([
198
+ // Programs
199
+ "core.sshcommand",
200
+ "core.gitproxy",
201
+ "core.askpass",
202
+ "core.editor",
203
+ "core.pager",
204
+ "core.alternaterefscommand",
205
+ "sequence.editor",
206
+ "diff.external",
207
+ "diff.tool",
208
+ "diff.guitool",
209
+ "merge.tool",
210
+ "merge.guitool",
211
+ "credential.helper",
212
+ "credential.*.helper",
213
+ "gpg.program",
214
+ "gpg.*.program",
215
+ "gpg.ssh.defaultkeycommand",
216
+ "filter.*.clean",
217
+ "filter.*.smudge",
218
+ "filter.*.process",
219
+ "diff.*.command",
220
+ "diff.*.textconv",
221
+ "merge.*.driver",
222
+ "difftool.*.cmd",
223
+ "difftool.*.path",
224
+ "mergetool.*.cmd",
225
+ "mergetool.*.path",
226
+ "remote.*.uploadpack",
227
+ "remote.*.receivepack",
228
+ "remote.*.vcs",
229
+ "uploadpack.packobjectshook",
230
+ "trailer.*.command",
231
+ "trailer.*.cmd",
232
+ // Files outside the repository
233
+ "core.worktree",
234
+ "core.excludesfile",
235
+ "core.attributesfile",
236
+ "commit.template",
237
+ "include.path",
238
+ "includeif.*.path",
239
+ "init.templatedir",
240
+ "mailmap.file",
241
+ "blame.ignorerevsfile",
242
+ "diff.orderfile",
243
+ "gpg.ssh.allowedsignersfile",
244
+ // A bundle URI can be a local file
245
+ "fetch.bundleuri",
246
+ "transfer.bundleuri",
247
+ ...["sslcert", "sslkey", "sslcainfo", "sslcapath", "cookiefile", "proxysslcert", "proxysslkey", "proxysslcainfo"].flatMap((name) => [
248
+ `http.${name}`,
249
+ `http.*.${name}`,
250
+ ]),
251
+ ]);
252
+ /**
253
+ * The sections that a root refuses as a whole: `pager.<command>` and the hooks of the
254
+ * configuration start a program, `protocol.*` allows a transport again, and a bundle URI can be
255
+ * a local file.
256
+ */
257
+ const REFUSED_SECTIONS = new Set(["pager", "hook", "protocol", "bundle"]);
258
+ /** The keys that start the signing program when they are not false. */
259
+ const SIGNING_KEYS = new Set(["commit.gpgsign", "tag.gpgsign", "tag.forcesignannotated", "push.gpgsign"]);
260
+ /** The values that turn off a boolean key. */
261
+ const FALSE_VALUES = new Set(["false", "no", "off", "0"]);
262
+ /** The parts of a configuration key: the section and the name in lower case, and the subsection. */
263
+ function partsOf(key) {
264
+ const first = key.indexOf(".");
265
+ const last = key.lastIndexOf(".");
266
+ if (first === -1)
267
+ return { section: key.toLowerCase(), subsection: undefined, name: "" };
268
+ return {
269
+ section: key.slice(0, first).toLowerCase(),
270
+ subsection: first === last ? undefined : key.slice(first + 1, last),
271
+ name: key.slice(last + 1).toLowerCase(),
272
+ };
273
+ }
274
+ /** True when a key of the configuration of a repository starts a program or names a file outside it. */
275
+ export function isRefusedConfigKey(key, value) {
276
+ const { section, subsection, name } = partsOf(key);
277
+ if (REFUSED_SECTIONS.has(section))
278
+ return true;
279
+ const pattern = subsection === undefined ? `${section}.${name}` : `${section}.*.${name}`;
280
+ if (REFUSED_KEYS.has(pattern))
281
+ return true;
282
+ // gpg.ssh.defaultKeyCommand and gpg.ssh.allowedSignersFile have a fixed subsection
283
+ if (subsection !== undefined && REFUSED_KEYS.has(`${section}.${subsection.toLowerCase()}.${name}`))
284
+ return true;
285
+ if (SIGNING_KEYS.has(pattern))
286
+ return !FALSE_VALUES.has((value ?? "true").trim().toLowerCase());
287
+ // A remote helper: `<transport>::<address>`, also as the base of url.<base>.insteadOf
288
+ if (section === "remote" && (name === "url" || name === "pushurl"))
289
+ return (value ?? "").includes("::");
290
+ if (section === "url" && (subsection ?? "").includes("::"))
291
+ return true;
292
+ // `!command` as the update of a submodule
293
+ if (section === "submodule" && name === "update")
294
+ return (value ?? "").trim().startsWith("!");
295
+ return false;
296
+ }
297
+ /** This function reads the output of `git config --list --show-scope -z`: the scope, then the key and the value. */
298
+ export function parseConfigList(output) {
299
+ const fields = output.split("\0");
300
+ const entries = [];
301
+ for (let i = 0; i + 1 < fields.length; i += 2) {
302
+ const scope = fields[i];
303
+ const entry = fields[i + 1];
304
+ const newline = entry.indexOf("\n");
305
+ entries.push(newline === -1 ? { scope, key: entry, value: undefined } : { scope, key: entry.slice(0, newline), value: entry.slice(newline + 1) });
306
+ }
307
+ return entries;
308
+ }
309
+ /**
310
+ * This function gives the keys of the configuration of a repository that a root refuses. It
311
+ * examines the files of the repository (the scopes `local` and `worktree`), not the global
312
+ * configuration of this process and not the `-c` options.
313
+ */
314
+ export function refusedConfigKeys(entries) {
315
+ const refused = new Set();
316
+ for (const entry of entries) {
317
+ if (entry.scope === "global" || entry.scope === "command")
318
+ continue;
319
+ if (isRefusedConfigKey(entry.key, entry.value))
320
+ refused.add(entry.key);
321
+ }
322
+ return [...refused];
323
+ }
324
+ // =============================================================================
325
+ // The examination of a repository
326
+ // =============================================================================
327
+ /** This function runs git for an examination, with the environment of the root. */
328
+ async function examine(args, cwd, env, hooks, input) {
329
+ return runCommand("git", { args: [...safeConfig(hooks), ...args], cwd, env, input });
330
+ }
331
+ /** True when the real path of a path is in the root. */
332
+ async function isInRoot(root, path) {
333
+ return isInside(root.real, await realPathOf(path));
334
+ }
335
+ /** True when a file or a folder exists. */
336
+ async function exists(path) {
337
+ return access(path).then(() => true, () => false);
338
+ }
339
+ /** True when a folder has the files of a git folder: `HEAD`, `objects` and `refs`, as a bare repository has. */
340
+ async function isGitFolder(folder) {
341
+ for (const name of ["HEAD", "objects", "refs"])
342
+ if (!(await exists(join(folder, name))))
343
+ return false;
344
+ return true;
345
+ }
346
+ /**
347
+ * True when a folder from the working folder up to the root has `.git`, or is a git folder. Git
348
+ * then found a repository that it could not examine.
349
+ */
350
+ async function looksLikeRepository(cwd, root) {
351
+ let folder = cwd;
352
+ for (;;) {
353
+ if ((await exists(join(folder, ".git"))) || (await isGitFolder(folder)))
354
+ return true;
355
+ const parent = dirname(folder);
356
+ if (!isInside(root, parent) || parent === folder)
357
+ return false;
358
+ folder = parent;
359
+ }
360
+ }
361
+ /**
362
+ * This function examines the repository of a working folder under a root. It refuses:
363
+ * - a repository whose folders are outside the root (a `.git` file can name another folder);
364
+ * - an alternate object folder outside the root;
365
+ * - a configuration with a key that starts a program or names a file outside the repository;
366
+ * - a repository with submodules: git starts git in each submodule, with its configuration.
367
+ *
368
+ * A folder that is not in a repository passes: the command makes one (`git init`, `git clone`),
369
+ * or it fails.
370
+ */
371
+ export async function examineRepository(current, cwd, env, hooks) {
372
+ const { root, procedure } = current;
373
+ const refuse = (message) => outsideRoot(procedure, `under the root ${root.folder}, ${message}`);
374
+ const located = await examine(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir", "--is-bare-repository"], cwd, env, hooks);
375
+ const [gitDir, commonDir, bare] = located.success ? located.stdout.split(/\r?\n/) : [];
376
+ if (!gitDir || !commonDir) {
377
+ // The text of the error of git depends on the language, so the folders decide
378
+ if (await looksLikeRepository(cwd, root.folder)) {
379
+ throw refuse(`git cannot examine the repository of ${cwd}: ${located.stderr.trim()}`);
380
+ }
381
+ return;
382
+ }
383
+ for (const path of [gitDir, commonDir, join(commonDir, "objects"), join(commonDir, "refs"), join(commonDir, "config"), join(gitDir, "index")]) {
384
+ if (!(await isInRoot(root, path)))
385
+ throw refuse(`the repository of ${cwd} is outside the root: ${path}`);
386
+ }
387
+ const alternates = await readFile(join(commonDir, "objects", "info", "alternates"), "utf8").catch(() => "");
388
+ for (const line of alternates.split(/\r?\n/)) {
389
+ const entry = line.trim();
390
+ if (entry === "" || entry.startsWith("#"))
391
+ continue;
392
+ if (!(await isInRoot(root, resolve(commonDir, "objects", entry))))
393
+ throw refuse(`the repository ${gitDir} reads objects outside the root: ${entry}`);
394
+ }
395
+ const config = await examine(["config", "--list", "--no-includes", "--show-scope", "-z"], cwd, env, hooks);
396
+ if (!config.success)
397
+ throw refuse(`git cannot read the configuration of the repository ${gitDir}: ${config.stderr.trim()}`);
398
+ const keys = refusedConfigKeys(parseConfigList(config.stdout));
399
+ if (keys.length > 0) {
400
+ throw refuse(`the configuration of the repository ${gitDir} can start a program or read a file outside the root: ${keys.join(", ")}`);
401
+ }
402
+ // A submodule: .gitmodules in the working tree, in the index or at HEAD
403
+ let modules = false;
404
+ if (bare !== "true") {
405
+ const top = await examine(["rev-parse", "--show-toplevel"], cwd, env, hooks);
406
+ if (top.success && (await exists(join(top.stdout.trim(), ".gitmodules"))))
407
+ modules = true;
408
+ }
409
+ if (!modules) {
410
+ const objects = await examine(["cat-file", "--batch-check"], cwd, env, hooks, ":.gitmodules\nHEAD:.gitmodules\n");
411
+ if (!objects.success)
412
+ throw refuse(`git cannot examine the repository ${gitDir}: ${objects.stderr.trim()}`);
413
+ modules = objects.stdout.split(/\r?\n/).some((line) => line !== "" && !line.endsWith(" missing"));
414
+ }
415
+ if (modules) {
416
+ throw refuse(`the repository ${gitDir} has submodules (.gitmodules), and git starts git in each submodule with the configuration of the submodule`);
417
+ }
418
+ }
419
+ /** The subcommands that recurse into submodules without `--no-recurse-submodules`. */
420
+ const RECURSING = new Set(["fetch", "pull"]);
421
+ /**
422
+ * This function gives the arguments, the working folder and the environment of one git command
423
+ * under a root. It examines the repository of the working folder before the first command there.
424
+ */
425
+ export async function confinedGitCommand(current, args, cwd, env) {
426
+ const folder = await inRoot(current.root, cwd ?? current.root.folder, current.procedure);
427
+ const files = await gitRootSetup();
428
+ const rootEnv = rootEnvironment(current.root, files);
429
+ let examined = current.examined.get(folder);
430
+ if (examined === undefined) {
431
+ examined = examineRepository(current, folder, rootEnv, files.hooks);
432
+ current.examined.set(folder, examined);
433
+ }
434
+ await examined;
435
+ const command = [...args];
436
+ // The checkout shows the changes of the working tree, also of a submodule that it brings: quiet, it does not
437
+ if (command[0] === "checkout")
438
+ command.splice(1, 0, "--quiet");
439
+ if (RECURSING.has(command[0] ?? ""))
440
+ command.splice(1, 0, "--no-recurse-submodules");
441
+ return { args: [...safeConfig(files.hooks), ...command], cwd: folder, env: { ...rootEnv, ...env } };
442
+ }
443
+ //# sourceMappingURL=root.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root.js","sourceRoot":"","sources":["../../../src/procedures/git/root.ts"],"names":[],"mappings":"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"}
@@ -26,7 +26,8 @@ export declare class GitError extends Error {
26
26
  }
27
27
  /**
28
28
  * This function runs `git <args>` and gives its stdout. It throws a `GitError` when git fails,
29
- * and an `AbortError` when the signal aborts.
29
+ * and an `AbortError` when the signal aborts. In the handler of a git procedure under a root
30
+ * (`underGitRoot()`), git stays in the root: `confinedGitCommand()` of `root.ts`.
30
31
  */
31
32
  export declare function git(args: string[], options?: GitRunOptions): Promise<string>;
32
33
  /** This function splits output that git wrote with `-z` into its fields. */
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/procedures/git/run.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,sEAAsE;AACtE,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;CAClC;AAED,MAAM,WAAW,aAAc,SAAQ,UAAU;IAC/C,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,+EAA+E;IAC/E,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,uEAAuE;IACvE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;CAC1C;AAED,+DAA+D;AAC/D,qBAAa,QAAS,SAAQ,KAAK;IAE/B,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM;IAJzB,YACW,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EAIxB;CACF;AAED;;;GAGG;AACH,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAwBtF;AAED,4EAA4E;AAC5E,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAIhD"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/procedures/git/run.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,sEAAsE;AACtE,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;CAClC;AAED,MAAM,WAAW,aAAc,SAAQ,UAAU;IAC/C,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,+EAA+E;IAC/E,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,uEAAuE;IACvE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;CAC1C;AAED,+DAA+D;AAC/D,qBAAa,QAAS,SAAQ,KAAK;IAE/B,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM;IAJzB,YACW,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EAIxB;CACF;AAED;;;;GAIG;AACH,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CA0BtF;AAED,4EAA4E;AAC5E,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAIhD"}
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import { runCommand } from "@procedures/shell/command";
9
9
  import { GIT_MAX_BUFFER } from "./args.js";
10
+ import { confinedGitCommand, currentGitRoot } from "./root.js";
10
11
  /** A git command that ended with an exit code other than 0. */
11
12
  export class GitError extends Error {
12
13
  args;
@@ -24,15 +25,18 @@ export class GitError extends Error {
24
25
  }
25
26
  /**
26
27
  * This function runs `git <args>` and gives its stdout. It throws a `GitError` when git fails,
27
- * and an `AbortError` when the signal aborts.
28
+ * and an `AbortError` when the signal aborts. In the handler of a git procedure under a root
29
+ * (`underGitRoot()`), git stays in the root: `confinedGitCommand()` of `root.ts`.
28
30
  */
29
31
  export async function git(args, options = {}) {
32
+ const root = currentGitRoot();
33
+ const command = root === undefined ? { args, cwd: options.cwd, env: options.env } : await confinedGitCommand(root, args, options.cwd, options.env);
30
34
  const result = await runCommand("git", {
31
- args,
32
- cwd: options.cwd,
35
+ args: command.args,
36
+ cwd: command.cwd,
33
37
  signal: options.signal,
34
38
  input: options.input,
35
- env: options.env,
39
+ env: command.env,
36
40
  maxOutputBytes: GIT_MAX_BUFFER,
37
41
  });
38
42
  if (result.aborted) {
@@ -1 +1 @@
1
- {"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/procedures/git/run.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAe3C,+DAA+D;AAC/D,MAAM,OAAO,QAAS,SAAQ,KAAK;IAEtB,IAAI;IACJ,QAAQ;IACR,MAAM;IACN,MAAM;IAJjB,YACW,IAAuB,EACvB,QAAgB,EAChB,MAAc,EACd,MAAc;QAEvB,KAAK,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,iBAAiB,QAAQ,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;oBAL9E,IAAI;wBACJ,QAAQ;sBACR,MAAM;sBACN,MAAM;QAGf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,IAAc,EAAE,OAAO,GAAkB,EAAE;IACnE,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE;QACrC,IAAI;QACJ,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,cAAc,EAAE,cAAc;KAC/B,CAAC,CAAC;IACH,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;QACtD,KAAK,CAAC,IAAI,GAAG,YAAY,CAAC;QAC1B,MAAM,KAAK,CAAC;IACd,CAAC;IACD,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,sBAAsB,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IACxD,CAAC;IACD,IAAI,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,qBAAqB,IAAI,CAAC,CAAC,CAAC,mBAAmB,cAAc,QAAQ,CAAC,CAAC;IACzF,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC;AACvB,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,OAAO,CAAC,MAAc;IACpC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE;QAAE,MAAM,CAAC,GAAG,EAAE,CAAC;IACvC,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/procedures/git/run.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAe/D,+DAA+D;AAC/D,MAAM,OAAO,QAAS,SAAQ,KAAK;IAEtB,IAAI;IACJ,QAAQ;IACR,MAAM;IACN,MAAM;IAJjB,YACW,IAAuB,EACvB,QAAgB,EAChB,MAAc,EACd,MAAc;QAEvB,KAAK,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,iBAAiB,QAAQ,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;oBAL9E,IAAI;wBACJ,QAAQ;sBACR,MAAM;sBACN,MAAM;QAGf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,IAAc,EAAE,OAAO,GAAkB,EAAE;IACnE,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,MAAM,OAAO,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IACnJ,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE;QACrC,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,cAAc,EAAE,cAAc;KAC/B,CAAC,CAAC;IACH,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;QACtD,KAAK,CAAC,IAAI,GAAG,YAAY,CAAC;QAC1B,MAAM,KAAK,CAAC;IACd,CAAC;IACD,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,sBAAsB,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IACxD,CAAC;IACD,IAAI,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,qBAAqB,IAAI,CAAC,CAAC,CAAC,mBAAmB,cAAc,QAAQ,CAAC,CAAC;IACzF,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC;AACvB,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,OAAO,CAAC,MAAc;IACpC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE;QAAE,MAAM,CAAC,GAAG,EAAE,CAAC;IACvC,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package",
3
3
  "name": "@procedures/git",
4
- "version": "0.8.1",
4
+ "version": "0.9.1",
5
5
  "description": "Git as 45 typed procedures: status, diff, log, show, blame, commit, push, pull, branch, tag, merge, rebase, worktree, stash and more, run with argv, async and abortable.",
6
6
  "keywords": [
7
7
  "git",
@@ -60,13 +60,13 @@
60
60
  "procedures": "./dist/register.js"
61
61
  },
62
62
  "dependencies": {
63
- "@procedures/client": "0.8.1",
64
- "@procedures/shell": "0.8.1",
63
+ "@procedures/client": "0.9.1",
64
+ "@procedures/fs": "0.9.1",
65
+ "@procedures/shell": "0.9.1",
65
66
  "zod": "^4.6.5"
66
67
  },
67
68
  "devDependencies": {
68
69
  "@mark1russell7/cue": "^1.0.0",
69
- "@procedures/fs": "0.8.1",
70
70
  "@types/node": "^22.0.0",
71
71
  "typescript": "^7.0.2",
72
72
  "vitest": "^5.0.3"
package/src/module.ts CHANGED
@@ -7,7 +7,8 @@
7
7
  * This file is referenced by package.json's client.procedures field.
8
8
  */
9
9
 
10
- import { createProcedure, zodAdapter, defineModule, type AnyProcedure, type Effects, type FsRootRules, type ProcedureModule, PROCEDURE_REGISTRY, registerModules, type ProcedureRegistry } from "@procedures/client";
10
+ import { createProcedure, zodAdapter, defineModule, type AnyProcedure, type Effects, type FsRootRules, type ProcedureContext, type ProcedureModule, PROCEDURE_REGISTRY, registerModules, type ProcedureRegistry } from "@procedures/client";
11
+ import { underGitRoot } from "./procedures/git/root.js";
11
12
  import { gitStatus } from "./procedures/git/status.js";
12
13
  import { gitAdd } from "./procedures/git/add.js";
13
14
  import { gitCommit } from "./procedures/git/commit.js";
@@ -245,20 +246,51 @@ const CHANGES_REPOSITORY: Effects = { reads: ["fs"], writes: ["fs"] };
245
246
  const RUNS_HOOKS: Effects = { reads: ["fs"], writes: ["fs", "process"] };
246
247
 
247
248
  /**
248
- * The rule of each git procedure under a root of the files (roadmap R33): a host with a root
249
- * refuses it. A root cannot keep git in its folder for two reasons:
250
- * - Git looks for the repository in the folders above the working folder, so a working folder in
251
- * the root can open a repository outside it.
252
- * - The configuration of a repository can start a program: a hook, `core.fsmonitor` or a filter.
253
- * A client with `fs.write` in the root can write that configuration.
249
+ * The rules of the git procedures under a root of the files (roadmap R33, tracker item O35). Each
250
+ * git procedure is confined: the working folder `cwd` is in the root, and its default is the root.
251
+ * The handler runs in `underGitRoot()`, so git stays in the root (`procedures/git/root.ts`):
252
+ * - `GIT_CEILING_DIRECTORIES` stops git at the root, so git finds no repository above the root.
253
+ * - The `-c` options turn off the hooks, `core.fsmonitor`, the local transports and the
254
+ * submodules. The configuration of the system and the global configuration do not apply.
255
+ * - Before the first command in a folder, git examines the repository: a configuration key that
256
+ * starts a program, a folder outside the root or a submodule is refused.
254
257
  */
255
- const REFUSED_UNDER_A_ROOT: FsRootRules = {
256
- refuse: "the configuration of a repository can start a program (a hook, core.fsmonitor, a filter), and git looks for the repository in the folders above the working folder",
258
+ const IN_A_ROOT: FsRootRules = { cwd: "cwd" };
259
+
260
+ /** A procedure whose `paths` are pathspecs from the working folder. */
261
+ const WITH_PATHSPECS: FsRootRules = { cwd: "cwd", names: ["paths"] };
262
+
263
+ /** The rules of the procedures that name more than the working folder. The others get `IN_A_ROOT`. */
264
+ const ROOT_RULES: Readonly<Record<string, FsRootRules>> = {
265
+ "git.status": WITH_PATHSPECS,
266
+ "git.add": WITH_PATHSPECS,
267
+ "git.commit": WITH_PATHSPECS,
268
+ "git.checkout": WITH_PATHSPECS,
269
+ "git.diff": WITH_PATHSPECS,
270
+ "git.stash.push": WITH_PATHSPECS,
271
+ "git.reset": WITH_PATHSPECS,
272
+ "git.restore": WITH_PATHSPECS,
273
+ "git.blame": { cwd: "cwd", names: ["path"] },
274
+ // The URL goes to the network only: the transports file and ext are off under a root
275
+ "git.clone": { cwd: "cwd", names: ["dest"] },
276
+ "git.worktree.add": { cwd: "cwd", names: ["path"] },
277
+ "git.worktree.remove": { cwd: "cwd", names: ["path"] },
257
278
  };
258
279
 
259
- /** The procedure with the rule of a root. */
280
+ /**
281
+ * The procedure with its rules under a root. Its handler runs in `underGitRoot()` with the root of
282
+ * the call (`ctx.fsRoot`). Without a root, the handler runs as before.
283
+ */
260
284
  function underARoot(procedure: AnyProcedure): AnyProcedure {
261
- return { ...procedure, metadata: { ...procedure.metadata, fsRoot: REFUSED_UNDER_A_ROOT } };
285
+ const handler = procedure.handler as ((input: unknown, ctx: ProcedureContext) => Promise<unknown>) | undefined;
286
+ const fsRoot = ROOT_RULES[procedure.path.join(".")] ?? IN_A_ROOT;
287
+ return {
288
+ ...procedure,
289
+ metadata: { ...procedure.metadata, fsRoot },
290
+ ...(handler === undefined
291
+ ? {}
292
+ : { handler: ((input: unknown, ctx: ProcedureContext) => underGitRoot(ctx?.fsRoot, procedure.path, () => handler(input, ctx))) as AnyProcedure["handler"] }),
293
+ };
262
294
  }
263
295
 
264
296
  // =============================================================================
@@ -7,6 +7,7 @@
7
7
  import { join, basename } from "node:path";
8
8
  import { gitArg } from "./args.js";
9
9
  import { git, type GitContext } from "./run.js";
10
+ import { pathInGitRoot, remoteUrlInGitRoot } from "./root.js";
10
11
  import type { GitCloneInput, GitCloneOutput } from "../../types.js";
11
12
 
12
13
  /**
@@ -18,6 +19,9 @@ export async function gitClone(input: GitCloneInput, ctx: GitContext = {}): Prom
18
19
  // Determine destination directory
19
20
  const destDir = dest || basename(url, ".git").replace(/\.git$/, "");
20
21
  const fullPath = cwd ? join(cwd, destDir) : destDir;
22
+ // Under a root, the folder of the clone is in the root, also when it comes from the URL ("..")
23
+ await pathInGitRoot(fullPath);
24
+ remoteUrlInGitRoot(url);
21
25
 
22
26
  const args: string[] = ["clone"];
23
27
  if (branch) args.push("-b", branch);