authorgravatar for saphid@gmail.comAlex Southwell <saphid@gmail.com> 2026-10-06 20:22:56+11:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-06 04:22:56-05:00
loge65063ccf6ce83aa33d10521aeaf929210f0ff04
tree4a2a11f299cb6cdfec13c47d1bf673a35aa892f9
parent411cef0008fa8e1e5251a8aae2cc6a215f700a9c
signature Signed by PGP key B5690EEEBB952194

fix(server): end clone options before the repository URL (#14781)


2 files changed, 45 insertions(+), 2 deletions(-)

apps/server/src/sourceControl/SourceControlRepositoryService.test.ts+38-1
...@@ -179,7 +179,7 @@ it.effect("clones a looked-up repository into the requested destination", () =>...@@ -179,7 +179,7 @@ it.effect("clones a looked-up repository into the requested destination", () =>
179 assert.deepStrictEqual(cloneCalls, [179 assert.deepStrictEqual(cloneCalls, [
180 {180 {
181 cwd: parent,181 cwd: parent,
182 args: ["clone", "--progress", CLONE_URLS.url, "t3code"],182 args: ["clone", "--progress", "--", CLONE_URLS.url, "t3code"],
183 },183 },
184 ]);184 ]);
185 }).pipe(185 }).pipe(
...@@ -198,6 +198,43 @@ it.effect("clones a looked-up repository into the requested destination", () =>...@@ -198,6 +198,43 @@ it.effect("clones a looked-up repository into the requested destination", () =>
198 }).pipe(Effect.provide(NodeServices.layer)),198 }).pipe(Effect.provide(NodeServices.layer)),
199);199);
200200
201it.effect.each(["--bare", "source.git"])(
202 "clones a local repository named %s as a working tree",
203 (repositoryName) =>
204 Effect.gen(function* () {
205 const fs = yield* FileSystem.FileSystem;
206 const path = yield* Path.Path;
207 const git = yield* GitVcsDriver.GitVcsDriver;
208 const parent = yield* fs.makeTempDirectoryScoped({ prefix: "t3-clone-options-" });
209 yield* git.execute({
210 operation: "test.init",
211 cwd: parent,
212 args: ["init", "--bare", path.join(parent, repositoryName)],
213 });
214 const destinationPath = path.join(parent, "checkout");
215 yield* Effect.gen(function* () {
216 const service = yield* SourceControlRepositoryService.SourceControlRepositoryService;
217 const result = yield* service.cloneRepository({
218 remoteUrl: repositoryName,
219 destinationPath,
220 });
221 assert.strictEqual(result.cwd, destinationPath);
222 assert.isTrue(yield* fs.exists(path.join(destinationPath, ".git")));
223 }).pipe(Effect.provide(makeLayer({ git: { execute: git.execute } })));
224 }).pipe(
225 Effect.provide(
226 GitVcsDriver.layer.pipe(
227 Layer.provide(
228 ServerConfig.layerTest(process.cwd(), {
229 prefix: "t3-clone-options-config-",
230 }),
231 ),
232 Layer.provideMerge(NodeServices.layer),
233 ),
234 ),
235 ),
236);
237
201it.effect("reports clone progress from git's stderr and keeps its error text on failure", () =>238it.effect("reports clone progress from git's stderr and keeps its error text on failure", () =>
202 Effect.gen(function* () {239 Effect.gen(function* () {
203 const fs = yield* FileSystem.FileSystem;240 const fs = yield* FileSystem.FileSystem;
apps/server/src/sourceControl/SourceControlRepositoryService.ts+7-1
...@@ -303,7 +303,13 @@ export const make = Effect.gen(function* () {...@@ -303,7 +303,13 @@ export const make = Effect.gen(function* () {
303 .execute({303 .execute({
304 operation: "SourceControlRepositoryService.cloneRepository",304 operation: "SourceControlRepositoryService.cloneRepository",
305 cwd: path.dirname(prepared.destinationPath),305 cwd: path.dirname(prepared.destinationPath),
306 args: ["clone", "--progress", prepared.cloneUrl, path.basename(prepared.destinationPath)],306 args: [
307 "clone",
308 "--progress",
309 "--",
310 prepared.cloneUrl,
311 path.basename(prepared.destinationPath),
312 ],
307 timeoutMs: options?.timeoutMs === undefined ? CLONE_TIMEOUT_MS : options.timeoutMs,313 timeoutMs: options?.timeoutMs === undefined ? CLONE_TIMEOUT_MS : options.timeoutMs,
308 // Progress redraws add up on a slow multi-GB clone. The buffered copy314 // Progress redraws add up on a slow multi-GB clone. The buffered copy
309 // is never read (the tail is kept by hand above), so keep it small315 // is never read (the tail is kept by hand above), so keep it small