Skip to content
Open
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
26 changes: 26 additions & 0 deletions apps/server/src/git/Layers/GitHubCli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,32 @@ layer("GitHubCliLive", (it) => {
}),
);

it.effect("creates a repository from the current directory", () =>
Effect.gen(function* () {
mockedRunProcess.mockResolvedValueOnce({
stdout: "https://github.com/octocat/codething-mvp\n",
stderr: "",
code: 0,
signal: null,
timedOut: false,
});

yield* Effect.gen(function* () {
const gh = yield* GitHubCli;
return yield* gh.createRepository({
cwd: "/repo",
visibility: "private",
});
});

expect(mockedRunProcess).toHaveBeenCalledWith(
"gh",
["repo", "create", "--source=.", "--private", "--remote", "origin"],
expect.objectContaining({ cwd: "/repo" }),
);
}),
);

it.effect("surfaces a friendly error when the pull request is not found", () =>
Effect.gen(function* () {
mockedRunProcess.mockRejectedValueOnce(
Expand Down
12 changes: 12 additions & 0 deletions apps/server/src/git/Layers/GitHubCli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,18 @@ const makeGitHubCli = Effect.sync(() => {
),
Effect.map(normalizeRepositoryCloneUrls),
),
createRepository: (input) =>
execute({
cwd: input.cwd,
args: [
"repo",
"create",
"--source=.",
`--${input.visibility}`,
"--remote",
input.remote ?? "origin",
],
}).pipe(Effect.asVoid),
createPullRequest: (input) =>
execute({
cwd: input.cwd,
Expand Down
12 changes: 12 additions & 0 deletions apps/server/src/git/Layers/GitManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,18 @@ function createGitHubCliWithFakeGh(scenario: FakeGhScenario = {}): {
input.bodyFile,
],
}).pipe(Effect.asVoid),
createRepository: (input) =>
execute({
cwd: input.cwd,
args: [
"repo",
"create",
"--source=.",
`--${input.visibility}`,
"--remote",
input.remote ?? "origin",
],
}).pipe(Effect.asVoid),
getDefaultBranch: (input) =>
execute({
cwd: input.cwd,
Expand Down
9 changes: 9 additions & 0 deletions apps/server/src/git/Services/GitHubCli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ export interface GitHubCliShape {
readonly repository: string;
}) => Effect.Effect<GitHubRepositoryCloneUrls, GitHubCliError>;

/**
* Create a GitHub repository from the current local repository.
*/
readonly createRepository: (input: {
readonly cwd: string;
readonly visibility: "private" | "public" | "internal";
readonly remote?: string;
}) => Effect.Effect<void, GitHubCliError>;

/**
* Create a pull request from branch context and body file.
*/
Expand Down
1 change: 1 addition & 0 deletions apps/server/src/serverLayers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ export function makeServerRuntimeServicesLayer() {
return Layer.mergeAll(
orchestrationReactorLayer,
gitCoreLayer,
GitHubCliLive,
gitManagerLayer,
terminalLayer,
KeybindingsLive,
Expand Down
13 changes: 13 additions & 0 deletions apps/server/src/wsServer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import { ProviderService, type ProviderServiceShape } from "./provider/Services/
import { ProviderHealth, type ProviderHealthShape } from "./provider/Services/ProviderHealth";
import { Open, type OpenShape } from "./open";
import { GitManager, type GitManagerShape } from "./git/Services/GitManager.ts";
import { GitHubCli, type GitHubCliShape } from "./git/Services/GitHubCli.ts";
import type { GitCoreShape } from "./git/Services/GitCore.ts";
import { GitCore } from "./git/Services/GitCore.ts";
import { GitCommandError, GitManagerError } from "./git/Errors.ts";
Expand Down Expand Up @@ -481,6 +482,7 @@ describe("WebSocket Server", () => {
open?: OpenShape;
gitManager?: GitManagerShape;
gitCore?: Pick<GitCoreShape, "listBranches" | "initRepo" | "pullCurrentBranch">;
gitHubCli?: Pick<GitHubCliShape, "createRepository">;
terminalManager?: TerminalManagerShape;
} = {},
): Promise<Http.Server> {
Expand Down Expand Up @@ -517,6 +519,9 @@ describe("WebSocket Server", () => {
options.gitCore
? Layer.succeed(GitCore, options.gitCore as unknown as GitCoreShape)
: Layer.empty,
options.gitHubCli
? Layer.succeed(GitHubCli, options.gitHubCli as unknown as GitHubCliShape)
: Layer.empty,
options.terminalManager
? Layer.succeed(TerminalManager, options.terminalManager)
: Layer.empty,
Expand Down Expand Up @@ -1630,6 +1635,7 @@ describe("WebSocket Server", () => {
}),
);
const initRepo = vi.fn(() => Effect.void);
const createRepository = vi.fn(() => Effect.void);
const pullCurrentBranch = vi.fn(() =>
Effect.fail(
new GitCommandError({
Expand All @@ -1648,6 +1654,9 @@ describe("WebSocket Server", () => {
initRepo,
pullCurrentBranch,
},
gitHubCli: {
createRepository,
},
});
const addr = server.address();
const port = typeof addr === "object" && addr !== null ? addr.port : 0;
Expand All @@ -1663,6 +1672,10 @@ describe("WebSocket Server", () => {
const initResponse = await sendRequest(ws, WS_METHODS.gitInit, { cwd: "/repo/path" });
expect(initResponse.error).toBeUndefined();
expect(initRepo).toHaveBeenCalledWith({ cwd: "/repo/path" });
expect(createRepository).toHaveBeenCalledWith({
cwd: "/repo/path",
visibility: "private",
});

const pullResponse = await sendRequest(ws, WS_METHODS.gitPull, { cwd: "/repo/path" });
expect(pullResponse.result).toBeUndefined();
Expand Down
9 changes: 8 additions & 1 deletion apps/server/src/wsServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import { WebSocketServer, type WebSocket } from "ws";

import { createLogger } from "./logger";
import { GitManager } from "./git/Services/GitManager.ts";
import { GitHubCli } from "./git/Services/GitHubCli.ts";
import { TerminalManager } from "./terminal/Services/Manager.ts";
import { Keybindings } from "./keybindings";
import { searchWorkspaceEntries } from "./workspaceEntries";
Expand Down Expand Up @@ -213,6 +214,7 @@ export type ServerCoreRuntimeServices =
export type ServerRuntimeServices =
| ServerCoreRuntimeServices
| GitManager
| GitHubCli
| GitCore
| TerminalManager
| Keybindings
Expand Down Expand Up @@ -255,6 +257,7 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return<
const keybindingsManager = yield* Keybindings;
const providerHealth = yield* ProviderHealth;
const git = yield* GitCore;
const gitHubCli = yield* GitHubCli;
const fileSystem = yield* FileSystem.FileSystem;
const path = yield* Path.Path;

Expand Down Expand Up @@ -833,7 +836,11 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return<

case WS_METHODS.gitInit: {
const body = stripRequestTag(request.body);
return yield* git.initRepo(body);
yield* git.initRepo(body);
return yield* gitHubCli.createRepository({
cwd: body.cwd,
visibility: "private",
});
}

case WS_METHODS.terminalOpen: {
Expand Down
Loading