From dce1183d45de5a958c3147acb1194556edba3f54 Mon Sep 17 00:00:00 2001 From: jason chien <3936511+jason5545@users.noreply.github.com> Date: Sat, 7 Nov 2026 08:31:05 +0800 Subject: [PATCH] fix(server): git failures are read in English when macOS git follows the system language --- apps/server/src/vcs/VcsProcess.test.ts | 49 ++++++++++++++++++++++++++ apps/server/src/vcs/VcsProcess.ts | 3 ++ 2 files changed, 52 insertions(+) diff --git a/apps/server/src/vcs/VcsProcess.test.ts b/apps/server/src/vcs/VcsProcess.test.ts index 199ded803ee1..b87dbf5e7825 100644 --- a/apps/server/src/vcs/VcsProcess.test.ts +++ b/apps/server/src/vcs/VcsProcess.test.ts @@ -149,6 +149,55 @@ describe("VcsProcess.run", () => { }), ); + // Without LANG, macOS Git follows the system language, as in an app launched from Finder. + it.effect("reads Git failures in English when Git would follow the system language", () => + Effect.gen(function* () { + let attempts = 0; + const service = yield* VcsProcess.make.pipe( + Effect.provideService( + ProcessRunner.ProcessRunner, + ProcessRunner.ProcessRunner.of({ + run: (input) => + Effect.sync(() => { + attempts += 1; + return { + stdout: "", + stderr: + attempts % 2 === 0 + ? "" + : input.env?.LANGUAGE === "C" + ? "fatal: Unable to create '/private/repo/.git/index.lock': File exists.\n" + : "致命錯誤: 無法建立「/private/repo/.git/index.lock」:File exists。\n", + code: ChildProcessSpawner.ExitCode(attempts % 2 === 0 ? 0 : 128), + timedOut: false, + stdoutTruncated: false, + stderrTruncated: false, + stdoutInvalidUtf8: false, + stderrInvalidUtf8: false, + }; + }), + }), + ), + ); + + const capture = yield* service + .run({ + ...baseInput, + operation: VcsProcess.CHECKPOINT_CAPTURE_OPERATION, + args: ["add", "--all"], + }) + .pipe(Effect.exit, Effect.forkChild({ startImmediately: true })); + yield* TestClock.adjust("1 second"); + expect(Exit.isSuccess(yield* Fiber.join(capture))).toBe(true); + expect(attempts).toBe(2); + + attempts = 0; + const error = yield* Effect.flip(service.run(baseInput)); + assert.instanceOf(error, VcsProcessExitError); + expect(error.detail).toBe(VcsProcess.GIT_INDEX_LOCKED_DETAIL); + }), + ); + it.effect.each([ { stderr: "fatal: fsync error on '.git/objects/a5/tmp_obj_Pseg66': Operation not supported", diff --git a/apps/server/src/vcs/VcsProcess.ts b/apps/server/src/vcs/VcsProcess.ts index c668a35e9927..c379caf48847 100644 --- a/apps/server/src/vcs/VcsProcess.ts +++ b/apps/server/src/vcs/VcsProcess.ts @@ -210,6 +210,9 @@ export const make = Effect.gen(function* () { ...(input.stdin !== undefined ? { stdin: input.stdin } : {}), ...(input.onStdoutChunk !== undefined ? { onStdoutChunk: input.onStdoutChunk } : {}), env: { + // Failures are read by English patterns; without LANG, macOS Git follows the system language. + // LANGUAGE leaves LC_CTYPE, which hooks inherit, alone. + ...(input.command === "git" ? { LANGUAGE: "C" } : {}), ...input.env, ...explicitBareRepositoryEnv({ ...process.env, ...input.env }, hostPlatform), },