Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const PAGE_TITLES: Record<SettingsPage, string> = {

const PAGE_PROJECT_KEYS: Record<SettingsPage, readonly ProjectScopedServerSettingKey[]> = {
"new-threads": ["defaultThreadEnvMode", "worktreeSubmodules", "defaultRuntimeMode"],
"source-control": ["defaultAutoPull", "newWorktreesStartFromOrigin"],
"source-control": ["enableVersionControl", "defaultAutoPull", "newWorktreesStartFromOrigin"],
"agent-behavior": ["responseStreamingMode", "enableAgentBrowserAccess"],
maintenance: ["continueThreadsAfterServerUpdate"],
};
Expand Down Expand Up @@ -325,6 +325,16 @@ function ServerSettingsDetail(props: { readonly page: SettingsPage }) {

{props.page === "source-control" ? (
<>
<SettingsSection title="Version control">
<FanoutSwitchRow
icon="arrow.triangle.branch"
label="Git"
subtitle="Turn off to skip Git status, fetch, and other Git commands."
value={uniform("enableVersionControl")}
disabled={disabledFor("enableVersionControl")}
onValueChange={(value) => write({ enableVersionControl: value })}
/>
</SettingsSection>
<SettingsSection title="Default branch">
<FanoutSwitchRow
icon="arrow.down.circle"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ import { deriveServerPaths, ServerConfig } from "../src/config.ts";
import * as WorkspaceEntries from "../src/workspace/WorkspaceEntries.ts";
import * as WorkspacePaths from "../src/workspace/WorkspacePaths.ts";
import * as VcsDriverRegistry from "../src/vcs/VcsDriverRegistry.ts";
import * as VersionControlPolicy from "../src/vcs/VersionControlPolicy.ts";
import { VcsStatusBroadcaster } from "../src/vcs/VcsStatusBroadcaster.ts";
import { GitWorkflowService } from "../src/git/GitWorkflowService.ts";
import * as VcsProcess from "../src/vcs/VcsProcess.ts";
Expand Down Expand Up @@ -431,6 +432,7 @@ export const makeOrchestrationIntegrationHarness = (
Layer.provideMerge(providerRegistryLayer),
Layer.provide(persistenceLayer),
Layer.provideMerge(RepositoryIdentityResolver.layer),
Layer.provide(VersionControlPolicy.layerTest),
Layer.provideMerge(ServerSettingsService.layerTest()),
Layer.provideMerge(ServerConfig.layerTest(workspaceDir, rootDir)),
Layer.provideMerge(NodeServices.layer),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import * as ProviderService from "../src/provider/Services/ProviderService.ts";
import * as ProviderSessionDirectory from "../src/provider/Services/ProviderSessionDirectory.ts";
import * as ProviderSessionReaper from "../src/provider/Services/ProviderSessionReaper.ts";
import * as RepositoryIdentityResolver from "../src/project/RepositoryIdentityResolver.ts";
import * as VersionControlPolicy from "../src/vcs/VersionControlPolicy.ts";
import * as ServerLifecycleEvents from "../src/serverLifecycleEvents.ts";
import * as ServerRuntimeStartup from "../src/serverRuntimeStartup.ts";
import * as ServerSettings from "../src/serverSettings.ts";
Expand All @@ -66,7 +67,9 @@ const makePersistedRuntimeLayer = (dbPath: string) => {
Layer.provide(ProviderSessionRuntime.layer),
Layer.provide(persistence),
);
return Layer.mergeAll(orchestration, directory);
return Layer.mergeAll(orchestration, directory).pipe(
Layer.provide(VersionControlPolicy.layerTest),
);
};

const startupDependencies = Layer.mergeAll(
Expand Down
8 changes: 7 additions & 1 deletion apps/server/scripts/evaluate-thread-titles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import * as AzureDevOpsCli from "../src/sourceControl/AzureDevOpsCli.ts";
import * as BitbucketApi from "../src/sourceControl/BitbucketApi.ts";
import * as VcsProcess from "../src/vcs/VcsProcess.ts";
import * as VcsDriverRegistry from "../src/vcs/VcsDriverRegistry.ts";
import * as VersionControlPolicy from "../src/vcs/VersionControlPolicy.ts";
import * as VcsProjectConfig from "../src/vcs/VcsProjectConfig.ts";
import * as GitVcsDriver from "../src/vcs/GitVcsDriver.ts";
import * as ProcessRunner from "../src/processRunner.ts";
Expand Down Expand Up @@ -158,7 +159,12 @@ await Effect.runPromise(
BitbucketApi.layer,
),
),
Layer.provide(VcsDriverRegistry.layer.pipe(Layer.provide(VcsProjectConfig.layer))),
Layer.provide(
VcsDriverRegistry.layer.pipe(
Layer.provide(VcsProjectConfig.layer),
Layer.provide(VersionControlPolicy.layerTest),
),
),
Layer.provide(GitVcsDriver.layer),
Layer.provide(VcsProcess.layer),
Layer.provide(FetchHttpClient.layer),
Expand Down
2 changes: 2 additions & 0 deletions apps/server/src/bin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import { orchestrationHttpApiLayer } from "./orchestration/http.ts";
import * as ProjectCloneTracker from "./project/ProjectCloneTracker.ts";
import { layerConfig as SqlitePersistenceLayerLive } from "./persistence/Layers/Sqlite.ts";
import * as RepositoryIdentityResolver from "./project/RepositoryIdentityResolver.ts";
import * as VersionControlPolicy from "./vcs/VersionControlPolicy.ts";
import {
makePersistedServerRuntimeState,
persistServerRuntimeState,
Expand Down Expand Up @@ -130,6 +131,7 @@ const makeProjectPersistenceLayer = (config: ServerConfig.ServerConfig["Service"
Layer.mergeAll(
OrchestrationLayerLive.pipe(
Layer.provideMerge(RepositoryIdentityResolver.layer),
Layer.provide(VersionControlPolicy.layerTest),
Layer.provideMerge(SqlitePersistenceLayerLive),
),
WorkspacePaths.layer,
Expand Down
6 changes: 5 additions & 1 deletion apps/server/src/checkpointing/CheckpointStore.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,18 @@ import { checkpointRefForThreadTurn } from "./Utils.ts";
import { parseTurnDiffFilesFromNumstat } from "./Diffs.ts";
import * as CheckpointStore from "./CheckpointStore.ts";
import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts";
import * as VersionControlPolicy from "../vcs/VersionControlPolicy.ts";
import * as VcsProcess from "../vcs/VcsProcess.ts";
import * as ServerConfig from "../config.ts";

const ServerConfigLayer = ServerConfig.ServerConfig.layerTest(process.cwd(), {
prefix: "t3-checkpoint-store-test-",
});
const VcsProcessTestLayer = VcsProcess.layer.pipe(Layer.provide(NodeServices.layer));
const VcsDriverTestLayer = VcsDriverRegistry.layer.pipe(Layer.provide(VcsProcessTestLayer));
const VcsDriverTestLayer = VcsDriverRegistry.layer.pipe(
Layer.provide(VcsProcessTestLayer),
Layer.provide(VersionControlPolicy.layerTest),
);
const CheckpointStoreTestLayer = CheckpointStore.layer.pipe(
Layer.provideMerge(VcsDriverTestLayer),
Layer.provideMerge(NodeServices.layer),
Expand Down
7 changes: 6 additions & 1 deletion apps/server/src/cli/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import * as ProjectionSnapshotQuery from "../orchestration/Services/ProjectionSn
import { OrchestrationLayerLive } from "../orchestration/runtimeLayer.ts";
import { layerConfig as SqlitePersistenceLayerLive } from "../persistence/Layers/Sqlite.ts";
import * as RepositoryIdentityResolver from "../project/RepositoryIdentityResolver.ts";
import * as VersionControlPolicy from "../vcs/VersionControlPolicy.ts";
import {
clearPersistedServerRuntimeState,
readPersistedServerRuntimeState,
Expand Down Expand Up @@ -197,10 +198,14 @@ const projectCommandUuid = Crypto.Crypto.pipe(
),
);

// Offline `t3 project` does not load server settings. The explicit test layer
// keeps Git available here; the running server uses VersionControlPolicy.layer.
const ProjectCliRuntimeLive = Layer.mergeAll(
WorkspacePaths.layer,
OrchestrationLayerLive.pipe(
Layer.provideMerge(RepositoryIdentityResolver.layer),
Layer.provideMerge(
RepositoryIdentityResolver.layer.pipe(Layer.provide(VersionControlPolicy.layerTest)),
),
Layer.provideMerge(SqlitePersistenceLayerLive),
),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import * as VcsDriverRegistry from "../../vcs/VcsDriverRegistry.ts";
import * as VcsProcess from "../../vcs/VcsProcess.ts";
import { VcsStatusBroadcaster } from "../../vcs/VcsStatusBroadcaster.ts";
import * as RepositoryIdentityResolver from "../../project/RepositoryIdentityResolver.ts";
import * as VersionControlPolicy from "../../vcs/VersionControlPolicy.ts";
import { CheckpointReactorLive } from "./CheckpointReactor.ts";
import { OrchestrationEngineLive } from "./OrchestrationEngine.ts";
import { OrchestrationProjectionPipelineLive } from "./ProjectionPipeline.ts";
Expand Down Expand Up @@ -400,6 +401,7 @@ describe("CheckpointReactor", () => {
Layer.provideMerge(VcsProcess.layer),
Layer.provideMerge(ServerConfigLayer),
Layer.provideMerge(NodeServices.layer),
Layer.provide(VersionControlPolicy.layerTest),
);

runtime = ManagedRuntime.make(layer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
type OrchestrationEventStoreShape,
} from "../../persistence/Services/OrchestrationEventStore.ts";
import * as RepositoryIdentityResolver from "../../project/RepositoryIdentityResolver.ts";
import * as VersionControlPolicy from "../../vcs/VersionControlPolicy.ts";
import { OrchestrationEngineLive } from "./OrchestrationEngine.ts";
import { OrchestrationProjectionPipelineLive } from "./ProjectionPipeline.ts";
import { OrchestrationProjectionSnapshotQueryLive } from "./ProjectionSnapshotQuery.ts";
Expand Down Expand Up @@ -92,6 +93,7 @@ function makeOrchestrationLayer(
Layer.provide(persistence),
Layer.provideMerge(ServerConfigLayer),
Layer.provideMerge(NodeServices.layer),
Layer.provide(VersionControlPolicy.layerTest),
);
}

Expand Down Expand Up @@ -1519,6 +1521,7 @@ describe("OrchestrationEngine", () => {
Layer.provide(Layer.succeed(OrchestrationEventStore, flakyStore)),
Layer.provide(OrchestrationCommandReceiptRepositoryLive),
Layer.provide(RepositoryIdentityResolver.layer),
Layer.provide(VersionControlPolicy.layerTest),
Layer.provide(SqlitePersistenceMemory),
Layer.provideMerge(ServerConfigLayer),
Layer.provideMerge(NodeServices.layer),
Expand Down Expand Up @@ -1627,6 +1630,7 @@ describe("OrchestrationEngine", () => {
Layer.provide(OrchestrationEventStoreLive),
Layer.provide(OrchestrationCommandReceiptRepositoryLive),
Layer.provide(RepositoryIdentityResolver.layer),
Layer.provide(VersionControlPolicy.layerTest),
Layer.provide(SqlitePersistenceMemory),
Layer.provide(NodeServices.layer),
),
Expand Down Expand Up @@ -1776,6 +1780,7 @@ describe("OrchestrationEngine", () => {
Layer.provide(Layer.succeed(OrchestrationEventStore, nonTransactionalStore)),
Layer.provide(OrchestrationCommandReceiptRepositoryLive),
Layer.provide(RepositoryIdentityResolver.layer),
Layer.provide(VersionControlPolicy.layerTest),
Layer.provide(SqlitePersistenceMemory),
Layer.provide(NodeServices.layer),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
import { OrchestrationEventStore } from "../../persistence/Services/OrchestrationEventStore.ts";
import { ProjectionStateRepository } from "../../persistence/Services/ProjectionState.ts";
import * as RepositoryIdentityResolver from "../../project/RepositoryIdentityResolver.ts";
import * as VersionControlPolicy from "../../vcs/VersionControlPolicy.ts";
import { OrchestrationEngineLive } from "./OrchestrationEngine.ts";
import {
ORCHESTRATION_PROJECTOR_NAMES,
Expand Down Expand Up @@ -4423,6 +4424,7 @@ const engineLayer = it.layer(
Layer.provide(OrchestrationEventStoreLive),
Layer.provide(OrchestrationCommandReceiptRepositoryLive),
Layer.provide(RepositoryIdentityResolver.layer),
Layer.provide(VersionControlPolicy.layerTest),
Layer.provideMerge(SqlitePersistenceMemory),
Layer.provideMerge(
ServerConfig.layerTest(process.cwd(), {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import * as SqlClient from "effect/unstable/sql/SqlClient";

import { SqlitePersistenceMemory } from "../../persistence/Layers/Sqlite.ts";
import * as RepositoryIdentityResolver from "../../project/RepositoryIdentityResolver.ts";
import * as VersionControlPolicy from "../../vcs/VersionControlPolicy.ts";
import { ORCHESTRATION_PROJECTOR_NAMES } from "./ProjectionPipeline.ts";
import { OrchestrationProjectionSnapshotQueryLive } from "./ProjectionSnapshotQuery.ts";
import * as ThreadBackgroundLiveness from "../ThreadBackgroundLiveness.ts";
Expand Down Expand Up @@ -105,6 +106,7 @@ const projectionSnapshotLayer = it.layer(
Layer.provide(ThreadBackgroundLiveness.layer),
Layer.provide(ThreadPlanProgress.layer),
Layer.provideMerge(RepositoryIdentityResolver.layer),
Layer.provide(VersionControlPolicy.layerTest),
Layer.provideMerge(SqlitePersistenceMemory),
Layer.provideMerge(NodeServices.layer),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import { makeProviderRegistryLayer } from "../../provider/testUtils/providerRegi
import { TextGeneration } from "../../textGeneration/TextGeneration.ts";
import { TerminalManager } from "../../terminal/Manager.ts";
import * as RepositoryIdentityResolver from "../../project/RepositoryIdentityResolver.ts";
import * as VersionControlPolicy from "../../vcs/VersionControlPolicy.ts";
import { OrchestrationEngineLive } from "./OrchestrationEngine.ts";
import { OrchestrationProjectionPipelineLive } from "./ProjectionPipeline.ts";
import { OrchestrationProjectionSnapshotQueryLive } from "./ProjectionSnapshotQuery.ts";
Expand Down Expand Up @@ -497,6 +498,7 @@ describe("ProviderCommandReactor", () => {
Layer.provideMerge(SqlitePersistenceMemory),
Layer.provideMerge(ServerConfig.layerTest(process.cwd(), baseDir)),
Layer.provideMerge(NodeServices.layer),
Layer.provide(VersionControlPolicy.layerTest),
);
runtime = ManagedRuntime.make(layer);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {
import * as RepositoryIdentityResolver from "../../project/RepositoryIdentityResolver.ts";
import * as CheckpointStore from "../../checkpointing/CheckpointStore.ts";
import * as VcsDriverRegistry from "../../vcs/VcsDriverRegistry.ts";
import * as VersionControlPolicy from "../../vcs/VersionControlPolicy.ts";
import * as VcsProcess from "../../vcs/VcsProcess.ts";
import { OrchestrationEngineLive } from "./OrchestrationEngine.ts";
import { OrchestrationProjectionPipelineLive } from "./ProjectionPipeline.ts";
Expand Down Expand Up @@ -344,6 +345,7 @@ describe("ProviderRuntimeIngestion", () => {
Layer.provideMerge(ServerConfig.layerTest(process.cwd(), process.cwd())),
Layer.provideMerge(NodeServices.layer),
Layer.provideMerge(Layer.succeed(Tracer.Tracer, sqlCounter.tracer)),
Layer.provide(VersionControlPolicy.layerTest),
);
const testRuntime = ManagedRuntime.make(layer);
runtime = testRuntime;
Expand Down
2 changes: 2 additions & 0 deletions apps/server/src/project/AgentSessionImporter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import { TextGeneration } from "../textGeneration/TextGeneration.ts";
import { TerminalManager } from "../terminal/Manager.ts";
import { VcsStatusBroadcaster } from "../vcs/VcsStatusBroadcaster.ts";
import * as RepositoryIdentityResolver from "./RepositoryIdentityResolver.ts";
import * as VersionControlPolicy from "../vcs/VersionControlPolicy.ts";
import { importRecentAgentThreads } from "./AgentSessionImporter.ts";
import * as AgentSessionScanner from "./AgentSessionScanner.ts";

Expand Down Expand Up @@ -576,6 +577,7 @@ const integrationLayer = Layer.mergeAll(
Layer.provide(OrchestrationEventStoreLive),
Layer.provide(OrchestrationCommandReceiptRepositoryLive),
Layer.provide(RepositoryIdentityResolver.layer),
Layer.provide(VersionControlPolicy.layerTest),
Layer.provide(SqlitePersistenceMemory),
Layer.provideMerge(integrationServerConfig),
Layer.provideMerge(NodeServices.layer),
Expand Down
44 changes: 36 additions & 8 deletions apps/server/src/project/RepositoryIdentityResolver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawne
import { TestClock } from "effect/testing";

import * as ProcessRunner from "../processRunner.ts";
import * as VersionControlPolicy from "../vcs/VersionControlPolicy.ts";
import * as RepositoryIdentityResolver from "./RepositoryIdentityResolver.ts";

const normalizePathSeparators = (value: string) => value.replaceAll("\\", "/");
Expand All @@ -36,7 +37,11 @@ const makeRepositoryIdentityResolverTestLayer = (options: {
cacheCapacity: 16,
...options,
}),
).pipe(Layer.provide(ProcessRunner.layer));
).pipe(Layer.provide(ProcessRunner.layer), Layer.provide(VersionControlPolicy.layerTest));

const repositoryIdentityResolverLayer = RepositoryIdentityResolver.layer.pipe(
Layer.provide(VersionControlPolicy.layerTest),
);

it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => {
it.effect("refreshes the Git root only when requested", () => {
Expand Down Expand Up @@ -88,7 +93,7 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => {
);
},
}),
).pipe(Layer.provide(processRunner));
).pipe(Layer.provide(processRunner), Layer.provide(VersionControlPolicy.layerTest));

return Effect.gen(function* () {
const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver;
Expand Down Expand Up @@ -156,7 +161,7 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => {
const resolverLayer = Layer.effect(
RepositoryIdentityResolver.RepositoryIdentityResolver,
RepositoryIdentityResolver.make(),
).pipe(Layer.provide(processRunner));
).pipe(Layer.provide(processRunner), Layer.provide(VersionControlPolicy.layerTest));

return Effect.gen(function* () {
const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver;
Expand Down Expand Up @@ -199,7 +204,7 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => {
expect(identity?.provider).toBe("github");
expect(identity?.owner).toBe("t3tools");
expect(identity?.name).toBe("t3code");
}).pipe(Effect.provide(RepositoryIdentityResolver.layer)),
}).pipe(Effect.provide(repositoryIdentityResolverLayer)),
);

it.effect("returns the git top-level root path when resolving from a nested workspace", () =>
Expand All @@ -226,7 +231,7 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => {
expect(normalizeResolvedPath(resolvedIdentityRoot)).toBe(
normalizeResolvedPath(resolvedRepoRoot),
);
}).pipe(Effect.provide(RepositoryIdentityResolver.layer)),
}).pipe(Effect.provide(repositoryIdentityResolverLayer)),
);

it.effect("returns null for non-git folders and repos without remotes", () =>
Expand All @@ -247,7 +252,7 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => {

expect(nonGitIdentity).toBeNull();
expect(noRemoteIdentity).toBeNull();
}).pipe(Effect.provide(RepositoryIdentityResolver.layer)),
}).pipe(Effect.provide(repositoryIdentityResolverLayer)),
);

it.effect.each(["add", "replace"] as const)(
Expand Down Expand Up @@ -285,7 +290,7 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => {
expect(identity?.canonicalKey).toBe("github.com/t3tools/t3code");
expect(identity?.displayName).toBe("t3tools/t3code");
expect(yield* resolver.resolve(cwd)).toEqual(identity);
}).pipe(Effect.provide(RepositoryIdentityResolver.layer)),
}).pipe(Effect.provide(repositoryIdentityResolverLayer)),
);

it.effect("uses the last remote path segment as the repository name for nested groups", () =>
Expand All @@ -306,7 +311,7 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => {
expect(identity?.displayName).toBe("t3tools/platform/t3code");
expect(identity?.owner).toBe("t3tools");
expect(identity?.name).toBe("t3code");
}).pipe(Effect.provide(RepositoryIdentityResolver.layer)),
}).pipe(Effect.provide(repositoryIdentityResolverLayer)),
);

it.effect(
Expand Down Expand Up @@ -390,4 +395,27 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => {
),
),
);

it.effect("does not spawn Git when version control is disabled", () => {
const resolverLayer = Layer.effect(
RepositoryIdentityResolver.RepositoryIdentityResolver,
RepositoryIdentityResolver.make(),
).pipe(
Layer.provide(
Layer.succeed(ProcessRunner.ProcessRunner, {
run: () => Effect.die("git should not run when version control is disabled"),
}),
),
Layer.provide(
Layer.succeed(VersionControlPolicy.VersionControlPolicy, {
isEnabled: () => Effect.succeed(false),
}),
),
);

return Effect.gen(function* () {
const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver;
expect(yield* resolver.resolve("/repo")).toBeNull();
}).pipe(Effect.provide(resolverLayer));
});
});
Loading
Loading