authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-09-04 13:33:21-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-04 20:33:21+00:00
log12b6d026bdfba0cc8438fa5e323a8587d65bd2af
tree942c5f28428ddf1fb448b0001d18a069efbb3a85
parentf083f520fbde1e1bf4332399df47b45c2bd99722
signature Signed by PGP key B5690EEEBB952194

test(server): pin git config for fixtures and compare native realpaths (#9570)

Co-authored-by: Claude Code <noreply@anthropic.com>

4 files changed, 61 insertions(+), 29 deletions(-)

apps/server/src/project/RepositoryIdentityResolver.test.ts+8-4
......@@ -1,3 +1,5 @@
1// @effect-diagnostics nodeBuiltinImport:off - realpathSync.native resolves Windows 8.3 short names, which the Effect realPath does not.
2import * as NodeFS from "node:fs";
13import * as NodeServices from "@effect/platform-node/NodeServices";
24import { expect, it } from "@effect/vitest";
35import * as Duration from "effect/Duration";
......@@ -131,9 +133,11 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => {
131133
132134 const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver;
133135 const identity = yield* resolver.resolve(cwd);
136 // Native realpath, since git reports the long form of a directory the
137 // temp dir may name by its 8.3 short form on Windows.
134138 const resolvedIdentityRoot =
135 identity?.rootPath === undefined ? "" : yield* fileSystem.realPath(identity.rootPath);
136 const resolvedCwd = yield* fileSystem.realPath(cwd);
139 identity?.rootPath === undefined ? "" : NodeFS.realpathSync.native(identity.rootPath);
140 const resolvedCwd = NodeFS.realpathSync.native(cwd);
137141
138142 expect(identity).not.toBeNull();
139143 expect(identity?.canonicalKey).toBe("github.com/t3tools/t3code");
......@@ -161,8 +165,8 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => {
161165 const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver;
162166 const identity = yield* resolver.resolve(nestedWorkspace);
163167 const resolvedIdentityRoot =
164 identity?.rootPath === undefined ? "" : yield* fileSystem.realPath(identity.rootPath);
165 const resolvedRepoRoot = yield* fileSystem.realPath(repoRoot);
168 identity?.rootPath === undefined ? "" : NodeFS.realpathSync.native(identity.rootPath);
169 const resolvedRepoRoot = NodeFS.realpathSync.native(repoRoot);
166170
167171 expect(identity).not.toBeNull();
168172 expect(identity?.canonicalKey).toBe("github.com/t3tools/t3code");
apps/server/src/testUtils/gitConfig.setup.ts created+21
......@@ -0,0 +1,21 @@
1// Pins git behaviour for every repository the suite creates, ahead of the
2// host's ~/.gitconfig. Git for Windows installs with core.autocrlf=true,
3// which checks committed LF files out as CRLF and breaks every byte-exact
4// content assertion; a signing key or a non-default init branch on the
5// developer's machine breaks fixtures the same way. Set as environment so
6// each git child the driver spawns sees it without touching the fixtures.
7const entries: ReadonlyArray<readonly [key: string, value: string]> = [
8 ["core.autocrlf", "false"],
9 ["core.filemode", "false"],
10 ["core.longpaths", "true"],
11 ["commit.gpgsign", "false"],
12 ["tag.gpgsign", "false"],
13 ["init.defaultBranch", "main"],
14];
15
16const existing = Number(process.env.GIT_CONFIG_COUNT ?? "0");
17process.env.GIT_CONFIG_COUNT = String(existing + entries.length);
18entries.forEach(([key, value], index) => {
19 process.env[`GIT_CONFIG_KEY_${existing + index}`] = key;
20 process.env[`GIT_CONFIG_VALUE_${existing + index}`] = value;
21});
apps/server/src/vcs/GitVcsDriverCore.test.ts+31-25
......@@ -1,4 +1,7 @@
1// @effect-diagnostics nodeBuiltinImport:off - realpathSync.native resolves Windows 8.3 short names, which the Effect realPath does not.
2import * as NodeFS from "node:fs";
13import * as NodeServices from "@effect/platform-node/NodeServices";
4import { HostProcessPlatform } from "@t3tools/shared/hostProcess";
25import { assert, it, describe } from "@effect/vitest";
36import * as Deferred from "effect/Deferred";
47import * as Effect from "effect/Effect";
......@@ -570,7 +573,6 @@ it.effect("backs off failed upstream refreshes across linked worktrees", () =>
570573 const driver = yield* makeGitVcsDriverCore().pipe(
571574 Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, failingFetchSpawner),
572575 );
573 const fileSystem = yield* FileSystem.FileSystem;
574576 const cwd = yield* makeTmpDir();
575577 const remote = yield* makeTmpDir("git-vcs-driver-remote-");
576578 const worktreesRoot = yield* makeTmpDir("git-vcs-driver-worktrees-");
......@@ -606,9 +608,11 @@ it.effect("backs off failed upstream refreshes across linked worktrees", () =>
606608 "rev-parse",
607609 "--git-common-dir",
608610 ])).stdout.trim();
611 // Native realpath, since git reports the long form of a directory the
612 // temp dir may name by its 8.3 short form on Windows.
609613 assert.equal(
610 yield* fileSystem.realPath(pathService.resolve(cwd, rootCommonDir)),
611 yield* fileSystem.realPath(pathService.resolve(worktreePath, linkedCommonDir)),
614 NodeFS.realpathSync.native(pathService.resolve(cwd, rootCommonDir)),
615 NodeFS.realpathSync.native(pathService.resolve(worktreePath, linkedCommonDir)),
612616 );
613617 yield* Ref.set(fetchAttempts, 0);
614618
......@@ -1376,31 +1380,33 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => {
13761380 });
13771381
13781382 describe("worktree operations", () => {
1379 it.effect("preserves newline characters in worktree paths when listing refs", () =>
1380 Effect.gen(function* () {
1381 const cwd = yield* makeTmpDir();
1382 yield* initRepoWithCommit(cwd);
1383 const worktreesRoot = yield* makeTmpDir("git-vcs-driver-worktrees-");
1384 const fileSystem = yield* FileSystem.FileSystem;
1385 const pathService = yield* Path.Path;
1386 const worktreePath = pathService.join(worktreesRoot, "linked\nworktree");
1387 const driver = yield* GitVcsDriver.GitVcsDriver;
1383 // NTFS rejects a newline in a file name, so there is nothing to preserve there.
1384 it.effect.skipIf(HostProcessPlatform.defaultValue() === "win32")(
1385 "preserves newline characters in worktree paths when listing refs",
1386 () =>
1387 Effect.gen(function* () {
1388 const cwd = yield* makeTmpDir();
1389 yield* initRepoWithCommit(cwd);
1390 const worktreesRoot = yield* makeTmpDir("git-vcs-driver-worktrees-");
1391 const pathService = yield* Path.Path;
1392 const worktreePath = pathService.join(worktreesRoot, "linked\nworktree");
1393 const driver = yield* GitVcsDriver.GitVcsDriver;
13881394
1389 yield* git(cwd, ["worktree", "add", "-b", "feature/newline-path", worktreePath]);
1395 yield* git(cwd, ["worktree", "add", "-b", "feature/newline-path", worktreePath]);
13901396
1391 const refs = yield* driver.listRefs({ cwd, refresh: true });
1392 const listedPath = refs.refs.find(
1393 (ref) => ref.name === "feature/newline-path",
1394 )?.worktreePath;
1397 const refs = yield* driver.listRefs({ cwd, refresh: true });
1398 const listedPath = refs.refs.find(
1399 (ref) => ref.name === "feature/newline-path",
1400 )?.worktreePath;
13951401
1396 if (typeof listedPath !== "string") {
1397 return assert.fail("expected the linked branch to include its worktree path");
1398 }
1399 assert.equal(
1400 yield* fileSystem.realPath(listedPath),
1401 yield* fileSystem.realPath(worktreePath),
1402 );
1403 }),
1402 if (typeof listedPath !== "string") {
1403 return assert.fail("expected the linked branch to include its worktree path");
1404 }
1405 assert.equal(
1406 NodeFS.realpathSync.native(listedPath),
1407 NodeFS.realpathSync.native(worktreePath),
1408 );
1409 }),
14041410 );
14051411
14061412 it.effect("checks out submodules in a new worktree", () =>
apps/server/vite.config.ts+1
......@@ -78,6 +78,7 @@ export default mergeConfig(
7878 // The server suite exercises sqlite, git, temp worktrees, and orchestration
7979 // runtimes heavily. Running files in parallel introduces load-sensitive flakes.
8080 fileParallelism: false,
81 setupFiles: ["./src/testUtils/gitConfig.setup.ts"],
8182 // Server integration tests exercise sqlite, git, and orchestration together.
8283 // Under package-wide runs they can exceed the default budget on loaded CI hosts.
8384 hookTimeout: 120_000,