@ricsam/r5d-worker 0.0.76 → 0.0.78
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 +279 -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 +278 -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
|
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
|
+
import { gitCredentialUsernameConfigKey, gitTransportSecurityArgs, workerGitProcessEnvironment } from "./git-process-environment.mjs";
|
|
5
6
|
import { validateManagedBranchName } from "./managed-paths.mjs";
|
|
6
7
|
import { mirrorWorkingTree } from "./working-tree-mirror.mjs";
|
|
7
8
|
const PROJECT_WORKTREE_SNAPSHOT_PREFIX = "r5d-project-worktrees-";
|
|
@@ -33,25 +34,15 @@ const NON_RECURSIVE_GIT_CONFIG = [
|
|
|
33
34
|
"-c",
|
|
34
35
|
"push.recurseSubmodules=false"
|
|
35
36
|
];
|
|
36
|
-
function
|
|
37
|
-
|
|
38
|
-
const url = new URL(value);
|
|
39
|
-
return `${url.protocol}//${url.host}`;
|
|
40
|
-
} catch {
|
|
41
|
-
return value.replace(/\/+$/, "");
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
function authArgs(auth) {
|
|
45
|
-
if (!auth) return [];
|
|
46
|
-
const key = `http.${normalizedHttpOrigin(auth.extraHeaderUrl)}/.extraHeader`;
|
|
47
|
-
return ["-c", "http.extraHeader=", "-c", `${key}=`, "-c", `${key}=${auth.header}`];
|
|
37
|
+
function gitCommandArgs(args) {
|
|
38
|
+
return ["git", ...NON_RECURSIVE_GIT_CONFIG, ...args];
|
|
48
39
|
}
|
|
49
|
-
function gitResult(cwd, args
|
|
50
|
-
const result = Bun.spawnSync(
|
|
40
|
+
function gitResult(cwd, args) {
|
|
41
|
+
const result = Bun.spawnSync(gitCommandArgs(args), {
|
|
51
42
|
cwd,
|
|
52
43
|
stdout: "pipe",
|
|
53
44
|
stderr: "pipe",
|
|
54
|
-
env:
|
|
45
|
+
env: workerGitProcessEnvironment()
|
|
55
46
|
});
|
|
56
47
|
return {
|
|
57
48
|
exitCode: result.exitCode,
|
|
@@ -59,13 +50,13 @@ function gitResult(cwd, args, auth) {
|
|
|
59
50
|
stderr: result.stderr.toString().trim()
|
|
60
51
|
};
|
|
61
52
|
}
|
|
62
|
-
function git(cwd, args, action
|
|
63
|
-
const result = gitResult(cwd, args
|
|
53
|
+
function git(cwd, args, action) {
|
|
54
|
+
const result = gitResult(cwd, args);
|
|
64
55
|
if (result.exitCode !== 0) throw new Error(`${action}: ${result.stderr || result.stdout || `git exited ${result.exitCode}`}`);
|
|
65
56
|
return result.stdout;
|
|
66
57
|
}
|
|
67
|
-
function tryGit(cwd, args
|
|
68
|
-
return gitResult(cwd, args
|
|
58
|
+
function tryGit(cwd, args) {
|
|
59
|
+
return gitResult(cwd, args).exitCode === 0;
|
|
69
60
|
}
|
|
70
61
|
function branchPath(projectRoot, branchName) {
|
|
71
62
|
validateManagedBranchName(branchName);
|
|
@@ -192,7 +183,16 @@ function configureRepository(input) {
|
|
|
192
183
|
} else {
|
|
193
184
|
git(input.primaryPath, ["remote", "add", "origin", input.originUrl], "configure project origin");
|
|
194
185
|
}
|
|
186
|
+
tryGit(input.primaryPath, ["config", "--local", "--unset-all", "remote.origin.pushurl"]);
|
|
195
187
|
git(input.primaryPath, ["config", "--local", "--replace-all", "credential.helper", ""], "reset project credential helpers");
|
|
188
|
+
git(input.primaryPath, ["config", "--local", "credential.useHttpPath", "false"], "configure project credential path matching");
|
|
189
|
+
if (input.originCredentialUsername) {
|
|
190
|
+
git(
|
|
191
|
+
input.primaryPath,
|
|
192
|
+
["config", "--local", "--replace-all", gitCredentialUsernameConfigKey(input.originUrl), input.originCredentialUsername],
|
|
193
|
+
"configure project credential username"
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
196
|
if (input.credentialHelper) {
|
|
197
197
|
git(
|
|
198
198
|
input.primaryPath,
|
|
@@ -209,19 +209,25 @@ function configureRepository(input) {
|
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
211
|
function fetchProjectHeads(input) {
|
|
212
|
-
const originFetch = gitResult(
|
|
213
|
-
input.
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
212
|
+
const originFetch = gitResult(input.primaryPath, [
|
|
213
|
+
...gitTransportSecurityArgs(input.originUrl, input.credentialHelper, input.originCredentialUsername),
|
|
214
|
+
"fetch",
|
|
215
|
+
"--no-recurse-submodules",
|
|
216
|
+
"--prune",
|
|
217
|
+
"origin",
|
|
218
|
+
"+refs/heads/*:refs/remotes/origin/*"
|
|
219
|
+
]);
|
|
217
220
|
if (originFetch.exitCode !== 0) {
|
|
218
221
|
throw new Error(`Fetch project origin: ${originFetch.stderr || originFetch.stdout || `git exited ${originFetch.exitCode}`}`);
|
|
219
222
|
}
|
|
220
|
-
const mirrorFetch = gitResult(
|
|
221
|
-
input.
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
223
|
+
const mirrorFetch = gitResult(input.primaryPath, [
|
|
224
|
+
...gitTransportSecurityArgs(input.mirrorUrl, input.credentialHelper, input.mirrorCredentialUsername),
|
|
225
|
+
"fetch",
|
|
226
|
+
"--no-recurse-submodules",
|
|
227
|
+
"--prune",
|
|
228
|
+
input.mirrorUrl,
|
|
229
|
+
"+refs/heads/*:refs/r5d/mirror/*"
|
|
230
|
+
]);
|
|
225
231
|
if (mirrorFetch.exitCode !== 0 && !/(couldn't find remote ref|does not have any commits|remote repository is empty|no such ref)/i.test(
|
|
226
232
|
`${mirrorFetch.stderr}
|
|
227
233
|
${mirrorFetch.stdout}`
|
|
@@ -231,11 +237,19 @@ ${mirrorFetch.stdout}`
|
|
|
231
237
|
}
|
|
232
238
|
function ensureCommitAvailable(input) {
|
|
233
239
|
if (tryGit(input.primaryPath, ["cat-file", "-e", `${input.commitHash}^{commit}`])) return;
|
|
234
|
-
for (const [url,
|
|
235
|
-
[input.mirrorUrl, input.
|
|
236
|
-
[input.originUrl, input.
|
|
240
|
+
for (const [url, username] of [
|
|
241
|
+
[input.mirrorUrl, input.mirrorCredentialUsername],
|
|
242
|
+
[input.originUrl, input.originCredentialUsername]
|
|
237
243
|
]) {
|
|
238
|
-
if (tryGit(input.primaryPath, [
|
|
244
|
+
if (tryGit(input.primaryPath, [
|
|
245
|
+
...gitTransportSecurityArgs(url, input.credentialHelper, username),
|
|
246
|
+
"fetch",
|
|
247
|
+
"--no-recurse-submodules",
|
|
248
|
+
"--no-tags",
|
|
249
|
+
url,
|
|
250
|
+
input.commitHash
|
|
251
|
+
]))
|
|
252
|
+
return;
|
|
239
253
|
}
|
|
240
254
|
throw new Error(`Project base commit ${input.commitHash} is unavailable from origin and the canonical mirror`);
|
|
241
255
|
}
|
|
@@ -439,11 +453,13 @@ function removeProjectWorktrees(input) {
|
|
|
439
453
|
}
|
|
440
454
|
function deleteProjectMirrorBranch(input) {
|
|
441
455
|
validateManagedBranchName(input.branchName);
|
|
442
|
-
const deleted = gitResult(
|
|
443
|
-
input.
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
456
|
+
const deleted = gitResult(input.gitDirectory, [
|
|
457
|
+
...gitTransportSecurityArgs(input.mirrorUrl, input.credentialHelper, input.credentialUsername),
|
|
458
|
+
"push",
|
|
459
|
+
"--no-recurse-submodules",
|
|
460
|
+
input.mirrorUrl,
|
|
461
|
+
`:refs/heads/${input.branchName}`
|
|
462
|
+
]);
|
|
447
463
|
if (deleted.exitCode !== 0 && !/(remote ref does not exist|unable to delete|no such ref)/i.test(`${deleted.stderr}
|
|
448
464
|
${deleted.stdout}`)) {
|
|
449
465
|
throw new Error(
|
|
@@ -463,9 +479,14 @@ function pushProjectMirrorHeads(input) {
|
|
|
463
479
|
}
|
|
464
480
|
git(
|
|
465
481
|
checkoutPath,
|
|
466
|
-
[
|
|
467
|
-
|
|
468
|
-
|
|
482
|
+
[
|
|
483
|
+
...gitTransportSecurityArgs(input.mirrorUrl, input.credentialHelper, input.credentialUsername),
|
|
484
|
+
"push",
|
|
485
|
+
"--no-recurse-submodules",
|
|
486
|
+
input.mirrorUrl,
|
|
487
|
+
`+HEAD:refs/heads/${branchName}`
|
|
488
|
+
],
|
|
489
|
+
`push hidden project mirror for ${branchName}`
|
|
469
490
|
);
|
|
470
491
|
results.push({ branchName, head, pushed: true });
|
|
471
492
|
}
|
|
@@ -473,11 +494,14 @@ function pushProjectMirrorHeads(input) {
|
|
|
473
494
|
}
|
|
474
495
|
function fastForwardProjectHeadsFromMirror(input) {
|
|
475
496
|
const primaryPath = branchPath(input.projectRoot, input.primaryBranchName);
|
|
476
|
-
const fetch = gitResult(
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
497
|
+
const fetch = gitResult(primaryPath, [
|
|
498
|
+
...gitTransportSecurityArgs(input.mirrorUrl, input.credentialHelper, input.credentialUsername),
|
|
499
|
+
"fetch",
|
|
500
|
+
"--no-recurse-submodules",
|
|
501
|
+
"--prune",
|
|
502
|
+
input.mirrorUrl,
|
|
503
|
+
"+refs/heads/*:refs/r5d/mirror/*"
|
|
504
|
+
]);
|
|
481
505
|
if (fetch.exitCode !== 0)
|
|
482
506
|
throw new Error(`Refresh project head mirror: ${fetch.stderr || fetch.stdout || `git exited ${fetch.exitCode}`}`);
|
|
483
507
|
return [...input.branchNames].sort().map((branchName) => {
|
|
@@ -494,7 +518,8 @@ function fastForwardProjectHeadsFromMirror(input) {
|
|
|
494
518
|
});
|
|
495
519
|
}
|
|
496
520
|
const projectWorktreesTestHarness = {
|
|
497
|
-
commandArgs:
|
|
521
|
+
commandArgs: gitCommandArgs,
|
|
522
|
+
configureRepository
|
|
498
523
|
};
|
|
499
524
|
export {
|
|
500
525
|
PROJECT_WORKTREE_SNAPSHOT_PREFIX,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { workerGitProcessEnvironment } from "./git-process-environment.mjs";
|
|
3
4
|
function isGitMetadataPath(relativePath) {
|
|
4
5
|
return relativePath.split("/").includes(".git");
|
|
5
6
|
}
|
|
@@ -36,7 +37,7 @@ function gitEligibleRoots(root) {
|
|
|
36
37
|
cwd: root,
|
|
37
38
|
stdout: "pipe",
|
|
38
39
|
stderr: "pipe",
|
|
39
|
-
env:
|
|
40
|
+
env: workerGitProcessEnvironment()
|
|
40
41
|
}
|
|
41
42
|
);
|
|
42
43
|
if (result.exitCode !== 0) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { gitCredentialUsernameConfigKey, gitTransportSecurityArgs, workerGitProcessEnvironment } from "./git-process-environment.mjs";
|
|
3
4
|
import { mirrorWorkingTree } from "./working-tree-mirror.mjs";
|
|
4
5
|
const WORKSPACE_GIT_BRANCH = "main";
|
|
5
6
|
const MAX_WORKSPACE_GIT_DIFF_BYTES = 5 * 1024 * 1024;
|
|
@@ -13,25 +14,18 @@ const NON_RECURSIVE_GIT_CONFIG = [
|
|
|
13
14
|
"-c",
|
|
14
15
|
"push.recurseSubmodules=false"
|
|
15
16
|
];
|
|
16
|
-
function
|
|
17
|
-
|
|
18
|
-
const url = new URL(value);
|
|
19
|
-
return `${url.protocol}//${url.host}`;
|
|
20
|
-
} catch {
|
|
21
|
-
return value.replace(/\/+$/, "");
|
|
22
|
-
}
|
|
17
|
+
function gitCommandArgs(args) {
|
|
18
|
+
return ["git", ...NON_RECURSIVE_GIT_CONFIG, ...args];
|
|
23
19
|
}
|
|
24
|
-
function
|
|
25
|
-
|
|
26
|
-
const key = `http.${normalizedHttpOrigin(auth.extraHeaderUrl)}/.extraHeader`;
|
|
27
|
-
return ["-c", "http.extraHeader=", "-c", `${key}=`, "-c", `${key}=${auth.header}`];
|
|
20
|
+
function workspaceCloneCommandArgs(args, remoteUrl, credentialHelper, credentialUsername) {
|
|
21
|
+
return gitCommandArgs([...gitTransportSecurityArgs(remoteUrl, credentialHelper, credentialUsername), ...args]);
|
|
28
22
|
}
|
|
29
|
-
function gitResult(cwd, args
|
|
30
|
-
const result = Bun.spawnSync(
|
|
23
|
+
function gitResult(cwd, args) {
|
|
24
|
+
const result = Bun.spawnSync(gitCommandArgs(args), {
|
|
31
25
|
cwd,
|
|
32
26
|
stdout: "pipe",
|
|
33
27
|
stderr: "pipe",
|
|
34
|
-
env:
|
|
28
|
+
env: workerGitProcessEnvironment()
|
|
35
29
|
});
|
|
36
30
|
return {
|
|
37
31
|
exitCode: result.exitCode,
|
|
@@ -39,13 +33,13 @@ function gitResult(cwd, args, auth) {
|
|
|
39
33
|
stderr: result.stderr.toString().trim()
|
|
40
34
|
};
|
|
41
35
|
}
|
|
42
|
-
function git(cwd, args, action
|
|
43
|
-
const result = gitResult(cwd, args
|
|
36
|
+
function git(cwd, args, action) {
|
|
37
|
+
const result = gitResult(cwd, args);
|
|
44
38
|
if (result.exitCode !== 0) throw new Error(`${action}: ${result.stderr || result.stdout || `git exited ${result.exitCode}`}`);
|
|
45
39
|
return result.stdout;
|
|
46
40
|
}
|
|
47
|
-
function tryGit(cwd, args
|
|
48
|
-
return gitResult(cwd, args
|
|
41
|
+
function tryGit(cwd, args) {
|
|
42
|
+
return gitResult(cwd, args).exitCode === 0;
|
|
49
43
|
}
|
|
50
44
|
function revParse(workspacePath, revision) {
|
|
51
45
|
const result = gitResult(workspacePath, ["rev-parse", "--verify", revision]);
|
|
@@ -91,9 +85,22 @@ function configureWorkspaceRepository(input) {
|
|
|
91
85
|
} else {
|
|
92
86
|
git(input.workspacePath, ["remote", "add", "origin", input.remoteUrl], "configure workspace origin");
|
|
93
87
|
}
|
|
88
|
+
tryGit(input.workspacePath, ["config", "--local", "--unset-all", "remote.origin.pushurl"]);
|
|
94
89
|
git(input.workspacePath, ["config", "--local", "--replace-all", "credential.helper", ""], "reset workspace credential helpers");
|
|
90
|
+
git(input.workspacePath, ["config", "--local", "credential.useHttpPath", "false"], "configure workspace credential path matching");
|
|
91
|
+
if (input.credentialUsername) {
|
|
92
|
+
git(
|
|
93
|
+
input.workspacePath,
|
|
94
|
+
["config", "--local", "--replace-all", gitCredentialUsernameConfigKey(input.remoteUrl), input.credentialUsername],
|
|
95
|
+
"configure workspace credential username"
|
|
96
|
+
);
|
|
97
|
+
}
|
|
95
98
|
if (input.credentialHelper) {
|
|
96
|
-
git(
|
|
99
|
+
git(
|
|
100
|
+
input.workspacePath,
|
|
101
|
+
["config", "--local", "--add", "credential.helper", input.credentialHelper],
|
|
102
|
+
"configure workspace credential helper"
|
|
103
|
+
);
|
|
97
104
|
}
|
|
98
105
|
const name = input.gitIdentity.name.trim();
|
|
99
106
|
const email = input.gitIdentity.email.trim();
|
|
@@ -101,19 +108,16 @@ function configureWorkspaceRepository(input) {
|
|
|
101
108
|
git(input.workspacePath, ["config", "--local", "user.name", name], "configure workspace Git user name");
|
|
102
109
|
git(input.workspacePath, ["config", "--local", "user.email", email], "configure workspace Git user email");
|
|
103
110
|
}
|
|
104
|
-
function fetchWorkspaceHead(workspacePath,
|
|
105
|
-
const result = gitResult(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
],
|
|
115
|
-
remoteAuth
|
|
116
|
-
);
|
|
111
|
+
function fetchWorkspaceHead(workspacePath, remoteUrl, credentialHelper, credentialUsername) {
|
|
112
|
+
const result = gitResult(workspacePath, [
|
|
113
|
+
...gitTransportSecurityArgs(remoteUrl, credentialHelper, credentialUsername),
|
|
114
|
+
"fetch",
|
|
115
|
+
"--no-recurse-submodules",
|
|
116
|
+
"--prune",
|
|
117
|
+
"--update-shallow",
|
|
118
|
+
"origin",
|
|
119
|
+
`+refs/heads/${WORKSPACE_GIT_BRANCH}:refs/remotes/origin/${WORKSPACE_GIT_BRANCH}`
|
|
120
|
+
]);
|
|
117
121
|
if (result.exitCode !== 0) {
|
|
118
122
|
const detail = `${result.stderr}
|
|
119
123
|
${result.stdout}`;
|
|
@@ -130,11 +134,17 @@ function ensureWorkspaceGitClone(input) {
|
|
|
130
134
|
if (!fs.existsSync(gitPath)) {
|
|
131
135
|
fs.rmSync(workspacePath, { recursive: true, force: true });
|
|
132
136
|
fs.mkdirSync(path.dirname(workspacePath), { recursive: true });
|
|
133
|
-
const
|
|
134
|
-
void 0,
|
|
137
|
+
const cloneCommand = workspaceCloneCommandArgs(
|
|
135
138
|
["clone", "--no-recurse-submodules", "--branch", WORKSPACE_GIT_BRANCH, input.remoteUrl, workspacePath],
|
|
136
|
-
input.
|
|
139
|
+
input.remoteUrl,
|
|
140
|
+
input.credentialHelper,
|
|
141
|
+
input.credentialUsername
|
|
137
142
|
);
|
|
143
|
+
const clone = Bun.spawnSync(cloneCommand, {
|
|
144
|
+
stdout: "pipe",
|
|
145
|
+
stderr: "pipe",
|
|
146
|
+
env: workerGitProcessEnvironment()
|
|
147
|
+
});
|
|
138
148
|
if (clone.exitCode !== 0) {
|
|
139
149
|
fs.rmSync(workspacePath, { recursive: true, force: true });
|
|
140
150
|
fs.mkdirSync(workspacePath, { recursive: true });
|
|
@@ -147,7 +157,7 @@ function ensureWorkspaceGitClone(input) {
|
|
|
147
157
|
if (!revParse(workspacePath, WORKSPACE_GIT_INTEGRATED_REF) && previousRemoteHead && localHeadBeforeFetch && tryGit(workspacePath, ["merge-base", "--is-ancestor", previousRemoteHead, localHeadBeforeFetch])) {
|
|
148
158
|
updateIntegratedWorkspaceHead(workspacePath, previousRemoteHead);
|
|
149
159
|
}
|
|
150
|
-
const remoteHead = fetchWorkspaceHead(workspacePath, input.
|
|
160
|
+
const remoteHead = fetchWorkspaceHead(workspacePath, input.remoteUrl, input.credentialHelper, input.credentialUsername);
|
|
151
161
|
let localHead = revParse(workspacePath, "HEAD");
|
|
152
162
|
if (!localHead && remoteHead) {
|
|
153
163
|
git(
|
|
@@ -233,7 +243,7 @@ function resetWorkspaceGit(input) {
|
|
|
233
243
|
const initial = ensureWorkspaceGitClone({ ...input, workspacePath });
|
|
234
244
|
const status = gitResult(workspacePath, ["status", "--porcelain=v1", "-z"]);
|
|
235
245
|
const discardedPaths = status.exitCode === 0 ? status.stdout.split("\0").filter(Boolean).map((entry) => entry.slice(3)).sort() : [];
|
|
236
|
-
const remoteHead = fetchWorkspaceHead(workspacePath, input.
|
|
246
|
+
const remoteHead = fetchWorkspaceHead(workspacePath, input.remoteUrl, input.credentialHelper, input.credentialUsername);
|
|
237
247
|
if (remoteHead) {
|
|
238
248
|
git(workspacePath, ["checkout", "--no-recurse-submodules", "-B", WORKSPACE_GIT_BRANCH, remoteHead], "reset workspace main");
|
|
239
249
|
git(workspacePath, ["clean", "-fd", "--", "."], "remove untracked workspace changes");
|
|
@@ -259,9 +269,10 @@ function emptyTreeHash(workspacePath) {
|
|
|
259
269
|
stdin: Buffer.alloc(0),
|
|
260
270
|
stdout: "pipe",
|
|
261
271
|
stderr: "pipe",
|
|
262
|
-
env:
|
|
272
|
+
env: workerGitProcessEnvironment()
|
|
263
273
|
});
|
|
264
|
-
if (result.exitCode !== 0)
|
|
274
|
+
if (result.exitCode !== 0)
|
|
275
|
+
throw new Error(`Create empty workspace tree: ${result.stderr.toString().trim() || `git exited ${result.exitCode}`}`);
|
|
265
276
|
return result.stdout.toString().trim();
|
|
266
277
|
}
|
|
267
278
|
function changedPaths(workspacePath, baseRevision, headRevision) {
|
|
@@ -270,15 +281,12 @@ function changedPaths(workspacePath, baseRevision, headRevision) {
|
|
|
270
281
|
}
|
|
271
282
|
async function diffSizeBytes(workspacePath, baseRevision, headRevision, limit) {
|
|
272
283
|
const base = baseRevision ?? emptyTreeHash(workspacePath);
|
|
273
|
-
const subprocess = Bun.spawn(
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
|
|
280
|
-
}
|
|
281
|
-
);
|
|
284
|
+
const subprocess = Bun.spawn(["git", ...NON_RECURSIVE_GIT_CONFIG, "diff", "--binary", "--no-ext-diff", base, headRevision], {
|
|
285
|
+
cwd: workspacePath,
|
|
286
|
+
stdout: "pipe",
|
|
287
|
+
stderr: "pipe",
|
|
288
|
+
env: workerGitProcessEnvironment()
|
|
289
|
+
});
|
|
282
290
|
const stderrPromise = new Response(subprocess.stderr).text();
|
|
283
291
|
const reader = subprocess.stdout.getReader();
|
|
284
292
|
let total = 0;
|
|
@@ -399,7 +407,7 @@ async function synchronizeWorkspaceGit(input) {
|
|
|
399
407
|
"commit workspace working trees"
|
|
400
408
|
);
|
|
401
409
|
}
|
|
402
|
-
let remoteHead = fetchWorkspaceHead(workspacePath, input.
|
|
410
|
+
let remoteHead = fetchWorkspaceHead(workspacePath, input.remoteUrl, input.credentialHelper, input.credentialUsername);
|
|
403
411
|
let rebaseCount = 0;
|
|
404
412
|
let updated = false;
|
|
405
413
|
for (let pushAttempt = 0; pushAttempt < maxPushAttempts; pushAttempt += 1) {
|
|
@@ -475,10 +483,14 @@ async function synchronizeWorkspaceGit(input) {
|
|
|
475
483
|
skippedMountIds: selected.skipped.map(({ id }) => id).sort()
|
|
476
484
|
};
|
|
477
485
|
}
|
|
478
|
-
const pushArgs = [
|
|
486
|
+
const pushArgs = [
|
|
487
|
+
...gitTransportSecurityArgs(input.remoteUrl, input.credentialHelper, input.credentialUsername),
|
|
488
|
+
"push",
|
|
489
|
+
"--no-recurse-submodules"
|
|
490
|
+
];
|
|
479
491
|
if (input.allowLargeDiff) pushArgs.push(`--push-option=${WORKSPACE_GIT_CONFIRMED_LARGE_DIFF_PUSH_OPTION}`);
|
|
480
492
|
pushArgs.push("origin", `HEAD:refs/heads/${WORKSPACE_GIT_BRANCH}`);
|
|
481
|
-
const push = gitResult(workspacePath, pushArgs
|
|
493
|
+
const push = gitResult(workspacePath, pushArgs);
|
|
482
494
|
if (push.exitCode === 0) {
|
|
483
495
|
updateIntegratedWorkspaceHead(workspacePath, localHead);
|
|
484
496
|
await input.afterWorkspacePublished?.({
|
|
@@ -503,12 +515,14 @@ async function synchronizeWorkspaceGit(input) {
|
|
|
503
515
|
${push.stdout}`)) {
|
|
504
516
|
throw new Error(`Push workspace main: ${push.stderr || push.stdout || `git exited ${push.exitCode}`}`);
|
|
505
517
|
}
|
|
506
|
-
remoteHead = fetchWorkspaceHead(workspacePath, input.
|
|
518
|
+
remoteHead = fetchWorkspaceHead(workspacePath, input.remoteUrl, input.credentialHelper, input.credentialUsername);
|
|
507
519
|
}
|
|
508
520
|
throw new Error(`Workspace push did not converge after ${maxPushAttempts} attempts`);
|
|
509
521
|
}
|
|
510
522
|
const workspaceGitSyncTestHarness = {
|
|
511
|
-
commandArgs:
|
|
523
|
+
commandArgs: gitCommandArgs,
|
|
524
|
+
workspaceCloneCommandArgs,
|
|
525
|
+
configureWorkspaceRepository,
|
|
512
526
|
mirrorMountsToWorkspace,
|
|
513
527
|
hydrateMountsFromWorkspace: hydrateWorkspaceGitMounts
|
|
514
528
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Build the complete environment for a worker-owned Git child process. */
|
|
2
|
+
export declare function workerGitProcessEnvironment(source?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
3
|
+
/** Clear inherited and repository-local HTTP authorization headers for one transport URL. */
|
|
4
|
+
export declare function gitHttpAuthorizationClearArgs(remoteUrl: string): string[];
|
|
5
|
+
/** Select only the app-owned credential store, regardless of repository-local helpers. */
|
|
6
|
+
export declare function gitCredentialHelperConfigArgs(credentialHelper?: string | null): string[];
|
|
7
|
+
export declare function gitCredentialUsernameConfigKey(remoteUrl: string): string;
|
|
8
|
+
/** Non-secret command configuration required for an authenticated HTTP transport. */
|
|
9
|
+
export declare function gitTransportSecurityArgs(remoteUrl: string, credentialHelper?: string | null, credentialUsername?: string | null): string[];
|
package/dist/types/main.d.ts
CHANGED
|
@@ -130,6 +130,30 @@ type ResolvedWorkerFilePath = {
|
|
|
130
130
|
virtualRootPath: string;
|
|
131
131
|
};
|
|
132
132
|
export declare function resolveWorkerFilePath(branchPath: string, inputPath: string, builtInPaths?: WorkerBuiltInToolPaths): ResolvedWorkerFilePath;
|
|
133
|
+
declare function readOnlyCredentialStoreHelper(storePath: string): string;
|
|
134
|
+
declare function legacySharedCredentialStorePath(): string;
|
|
135
|
+
declare function credentialStorePathForProject(projectId: string): string;
|
|
136
|
+
declare function credentialStorePathForWorkspace(remoteUrl: string): string;
|
|
137
|
+
declare function removeLegacySharedCredentialStore(storePath?: string): void;
|
|
138
|
+
declare function resetCredentialStoreDirectory(directoryPath?: string): void;
|
|
139
|
+
declare function pruneCredentialStoreFiles(requiredStorePaths: ReadonlySet<string>, directoryPath?: string): string[];
|
|
140
|
+
declare function replaceCredentialStoreFile(storePath: string, content: string): void;
|
|
141
|
+
declare function credentialHelperForRemotes(remotes: readonly {
|
|
142
|
+
remoteUrl: string;
|
|
143
|
+
authHeader: string | null | undefined;
|
|
144
|
+
}[], storePath: string): string | null;
|
|
145
|
+
export declare const workerGitSecurityTestHarness: {
|
|
146
|
+
commandArgs: (args: string[]) => string[];
|
|
147
|
+
credentialHelperForRemotes: typeof credentialHelperForRemotes;
|
|
148
|
+
credentialStorePathForProject: typeof credentialStorePathForProject;
|
|
149
|
+
credentialStorePathForWorkspace: typeof credentialStorePathForWorkspace;
|
|
150
|
+
legacySharedCredentialStorePath: typeof legacySharedCredentialStorePath;
|
|
151
|
+
removeLegacySharedCredentialStore: typeof removeLegacySharedCredentialStore;
|
|
152
|
+
pruneCredentialStoreFiles: typeof pruneCredentialStoreFiles;
|
|
153
|
+
readOnlyCredentialStoreHelper: typeof readOnlyCredentialStoreHelper;
|
|
154
|
+
resetCredentialStoreDirectory: typeof resetCredentialStoreDirectory;
|
|
155
|
+
replaceCredentialStoreFile: typeof replaceCredentialStoreFile;
|
|
156
|
+
};
|
|
133
157
|
export declare function githubCliEnv(token: string | null | undefined): Record<string, string>;
|
|
134
158
|
type ResolvedWorkerSessionTarget = {
|
|
135
159
|
target: WorkerSessionTarget;
|
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
export declare const PROJECT_WORKTREE_SNAPSHOT_PREFIX = "r5d-project-worktrees-";
|
|
2
|
-
export type GitHttpAuth = {
|
|
3
|
-
extraHeaderUrl: string;
|
|
4
|
-
header: string;
|
|
5
|
-
};
|
|
6
2
|
export type ProjectWorktreeBranch = {
|
|
7
3
|
branchName: string;
|
|
8
4
|
baseCommitHash: string;
|
|
@@ -28,6 +24,7 @@ export declare function projectWorktreeConfigurationFingerprint(input: {
|
|
|
28
24
|
email: string;
|
|
29
25
|
} | null;
|
|
30
26
|
}): string;
|
|
27
|
+
declare function gitCommandArgs(args: string[]): string[];
|
|
31
28
|
export declare function hasLinkedProjectWorktreeLayout(input: {
|
|
32
29
|
projectRoot: string;
|
|
33
30
|
primaryBranchName: string;
|
|
@@ -43,15 +40,25 @@ export declare function cleanupStaleProjectWorktreeSnapshots(input?: {
|
|
|
43
40
|
error: string;
|
|
44
41
|
}>;
|
|
45
42
|
};
|
|
43
|
+
declare function configureRepository(input: {
|
|
44
|
+
primaryPath: string;
|
|
45
|
+
originUrl: string;
|
|
46
|
+
credentialHelper?: string | null;
|
|
47
|
+
originCredentialUsername?: string | null;
|
|
48
|
+
gitIdentity?: {
|
|
49
|
+
name: string;
|
|
50
|
+
email: string;
|
|
51
|
+
} | null;
|
|
52
|
+
}): void;
|
|
46
53
|
export declare function ensureProjectWorktrees(input: {
|
|
47
54
|
projectRoot: string;
|
|
48
55
|
primaryBranchName: string;
|
|
49
56
|
branches: readonly ProjectWorktreeBranch[];
|
|
50
57
|
originUrl: string;
|
|
51
|
-
originAuth?: GitHttpAuth;
|
|
52
58
|
mirrorUrl: string;
|
|
53
|
-
mirrorAuth?: GitHttpAuth;
|
|
54
59
|
credentialHelper?: string | null;
|
|
60
|
+
originCredentialUsername?: string | null;
|
|
61
|
+
mirrorCredentialUsername?: string | null;
|
|
55
62
|
gitIdentity?: {
|
|
56
63
|
name: string;
|
|
57
64
|
email: string;
|
|
@@ -81,13 +88,15 @@ export declare function deleteProjectMirrorBranch(input: {
|
|
|
81
88
|
gitDirectory: string;
|
|
82
89
|
branchName: string;
|
|
83
90
|
mirrorUrl: string;
|
|
84
|
-
|
|
91
|
+
credentialHelper?: string | null;
|
|
92
|
+
credentialUsername?: string | null;
|
|
85
93
|
}): void;
|
|
86
94
|
export declare function pushProjectMirrorHeads(input: {
|
|
87
95
|
projectRoot: string;
|
|
88
96
|
branchNames: readonly string[];
|
|
89
97
|
mirrorUrl: string;
|
|
90
|
-
|
|
98
|
+
credentialHelper?: string | null;
|
|
99
|
+
credentialUsername?: string | null;
|
|
91
100
|
onlyBranches?: ReadonlySet<string>;
|
|
92
101
|
}): Array<{
|
|
93
102
|
branchName: string;
|
|
@@ -100,7 +109,8 @@ export declare function fastForwardProjectHeadsFromMirror(input: {
|
|
|
100
109
|
primaryBranchName: string;
|
|
101
110
|
branchNames: readonly string[];
|
|
102
111
|
mirrorUrl: string;
|
|
103
|
-
|
|
112
|
+
credentialHelper?: string | null;
|
|
113
|
+
credentialUsername?: string | null;
|
|
104
114
|
}): Array<{
|
|
105
115
|
branchName: string;
|
|
106
116
|
previousHead: string;
|
|
@@ -108,5 +118,7 @@ export declare function fastForwardProjectHeadsFromMirror(input: {
|
|
|
108
118
|
fastForwarded: boolean;
|
|
109
119
|
}>;
|
|
110
120
|
export declare const projectWorktreesTestHarness: {
|
|
111
|
-
commandArgs:
|
|
121
|
+
commandArgs: typeof gitCommandArgs;
|
|
122
|
+
configureRepository: typeof configureRepository;
|
|
112
123
|
};
|
|
124
|
+
export {};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { type WorkingTreeSourceMode } from "./working-tree-mirror";
|
|
2
|
-
import type { GitHttpAuth } from "./project-worktrees";
|
|
3
2
|
export declare const WORKSPACE_GIT_BRANCH = "main";
|
|
4
3
|
export declare const MAX_WORKSPACE_GIT_DIFF_BYTES: number;
|
|
5
4
|
export declare const WORKSPACE_GIT_CONFIRMED_LARGE_DIFF_PUSH_OPTION = "r5d-confirm-large-diff-v1";
|
|
@@ -33,11 +32,23 @@ export type WorkspaceGitSyncResult = {
|
|
|
33
32
|
};
|
|
34
33
|
error?: string;
|
|
35
34
|
};
|
|
35
|
+
declare function gitCommandArgs(args: string[]): string[];
|
|
36
|
+
declare function workspaceCloneCommandArgs(args: string[], remoteUrl: string, credentialHelper?: string | null, credentialUsername?: string | null): string[];
|
|
37
|
+
declare function configureWorkspaceRepository(input: {
|
|
38
|
+
workspacePath: string;
|
|
39
|
+
remoteUrl: string;
|
|
40
|
+
credentialHelper?: string | null;
|
|
41
|
+
credentialUsername?: string | null;
|
|
42
|
+
gitIdentity: {
|
|
43
|
+
name: string;
|
|
44
|
+
email: string;
|
|
45
|
+
};
|
|
46
|
+
}): void;
|
|
36
47
|
export declare function ensureWorkspaceGitClone(input: {
|
|
37
48
|
workspacePath: string;
|
|
38
49
|
remoteUrl: string;
|
|
39
|
-
remoteAuth?: GitHttpAuth;
|
|
40
50
|
credentialHelper?: string | null;
|
|
51
|
+
credentialUsername?: string | null;
|
|
41
52
|
gitIdentity: {
|
|
42
53
|
name: string;
|
|
43
54
|
email: string;
|
|
@@ -51,8 +62,8 @@ export declare function hydrateWorkspaceGitMounts(workspacePath: string, mounts:
|
|
|
51
62
|
export declare function resetWorkspaceGit(input: {
|
|
52
63
|
workspacePath: string;
|
|
53
64
|
remoteUrl: string;
|
|
54
|
-
remoteAuth?: GitHttpAuth;
|
|
55
65
|
credentialHelper?: string | null;
|
|
66
|
+
credentialUsername?: string | null;
|
|
56
67
|
gitIdentity: {
|
|
57
68
|
name: string;
|
|
58
69
|
email: string;
|
|
@@ -69,8 +80,8 @@ export declare function synchronizeWorkspaceGit(input: {
|
|
|
69
80
|
workerLabel: string;
|
|
70
81
|
workspacePath: string;
|
|
71
82
|
remoteUrl: string;
|
|
72
|
-
remoteAuth?: GitHttpAuth;
|
|
73
83
|
credentialHelper?: string | null;
|
|
84
|
+
credentialUsername?: string | null;
|
|
74
85
|
gitIdentity: {
|
|
75
86
|
name: string;
|
|
76
87
|
email: string;
|
|
@@ -90,7 +101,9 @@ export declare function synchronizeWorkspaceGit(input: {
|
|
|
90
101
|
}) => void | Promise<void>;
|
|
91
102
|
}): Promise<WorkspaceGitSyncResult>;
|
|
92
103
|
export declare const workspaceGitSyncTestHarness: {
|
|
93
|
-
commandArgs:
|
|
104
|
+
commandArgs: typeof gitCommandArgs;
|
|
105
|
+
workspaceCloneCommandArgs: typeof workspaceCloneCommandArgs;
|
|
106
|
+
configureWorkspaceRepository: typeof configureWorkspaceRepository;
|
|
94
107
|
mirrorMountsToWorkspace: typeof mirrorMountsToWorkspace;
|
|
95
108
|
hydrateMountsFromWorkspace: typeof hydrateWorkspaceGitMounts;
|
|
96
109
|
};
|