History

Split git VCS driver from core service

- Introduce a dedicated `GitVcsDriver` service and `vcsLayer` - Update server, orchestration, and workspace wiring to use the new layer - Adjust tests and mocks for the new git-specific dependency

Julius Marminge committed 6 months ago

Commit 82a83941 parent 0ce293b15 files · +97 −87Browse files

Unified · Split

15 files
  1. apps/server/integration/OrchestrationEngineHarness.integration.ts +6 −7
  2. apps/server/src/checkpointing/Layers/CheckpointStore.test.ts +1 −1
  3. apps/server/src/git/Layers/GitManager.test.ts +12 −25
  4. apps/server/src/git/Layers/GitManager.ts +2 −2
  5. apps/server/src/orchestration/Layers/CheckpointReactor.test.ts +2 −2
  6. apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts +6 −2
  7. apps/server/src/orchestration/Layers/ProviderCommandReactor.ts +2 −2
  8. apps/server/src/server.test.ts +40 −30
  9. apps/server/src/server.ts +2 −2
  10. apps/server/src/vcs/GitVcsDriver.test.ts +3 −3
  11. apps/server/src/vcs/GitVcsDriver.ts +16 −5
  12. apps/server/src/vcs/VcsDriver.ts +1 −2
  13. apps/server/src/workspace/Layers/WorkspaceEntries.test.ts +1 −1
  14. apps/server/src/workspace/Layers/WorkspaceFileSystem.test.ts +1 −1
  15. apps/server/src/ws.ts +2 −2
apps/server/integration/OrchestrationEngineHarness.integration.ts+6−7
@@ -33,7 +33,6 @@ import { ProjectionCheckpointRepositoryLive } from "../src/persistence/Layers/Pr · 32 unmodified lines
33import { ProjectionPendingApprovalRepositoryLive } from "../src/persistence/Layers/ProjectionPendingApprovals.ts";33import { ProjectionPendingApprovalRepositoryLive } from "../src/persistence/Layers/ProjectionPendingApprovals.ts";
34import { ProviderSessionRuntimeRepositoryLive } from "../src/persistence/Layers/ProviderSessionRuntime.ts";34import { ProviderSessionRuntimeRepositoryLive } from "../src/persistence/Layers/ProviderSessionRuntime.ts";
35import { makeSqlitePersistenceLive } from "../src/persistence/Layers/Sqlite.ts";35import { makeSqlitePersistenceLive } from "../src/persistence/Layers/Sqlite.ts";
36import { VcsDriver, type VcsDriverShape } from "../src/vcs/VcsDriver.ts";
37import { ProjectionCheckpointRepository } from "../src/persistence/Services/ProjectionCheckpoints.ts";36import { ProjectionCheckpointRepository } from "../src/persistence/Services/ProjectionCheckpoints.ts";
38import { ProjectionPendingApprovalRepository } from "../src/persistence/Services/ProjectionPendingApprovals.ts";37import { ProjectionPendingApprovalRepository } from "../src/persistence/Services/ProjectionPendingApprovals.ts";
39import { makeAdapterRegistryMock } from "../src/provider/testUtils/providerAdapterRegistryMock.ts";38import { makeAdapterRegistryMock } from "../src/provider/testUtils/providerAdapterRegistryMock.ts";
@@ -292,7 +291,7 @@ export const makeOrchestrationIntegrationHarness = ( · 252 unmodified lines
292 Layer.provide(providerEventLoggersLayer),291 Layer.provide(providerEventLoggersLayer),
293 );292 );
294 293
295 const checkpointStoreLayer = CheckpointStoreLive.pipe(Layer.provide(GitVcsDriver.layer));294 const checkpointStoreLayer = CheckpointStoreLive.pipe(Layer.provide(GitVcsDriver.vcsLayer));
296 const projectionSnapshotQueryLayer = OrchestrationProjectionSnapshotQueryLive;295 const projectionSnapshotQueryLayer = OrchestrationProjectionSnapshotQueryLive;
297 const runtimeServicesLayer = Layer.mergeAll(296 const runtimeServicesLayer = Layer.mergeAll(
298 projectionSnapshotQueryLayer,297 projectionSnapshotQueryLayer,
@@ -308,17 +307,17 @@ export const makeOrchestrationIntegrationHarness = ( · 9 unmodified lines
308 Layer.provideMerge(runtimeServicesLayer),307 Layer.provideMerge(runtimeServicesLayer),
309 Layer.provideMerge(serverSettingsLayer),308 Layer.provideMerge(serverSettingsLayer),
310 );309 );
311 const vcsDriverLayer = Layer.succeed(VcsDriver, {310 const gitVcsDriverLayer = Layer.succeed(GitVcsDriver.GitVcsDriver, {
312 renameBranch: (input: Parameters<VcsDriverShape["renameBranch"]>[0]) =>311 renameBranch: (input: Parameters<GitVcsDriver.GitVcsDriverShape["renameBranch"]>[0]) =>
313 Effect.succeed({ branch: input.newBranch }),312 Effect.succeed({ branch: input.newBranch }),
314 } as unknown as VcsDriverShape);313 } as unknown as GitVcsDriver.GitVcsDriverShape);
315 const textGenerationLayer = Layer.succeed(TextGeneration, {314 const textGenerationLayer = Layer.succeed(TextGeneration, {
316 generateBranchName: () => Effect.succeed({ branch: "update" }),315 generateBranchName: () => Effect.succeed({ branch: "update" }),
317 generateThreadTitle: () => Effect.succeed({ title: "New thread" }),316 generateThreadTitle: () => Effect.succeed({ title: "New thread" }),
318 } as unknown as TextGenerationShape);317 } as unknown as TextGenerationShape);
319 const providerCommandReactorLayer = ProviderCommandReactorLive.pipe(318 const providerCommandReactorLayer = ProviderCommandReactorLive.pipe(
320 Layer.provideMerge(runtimeServicesLayer),319 Layer.provideMerge(runtimeServicesLayer),
321 Layer.provideMerge(vcsDriverLayer),320 Layer.provideMerge(gitVcsDriverLayer),
322 Layer.provideMerge(textGenerationLayer),321 Layer.provideMerge(textGenerationLayer),
323 Layer.provideMerge(serverSettingsLayer),322 Layer.provideMerge(serverSettingsLayer),
324 );323 );
@@ -343,7 +342,7 @@ export const makeOrchestrationIntegrationHarness = ( · 18 unmodified lines
343 Layer.provideMerge(342 Layer.provideMerge(
344 WorkspaceEntriesLive.pipe(343 WorkspaceEntriesLive.pipe(
345 Layer.provide(WorkspacePathsLive),344 Layer.provide(WorkspacePathsLive),
346 Layer.provideMerge(GitVcsDriver.layer),345 Layer.provideMerge(GitVcsDriver.vcsLayer),
347 Layer.provide(NodeServices.layer),346 Layer.provide(NodeServices.layer),
348 ),347 ),
349 ),348 ),
apps/server/src/checkpointing/Layers/CheckpointStore.test.ts+1−1
@@ -18,7 +18,7 @@ const ServerConfigLayer = ServerConfig.layerTest(process.cwd(), { · 17 unmodified lines
18 prefix: "t3-checkpoint-store-test-",18 prefix: "t3-checkpoint-store-test-",
19});19});
20const VcsProcessTestLayer = VcsProcess.layer.pipe(Layer.provide(NodeServices.layer));20const VcsProcessTestLayer = VcsProcess.layer.pipe(Layer.provide(NodeServices.layer));
21const VcsDriverTestLayer = GitVcsDriver.layer.pipe(Layer.provide(VcsProcessTestLayer));21const VcsDriverTestLayer = GitVcsDriver.vcsLayer.pipe(Layer.provide(VcsProcessTestLayer));
22const CheckpointStoreTestLayer = CheckpointStoreLive.pipe(22const CheckpointStoreTestLayer = CheckpointStoreLive.pipe(
23 Layer.provideMerge(VcsDriverTestLayer),23 Layer.provideMerge(VcsDriverTestLayer),
24 Layer.provideMerge(NodeServices.layer),24 Layer.provideMerge(NodeServices.layer),
apps/server/src/git/Layers/GitManager.test.ts+12−25
@@ -23,7 +23,6 @@ import { · 22 unmodified lines
23import { type TextGenerationShape, TextGeneration } from "../Services/TextGeneration.ts";23import { type TextGenerationShape, TextGeneration } from "../Services/TextGeneration.ts";
24import * as GitVcsDriver from "../../vcs/GitVcsDriver.ts";24import * as GitVcsDriver from "../../vcs/GitVcsDriver.ts";
25import * as VcsProcess from "../../vcs/VcsProcess.ts";25import * as VcsProcess from "../../vcs/VcsProcess.ts";
26import { VcsDriver } from "../../vcs/VcsDriver.ts";
27import { makeGitManager } from "./GitManager.ts";26import { makeGitManager } from "./GitManager.ts";
28import { ServerConfig } from "../../config.ts";27import { ServerConfig } from "../../config.ts";
29import { ServerSettingsService } from "../../serverSettings.ts";28import { ServerSettingsService } from "../../serverSettings.ts";
@@ -212,30 +211,18 @@ function runGit( · 182 unmodified lines
212): Effect.Effect<211): Effect.Effect<
213 { readonly code: number; readonly stdout: string; readonly stderr: string },212 { readonly code: number; readonly stdout: string; readonly stderr: string },
214 GitCommandError,213 GitCommandError,
215 VcsDriver214 GitVcsDriver.GitVcsDriver
216> {215> {
217 return Effect.gen(function* () {216 return Effect.gen(function* () {
218 const vcs = yield* VcsDriver;217 const git = yield* GitVcsDriver.GitVcsDriver;
219 const result = yield* vcs218 const result = yield* git.execute({
220 .execute({219 operation: "GitManager.test.runGit",
221 operation: "GitManager.test.runGit",220 cwd,
222 cwd,221 args,
223 args,222 allowNonZeroExit,
224 allowNonZeroExit,223 });
225 })
226 .pipe(
227 Effect.mapError(
228 (error) =>
229 new GitCommandError({
230 operation: "GitManager.test.runGit",
231 command: `git ${args.join(" ")}`,
232 cwd,
233 detail: error.message,
234 }),
235 ),
236 );
237 return {224 return {
238 code: result.exitCode,225 code: result.code,
239 stdout: result.stdout,226 stdout: result.stdout,
240 stderr: result.stderr,227 stderr: result.stderr,
241 };228 };
@@ -247,7 +234,7 @@ function initRepo( · 5 unmodified lines
247): Effect.Effect<234): Effect.Effect<
248 void,235 void,
249 PlatformError.PlatformError | GitCommandError,236 PlatformError.PlatformError | GitCommandError,
250 FileSystem.FileSystem | Scope.Scope | VcsDriver237 FileSystem.FileSystem | Scope.Scope | GitVcsDriver.GitVcsDriver
251> {238> {
252 return Effect.gen(function* () {239 return Effect.gen(function* () {
253 const fs = yield* FileSystem.FileSystem;240 const fs = yield* FileSystem.FileSystem;
@@ -263,7 +250,7 @@ function initRepo( · 9 unmodified lines
263function createBareRemote(): Effect.Effect<250function createBareRemote(): Effect.Effect<
264 string,251 string,
265 PlatformError.PlatformError | GitCommandError,252 PlatformError.PlatformError | GitCommandError,
266 FileSystem.FileSystem | Scope.Scope | VcsDriver253 FileSystem.FileSystem | Scope.Scope | GitVcsDriver.GitVcsDriver
267> {254> {
268 return Effect.gen(function* () {255 return Effect.gen(function* () {
269 const remoteDir = yield* makeTempDir("t3code-git-remote-");256 const remoteDir = yield* makeTempDir("t3code-git-remote-");
@@ -277,7 +264,7 @@ function configureRemote( · 7 unmodified lines
277 remoteName: string,264 remoteName: string,
278 remotePath: string,265 remotePath: string,
279 fetchNamespace: string,266 fetchNamespace: string,
280): Effect.Effect<void, GitCommandError, VcsDriver> {267): Effect.Effect<void, GitCommandError, GitVcsDriver.GitVcsDriver> {
281 return Effect.gen(function* () {268 return Effect.gen(function* () {
282 yield* runGit(cwd, ["config", `remote.${remoteName}.url`, remotePath]);269 yield* runGit(cwd, ["config", `remote.${remoteName}.url`, remotePath]);
283 yield* runGit(cwd, [270 yield* runGit(cwd, [
apps/server/src/git/Layers/GitManager.ts+2−2
@@ -49,7 +49,7 @@ import { · 48 unmodified lines
49 decodeGitHubPullRequestListJson,49 decodeGitHubPullRequestListJson,
50 formatGitHubJsonDecodeError,50 formatGitHubJsonDecodeError,
51} from "../githubPullRequests.ts";51} from "../githubPullRequests.ts";
52import { VcsDriver } from "../../vcs/VcsDriver.ts";52import { GitVcsDriver } from "../../vcs/GitVcsDriver.ts";
53 53
54const COMMIT_TIMEOUT_MS = 10 * 60_000;54const COMMIT_TIMEOUT_MS = 10 * 60_000;
55const MAX_PROGRESS_TEXT_LENGTH = 500;55const MAX_PROGRESS_TEXT_LENGTH = 500;
@@ -491,7 +491,7 @@ function toPullRequestHeadRemoteInfo(pr: { · 435 unmodified lines
491}491}
492 492
493export const makeGitManager = Effect.fn("makeGitManager")(function* () {493export const makeGitManager = Effect.fn("makeGitManager")(function* () {
494 const gitCore = yield* VcsDriver;494 const gitCore = yield* GitVcsDriver;
495 const gitHubCli = yield* GitHubCli;495 const gitHubCli = yield* GitHubCli;
496 const textGeneration = yield* TextGeneration;496 const textGeneration = yield* TextGeneration;
497 const projectSetupScriptRunner = yield* ProjectSetupScriptRunner;497 const projectSetupScriptRunner = yield* ProjectSetupScriptRunner;
apps/server/src/orchestration/Layers/CheckpointReactor.test.ts+2−2
@@ -307,11 +307,11 @@ describe("CheckpointReactor", () => { · 306 unmodified lines
307 Layer.provideMerge(RuntimeReceiptBusLive),307 Layer.provideMerge(RuntimeReceiptBusLive),
308 Layer.provideMerge(Layer.succeed(ProviderService, provider.service)),308 Layer.provideMerge(Layer.succeed(ProviderService, provider.service)),
309 Layer.provideMerge(gitStatusBroadcasterLayer),309 Layer.provideMerge(gitStatusBroadcasterLayer),
310 Layer.provideMerge(CheckpointStoreLive.pipe(Layer.provide(GitVcsDriver.layer))),310 Layer.provideMerge(CheckpointStoreLive.pipe(Layer.provide(GitVcsDriver.vcsLayer))),
311 Layer.provideMerge(311 Layer.provideMerge(
312 WorkspaceEntriesLive.pipe(312 WorkspaceEntriesLive.pipe(
313 Layer.provide(WorkspacePathsLive),313 Layer.provide(WorkspacePathsLive),
314 Layer.provideMerge(GitVcsDriver.layer),314 Layer.provideMerge(GitVcsDriver.vcsLayer),
315 ),315 ),
316 ),316 ),
317 Layer.provideMerge(WorkspacePathsLive),317 Layer.provideMerge(WorkspacePathsLive),
apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts+6−2
@@ -51,7 +51,7 @@ import { OrchestrationEngineService } from "../Services/OrchestrationEngine.ts"; · 50 unmodified lines
51import { ProviderCommandReactor } from "../Services/ProviderCommandReactor.ts";51import { ProviderCommandReactor } from "../Services/ProviderCommandReactor.ts";
52import * as NodeServices from "@effect/platform-node/NodeServices";52import * as NodeServices from "@effect/platform-node/NodeServices";
53import { ServerSettingsService } from "../../serverSettings.ts";53import { ServerSettingsService } from "../../serverSettings.ts";
54import { VcsDriver, type VcsDriverShape } from "../../vcs/VcsDriver.ts";54import * as GitVcsDriver from "../../vcs/GitVcsDriver.ts";
55 55
56const asProjectId = (value: string): ProjectId => ProjectId.make(value);56const asProjectId = (value: string): ProjectId => ProjectId.make(value);
57const asApprovalRequestId = (value: string): ApprovalRequestId => ApprovalRequestId.make(value);57const asApprovalRequestId = (value: string): ApprovalRequestId => ApprovalRequestId.make(value);
@@ -324,7 +324,11 @@ describe("ProviderCommandReactor", () => { · 266 unmodified lines
324 const layer = ProviderCommandReactorLive.pipe(324 const layer = ProviderCommandReactorLive.pipe(
325 Layer.provideMerge(orchestrationLayer),325 Layer.provideMerge(orchestrationLayer),
326 Layer.provideMerge(Layer.succeed(ProviderService, service)),326 Layer.provideMerge(Layer.succeed(ProviderService, service)),
327 Layer.provideMerge(Layer.succeed(VcsDriver, { renameBranch } as unknown as VcsDriverShape)),327 Layer.provideMerge(
328 Layer.succeed(GitVcsDriver.GitVcsDriver, {
329 renameBranch,
330 } as unknown as GitVcsDriver.GitVcsDriverShape),
331 ),
328 Layer.provideMerge(332 Layer.provideMerge(
329 Layer.succeed(GitStatusBroadcaster, {333 Layer.succeed(GitStatusBroadcaster, {
330 getStatus: () => Effect.die("getStatus should not be called in this test"),334 getStatus: () => Effect.die("getStatus should not be called in this test"),
apps/server/src/orchestration/Layers/ProviderCommandReactor.ts+2−2
@@ -28,7 +28,7 @@ import { · 27 unmodified lines
28 type ProviderCommandReactorShape,28 type ProviderCommandReactorShape,
29} from "../Services/ProviderCommandReactor.ts";29} from "../Services/ProviderCommandReactor.ts";
30import { ServerSettingsService } from "../../serverSettings.ts";30import { ServerSettingsService } from "../../serverSettings.ts";
31import { VcsDriver } from "../../vcs/VcsDriver.ts";31import { GitVcsDriver } from "../../vcs/GitVcsDriver.ts";
32 32
33type ProviderIntentEvent = Extract<33type ProviderIntentEvent = Extract<
34 OrchestrationEvent,34 OrchestrationEvent,
@@ -168,7 +168,7 @@ function buildGeneratedWorktreeBranchName(raw: string): string { · 133 unmodified lines
168const make = Effect.gen(function* () {168const make = Effect.gen(function* () {
169 const orchestrationEngine = yield* OrchestrationEngineService;169 const orchestrationEngine = yield* OrchestrationEngineService;
170 const providerService = yield* ProviderService;170 const providerService = yield* ProviderService;
171 const git = yield* VcsDriver;171 const git = yield* GitVcsDriver;
172 const gitStatusBroadcaster = yield* GitStatusBroadcaster;172 const gitStatusBroadcaster = yield* GitStatusBroadcaster;
173 const textGeneration = yield* TextGeneration;173 const textGeneration = yield* TextGeneration;
174 const serverSettingsService = yield* ServerSettingsService;174 const serverSettingsService = yield* ServerSettingsService;
apps/server/src/server.test.ts+40−30
@@ -104,6 +104,7 @@ import { · 103 unmodified lines
104import { WorkspaceEntriesLive } from "./workspace/Layers/WorkspaceEntries.ts";104import { WorkspaceEntriesLive } from "./workspace/Layers/WorkspaceEntries.ts";
105import { WorkspaceFileSystemLive } from "./workspace/Layers/WorkspaceFileSystem.ts";105import { WorkspaceFileSystemLive } from "./workspace/Layers/WorkspaceFileSystem.ts";
106import { WorkspacePathsLive } from "./workspace/Layers/WorkspacePaths.ts";106import { WorkspacePathsLive } from "./workspace/Layers/WorkspacePaths.ts";
107import * as GitVcsDriver from "./vcs/GitVcsDriver.ts";
107import { VcsDriver, type VcsDriverShape } from "./vcs/VcsDriver.ts";108import { VcsDriver, type VcsDriverShape } from "./vcs/VcsDriver.ts";
108import { ServerSecretStoreLive } from "./auth/Layers/ServerSecretStore.ts";109import { ServerSecretStoreLive } from "./auth/Layers/ServerSecretStore.ts";
109import { ServerAuthLive } from "./auth/Layers/ServerAuth.ts";110import { ServerAuthLive } from "./auth/Layers/ServerAuth.ts";
@@ -315,6 +316,7 @@ const buildAppUnderTest = (options?: { · 205 unmodified lines
315 serverSettings?: Partial<ServerSettingsShape>;316 serverSettings?: Partial<ServerSettingsShape>;
316 open?: Partial<OpenShape>;317 open?: Partial<OpenShape>;
317 vcsDriver?: Partial<VcsDriverShape>;318 vcsDriver?: Partial<VcsDriverShape>;
319 gitVcsDriver?: Partial<GitVcsDriver.GitVcsDriverShape>;
318 gitManager?: Partial<GitManagerShape>;320 gitManager?: Partial<GitManagerShape>;
319 gitStatusBroadcaster?: Partial<GitStatusBroadcasterShape>;321 gitStatusBroadcaster?: Partial<GitStatusBroadcasterShape>;
320 projectSetupScriptRunner?: Partial<ProjectSetupScriptRunnerShape>;322 projectSetupScriptRunner?: Partial<ProjectSetupScriptRunnerShape>;
@@ -384,6 +386,9 @@ const buildAppUnderTest = (options?: { · 63 unmodified lines
384 filterIgnoredPaths: (_cwd, relativePaths) => Effect.succeed(relativePaths),386 filterIgnoredPaths: (_cwd, relativePaths) => Effect.succeed(relativePaths),
385 ...options?.layers?.vcsDriver,387 ...options?.layers?.vcsDriver,
386 });388 });
389 const gitVcsDriverLayer = Layer.mock(GitVcsDriver.GitVcsDriver)({
390 ...options?.layers?.gitVcsDriver,
391 });
387 const gitManagerLayer = Layer.mock(GitManager)({392 const gitManagerLayer = Layer.mock(GitManager)({
388 ...options?.layers?.gitManager,393 ...options?.layers?.gitManager,
389 });394 });
@@ -444,6 +449,7 @@ const buildAppUnderTest = (options?: { · 54 unmodified lines
444 }),449 }),
445 ),450 ),
446 Layer.provide(gitManagerLayer),451 Layer.provide(gitManagerLayer),
452 Layer.provide(gitVcsDriverLayer),
447 Layer.provideMerge(gitStatusBroadcasterLayer),453 Layer.provideMerge(gitStatusBroadcasterLayer),
448 Layer.provide(454 Layer.provide(
449 Layer.mock(ProjectSetupScriptRunner)({455 Layer.mock(ProjectSetupScriptRunner)({
@@ -2377,7 +2383,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { · 1927 unmodified lines
2377 worktreePath: null,2383 worktreePath: null,
2378 }),2384 }),
2379 },2385 },
2380 vcsDriver: {2386 gitVcsDriver: {
2381 pullCurrentBranch: () =>2387 pullCurrentBranch: () =>
2382 Effect.succeed({2388 Effect.succeed({
2383 status: "pulled",2389 status: "pulled",
@@ -2531,7 +2537,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { · 147 unmodified lines
2531 let statusCalls = 0;2537 let statusCalls = 0;
2532 yield* buildAppUnderTest({2538 yield* buildAppUnderTest({
2533 layers: {2539 layers: {
2534 vcsDriver: {2540 gitVcsDriver: {
2535 pullCurrentBranch: () => Effect.fail(gitError),2541 pullCurrentBranch: () => Effect.fail(gitError),
2536 },2542 },
2537 gitManager: {2543 gitManager: {
@@ -2685,7 +2691,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { · 147 unmodified lines
2685 Effect.gen(function* () {2691 Effect.gen(function* () {
2686 yield* buildAppUnderTest({2692 yield* buildAppUnderTest({
2687 layers: {2693 layers: {
2688 vcsDriver: {2694 gitVcsDriver: {
2689 pullCurrentBranch: () =>2695 pullCurrentBranch: () =>
2690 Effect.succeed({2696 Effect.succeed({
2691 status: "pulled" as const,2697 status: "pulled" as const,
@@ -3499,13 +3505,14 @@ it.layer(NodeServices.layer)("server router seam", (it) => { · 807 unmodified lines
3499 pr: null,3505 pr: null,
3500 }),3506 }),
3501 );3507 );
3502 const createWorktree = vi.fn((_: Parameters<VcsDriverShape["createWorktree"]>[0]) =>3508 const createWorktree = vi.fn(
3503 Effect.succeed({3509 (_: Parameters<GitVcsDriver.GitVcsDriverShape["createWorktree"]>[0]) =>
3504 worktree: {3510 Effect.succeed({
3505 branch: "t3code/bootstrap-branch",3511 worktree: {
3506 path: "/tmp/bootstrap-worktree",3512 branch: "t3code/bootstrap-branch",
3507 },3513 path: "/tmp/bootstrap-worktree",
3508 }),3514 },
3515 }),
3509 );3516 );
3510 const runForThread = vi.fn(3517 const runForThread = vi.fn(
3511 (_: Parameters<ProjectSetupScriptRunnerShape["runForThread"]>[0]) =>3518 (_: Parameters<ProjectSetupScriptRunnerShape["runForThread"]>[0]) =>
@@ -3520,7 +3527,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { · 8 unmodified lines
3520 3527
3521 yield* buildAppUnderTest({3528 yield* buildAppUnderTest({
3522 layers: {3529 layers: {
3523 vcsDriver: {3530 gitVcsDriver: {
3524 createWorktree,3531 createWorktree,
3525 },3532 },
3526 gitStatusBroadcaster: {3533 gitStatusBroadcaster: {
@@ -3624,13 +3631,14 @@ it.layer(NodeServices.layer)("server router seam", (it) => { · 97 unmodified lines
3624 it.effect("records setup-script failures without aborting bootstrap turn start", () =>3631 it.effect("records setup-script failures without aborting bootstrap turn start", () =>
3625 Effect.gen(function* () {3632 Effect.gen(function* () {
3626 const dispatchedCommands: Array<OrchestrationCommand> = [];3633 const dispatchedCommands: Array<OrchestrationCommand> = [];
3627 const createWorktree = vi.fn((_: Parameters<VcsDriverShape["createWorktree"]>[0]) =>3634 const createWorktree = vi.fn(
3628 Effect.succeed({3635 (_: Parameters<GitVcsDriver.GitVcsDriverShape["createWorktree"]>[0]) =>
3629 worktree: {3636 Effect.succeed({
3630 branch: "t3code/bootstrap-branch",3637 worktree: {
3631 path: "/tmp/bootstrap-worktree",3638 branch: "t3code/bootstrap-branch",
3632 },3639 path: "/tmp/bootstrap-worktree",
3633 }),3640 },
3641 }),
3634 );3642 );
3635 const runForThread = vi.fn(3643 const runForThread = vi.fn(
3636 (_: Parameters<ProjectSetupScriptRunnerShape["runForThread"]>[0]) =>3644 (_: Parameters<ProjectSetupScriptRunnerShape["runForThread"]>[0]) =>
@@ -3639,7 +3647,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { · 2 unmodified lines
3639 3647
3640 yield* buildAppUnderTest({3648 yield* buildAppUnderTest({
3641 layers: {3649 layers: {
3642 vcsDriver: {3650 gitVcsDriver: {
3643 createWorktree,3651 createWorktree,
3644 },3652 },
3645 orchestrationEngine: {3653 orchestrationEngine: {
@@ -3717,13 +3725,14 @@ it.layer(NodeServices.layer)("server router seam", (it) => { · 71 unmodified lines
3717 it.effect("does not misattribute setup activity dispatch failures as setup launch failures", () =>3725 it.effect("does not misattribute setup activity dispatch failures as setup launch failures", () =>
3718 Effect.gen(function* () {3726 Effect.gen(function* () {
3719 const dispatchedCommands: Array<OrchestrationCommand> = [];3727 const dispatchedCommands: Array<OrchestrationCommand> = [];
3720 const createWorktree = vi.fn((_: Parameters<VcsDriverShape["createWorktree"]>[0]) =>3728 const createWorktree = vi.fn(
3721 Effect.succeed({3729 (_: Parameters<GitVcsDriver.GitVcsDriverShape["createWorktree"]>[0]) =>
3722 worktree: {3730 Effect.succeed({
3723 branch: "t3code/bootstrap-branch",3731 worktree: {
3724 path: "/tmp/bootstrap-worktree",3732 branch: "t3code/bootstrap-branch",
3725 },3733 path: "/tmp/bootstrap-worktree",
3726 }),3734 },
3735 }),
3727 );3736 );
3728 const runForThread = vi.fn(3737 const runForThread = vi.fn(
3729 (_: Parameters<ProjectSetupScriptRunnerShape["runForThread"]>[0]) =>3738 (_: Parameters<ProjectSetupScriptRunnerShape["runForThread"]>[0]) =>
@@ -3739,7 +3748,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { · 9 unmodified lines
3739 3748
3740 yield* buildAppUnderTest({3749 yield* buildAppUnderTest({
3741 layers: {3750 layers: {
3742 vcsDriver: {3751 gitVcsDriver: {
3743 createWorktree,3752 createWorktree,
3744 },3753 },
3745 orchestrationEngine: {3754 orchestrationEngine: {
@@ -3835,13 +3844,14 @@ it.layer(NodeServices.layer)("server router seam", (it) => { · 89 unmodified lines
3835 it.effect("cleans up created bootstrap threads when worktree creation defects", () =>3844 it.effect("cleans up created bootstrap threads when worktree creation defects", () =>
3836 Effect.gen(function* () {3845 Effect.gen(function* () {
3837 const dispatchedCommands: Array<OrchestrationCommand> = [];3846 const dispatchedCommands: Array<OrchestrationCommand> = [];
3838 const createWorktree = vi.fn((_: Parameters<VcsDriverShape["createWorktree"]>[0]) =>3847 const createWorktree = vi.fn(
3839 Effect.die(new Error("worktree exploded")),3848 (_: Parameters<GitVcsDriver.GitVcsDriverShape["createWorktree"]>[0]) =>
3849 Effect.die(new Error("worktree exploded")),
3840 );3850 );
3841 3851
3842 yield* buildAppUnderTest({3852 yield* buildAppUnderTest({
3843 layers: {3853 layers: {
3844 vcsDriver: {3854 gitVcsDriver: {
3845 createWorktree,3855 createWorktree,
3846 },3856 },
3847 orchestrationEngine: {3857 orchestrationEngine: {
apps/server/src/server.ts+2−2
@@ -136,7 +136,7 @@ const ReactorLayerLive = Layer.empty.pipe( · 135 unmodified lines
136 136
137const CheckpointingLayerLive = Layer.empty.pipe(137const CheckpointingLayerLive = Layer.empty.pipe(
138 Layer.provideMerge(CheckpointDiffQueryLive),138 Layer.provideMerge(CheckpointDiffQueryLive),
139 Layer.provideMerge(CheckpointStoreLive.pipe(Layer.provide(GitVcsDriver.layer))),139 Layer.provideMerge(CheckpointStoreLive.pipe(Layer.provide(GitVcsDriver.vcsLayer))),
140);140);
141 141
142const ProviderSessionDirectoryLayerLive = ProviderSessionDirectoryLive.pipe(142const ProviderSessionDirectoryLayerLive = ProviderSessionDirectoryLive.pipe(
@@ -173,7 +173,7 @@ const TerminalLayerLive = TerminalManagerLive.pipe(Layer.provide(PtyAdapterLive) · 30 unmodified lines
173 173
174const WorkspaceEntriesLayerLive = WorkspaceEntriesLive.pipe(174const WorkspaceEntriesLayerLive = WorkspaceEntriesLive.pipe(
175 Layer.provide(WorkspacePathsLive),175 Layer.provide(WorkspacePathsLive),
176 Layer.provideMerge(GitVcsDriver.layer),176 Layer.provideMerge(GitVcsDriver.vcsLayer),
177);177);
178 178
179const WorkspaceFileSystemLayerLive = WorkspaceFileSystemLive.pipe(179const WorkspaceFileSystemLayerLive = WorkspaceFileSystemLive.pipe(
apps/server/src/vcs/GitVcsDriver.test.ts+3−3
@@ -18,7 +18,7 @@ const GitVcsDriverTestDependencies = ServerConfig.layerTest(process.cwd(), { · 17 unmodified lines
18 prefix: "t3-git-vcs-driver-test-",18 prefix: "t3-git-vcs-driver-test-",
19}).pipe(Layer.provide(NodeServices.layer));19}).pipe(Layer.provide(NodeServices.layer));
20 20
21it.layer(Layer.empty)("GitVcsDriver.layer", (it) => {21it.layer(Layer.empty)("GitVcsDriver.vcsLayer", (it) => {
22 describe("workspace helpers", () => {22 describe("workspace helpers", () => {
23 it.effect("filterIgnoredPaths chunks large path lists and preserves kept paths", () =>23 it.effect("filterIgnoredPaths chunks large path lists and preserves kept paths", () =>
24 Effect.gen(function* () {24 Effect.gen(function* () {
@@ -32,7 +32,7 @@ it.layer(Layer.empty)("GitVcsDriver.layer", (it) => { · 7 unmodified lines
32 );32 );
33 33
34 const seenChunks: string[][] = [];34 const seenChunks: string[][] = [];
35 const layer = GitVcsDriver.layer.pipe(35 const layer = GitVcsDriver.vcsLayer.pipe(
36 Layer.provideMerge(GitVcsDriverTestDependencies),36 Layer.provideMerge(GitVcsDriverTestDependencies),
37 Layer.provideMerge(NodeServices.layer),37 Layer.provideMerge(NodeServices.layer),
38 Layer.provide(38 Layer.provide(
@@ -81,7 +81,7 @@ it.layer(Layer.empty)("GitVcsDriver.layer", (it) => { · 42 unmodified lines
81 81
82 it.effect("listWorkspaceFiles disables fsmonitor and untracked cache helpers", () =>82 it.effect("listWorkspaceFiles disables fsmonitor and untracked cache helpers", () =>
83 Effect.gen(function* () {83 Effect.gen(function* () {
84 const layer = GitVcsDriver.layer.pipe(84 const layer = GitVcsDriver.vcsLayer.pipe(
85 Layer.provideMerge(GitVcsDriverTestDependencies),85 Layer.provideMerge(GitVcsDriverTestDependencies),
86 Layer.provideMerge(NodeServices.layer),86 Layer.provideMerge(NodeServices.layer),
87 Layer.provide(87 Layer.provide(
apps/server/src/vcs/GitVcsDriver.ts+16−5
@@ -1,10 +1,17 @@
1import { DateTime, Effect, Layer } from "effect";1import { Context, DateTime, Effect, Layer } from "effect";
2 2
3import { VcsProcessExitError } from "@t3tools/contracts";3import { VcsProcessExitError } from "@t3tools/contracts";
4import { makeGitCore } from "../git/Layers/GitCore.ts";4import { makeGitCore } from "../git/Layers/GitCore.ts";
5import type { GitCoreShape } from "../git/Services/GitCore.ts";
5import { VcsDriver, type VcsDriverShape } from "./VcsDriver.ts";6import { VcsDriver, type VcsDriverShape } from "./VcsDriver.ts";
6import { VcsProcess, type VcsProcessShape } from "./VcsProcess.ts";7import { VcsProcess, type VcsProcessShape } from "./VcsProcess.ts";
7 8
9export interface GitVcsDriverShape extends GitCoreShape {}
10
11export class GitVcsDriver extends Context.Service<GitVcsDriver, GitVcsDriverShape>()(
12 "t3/vcs/GitVcsDriver",
13) {}
14
8const WORKSPACE_FILES_MAX_OUTPUT_BYTES = 16 * 1024 * 1024;15const WORKSPACE_FILES_MAX_OUTPUT_BYTES = 16 * 1024 * 1024;
9const GIT_CHECK_IGNORE_MAX_STDIN_BYTES = 256 * 1024;16const GIT_CHECK_IGNORE_MAX_STDIN_BYTES = 256 * 1024;
10const WORKSPACE_GIT_HARDENED_CONFIG_ARGS = [17const WORKSPACE_GIT_HARDENED_CONFIG_ARGS = [
@@ -92,9 +99,8 @@ const gitCommand = ( · 81 unmodified lines
92 : {}),99 : {}),
93 });100 });
94 101
95export const make = Effect.fn("makeGitVcsDriver")(function* () {102export const makeVcsDriver = Effect.fn("makeGitVcsDriver")(function* () {
96 const process = yield* VcsProcess;103 const process = yield* VcsProcess;
97 const legacyGit = yield* makeGitCore();
98 const capabilities = {104 const capabilities = {
99 kind: "git" as const,105 kind: "git" as const,
100 supportsWorktrees: true,106 supportsWorktrees: true,
@@ -243,7 +249,6 @@ export const make = Effect.fn("makeGitVcsDriver")(function* () { · 142 unmodified lines
243 );249 );
244 250
245 return VcsDriver.of({251 return VcsDriver.of({
246 ...legacyGit,
247 capabilities,252 capabilities,
248 execute,253 execute,
249 detectRepository,254 detectRepository,
@@ -253,4 +258,10 @@ export const make = Effect.fn("makeGitVcsDriver")(function* () { · 3 unmodified lines
253 });258 });
254});259});
255 260
256export const layer = Layer.effect(VcsDriver, make());261export const make = Effect.fn("makeGitVcsDriverService")(function* () {
262 const legacyGit = yield* makeGitCore();
263 return GitVcsDriver.of(legacyGit);
264});
265
266export const vcsLayer = Layer.effect(VcsDriver, makeVcsDriver());
267export const layer = Layer.effect(GitVcsDriver, make());
apps/server/src/vcs/VcsDriver.ts+1−2
@@ -6,10 +6,9 @@ import type { · 5 unmodified lines
6 VcsListWorkspaceFilesResult,6 VcsListWorkspaceFilesResult,
7 VcsRepositoryIdentity,7 VcsRepositoryIdentity,
8} from "@t3tools/contracts";8} from "@t3tools/contracts";
9import type { GitCoreShape } from "../git/Services/GitCore.ts";
10import type { VcsProcessInput, VcsProcessOutput } from "./VcsProcess.ts";9import type { VcsProcessInput, VcsProcessOutput } from "./VcsProcess.ts";
11 10
12export interface VcsDriverShape extends Omit<GitCoreShape, "execute"> {11export interface VcsDriverShape {
13 readonly capabilities: VcsDriverCapabilities;12 readonly capabilities: VcsDriverCapabilities;
14 readonly execute: (13 readonly execute: (
15 input: Omit<VcsProcessInput, "command">,14 input: Omit<VcsProcessInput, "command">,
apps/server/src/workspace/Layers/WorkspaceEntries.test.ts+1−1
@@ -15,7 +15,7 @@ const TestLayer = Layer.empty.pipe( · 14 unmodified lines
15 Layer.provideMerge(WorkspaceEntriesLive.pipe(Layer.provide(WorkspacePathsLive))),15 Layer.provideMerge(WorkspaceEntriesLive.pipe(Layer.provide(WorkspacePathsLive))),
16 Layer.provideMerge(WorkspacePathsLive),16 Layer.provideMerge(WorkspacePathsLive),
17 Layer.provideMerge(VcsProcess.layer),17 Layer.provideMerge(VcsProcess.layer),
18 Layer.provideMerge(GitVcsDriver.layer.pipe(Layer.provide(VcsProcess.layer))),18 Layer.provideMerge(GitVcsDriver.vcsLayer.pipe(Layer.provide(VcsProcess.layer))),
19 Layer.provide(19 Layer.provide(
20 ServerConfig.layerTest(process.cwd(), {20 ServerConfig.layerTest(process.cwd(), {
21 prefix: "t3-workspace-entries-test-",21 prefix: "t3-workspace-entries-test-",
apps/server/src/workspace/Layers/WorkspaceFileSystem.test.ts+1−1
@@ -20,7 +20,7 @@ const TestLayer = Layer.empty.pipe( · 19 unmodified lines
20 Layer.provideMerge(ProjectLayer),20 Layer.provideMerge(ProjectLayer),
21 Layer.provideMerge(WorkspaceEntriesLive.pipe(Layer.provide(WorkspacePathsLive))),21 Layer.provideMerge(WorkspaceEntriesLive.pipe(Layer.provide(WorkspacePathsLive))),
22 Layer.provideMerge(WorkspacePathsLive),22 Layer.provideMerge(WorkspacePathsLive),
23 Layer.provideMerge(GitVcsDriver.layer.pipe(Layer.provide(VcsProcess.layer))),23 Layer.provideMerge(GitVcsDriver.vcsLayer.pipe(Layer.provide(VcsProcess.layer))),
24 Layer.provide(24 Layer.provide(
25 ServerConfig.layerTest(process.cwd(), {25 ServerConfig.layerTest(process.cwd(), {
26 prefix: "t3-workspace-files-test-",26 prefix: "t3-workspace-files-test-",
apps/server/src/ws.ts+2−2
@@ -49,7 +49,7 @@ import { TerminalManager } from "./terminal/Services/Manager.ts"; · 48 unmodified lines
49import { WorkspaceEntries } from "./workspace/Services/WorkspaceEntries.ts";49import { WorkspaceEntries } from "./workspace/Services/WorkspaceEntries.ts";
50import { WorkspaceFileSystem } from "./workspace/Services/WorkspaceFileSystem.ts";50import { WorkspaceFileSystem } from "./workspace/Services/WorkspaceFileSystem.ts";
51import { WorkspacePathOutsideRootError } from "./workspace/Services/WorkspacePaths.ts";51import { WorkspacePathOutsideRootError } from "./workspace/Services/WorkspacePaths.ts";
52import { VcsDriver } from "./vcs/VcsDriver.ts";52import { GitVcsDriver } from "./vcs/GitVcsDriver.ts";
53import { ProjectSetupScriptRunner } from "./project/Services/ProjectSetupScriptRunner.ts";53import { ProjectSetupScriptRunner } from "./project/Services/ProjectSetupScriptRunner.ts";
54import { RepositoryIdentityResolver } from "./project/Services/RepositoryIdentityResolver.ts";54import { RepositoryIdentityResolver } from "./project/Services/RepositoryIdentityResolver.ts";
55import { ServerEnvironment } from "./environment/Services/ServerEnvironment.ts";55import { ServerEnvironment } from "./environment/Services/ServerEnvironment.ts";
@@ -137,7 +137,7 @@ const makeWsRpcLayer = (currentSessionId: AuthSessionId) => · 81 unmodified lines
137 const keybindings = yield* Keybindings;137 const keybindings = yield* Keybindings;
138 const open = yield* Open;138 const open = yield* Open;
139 const gitManager = yield* GitManager;139 const gitManager = yield* GitManager;
140 const git = yield* VcsDriver;140 const git = yield* GitVcsDriver;
141 const gitStatusBroadcaster = yield* GitStatusBroadcaster;141 const gitStatusBroadcaster = yield* GitStatusBroadcaster;
142 const terminalManager = yield* TerminalManager;142 const terminalManager = yield* TerminalManager;
143 const providerRegistry = yield* ProviderRegistry;143 const providerRegistry = yield* ProviderRegistry;