Skip to content
Merged
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
73 changes: 73 additions & 0 deletions apps/server/src/vcs/GitVcsDriverCore.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import * as Layer from "effect/Layer";
import * as Path from "effect/Path";
import * as PlatformError from "effect/PlatformError";
import * as Scope from "effect/Scope";
import * as Sink from "effect/Sink";
import * as Stream from "effect/Stream";
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";

import { GitCommandError } from "@t3tools/contracts";
import { ServerConfig } from "../config.ts";
Expand All @@ -20,6 +23,21 @@ const TestLayer = GitVcsDriver.layer.pipe(
Layer.provideMerge(NodeServices.layer),
);

const makeNonRepositoryHandle = () =>
ChildProcessSpawner.makeHandle({
pid: ChildProcessSpawner.ProcessId(1),
exitCode: Effect.succeed(ChildProcessSpawner.ExitCode(128)),
isRunning: Effect.succeed(false),
kill: () => Effect.void,
unref: Effect.succeed(Effect.void),
stdin: Sink.drain,
stdout: Stream.empty,
stderr: Stream.encodeText(Stream.make("fatal: not a git repository")),
all: Stream.empty,
getInputFd: () => Sink.drain,
getOutputFd: () => Stream.empty,
});

const makeTmpDir = (
prefix = "git-vcs-driver-test-",
): Effect.Effect<string, PlatformError.PlatformError, FileSystem.FileSystem | Scope.Scope> =>
Expand Down Expand Up @@ -77,7 +95,62 @@ const initRepoWithCommit = (
return { initialBranch };
});

it.effect("uses stable diagnostics for every parsed non-repository command", () => {
const commands: Array<{ readonly args: ReadonlyArray<string>; readonly lcAll?: string }> = [];
const spawner = ChildProcessSpawner.make((command) =>
Effect.sync(() => {
if (!ChildProcess.isStandardCommand(command)) {
return assert.fail("expected a standard Git command");
}
commands.push({
args: command.args,
...(command.options.env?.LC_ALL ? { lcAll: command.options.env.LC_ALL } : {}),
});
return makeNonRepositoryHandle();
}),
);
const nodeServicesLayer = Layer.merge(
NodeServices.layer,
Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner),
);
const layer = GitVcsDriver.layer.pipe(
Layer.provide(ServerConfigLayer),
Layer.provideMerge(nodeServicesLayer),
);

return Effect.gen(function* () {
const driver = yield* GitVcsDriver.GitVcsDriver;
const cwd = "/repo";

yield* driver.statusDetailsLocal(cwd);
yield* driver.statusDetailsRemote(cwd, { refreshUpstream: false });
yield* driver.listRefs({ cwd });

assert.deepStrictEqual(commands, [
{ args: ["status", "--porcelain=2", "--branch"], lcAll: "C" },
{ args: ["rev-parse", "--abbrev-ref", "HEAD"], lcAll: "C" },
{ args: ["branch", "--no-color", "--no-column"], lcAll: "C" },
]);
}).pipe(Effect.provide(layer));
});

it.layer(TestLayer)("GitVcsDriver core integration", (it) => {
describe("process environment", () => {
it.effect("preserves the caller locale for general Git subprocesses", () =>
Effect.gen(function* () {
const cwd = yield* makeTmpDir();

const locale = yield* git(
cwd,
["-c", 'alias.print-locale=!printf "%s" "$LC_ALL"', "print-locale"],
{ LC_ALL: "zh_CN.UTF-8" },
);

assert.equal(locale, "zh_CN.UTF-8");
}),
);
});

describe("structured errors", () => {
it.effect("preserves structured spawn context and the platform cause", () =>
Effect.gen(function* () {
Expand Down
20 changes: 17 additions & 3 deletions apps/server/src/vcs/GitVcsDriverCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,20 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function*
}),
);

const executeGitWithStableDiagnostics = (
operation: string,
cwd: string,
args: readonly string[],
options: ExecuteGitOptions = {},
): Effect.Effect<GitVcsDriver.ExecuteGitResult, GitCommandError> =>
executeGit(operation, cwd, args, {
...options,
env: {
...options.env,
LC_ALL: "C",
},
});

const runGit = (
operation: string,
cwd: string,
Expand Down Expand Up @@ -1184,7 +1198,7 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function*
});

const readStatusDetailsRemote = Effect.fn("readStatusDetailsRemote")(function* (cwd: string) {
const branchResult = yield* executeGit(
const branchResult = yield* executeGitWithStableDiagnostics(
"GitVcsDriver.statusDetailsRemote.branch",
cwd,
["rev-parse", "--abbrev-ref", "HEAD"],
Expand Down Expand Up @@ -1304,7 +1318,7 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function*
});

const readStatusDetailsLocal = Effect.fn("readStatusDetailsLocal")(function* (cwd: string) {
const statusResult = yield* executeGit(
const statusResult = yield* executeGitWithStableDiagnostics(
"GitVcsDriver.statusDetails.status",
cwd,
["status", "--porcelain=2", "--branch"],
Expand Down Expand Up @@ -1985,7 +1999,7 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function*
const branchRecencyPromise = readBranchRecency(input.cwd).pipe(
Effect.orElseSucceed(() => new Map<string, number>()),
);
const localBranchResult = yield* executeGit(
const localBranchResult = yield* executeGitWithStableDiagnostics(
"GitVcsDriver.listRefs.branchNoColor",
input.cwd,
["branch", "--no-color", "--no-column"],
Expand Down
Loading