@ricsam/r5d-worker 0.0.76 → 0.0.77
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/cjs/git-process-environment.cjs +133 -0
- package/dist/cjs/main.cjs +267 -100
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/project-worktrees.cjs +72 -47
- package/dist/cjs/working-tree-mirror.cjs +2 -1
- package/dist/cjs/workspace-git-sync.cjs +67 -53
- package/dist/mjs/git-process-environment.mjs +105 -0
- package/dist/mjs/main.mjs +266 -100
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/project-worktrees.mjs +72 -47
- package/dist/mjs/working-tree-mirror.mjs +2 -1
- package/dist/mjs/workspace-git-sync.mjs +67 -53
- package/dist/types/git-process-environment.d.ts +9 -0
- package/dist/types/main.d.ts +24 -0
- package/dist/types/project-worktrees.d.ts +22 -10
- package/dist/types/workspace-git-sync.d.ts +18 -5
- package/package.json +1 -1
|
@@ -47,6 +47,7 @@ var import_node_crypto = require("node:crypto");
|
|
|
47
47
|
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
48
48
|
var import_node_os = __toESM(require("node:os"), 1);
|
|
49
49
|
var import_node_path = __toESM(require("node:path"), 1);
|
|
50
|
+
var import_git_process_environment = require("./git-process-environment.cjs");
|
|
50
51
|
var import_managed_paths = require("./managed-paths.cjs");
|
|
51
52
|
var import_working_tree_mirror = require("./working-tree-mirror.cjs");
|
|
52
53
|
const PROJECT_WORKTREE_SNAPSHOT_PREFIX = "r5d-project-worktrees-";
|
|
@@ -78,25 +79,15 @@ const NON_RECURSIVE_GIT_CONFIG = [
|
|
|
78
79
|
"-c",
|
|
79
80
|
"push.recurseSubmodules=false"
|
|
80
81
|
];
|
|
81
|
-
function
|
|
82
|
-
|
|
83
|
-
const url = new URL(value);
|
|
84
|
-
return `${url.protocol}//${url.host}`;
|
|
85
|
-
} catch {
|
|
86
|
-
return value.replace(/\/+$/, "");
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
function authArgs(auth) {
|
|
90
|
-
if (!auth) return [];
|
|
91
|
-
const key = `http.${normalizedHttpOrigin(auth.extraHeaderUrl)}/.extraHeader`;
|
|
92
|
-
return ["-c", "http.extraHeader=", "-c", `${key}=`, "-c", `${key}=${auth.header}`];
|
|
82
|
+
function gitCommandArgs(args) {
|
|
83
|
+
return ["git", ...NON_RECURSIVE_GIT_CONFIG, ...args];
|
|
93
84
|
}
|
|
94
|
-
function gitResult(cwd, args
|
|
95
|
-
const result = Bun.spawnSync(
|
|
85
|
+
function gitResult(cwd, args) {
|
|
86
|
+
const result = Bun.spawnSync(gitCommandArgs(args), {
|
|
96
87
|
cwd,
|
|
97
88
|
stdout: "pipe",
|
|
98
89
|
stderr: "pipe",
|
|
99
|
-
env:
|
|
90
|
+
env: (0, import_git_process_environment.workerGitProcessEnvironment)()
|
|
100
91
|
});
|
|
101
92
|
return {
|
|
102
93
|
exitCode: result.exitCode,
|
|
@@ -104,13 +95,13 @@ function gitResult(cwd, args, auth) {
|
|
|
104
95
|
stderr: result.stderr.toString().trim()
|
|
105
96
|
};
|
|
106
97
|
}
|
|
107
|
-
function git(cwd, args, action
|
|
108
|
-
const result = gitResult(cwd, args
|
|
98
|
+
function git(cwd, args, action) {
|
|
99
|
+
const result = gitResult(cwd, args);
|
|
109
100
|
if (result.exitCode !== 0) throw new Error(`${action}: ${result.stderr || result.stdout || `git exited ${result.exitCode}`}`);
|
|
110
101
|
return result.stdout;
|
|
111
102
|
}
|
|
112
|
-
function tryGit(cwd, args
|
|
113
|
-
return gitResult(cwd, args
|
|
103
|
+
function tryGit(cwd, args) {
|
|
104
|
+
return gitResult(cwd, args).exitCode === 0;
|
|
114
105
|
}
|
|
115
106
|
function branchPath(projectRoot, branchName) {
|
|
116
107
|
(0, import_managed_paths.validateManagedBranchName)(branchName);
|
|
@@ -237,7 +228,16 @@ function configureRepository(input) {
|
|
|
237
228
|
} else {
|
|
238
229
|
git(input.primaryPath, ["remote", "add", "origin", input.originUrl], "configure project origin");
|
|
239
230
|
}
|
|
231
|
+
tryGit(input.primaryPath, ["config", "--local", "--unset-all", "remote.origin.pushurl"]);
|
|
240
232
|
git(input.primaryPath, ["config", "--local", "--replace-all", "credential.helper", ""], "reset project credential helpers");
|
|
233
|
+
git(input.primaryPath, ["config", "--local", "credential.useHttpPath", "false"], "configure project credential path matching");
|
|
234
|
+
if (input.originCredentialUsername) {
|
|
235
|
+
git(
|
|
236
|
+
input.primaryPath,
|
|
237
|
+
["config", "--local", "--replace-all", (0, import_git_process_environment.gitCredentialUsernameConfigKey)(input.originUrl), input.originCredentialUsername],
|
|
238
|
+
"configure project credential username"
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
241
|
if (input.credentialHelper) {
|
|
242
242
|
git(
|
|
243
243
|
input.primaryPath,
|
|
@@ -254,19 +254,25 @@ function configureRepository(input) {
|
|
|
254
254
|
}
|
|
255
255
|
}
|
|
256
256
|
function fetchProjectHeads(input) {
|
|
257
|
-
const originFetch = gitResult(
|
|
258
|
-
input.
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
257
|
+
const originFetch = gitResult(input.primaryPath, [
|
|
258
|
+
...(0, import_git_process_environment.gitTransportSecurityArgs)(input.originUrl, input.credentialHelper, input.originCredentialUsername),
|
|
259
|
+
"fetch",
|
|
260
|
+
"--no-recurse-submodules",
|
|
261
|
+
"--prune",
|
|
262
|
+
"origin",
|
|
263
|
+
"+refs/heads/*:refs/remotes/origin/*"
|
|
264
|
+
]);
|
|
262
265
|
if (originFetch.exitCode !== 0) {
|
|
263
266
|
throw new Error(`Fetch project origin: ${originFetch.stderr || originFetch.stdout || `git exited ${originFetch.exitCode}`}`);
|
|
264
267
|
}
|
|
265
|
-
const mirrorFetch = gitResult(
|
|
266
|
-
input.
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
268
|
+
const mirrorFetch = gitResult(input.primaryPath, [
|
|
269
|
+
...(0, import_git_process_environment.gitTransportSecurityArgs)(input.mirrorUrl, input.credentialHelper, input.mirrorCredentialUsername),
|
|
270
|
+
"fetch",
|
|
271
|
+
"--no-recurse-submodules",
|
|
272
|
+
"--prune",
|
|
273
|
+
input.mirrorUrl,
|
|
274
|
+
"+refs/heads/*:refs/r5d/mirror/*"
|
|
275
|
+
]);
|
|
270
276
|
if (mirrorFetch.exitCode !== 0 && !/(couldn't find remote ref|does not have any commits|remote repository is empty|no such ref)/i.test(
|
|
271
277
|
`${mirrorFetch.stderr}
|
|
272
278
|
${mirrorFetch.stdout}`
|
|
@@ -276,11 +282,19 @@ ${mirrorFetch.stdout}`
|
|
|
276
282
|
}
|
|
277
283
|
function ensureCommitAvailable(input) {
|
|
278
284
|
if (tryGit(input.primaryPath, ["cat-file", "-e", `${input.commitHash}^{commit}`])) return;
|
|
279
|
-
for (const [url,
|
|
280
|
-
[input.mirrorUrl, input.
|
|
281
|
-
[input.originUrl, input.
|
|
285
|
+
for (const [url, username] of [
|
|
286
|
+
[input.mirrorUrl, input.mirrorCredentialUsername],
|
|
287
|
+
[input.originUrl, input.originCredentialUsername]
|
|
282
288
|
]) {
|
|
283
|
-
if (tryGit(input.primaryPath, [
|
|
289
|
+
if (tryGit(input.primaryPath, [
|
|
290
|
+
...(0, import_git_process_environment.gitTransportSecurityArgs)(url, input.credentialHelper, username),
|
|
291
|
+
"fetch",
|
|
292
|
+
"--no-recurse-submodules",
|
|
293
|
+
"--no-tags",
|
|
294
|
+
url,
|
|
295
|
+
input.commitHash
|
|
296
|
+
]))
|
|
297
|
+
return;
|
|
284
298
|
}
|
|
285
299
|
throw new Error(`Project base commit ${input.commitHash} is unavailable from origin and the canonical mirror`);
|
|
286
300
|
}
|
|
@@ -484,11 +498,13 @@ function removeProjectWorktrees(input) {
|
|
|
484
498
|
}
|
|
485
499
|
function deleteProjectMirrorBranch(input) {
|
|
486
500
|
(0, import_managed_paths.validateManagedBranchName)(input.branchName);
|
|
487
|
-
const deleted = gitResult(
|
|
488
|
-
input.
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
501
|
+
const deleted = gitResult(input.gitDirectory, [
|
|
502
|
+
...(0, import_git_process_environment.gitTransportSecurityArgs)(input.mirrorUrl, input.credentialHelper, input.credentialUsername),
|
|
503
|
+
"push",
|
|
504
|
+
"--no-recurse-submodules",
|
|
505
|
+
input.mirrorUrl,
|
|
506
|
+
`:refs/heads/${input.branchName}`
|
|
507
|
+
]);
|
|
492
508
|
if (deleted.exitCode !== 0 && !/(remote ref does not exist|unable to delete|no such ref)/i.test(`${deleted.stderr}
|
|
493
509
|
${deleted.stdout}`)) {
|
|
494
510
|
throw new Error(
|
|
@@ -508,9 +524,14 @@ function pushProjectMirrorHeads(input) {
|
|
|
508
524
|
}
|
|
509
525
|
git(
|
|
510
526
|
checkoutPath,
|
|
511
|
-
[
|
|
512
|
-
|
|
513
|
-
|
|
527
|
+
[
|
|
528
|
+
...(0, import_git_process_environment.gitTransportSecurityArgs)(input.mirrorUrl, input.credentialHelper, input.credentialUsername),
|
|
529
|
+
"push",
|
|
530
|
+
"--no-recurse-submodules",
|
|
531
|
+
input.mirrorUrl,
|
|
532
|
+
`+HEAD:refs/heads/${branchName}`
|
|
533
|
+
],
|
|
534
|
+
`push hidden project mirror for ${branchName}`
|
|
514
535
|
);
|
|
515
536
|
results.push({ branchName, head, pushed: true });
|
|
516
537
|
}
|
|
@@ -518,11 +539,14 @@ function pushProjectMirrorHeads(input) {
|
|
|
518
539
|
}
|
|
519
540
|
function fastForwardProjectHeadsFromMirror(input) {
|
|
520
541
|
const primaryPath = branchPath(input.projectRoot, input.primaryBranchName);
|
|
521
|
-
const fetch = gitResult(
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
542
|
+
const fetch = gitResult(primaryPath, [
|
|
543
|
+
...(0, import_git_process_environment.gitTransportSecurityArgs)(input.mirrorUrl, input.credentialHelper, input.credentialUsername),
|
|
544
|
+
"fetch",
|
|
545
|
+
"--no-recurse-submodules",
|
|
546
|
+
"--prune",
|
|
547
|
+
input.mirrorUrl,
|
|
548
|
+
"+refs/heads/*:refs/r5d/mirror/*"
|
|
549
|
+
]);
|
|
526
550
|
if (fetch.exitCode !== 0)
|
|
527
551
|
throw new Error(`Refresh project head mirror: ${fetch.stderr || fetch.stdout || `git exited ${fetch.exitCode}`}`);
|
|
528
552
|
return [...input.branchNames].sort().map((branchName) => {
|
|
@@ -539,7 +563,8 @@ function fastForwardProjectHeadsFromMirror(input) {
|
|
|
539
563
|
});
|
|
540
564
|
}
|
|
541
565
|
const projectWorktreesTestHarness = {
|
|
542
|
-
commandArgs:
|
|
566
|
+
commandArgs: gitCommandArgs,
|
|
567
|
+
configureRepository
|
|
543
568
|
};
|
|
544
569
|
// Annotate the CommonJS export names for ESM import in node:
|
|
545
570
|
0 && (module.exports = {
|
|
@@ -34,6 +34,7 @@ __export(working_tree_mirror_exports, {
|
|
|
34
34
|
module.exports = __toCommonJS(working_tree_mirror_exports);
|
|
35
35
|
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
36
36
|
var import_node_path = __toESM(require("node:path"), 1);
|
|
37
|
+
var import_git_process_environment = require("./git-process-environment.cjs");
|
|
37
38
|
function isGitMetadataPath(relativePath) {
|
|
38
39
|
return relativePath.split("/").includes(".git");
|
|
39
40
|
}
|
|
@@ -70,7 +71,7 @@ function gitEligibleRoots(root) {
|
|
|
70
71
|
cwd: root,
|
|
71
72
|
stdout: "pipe",
|
|
72
73
|
stderr: "pipe",
|
|
73
|
-
env:
|
|
74
|
+
env: (0, import_git_process_environment.workerGitProcessEnvironment)()
|
|
74
75
|
}
|
|
75
76
|
);
|
|
76
77
|
if (result.exitCode !== 0) {
|
|
@@ -40,6 +40,7 @@ __export(workspace_git_sync_exports, {
|
|
|
40
40
|
module.exports = __toCommonJS(workspace_git_sync_exports);
|
|
41
41
|
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
42
42
|
var import_node_path = __toESM(require("node:path"), 1);
|
|
43
|
+
var import_git_process_environment = require("./git-process-environment.cjs");
|
|
43
44
|
var import_working_tree_mirror = require("./working-tree-mirror.cjs");
|
|
44
45
|
const WORKSPACE_GIT_BRANCH = "main";
|
|
45
46
|
const MAX_WORKSPACE_GIT_DIFF_BYTES = 5 * 1024 * 1024;
|
|
@@ -53,25 +54,18 @@ const NON_RECURSIVE_GIT_CONFIG = [
|
|
|
53
54
|
"-c",
|
|
54
55
|
"push.recurseSubmodules=false"
|
|
55
56
|
];
|
|
56
|
-
function
|
|
57
|
-
|
|
58
|
-
const url = new URL(value);
|
|
59
|
-
return `${url.protocol}//${url.host}`;
|
|
60
|
-
} catch {
|
|
61
|
-
return value.replace(/\/+$/, "");
|
|
62
|
-
}
|
|
57
|
+
function gitCommandArgs(args) {
|
|
58
|
+
return ["git", ...NON_RECURSIVE_GIT_CONFIG, ...args];
|
|
63
59
|
}
|
|
64
|
-
function
|
|
65
|
-
|
|
66
|
-
const key = `http.${normalizedHttpOrigin(auth.extraHeaderUrl)}/.extraHeader`;
|
|
67
|
-
return ["-c", "http.extraHeader=", "-c", `${key}=`, "-c", `${key}=${auth.header}`];
|
|
60
|
+
function workspaceCloneCommandArgs(args, remoteUrl, credentialHelper, credentialUsername) {
|
|
61
|
+
return gitCommandArgs([...(0, import_git_process_environment.gitTransportSecurityArgs)(remoteUrl, credentialHelper, credentialUsername), ...args]);
|
|
68
62
|
}
|
|
69
|
-
function gitResult(cwd, args
|
|
70
|
-
const result = Bun.spawnSync(
|
|
63
|
+
function gitResult(cwd, args) {
|
|
64
|
+
const result = Bun.spawnSync(gitCommandArgs(args), {
|
|
71
65
|
cwd,
|
|
72
66
|
stdout: "pipe",
|
|
73
67
|
stderr: "pipe",
|
|
74
|
-
env:
|
|
68
|
+
env: (0, import_git_process_environment.workerGitProcessEnvironment)()
|
|
75
69
|
});
|
|
76
70
|
return {
|
|
77
71
|
exitCode: result.exitCode,
|
|
@@ -79,13 +73,13 @@ function gitResult(cwd, args, auth) {
|
|
|
79
73
|
stderr: result.stderr.toString().trim()
|
|
80
74
|
};
|
|
81
75
|
}
|
|
82
|
-
function git(cwd, args, action
|
|
83
|
-
const result = gitResult(cwd, args
|
|
76
|
+
function git(cwd, args, action) {
|
|
77
|
+
const result = gitResult(cwd, args);
|
|
84
78
|
if (result.exitCode !== 0) throw new Error(`${action}: ${result.stderr || result.stdout || `git exited ${result.exitCode}`}`);
|
|
85
79
|
return result.stdout;
|
|
86
80
|
}
|
|
87
|
-
function tryGit(cwd, args
|
|
88
|
-
return gitResult(cwd, args
|
|
81
|
+
function tryGit(cwd, args) {
|
|
82
|
+
return gitResult(cwd, args).exitCode === 0;
|
|
89
83
|
}
|
|
90
84
|
function revParse(workspacePath, revision) {
|
|
91
85
|
const result = gitResult(workspacePath, ["rev-parse", "--verify", revision]);
|
|
@@ -131,9 +125,22 @@ function configureWorkspaceRepository(input) {
|
|
|
131
125
|
} else {
|
|
132
126
|
git(input.workspacePath, ["remote", "add", "origin", input.remoteUrl], "configure workspace origin");
|
|
133
127
|
}
|
|
128
|
+
tryGit(input.workspacePath, ["config", "--local", "--unset-all", "remote.origin.pushurl"]);
|
|
134
129
|
git(input.workspacePath, ["config", "--local", "--replace-all", "credential.helper", ""], "reset workspace credential helpers");
|
|
130
|
+
git(input.workspacePath, ["config", "--local", "credential.useHttpPath", "false"], "configure workspace credential path matching");
|
|
131
|
+
if (input.credentialUsername) {
|
|
132
|
+
git(
|
|
133
|
+
input.workspacePath,
|
|
134
|
+
["config", "--local", "--replace-all", (0, import_git_process_environment.gitCredentialUsernameConfigKey)(input.remoteUrl), input.credentialUsername],
|
|
135
|
+
"configure workspace credential username"
|
|
136
|
+
);
|
|
137
|
+
}
|
|
135
138
|
if (input.credentialHelper) {
|
|
136
|
-
git(
|
|
139
|
+
git(
|
|
140
|
+
input.workspacePath,
|
|
141
|
+
["config", "--local", "--add", "credential.helper", input.credentialHelper],
|
|
142
|
+
"configure workspace credential helper"
|
|
143
|
+
);
|
|
137
144
|
}
|
|
138
145
|
const name = input.gitIdentity.name.trim();
|
|
139
146
|
const email = input.gitIdentity.email.trim();
|
|
@@ -141,19 +148,16 @@ function configureWorkspaceRepository(input) {
|
|
|
141
148
|
git(input.workspacePath, ["config", "--local", "user.name", name], "configure workspace Git user name");
|
|
142
149
|
git(input.workspacePath, ["config", "--local", "user.email", email], "configure workspace Git user email");
|
|
143
150
|
}
|
|
144
|
-
function fetchWorkspaceHead(workspacePath,
|
|
145
|
-
const result = gitResult(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
],
|
|
155
|
-
remoteAuth
|
|
156
|
-
);
|
|
151
|
+
function fetchWorkspaceHead(workspacePath, remoteUrl, credentialHelper, credentialUsername) {
|
|
152
|
+
const result = gitResult(workspacePath, [
|
|
153
|
+
...(0, import_git_process_environment.gitTransportSecurityArgs)(remoteUrl, credentialHelper, credentialUsername),
|
|
154
|
+
"fetch",
|
|
155
|
+
"--no-recurse-submodules",
|
|
156
|
+
"--prune",
|
|
157
|
+
"--update-shallow",
|
|
158
|
+
"origin",
|
|
159
|
+
`+refs/heads/${WORKSPACE_GIT_BRANCH}:refs/remotes/origin/${WORKSPACE_GIT_BRANCH}`
|
|
160
|
+
]);
|
|
157
161
|
if (result.exitCode !== 0) {
|
|
158
162
|
const detail = `${result.stderr}
|
|
159
163
|
${result.stdout}`;
|
|
@@ -170,11 +174,17 @@ function ensureWorkspaceGitClone(input) {
|
|
|
170
174
|
if (!import_node_fs.default.existsSync(gitPath)) {
|
|
171
175
|
import_node_fs.default.rmSync(workspacePath, { recursive: true, force: true });
|
|
172
176
|
import_node_fs.default.mkdirSync(import_node_path.default.dirname(workspacePath), { recursive: true });
|
|
173
|
-
const
|
|
174
|
-
void 0,
|
|
177
|
+
const cloneCommand = workspaceCloneCommandArgs(
|
|
175
178
|
["clone", "--no-recurse-submodules", "--branch", WORKSPACE_GIT_BRANCH, input.remoteUrl, workspacePath],
|
|
176
|
-
input.
|
|
179
|
+
input.remoteUrl,
|
|
180
|
+
input.credentialHelper,
|
|
181
|
+
input.credentialUsername
|
|
177
182
|
);
|
|
183
|
+
const clone = Bun.spawnSync(cloneCommand, {
|
|
184
|
+
stdout: "pipe",
|
|
185
|
+
stderr: "pipe",
|
|
186
|
+
env: (0, import_git_process_environment.workerGitProcessEnvironment)()
|
|
187
|
+
});
|
|
178
188
|
if (clone.exitCode !== 0) {
|
|
179
189
|
import_node_fs.default.rmSync(workspacePath, { recursive: true, force: true });
|
|
180
190
|
import_node_fs.default.mkdirSync(workspacePath, { recursive: true });
|
|
@@ -187,7 +197,7 @@ function ensureWorkspaceGitClone(input) {
|
|
|
187
197
|
if (!revParse(workspacePath, WORKSPACE_GIT_INTEGRATED_REF) && previousRemoteHead && localHeadBeforeFetch && tryGit(workspacePath, ["merge-base", "--is-ancestor", previousRemoteHead, localHeadBeforeFetch])) {
|
|
188
198
|
updateIntegratedWorkspaceHead(workspacePath, previousRemoteHead);
|
|
189
199
|
}
|
|
190
|
-
const remoteHead = fetchWorkspaceHead(workspacePath, input.
|
|
200
|
+
const remoteHead = fetchWorkspaceHead(workspacePath, input.remoteUrl, input.credentialHelper, input.credentialUsername);
|
|
191
201
|
let localHead = revParse(workspacePath, "HEAD");
|
|
192
202
|
if (!localHead && remoteHead) {
|
|
193
203
|
git(
|
|
@@ -273,7 +283,7 @@ function resetWorkspaceGit(input) {
|
|
|
273
283
|
const initial = ensureWorkspaceGitClone({ ...input, workspacePath });
|
|
274
284
|
const status = gitResult(workspacePath, ["status", "--porcelain=v1", "-z"]);
|
|
275
285
|
const discardedPaths = status.exitCode === 0 ? status.stdout.split("\0").filter(Boolean).map((entry) => entry.slice(3)).sort() : [];
|
|
276
|
-
const remoteHead = fetchWorkspaceHead(workspacePath, input.
|
|
286
|
+
const remoteHead = fetchWorkspaceHead(workspacePath, input.remoteUrl, input.credentialHelper, input.credentialUsername);
|
|
277
287
|
if (remoteHead) {
|
|
278
288
|
git(workspacePath, ["checkout", "--no-recurse-submodules", "-B", WORKSPACE_GIT_BRANCH, remoteHead], "reset workspace main");
|
|
279
289
|
git(workspacePath, ["clean", "-fd", "--", "."], "remove untracked workspace changes");
|
|
@@ -299,9 +309,10 @@ function emptyTreeHash(workspacePath) {
|
|
|
299
309
|
stdin: Buffer.alloc(0),
|
|
300
310
|
stdout: "pipe",
|
|
301
311
|
stderr: "pipe",
|
|
302
|
-
env:
|
|
312
|
+
env: (0, import_git_process_environment.workerGitProcessEnvironment)()
|
|
303
313
|
});
|
|
304
|
-
if (result.exitCode !== 0)
|
|
314
|
+
if (result.exitCode !== 0)
|
|
315
|
+
throw new Error(`Create empty workspace tree: ${result.stderr.toString().trim() || `git exited ${result.exitCode}`}`);
|
|
305
316
|
return result.stdout.toString().trim();
|
|
306
317
|
}
|
|
307
318
|
function changedPaths(workspacePath, baseRevision, headRevision) {
|
|
@@ -310,15 +321,12 @@ function changedPaths(workspacePath, baseRevision, headRevision) {
|
|
|
310
321
|
}
|
|
311
322
|
async function diffSizeBytes(workspacePath, baseRevision, headRevision, limit) {
|
|
312
323
|
const base = baseRevision ?? emptyTreeHash(workspacePath);
|
|
313
|
-
const subprocess = Bun.spawn(
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
|
|
320
|
-
}
|
|
321
|
-
);
|
|
324
|
+
const subprocess = Bun.spawn(["git", ...NON_RECURSIVE_GIT_CONFIG, "diff", "--binary", "--no-ext-diff", base, headRevision], {
|
|
325
|
+
cwd: workspacePath,
|
|
326
|
+
stdout: "pipe",
|
|
327
|
+
stderr: "pipe",
|
|
328
|
+
env: (0, import_git_process_environment.workerGitProcessEnvironment)()
|
|
329
|
+
});
|
|
322
330
|
const stderrPromise = new Response(subprocess.stderr).text();
|
|
323
331
|
const reader = subprocess.stdout.getReader();
|
|
324
332
|
let total = 0;
|
|
@@ -439,7 +447,7 @@ async function synchronizeWorkspaceGit(input) {
|
|
|
439
447
|
"commit workspace working trees"
|
|
440
448
|
);
|
|
441
449
|
}
|
|
442
|
-
let remoteHead = fetchWorkspaceHead(workspacePath, input.
|
|
450
|
+
let remoteHead = fetchWorkspaceHead(workspacePath, input.remoteUrl, input.credentialHelper, input.credentialUsername);
|
|
443
451
|
let rebaseCount = 0;
|
|
444
452
|
let updated = false;
|
|
445
453
|
for (let pushAttempt = 0; pushAttempt < maxPushAttempts; pushAttempt += 1) {
|
|
@@ -515,10 +523,14 @@ async function synchronizeWorkspaceGit(input) {
|
|
|
515
523
|
skippedMountIds: selected.skipped.map(({ id }) => id).sort()
|
|
516
524
|
};
|
|
517
525
|
}
|
|
518
|
-
const pushArgs = [
|
|
526
|
+
const pushArgs = [
|
|
527
|
+
...(0, import_git_process_environment.gitTransportSecurityArgs)(input.remoteUrl, input.credentialHelper, input.credentialUsername),
|
|
528
|
+
"push",
|
|
529
|
+
"--no-recurse-submodules"
|
|
530
|
+
];
|
|
519
531
|
if (input.allowLargeDiff) pushArgs.push(`--push-option=${WORKSPACE_GIT_CONFIRMED_LARGE_DIFF_PUSH_OPTION}`);
|
|
520
532
|
pushArgs.push("origin", `HEAD:refs/heads/${WORKSPACE_GIT_BRANCH}`);
|
|
521
|
-
const push = gitResult(workspacePath, pushArgs
|
|
533
|
+
const push = gitResult(workspacePath, pushArgs);
|
|
522
534
|
if (push.exitCode === 0) {
|
|
523
535
|
updateIntegratedWorkspaceHead(workspacePath, localHead);
|
|
524
536
|
await input.afterWorkspacePublished?.({
|
|
@@ -543,12 +555,14 @@ async function synchronizeWorkspaceGit(input) {
|
|
|
543
555
|
${push.stdout}`)) {
|
|
544
556
|
throw new Error(`Push workspace main: ${push.stderr || push.stdout || `git exited ${push.exitCode}`}`);
|
|
545
557
|
}
|
|
546
|
-
remoteHead = fetchWorkspaceHead(workspacePath, input.
|
|
558
|
+
remoteHead = fetchWorkspaceHead(workspacePath, input.remoteUrl, input.credentialHelper, input.credentialUsername);
|
|
547
559
|
}
|
|
548
560
|
throw new Error(`Workspace push did not converge after ${maxPushAttempts} attempts`);
|
|
549
561
|
}
|
|
550
562
|
const workspaceGitSyncTestHarness = {
|
|
551
|
-
commandArgs:
|
|
563
|
+
commandArgs: gitCommandArgs,
|
|
564
|
+
workspaceCloneCommandArgs,
|
|
565
|
+
configureWorkspaceRepository,
|
|
552
566
|
mirrorMountsToWorkspace,
|
|
553
567
|
hydrateMountsFromWorkspace: hydrateWorkspaceGitMounts
|
|
554
568
|
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { devNull } from "node:os";
|
|
2
|
+
const ALLOWED_GIT_ENVIRONMENT_KEYS = [
|
|
3
|
+
"PATH",
|
|
4
|
+
"USER",
|
|
5
|
+
"LOGNAME",
|
|
6
|
+
"SHELL",
|
|
7
|
+
"TMPDIR",
|
|
8
|
+
"TMP",
|
|
9
|
+
"TEMP",
|
|
10
|
+
"TZ",
|
|
11
|
+
"LANG",
|
|
12
|
+
"LANGUAGE",
|
|
13
|
+
"LC_ALL",
|
|
14
|
+
"LC_CTYPE",
|
|
15
|
+
"LC_COLLATE",
|
|
16
|
+
"LC_MESSAGES",
|
|
17
|
+
"LC_MONETARY",
|
|
18
|
+
"LC_NUMERIC",
|
|
19
|
+
"LC_TIME",
|
|
20
|
+
"LC_ADDRESS",
|
|
21
|
+
"LC_IDENTIFICATION",
|
|
22
|
+
"LC_MEASUREMENT",
|
|
23
|
+
"LC_NAME",
|
|
24
|
+
"LC_PAPER",
|
|
25
|
+
"LC_TELEPHONE",
|
|
26
|
+
"SystemRoot",
|
|
27
|
+
"SYSTEMROOT",
|
|
28
|
+
"WINDIR",
|
|
29
|
+
"COMSPEC",
|
|
30
|
+
"PATHEXT",
|
|
31
|
+
"SSL_CERT_FILE",
|
|
32
|
+
"SSL_CERT_DIR",
|
|
33
|
+
"CURL_CA_BUNDLE",
|
|
34
|
+
"GIT_SSL_CAINFO",
|
|
35
|
+
"GIT_SSL_CAPATH",
|
|
36
|
+
"GIT_EXEC_PATH",
|
|
37
|
+
"GIT_OPTIONAL_LOCKS"
|
|
38
|
+
];
|
|
39
|
+
function workerGitProcessEnvironment(source = process.env) {
|
|
40
|
+
const environment = {
|
|
41
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
42
|
+
GIT_CONFIG_GLOBAL: devNull,
|
|
43
|
+
GIT_CONFIG_NOSYSTEM: "1",
|
|
44
|
+
GIT_ATTR_NOSYSTEM: "1"
|
|
45
|
+
};
|
|
46
|
+
for (const key of ALLOWED_GIT_ENVIRONMENT_KEYS) {
|
|
47
|
+
if (source[key] !== void 0) environment[key] = source[key];
|
|
48
|
+
}
|
|
49
|
+
return environment;
|
|
50
|
+
}
|
|
51
|
+
function gitHttpAuthorizationClearArgs(remoteUrl) {
|
|
52
|
+
let remote;
|
|
53
|
+
try {
|
|
54
|
+
remote = new URL(remoteUrl);
|
|
55
|
+
} catch {
|
|
56
|
+
return ["-c", "http.extraHeader="];
|
|
57
|
+
}
|
|
58
|
+
if ((remote.protocol === "https:" || remote.protocol === "http:") && (remote.username || remote.password)) {
|
|
59
|
+
throw new Error("Git credentials must not be embedded in a remote URL");
|
|
60
|
+
}
|
|
61
|
+
if (remote.protocol !== "https:" && remote.protocol !== "http:") return ["-c", "http.extraHeader="];
|
|
62
|
+
remote.hash = "";
|
|
63
|
+
remote.search = "";
|
|
64
|
+
return ["-c", "http.extraHeader=", "-c", `http.${remote.origin}/.extraHeader=`, "-c", `http.${remote.href}.extraHeader=`];
|
|
65
|
+
}
|
|
66
|
+
function gitCredentialHelperConfigArgs(credentialHelper) {
|
|
67
|
+
return [
|
|
68
|
+
"-c",
|
|
69
|
+
"credential.helper=",
|
|
70
|
+
...credentialHelper ? ["-c", `credential.helper=${credentialHelper}`] : [],
|
|
71
|
+
"-c",
|
|
72
|
+
"credential.useHttpPath=false"
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
function gitCredentialUsernameConfigKey(remoteUrl) {
|
|
76
|
+
const remote = new URL(remoteUrl);
|
|
77
|
+
if ((remote.protocol === "https:" || remote.protocol === "http:") && (remote.username || remote.password)) {
|
|
78
|
+
throw new Error("Git credentials must not be embedded in a remote URL");
|
|
79
|
+
}
|
|
80
|
+
if (remote.protocol !== "https:" && remote.protocol !== "http:") {
|
|
81
|
+
throw new Error("Git credential usernames require an HTTP remote URL");
|
|
82
|
+
}
|
|
83
|
+
remote.hash = "";
|
|
84
|
+
remote.search = "";
|
|
85
|
+
return `credential.${remote.href}.username`;
|
|
86
|
+
}
|
|
87
|
+
function gitCredentialUsernameConfigArgs(remoteUrl, credentialUsername) {
|
|
88
|
+
if (!credentialUsername) return [];
|
|
89
|
+
if (/[\0\r\n]/.test(credentialUsername)) throw new Error("Invalid Git credential username");
|
|
90
|
+
return ["-c", `${gitCredentialUsernameConfigKey(remoteUrl)}=${credentialUsername}`];
|
|
91
|
+
}
|
|
92
|
+
function gitTransportSecurityArgs(remoteUrl, credentialHelper, credentialUsername) {
|
|
93
|
+
return [
|
|
94
|
+
...gitHttpAuthorizationClearArgs(remoteUrl),
|
|
95
|
+
...gitCredentialHelperConfigArgs(credentialHelper),
|
|
96
|
+
...gitCredentialUsernameConfigArgs(remoteUrl, credentialUsername)
|
|
97
|
+
];
|
|
98
|
+
}
|
|
99
|
+
export {
|
|
100
|
+
gitCredentialHelperConfigArgs,
|
|
101
|
+
gitCredentialUsernameConfigKey,
|
|
102
|
+
gitHttpAuthorizationClearArgs,
|
|
103
|
+
gitTransportSecurityArgs,
|
|
104
|
+
workerGitProcessEnvironment
|
|
105
|
+
};
|