From e65063ccf6ce83aa33d10521aeaf929210f0ff04 Mon Sep 17 00:00:00 2001 From: Alex Southwell Date: Tue, 6 Oct 2026 20:22:56 +1100 Subject: [PATCH] fix(server): end clone options before the repository URL (#14781) --- .../SourceControlRepositoryService.test.ts | 39 ++++++++++++++++++- .../SourceControlRepositoryService.ts | 8 +++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/apps/server/src/sourceControl/SourceControlRepositoryService.test.ts b/apps/server/src/sourceControl/SourceControlRepositoryService.test.ts index fa9a4b885e14c038a66ab557e9fc4757d3f42abc..8ee62bbaffec34a9e98b1417e916073de0f3c6a1 100644 --- a/apps/server/src/sourceControl/SourceControlRepositoryService.test.ts +++ b/apps/server/src/sourceControl/SourceControlRepositoryService.test.ts @@ -179,7 +179,7 @@ it.effect("clones a looked-up repository into the requested destination", () => assert.deepStrictEqual(cloneCalls, [ { cwd: parent, - args: ["clone", "--progress", CLONE_URLS.url, "t3code"], + args: ["clone", "--progress", "--", CLONE_URLS.url, "t3code"], }, ]); }).pipe( @@ -198,6 +198,43 @@ it.effect("clones a looked-up repository into the requested destination", () => }).pipe(Effect.provide(NodeServices.layer)), ); +it.effect.each(["--bare", "source.git"])( + "clones a local repository named %s as a working tree", + (repositoryName) => + Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + const git = yield* GitVcsDriver.GitVcsDriver; + const parent = yield* fs.makeTempDirectoryScoped({ prefix: "t3-clone-options-" }); + yield* git.execute({ + operation: "test.init", + cwd: parent, + args: ["init", "--bare", path.join(parent, repositoryName)], + }); + const destinationPath = path.join(parent, "checkout"); + yield* Effect.gen(function* () { + const service = yield* SourceControlRepositoryService.SourceControlRepositoryService; + const result = yield* service.cloneRepository({ + remoteUrl: repositoryName, + destinationPath, + }); + assert.strictEqual(result.cwd, destinationPath); + assert.isTrue(yield* fs.exists(path.join(destinationPath, ".git"))); + }).pipe(Effect.provide(makeLayer({ git: { execute: git.execute } }))); + }).pipe( + Effect.provide( + GitVcsDriver.layer.pipe( + Layer.provide( + ServerConfig.layerTest(process.cwd(), { + prefix: "t3-clone-options-config-", + }), + ), + Layer.provideMerge(NodeServices.layer), + ), + ), + ), +); + it.effect("reports clone progress from git's stderr and keeps its error text on failure", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; diff --git a/apps/server/src/sourceControl/SourceControlRepositoryService.ts b/apps/server/src/sourceControl/SourceControlRepositoryService.ts index eaf65b5cd66242b3b3872b18af30f0474698740e..ebab84b53a2c5167babcfd17199514eb25d60d41 100644 --- a/apps/server/src/sourceControl/SourceControlRepositoryService.ts +++ b/apps/server/src/sourceControl/SourceControlRepositoryService.ts @@ -303,7 +303,13 @@ export const make = Effect.gen(function* () { .execute({ operation: "SourceControlRepositoryService.cloneRepository", cwd: path.dirname(prepared.destinationPath), - args: ["clone", "--progress", prepared.cloneUrl, path.basename(prepared.destinationPath)], + args: [ + "clone", + "--progress", + "--", + prepared.cloneUrl, + path.basename(prepared.destinationPath), + ], timeoutMs: options?.timeoutMs === undefined ? CLONE_TIMEOUT_MS : options.timeoutMs, // Progress redraws add up on a slow multi-GB clone. The buffered copy // is never read (the tail is kept by hand above), so keep it small -- 2.54.0