Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
7c90ddd
Add Bitbucket source control provider
May 2, 2026
f6ae51a
Enable Bitbucket source control discovery
May 2, 2026
1d66235
Use Bitbucket REST API provider
May 3, 2026
3eeb1e3
Address Bitbucket provider review feedback
May 3, 2026
ea073be
Implement Bitbucket PR checkout
May 3, 2026
c72120b
Document Bitbucket checkout Git coupling
May 3, 2026
7a5382e
Move source control discovery into provider registry
May 3, 2026
cb6dd20
Document source control provider setup
May 3, 2026
69701a5
Fix Bitbucket closed PR state filtering
May 3, 2026
7e848b5
Make checkout command examples optional
May 3, 2026
35897a4
Provide Git VCS driver for websocket source control
May 3, 2026
9f10d14
Support Bitbucket repository publishing
May 4, 2026
3d3960f
feat(scm): Azure Devops (#2463)
juliusmarminge May 4, 2026
7cf2c5f
refactor(source control): streamline imports and enhance type usage a…
juliusmarminge May 4, 2026
7185db2
nit
juliusmarminge May 4, 2026
7166117
refactor: reuse shared parseSourceControlOwnerRef in AzureDevOpsCli n…
cursoragent May 4, 2026
daacfce
Merge branch 'main' into t3code/bitbucket-adapter
juliusmarminge May 4, 2026
82a6f5a
refactor: reuse shared parseSourceControlOwnerRef in AzureDevOpsCli n…
cursor[bot] May 4, 2026
43a46a5
Extract duplicated normalizeSourceBranch/sourceBranch helpers to Sour…
cursoragent May 4, 2026
63fc7c7
refactor(source control): update install hints and remove implemented…
juliusmarminge May 4, 2026
e8ea159
Fix source control publish review feedback
May 4, 2026
4ca302d
Make add project palette environment-first
May 4, 2026
a090115
Sort add project providers by availability
May 4, 2026
4b7dfcf
Avoid blocking add project on discovery
May 4, 2026
0b3bf90
Align disabled command palette row height
May 4, 2026
5505016
Sort publish providers by availability
May 4, 2026
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
1 change: 1 addition & 0 deletions apps/server/src/git/GitManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,7 @@ function makeManager(input?: {
get: () => Effect.succeed(provider),
resolveHandle: () => Effect.succeed({ provider, context: null }),
resolve: () => Effect.succeed(provider),
discover: Effect.succeed([]),
}),
),
Effect.provide(Layer.succeed(GitHubCli, gitHubCli)),
Expand Down
37 changes: 18 additions & 19 deletions apps/server/src/git/GitWorkflowService.test.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
import { assert, it } from "@effect/vitest";
import { assert, describe, it, vi } from "@effect/vitest";
import { Effect, Layer } from "effect";
import { describe, vi } from "vitest";

import { GitManager } from "./GitManager.ts";
import { GitWorkflowService, layer as GitWorkflowServiceLayer } from "./GitWorkflowService.ts";
import { GitVcsDriver } from "../vcs/GitVcsDriver.ts";
import { VcsDriverRegistry, type VcsDriverRegistryShape } from "../vcs/VcsDriverRegistry.ts";
import * as GitManager from "./GitManager.ts";
import * as GitWorkflowService from "./GitWorkflowService.ts";
import * as GitVcsDriver from "../vcs/GitVcsDriver.ts";
import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts";

function makeLayer(input: { readonly detect: VcsDriverRegistryShape["detect"] }) {
return GitWorkflowServiceLayer.pipe(
function makeLayer(input: { readonly detect: VcsDriverRegistry.VcsDriverRegistryShape["detect"] }) {
return GitWorkflowService.layer.pipe(
Layer.provide(
Layer.mock(VcsDriverRegistry)({
Layer.mock(VcsDriverRegistry.VcsDriverRegistry)({
detect: input.detect,
}),
),
Layer.provide(Layer.mock(GitVcsDriver)({})),
Layer.provide(Layer.mock(GitManager)({})),
Layer.provide(Layer.mock(GitVcsDriver.GitVcsDriver)({})),
Layer.provide(Layer.mock(GitManager.GitManager)({})),
);
}

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

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

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

assert.deepStrictEqual(status, {
Expand Down Expand Up @@ -82,15 +81,15 @@ describe("GitWorkflowService", () => {
const remoteStatus = vi.fn();
const status = vi.fn();

const testLayer = GitWorkflowServiceLayer.pipe(
const testLayer = GitWorkflowService.layer.pipe(
Layer.provide(
Layer.mock(VcsDriverRegistry)({
Layer.mock(VcsDriverRegistry.VcsDriverRegistry)({
detect: () => Effect.succeed(null),
}),
),
Layer.provide(Layer.mock(GitVcsDriver)({})),
Layer.provide(Layer.mock(GitVcsDriver.GitVcsDriver)({})),
Layer.provide(
Layer.mock(GitManager)({
Layer.mock(GitManager.GitManager)({
localStatus,
remoteStatus,
status,
Expand All @@ -99,7 +98,7 @@ describe("GitWorkflowService", () => {
);

return Effect.gen(function* () {
const workflow = yield* GitWorkflowService;
const workflow = yield* GitWorkflowService.GitWorkflowService;
yield* workflow.localStatus({ cwd: "/not-a-repo" });
yield* workflow.remoteStatus({ cwd: "/not-a-repo" });
yield* workflow.status({ cwd: "/not-a-repo" });
Expand All @@ -112,7 +111,7 @@ describe("GitWorkflowService", () => {

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

assert.deepStrictEqual(refs, {
Expand Down
47 changes: 18 additions & 29 deletions apps/server/src/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,23 +105,12 @@ import { WorkspaceEntriesLive } from "./workspace/Layers/WorkspaceEntries.ts";
import { WorkspaceFileSystemLive } from "./workspace/Layers/WorkspaceFileSystem.ts";
import { WorkspacePathsLive } from "./workspace/Layers/WorkspacePaths.ts";
import * as GitVcsDriver from "./vcs/GitVcsDriver.ts";
import type { VcsDriverShape } from "./vcs/VcsDriver.ts";
import {
VcsStatusBroadcaster,
type VcsStatusBroadcasterShape,
layer as VcsStatusBroadcasterLayer,
} from "./vcs/VcsStatusBroadcaster.ts";
import {
VcsDriverRegistry,
type VcsDriverRegistryShape,
type VcsDriverHandle,
} from "./vcs/VcsDriverRegistry.ts";
import { layer as VcsProvisioningServiceLayer } from "./vcs/VcsProvisioningService.ts";
import { layer as GitWorkflowServiceLayer } from "./git/GitWorkflowService.ts";
import {
SourceControlRepositoryService,
type SourceControlRepositoryServiceShape,
} from "./sourceControl/SourceControlRepositoryService.ts";
import * as VcsDriver from "./vcs/VcsDriver.ts";
import * as VcsStatusBroadcaster from "./vcs/VcsStatusBroadcaster.ts";
import * as VcsDriverRegistry from "./vcs/VcsDriverRegistry.ts";
import * as VcsProvisioningService from "./vcs/VcsProvisioningService.ts";
import * as GitWorkflowService from "./git/GitWorkflowService.ts";
import * as SourceControlRepositoryService from "./sourceControl/SourceControlRepositoryService.ts";
import { ServerSecretStoreLive } from "./auth/Layers/ServerSecretStore.ts";
import { ServerAuthLive } from "./auth/Layers/ServerAuth.ts";

Expand Down Expand Up @@ -329,12 +318,12 @@ const buildAppUnderTest = (options?: {
providerRegistry?: Partial<ProviderRegistryShape>;
serverSettings?: Partial<ServerSettingsShape>;
open?: Partial<OpenShape>;
vcsDriver?: Partial<VcsDriverShape>;
vcsDriverRegistry?: Partial<VcsDriverRegistryShape>;
vcsDriver?: Partial<VcsDriver.VcsDriverShape>;
vcsDriverRegistry?: Partial<VcsDriverRegistry.VcsDriverRegistryShape>;
gitVcsDriver?: Partial<GitVcsDriver.GitVcsDriverShape>;
gitManager?: Partial<GitManagerShape>;
sourceControlRepositoryService?: Partial<SourceControlRepositoryServiceShape>;
vcsStatusBroadcaster?: Partial<VcsStatusBroadcasterShape>;
sourceControlRepositoryService?: Partial<SourceControlRepositoryService.SourceControlRepositoryServiceShape>;
vcsStatusBroadcaster?: Partial<VcsStatusBroadcaster.VcsStatusBroadcasterShape>;
projectSetupScriptRunner?: Partial<ProjectSetupScriptRunnerShape>;
terminalManager?: Partial<TerminalManagerShape>;
orchestrationEngine?: Partial<OrchestrationEngineShape>;
Expand Down Expand Up @@ -382,7 +371,7 @@ const buildAppUnderTest = (options?: {
...options?.config,
};
const layerConfig = Layer.succeed(ServerConfig, config);
const defaultVcsDriver: VcsDriverShape = {
const defaultVcsDriver: VcsDriver.VcsDriverShape = {
capabilities: {
kind: "git",
supportsWorktrees: true,
Expand Down Expand Up @@ -424,7 +413,7 @@ const buildAppUnderTest = (options?: {
initRepository: () => Effect.void,
...options?.layers?.vcsDriver,
};
const vcsDriverRegistryLayer = Layer.mock(VcsDriverRegistry)({
const vcsDriverRegistryLayer = Layer.mock(VcsDriverRegistry.VcsDriverRegistry)({
get: () => Effect.succeed(defaultVcsDriver),
detect: (input) =>
defaultVcsDriver.detectRepository(input.cwd).pipe(
Expand Down Expand Up @@ -454,7 +443,7 @@ const buildAppUnderTest = (options?: {
kind: repository.kind,
repository,
driver: defaultVcsDriver,
} satisfies VcsDriverHandle)
} satisfies VcsDriverRegistry.VcsDriverHandle)
: null,
),
),
Expand Down Expand Up @@ -496,19 +485,19 @@ const buildAppUnderTest = (options?: {
),
ProjectFaviconResolverLive,
);
const gitWorkflowLayer = GitWorkflowServiceLayer.pipe(
const gitWorkflowLayer = GitWorkflowService.layer.pipe(
Layer.provideMerge(vcsDriverRegistryLayer),
Layer.provideMerge(gitVcsDriverLayer),
Layer.provideMerge(gitManagerLayer),
);
const vcsProvisioningLayer = VcsProvisioningServiceLayer.pipe(
const vcsProvisioningLayer = VcsProvisioningService.layer.pipe(
Layer.provide(vcsDriverRegistryLayer),
);
const vcsStatusBroadcasterLayer = options?.layers?.vcsStatusBroadcaster
? Layer.mock(VcsStatusBroadcaster)({
? Layer.mock(VcsStatusBroadcaster.VcsStatusBroadcaster)({
...options.layers.vcsStatusBroadcaster,
})
: VcsStatusBroadcasterLayer.pipe(Layer.provide(gitWorkflowLayer));
: VcsStatusBroadcaster.layer.pipe(Layer.provide(gitWorkflowLayer));

const servedRoutesLayer = HttpRouter.serve(makeRoutesLayer, {
disableListenLog: true,
Expand Down Expand Up @@ -552,7 +541,7 @@ const buildAppUnderTest = (options?: {
Layer.provide(gitWorkflowLayer),
Layer.provide(vcsProvisioningLayer),
Layer.provide(
Layer.mock(SourceControlRepositoryService)({
Layer.mock(SourceControlRepositoryService.SourceControlRepositoryService)({
...options?.layers?.sourceControlRepositoryService,
}),
),
Expand Down
8 changes: 7 additions & 1 deletion apps/server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import { ProviderSessionReaperLive } from "./provider/Layers/ProviderSessionReap
import { OpenCodeRuntimeLive } from "./provider/opencodeRuntime.ts";
import { CheckpointDiffQueryLive } from "./checkpointing/Layers/CheckpointDiffQuery.ts";
import { CheckpointStoreLive } from "./checkpointing/Layers/CheckpointStore.ts";
import * as AzureDevOpsCli from "./sourceControl/AzureDevOpsCli.ts";
import * as BitbucketApi from "./sourceControl/BitbucketApi.ts";
import * as GitHubCli from "./sourceControl/GitHubCli.ts";
import * as GitLabCli from "./sourceControl/GitLabCli.ts";
import * as TextGeneration from "./textGeneration/TextGeneration.ts";
Expand Down Expand Up @@ -164,7 +166,10 @@ const VcsDriverRegistryLayerLive = VcsDriverRegistry.layer.pipe(
);

const SourceControlProviderRegistryLayerLive = SourceControlProviderRegistry.layer.pipe(
Layer.provide(Layer.mergeAll(GitHubCli.layer, GitLabCli.layer)),
Layer.provide(
Layer.mergeAll(AzureDevOpsCli.layer, BitbucketApi.layer, GitHubCli.layer, GitLabCli.layer),
),
Layer.provideMerge(GitVcsDriver.layer),
Layer.provideMerge(VcsDriverRegistryLayerLive),
);

Expand Down Expand Up @@ -235,6 +240,7 @@ const ProviderRuntimeLayerLive = ProviderSessionReaperLive.pipe(
const RuntimeCoreDependenciesLive = ReactorLayerLive.pipe(
// Core Services
Layer.provideMerge(CheckpointingLayerLive),
Layer.provideMerge(SourceControlProviderRegistryLayerLive),
Layer.provideMerge(GitLayerLive),
Layer.provideMerge(VcsLayerLive),
Layer.provideMerge(ProviderRuntimeLayerLive),
Expand Down
Loading
Loading