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
12 changes: 8 additions & 4 deletions apps/server/src/project/RepositoryIdentityResolver.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// @effect-diagnostics nodeBuiltinImport:off - realpathSync.native resolves Windows 8.3 short names, which the Effect realPath does not.
import * as NodeFS from "node:fs";
import * as NodeServices from "@effect/platform-node/NodeServices";
import { expect, it } from "@effect/vitest";
import * as Duration from "effect/Duration";
Expand Down Expand Up @@ -131,9 +133,11 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => {

const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver;
const identity = yield* resolver.resolve(cwd);
// Native realpath, since git reports the long form of a directory the
// temp dir may name by its 8.3 short form on Windows.
const resolvedIdentityRoot =
identity?.rootPath === undefined ? "" : yield* fileSystem.realPath(identity.rootPath);
const resolvedCwd = yield* fileSystem.realPath(cwd);
identity?.rootPath === undefined ? "" : NodeFS.realpathSync.native(identity.rootPath);
const resolvedCwd = NodeFS.realpathSync.native(cwd);

expect(identity).not.toBeNull();
expect(identity?.canonicalKey).toBe("github.com/t3tools/t3code");
Expand Down Expand Up @@ -161,8 +165,8 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => {
const resolver = yield* RepositoryIdentityResolver.RepositoryIdentityResolver;
const identity = yield* resolver.resolve(nestedWorkspace);
const resolvedIdentityRoot =
identity?.rootPath === undefined ? "" : yield* fileSystem.realPath(identity.rootPath);
const resolvedRepoRoot = yield* fileSystem.realPath(repoRoot);
identity?.rootPath === undefined ? "" : NodeFS.realpathSync.native(identity.rootPath);
const resolvedRepoRoot = NodeFS.realpathSync.native(repoRoot);

expect(identity).not.toBeNull();
expect(identity?.canonicalKey).toBe("github.com/t3tools/t3code");
Expand Down
21 changes: 21 additions & 0 deletions apps/server/src/testUtils/gitConfig.setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Pins git behaviour for every repository the suite creates, ahead of the
// host's ~/.gitconfig. Git for Windows installs with core.autocrlf=true,
// which checks committed LF files out as CRLF and breaks every byte-exact
// content assertion; a signing key or a non-default init branch on the
// developer's machine breaks fixtures the same way. Set as environment so
// each git child the driver spawns sees it without touching the fixtures.
const entries: ReadonlyArray<readonly [key: string, value: string]> = [
["core.autocrlf", "false"],
["core.filemode", "false"],
["core.longpaths", "true"],
["commit.gpgsign", "false"],
["tag.gpgsign", "false"],
["init.defaultBranch", "main"],
];

const existing = Number(process.env.GIT_CONFIG_COUNT ?? "0");
process.env.GIT_CONFIG_COUNT = String(existing + entries.length);
entries.forEach(([key, value], index) => {
process.env[`GIT_CONFIG_KEY_${existing + index}`] = key;
process.env[`GIT_CONFIG_VALUE_${existing + index}`] = value;
});
56 changes: 31 additions & 25 deletions apps/server/src/vcs/GitVcsDriverCore.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
// @effect-diagnostics nodeBuiltinImport:off - realpathSync.native resolves Windows 8.3 short names, which the Effect realPath does not.
import * as NodeFS from "node:fs";
import * as NodeServices from "@effect/platform-node/NodeServices";
import { HostProcessPlatform } from "@t3tools/shared/hostProcess";
import { assert, it, describe } from "@effect/vitest";
import * as Deferred from "effect/Deferred";
import * as Effect from "effect/Effect";
Expand Down Expand Up @@ -570,7 +573,6 @@ it.effect("backs off failed upstream refreshes across linked worktrees", () =>
const driver = yield* makeGitVcsDriverCore().pipe(
Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, failingFetchSpawner),
);
const fileSystem = yield* FileSystem.FileSystem;
const cwd = yield* makeTmpDir();
const remote = yield* makeTmpDir("git-vcs-driver-remote-");
const worktreesRoot = yield* makeTmpDir("git-vcs-driver-worktrees-");
Expand Down Expand Up @@ -606,9 +608,11 @@ it.effect("backs off failed upstream refreshes across linked worktrees", () =>
"rev-parse",
"--git-common-dir",
])).stdout.trim();
// Native realpath, since git reports the long form of a directory the
// temp dir may name by its 8.3 short form on Windows.
assert.equal(
yield* fileSystem.realPath(pathService.resolve(cwd, rootCommonDir)),
yield* fileSystem.realPath(pathService.resolve(worktreePath, linkedCommonDir)),
NodeFS.realpathSync.native(pathService.resolve(cwd, rootCommonDir)),
NodeFS.realpathSync.native(pathService.resolve(worktreePath, linkedCommonDir)),
);
yield* Ref.set(fetchAttempts, 0);

Expand Down Expand Up @@ -1376,31 +1380,33 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => {
});

describe("worktree operations", () => {
it.effect("preserves newline characters in worktree paths when listing refs", () =>
Effect.gen(function* () {
const cwd = yield* makeTmpDir();
yield* initRepoWithCommit(cwd);
const worktreesRoot = yield* makeTmpDir("git-vcs-driver-worktrees-");
const fileSystem = yield* FileSystem.FileSystem;
const pathService = yield* Path.Path;
const worktreePath = pathService.join(worktreesRoot, "linked\nworktree");
const driver = yield* GitVcsDriver.GitVcsDriver;
// NTFS rejects a newline in a file name, so there is nothing to preserve there.
it.effect.skipIf(HostProcessPlatform.defaultValue() === "win32")(
"preserves newline characters in worktree paths when listing refs",
() =>
Effect.gen(function* () {
const cwd = yield* makeTmpDir();
yield* initRepoWithCommit(cwd);
const worktreesRoot = yield* makeTmpDir("git-vcs-driver-worktrees-");
const pathService = yield* Path.Path;
const worktreePath = pathService.join(worktreesRoot, "linked\nworktree");
const driver = yield* GitVcsDriver.GitVcsDriver;

yield* git(cwd, ["worktree", "add", "-b", "feature/newline-path", worktreePath]);
yield* git(cwd, ["worktree", "add", "-b", "feature/newline-path", worktreePath]);

const refs = yield* driver.listRefs({ cwd, refresh: true });
const listedPath = refs.refs.find(
(ref) => ref.name === "feature/newline-path",
)?.worktreePath;
const refs = yield* driver.listRefs({ cwd, refresh: true });
const listedPath = refs.refs.find(
(ref) => ref.name === "feature/newline-path",
)?.worktreePath;

if (typeof listedPath !== "string") {
return assert.fail("expected the linked branch to include its worktree path");
}
assert.equal(
yield* fileSystem.realPath(listedPath),
yield* fileSystem.realPath(worktreePath),
);
}),
if (typeof listedPath !== "string") {
return assert.fail("expected the linked branch to include its worktree path");
}
assert.equal(
NodeFS.realpathSync.native(listedPath),
NodeFS.realpathSync.native(worktreePath),
);
}),
);

it.effect("checks out submodules in a new worktree", () =>
Expand Down
1 change: 1 addition & 0 deletions apps/server/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export default mergeConfig(
// The server suite exercises sqlite, git, temp worktrees, and orchestration
// runtimes heavily. Running files in parallel introduces load-sensitive flakes.
fileParallelism: false,
setupFiles: ["./src/testUtils/gitConfig.setup.ts"],
// Server integration tests exercise sqlite, git, and orchestration together.
// Under package-wide runs they can exceed the default budget on loaded CI hosts.
hookTimeout: 120_000,
Expand Down
Loading