Skip to content

Commit 91a03e0

Browse files
juliusmarmingeJulius Marmingecursoragentcursor[bot]
authored
feat(source-control): add Bitbucket & Azure Devops providers (#2473)
Co-authored-by: Julius Marminge <julius@macmini.local> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com>
1 parent d501ebe commit 91a03e0

59 files changed

Lines changed: 5834 additions & 2327 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/server/src/git/GitManager.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,7 @@ function makeManager(input?: {
655655
get: () => Effect.succeed(provider),
656656
resolveHandle: () => Effect.succeed({ provider, context: null }),
657657
resolve: () => Effect.succeed(provider),
658+
discover: Effect.succeed([]),
658659
}),
659660
),
660661
Effect.provide(Layer.succeed(GitHubCli, gitHubCli)),

apps/server/src/git/GitWorkflowService.test.ts

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,27 @@
1-
import { assert, it } from "@effect/vitest";
1+
import { assert, describe, it, vi } from "@effect/vitest";
22
import { Effect, Layer } from "effect";
3-
import { describe, vi } from "vitest";
43

5-
import { GitManager } from "./GitManager.ts";
6-
import { GitWorkflowService, layer as GitWorkflowServiceLayer } from "./GitWorkflowService.ts";
7-
import { GitVcsDriver } from "../vcs/GitVcsDriver.ts";
8-
import { VcsDriverRegistry, type VcsDriverRegistryShape } from "../vcs/VcsDriverRegistry.ts";
4+
import * as GitManager from "./GitManager.ts";
5+
import * as GitWorkflowService from "./GitWorkflowService.ts";
6+
import * as GitVcsDriver from "../vcs/GitVcsDriver.ts";
7+
import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts";
98

10-
function makeLayer(input: { readonly detect: VcsDriverRegistryShape["detect"] }) {
11-
return GitWorkflowServiceLayer.pipe(
9+
function makeLayer(input: { readonly detect: VcsDriverRegistry.VcsDriverRegistryShape["detect"] }) {
10+
return GitWorkflowService.layer.pipe(
1211
Layer.provide(
13-
Layer.mock(VcsDriverRegistry)({
12+
Layer.mock(VcsDriverRegistry.VcsDriverRegistry)({
1413
detect: input.detect,
1514
}),
1615
),
17-
Layer.provide(Layer.mock(GitVcsDriver)({})),
18-
Layer.provide(Layer.mock(GitManager)({})),
16+
Layer.provide(Layer.mock(GitVcsDriver.GitVcsDriver)({})),
17+
Layer.provide(Layer.mock(GitManager.GitManager)({})),
1918
);
2019
}
2120

2221
describe("GitWorkflowService", () => {
2322
it.effect("returns an empty local status when no VCS repository is detected", () =>
2423
Effect.gen(function* () {
25-
const workflow = yield* GitWorkflowService;
24+
const workflow = yield* GitWorkflowService.GitWorkflowService;
2625
const status = yield* workflow.localStatus({ cwd: "/not-a-repo" });
2726

2827
assert.deepStrictEqual(status, {
@@ -48,7 +47,7 @@ describe("GitWorkflowService", () => {
4847

4948
it.effect("returns an empty full status when no VCS repository is detected", () =>
5049
Effect.gen(function* () {
51-
const workflow = yield* GitWorkflowService;
50+
const workflow = yield* GitWorkflowService.GitWorkflowService;
5251
const status = yield* workflow.status({ cwd: "/not-a-repo" });
5352

5453
assert.deepStrictEqual(status, {
@@ -82,15 +81,15 @@ describe("GitWorkflowService", () => {
8281
const remoteStatus = vi.fn();
8382
const status = vi.fn();
8483

85-
const testLayer = GitWorkflowServiceLayer.pipe(
84+
const testLayer = GitWorkflowService.layer.pipe(
8685
Layer.provide(
87-
Layer.mock(VcsDriverRegistry)({
86+
Layer.mock(VcsDriverRegistry.VcsDriverRegistry)({
8887
detect: () => Effect.succeed(null),
8988
}),
9089
),
91-
Layer.provide(Layer.mock(GitVcsDriver)({})),
90+
Layer.provide(Layer.mock(GitVcsDriver.GitVcsDriver)({})),
9291
Layer.provide(
93-
Layer.mock(GitManager)({
92+
Layer.mock(GitManager.GitManager)({
9493
localStatus,
9594
remoteStatus,
9695
status,
@@ -99,7 +98,7 @@ describe("GitWorkflowService", () => {
9998
);
10099

101100
return Effect.gen(function* () {
102-
const workflow = yield* GitWorkflowService;
101+
const workflow = yield* GitWorkflowService.GitWorkflowService;
103102
yield* workflow.localStatus({ cwd: "/not-a-repo" });
104103
yield* workflow.remoteStatus({ cwd: "/not-a-repo" });
105104
yield* workflow.status({ cwd: "/not-a-repo" });
@@ -112,7 +111,7 @@ describe("GitWorkflowService", () => {
112111

113112
it.effect("returns an empty ref list when no VCS repository is detected", () =>
114113
Effect.gen(function* () {
115-
const workflow = yield* GitWorkflowService;
114+
const workflow = yield* GitWorkflowService.GitWorkflowService;
116115
const refs = yield* workflow.listRefs({ cwd: "/not-a-repo" });
117116

118117
assert.deepStrictEqual(refs, {

apps/server/src/server.test.ts

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -105,23 +105,12 @@ import { WorkspaceEntriesLive } from "./workspace/Layers/WorkspaceEntries.ts";
105105
import { WorkspaceFileSystemLive } from "./workspace/Layers/WorkspaceFileSystem.ts";
106106
import { WorkspacePathsLive } from "./workspace/Layers/WorkspacePaths.ts";
107107
import * as GitVcsDriver from "./vcs/GitVcsDriver.ts";
108-
import type { VcsDriverShape } from "./vcs/VcsDriver.ts";
109-
import {
110-
VcsStatusBroadcaster,
111-
type VcsStatusBroadcasterShape,
112-
layer as VcsStatusBroadcasterLayer,
113-
} from "./vcs/VcsStatusBroadcaster.ts";
114-
import {
115-
VcsDriverRegistry,
116-
type VcsDriverRegistryShape,
117-
type VcsDriverHandle,
118-
} from "./vcs/VcsDriverRegistry.ts";
119-
import { layer as VcsProvisioningServiceLayer } from "./vcs/VcsProvisioningService.ts";
120-
import { layer as GitWorkflowServiceLayer } from "./git/GitWorkflowService.ts";
121-
import {
122-
SourceControlRepositoryService,
123-
type SourceControlRepositoryServiceShape,
124-
} from "./sourceControl/SourceControlRepositoryService.ts";
108+
import * as VcsDriver from "./vcs/VcsDriver.ts";
109+
import * as VcsStatusBroadcaster from "./vcs/VcsStatusBroadcaster.ts";
110+
import * as VcsDriverRegistry from "./vcs/VcsDriverRegistry.ts";
111+
import * as VcsProvisioningService from "./vcs/VcsProvisioningService.ts";
112+
import * as GitWorkflowService from "./git/GitWorkflowService.ts";
113+
import * as SourceControlRepositoryService from "./sourceControl/SourceControlRepositoryService.ts";
125114
import { ServerSecretStoreLive } from "./auth/Layers/ServerSecretStore.ts";
126115
import { ServerAuthLive } from "./auth/Layers/ServerAuth.ts";
127116

@@ -329,12 +318,12 @@ const buildAppUnderTest = (options?: {
329318
providerRegistry?: Partial<ProviderRegistryShape>;
330319
serverSettings?: Partial<ServerSettingsShape>;
331320
open?: Partial<OpenShape>;
332-
vcsDriver?: Partial<VcsDriverShape>;
333-
vcsDriverRegistry?: Partial<VcsDriverRegistryShape>;
321+
vcsDriver?: Partial<VcsDriver.VcsDriverShape>;
322+
vcsDriverRegistry?: Partial<VcsDriverRegistry.VcsDriverRegistryShape>;
334323
gitVcsDriver?: Partial<GitVcsDriver.GitVcsDriverShape>;
335324
gitManager?: Partial<GitManagerShape>;
336-
sourceControlRepositoryService?: Partial<SourceControlRepositoryServiceShape>;
337-
vcsStatusBroadcaster?: Partial<VcsStatusBroadcasterShape>;
325+
sourceControlRepositoryService?: Partial<SourceControlRepositoryService.SourceControlRepositoryServiceShape>;
326+
vcsStatusBroadcaster?: Partial<VcsStatusBroadcaster.VcsStatusBroadcasterShape>;
338327
projectSetupScriptRunner?: Partial<ProjectSetupScriptRunnerShape>;
339328
terminalManager?: Partial<TerminalManagerShape>;
340329
orchestrationEngine?: Partial<OrchestrationEngineShape>;
@@ -382,7 +371,7 @@ const buildAppUnderTest = (options?: {
382371
...options?.config,
383372
};
384373
const layerConfig = Layer.succeed(ServerConfig, config);
385-
const defaultVcsDriver: VcsDriverShape = {
374+
const defaultVcsDriver: VcsDriver.VcsDriverShape = {
386375
capabilities: {
387376
kind: "git",
388377
supportsWorktrees: true,
@@ -424,7 +413,7 @@ const buildAppUnderTest = (options?: {
424413
initRepository: () => Effect.void,
425414
...options?.layers?.vcsDriver,
426415
};
427-
const vcsDriverRegistryLayer = Layer.mock(VcsDriverRegistry)({
416+
const vcsDriverRegistryLayer = Layer.mock(VcsDriverRegistry.VcsDriverRegistry)({
428417
get: () => Effect.succeed(defaultVcsDriver),
429418
detect: (input) =>
430419
defaultVcsDriver.detectRepository(input.cwd).pipe(
@@ -454,7 +443,7 @@ const buildAppUnderTest = (options?: {
454443
kind: repository.kind,
455444
repository,
456445
driver: defaultVcsDriver,
457-
} satisfies VcsDriverHandle)
446+
} satisfies VcsDriverRegistry.VcsDriverHandle)
458447
: null,
459448
),
460449
),
@@ -496,19 +485,19 @@ const buildAppUnderTest = (options?: {
496485
),
497486
ProjectFaviconResolverLive,
498487
);
499-
const gitWorkflowLayer = GitWorkflowServiceLayer.pipe(
488+
const gitWorkflowLayer = GitWorkflowService.layer.pipe(
500489
Layer.provideMerge(vcsDriverRegistryLayer),
501490
Layer.provideMerge(gitVcsDriverLayer),
502491
Layer.provideMerge(gitManagerLayer),
503492
);
504-
const vcsProvisioningLayer = VcsProvisioningServiceLayer.pipe(
493+
const vcsProvisioningLayer = VcsProvisioningService.layer.pipe(
505494
Layer.provide(vcsDriverRegistryLayer),
506495
);
507496
const vcsStatusBroadcasterLayer = options?.layers?.vcsStatusBroadcaster
508-
? Layer.mock(VcsStatusBroadcaster)({
497+
? Layer.mock(VcsStatusBroadcaster.VcsStatusBroadcaster)({
509498
...options.layers.vcsStatusBroadcaster,
510499
})
511-
: VcsStatusBroadcasterLayer.pipe(Layer.provide(gitWorkflowLayer));
500+
: VcsStatusBroadcaster.layer.pipe(Layer.provide(gitWorkflowLayer));
512501

513502
const servedRoutesLayer = HttpRouter.serve(makeRoutesLayer, {
514503
disableListenLog: true,
@@ -552,7 +541,7 @@ const buildAppUnderTest = (options?: {
552541
Layer.provide(gitWorkflowLayer),
553542
Layer.provide(vcsProvisioningLayer),
554543
Layer.provide(
555-
Layer.mock(SourceControlRepositoryService)({
544+
Layer.mock(SourceControlRepositoryService.SourceControlRepositoryService)({
556545
...options?.layers?.sourceControlRepositoryService,
557546
}),
558547
),

apps/server/src/server.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import { ProviderSessionReaperLive } from "./provider/Layers/ProviderSessionReap
2525
import { OpenCodeRuntimeLive } from "./provider/opencodeRuntime.ts";
2626
import { CheckpointDiffQueryLive } from "./checkpointing/Layers/CheckpointDiffQuery.ts";
2727
import { CheckpointStoreLive } from "./checkpointing/Layers/CheckpointStore.ts";
28+
import * as AzureDevOpsCli from "./sourceControl/AzureDevOpsCli.ts";
29+
import * as BitbucketApi from "./sourceControl/BitbucketApi.ts";
2830
import * as GitHubCli from "./sourceControl/GitHubCli.ts";
2931
import * as GitLabCli from "./sourceControl/GitLabCli.ts";
3032
import * as TextGeneration from "./textGeneration/TextGeneration.ts";
@@ -164,7 +166,10 @@ const VcsDriverRegistryLayerLive = VcsDriverRegistry.layer.pipe(
164166
);
165167

166168
const SourceControlProviderRegistryLayerLive = SourceControlProviderRegistry.layer.pipe(
167-
Layer.provide(Layer.mergeAll(GitHubCli.layer, GitLabCli.layer)),
169+
Layer.provide(
170+
Layer.mergeAll(AzureDevOpsCli.layer, BitbucketApi.layer, GitHubCli.layer, GitLabCli.layer),
171+
),
172+
Layer.provideMerge(GitVcsDriver.layer),
168173
Layer.provideMerge(VcsDriverRegistryLayerLive),
169174
);
170175

@@ -235,6 +240,7 @@ const ProviderRuntimeLayerLive = ProviderSessionReaperLive.pipe(
235240
const RuntimeCoreDependenciesLive = ReactorLayerLive.pipe(
236241
// Core Services
237242
Layer.provideMerge(CheckpointingLayerLive),
243+
Layer.provideMerge(SourceControlProviderRegistryLayerLive),
238244
Layer.provideMerge(GitLayerLive),
239245
Layer.provideMerge(VcsLayerLive),
240246
Layer.provideMerge(ProviderRuntimeLayerLive),

0 commit comments

Comments
 (0)