From 7ebcb7663d8f175e27591f67ed4ee7c727caa263 Mon Sep 17 00:00:00 2001 From: Serhii Kucherenko Date: Fri, 30 Oct 2026 15:04:18 -0700 Subject: [PATCH 1/2] test(host-service): pin core.hooksPath in the dirty-hook fixture The materialize test installs a post-checkout hook in its repo's .git/hooks and expects a plain checkout to fail on it. On a machine with a global core.hooksPath, git never runs that hook, the checkout succeeds, and the test fails. Pinning core.hooksPath in the repo's own config makes the hook run on every machine. --- .../pr-branch-materialize.integration.test.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/host-service/test/integration/pr-branch-materialize.integration.test.ts b/packages/host-service/test/integration/pr-branch-materialize.integration.test.ts index 145b9d52b2b..0962ea72689 100644 --- a/packages/host-service/test/integration/pr-branch-materialize.integration.test.ts +++ b/packages/host-service/test/integration/pr-branch-materialize.integration.test.ts @@ -1,5 +1,6 @@ import { afterEach, beforeEach, describe, expect, test } from "bun:test"; import { + appendFileSync, chmodSync, mkdtempSync, realpathSync, @@ -72,7 +73,13 @@ async function createPrScenario(prNumber: number): Promise<{ } function installDirtyPostCheckoutHook(repoPath: string): void { - const hookPath = join(repoPath, ".git", "hooks", "post-checkout"); + const hooksDir = join(repoPath, ".git", "hooks"); + const hookPath = join(hooksDir, "post-checkout"); + // A machine-wide core.hooksPath would otherwise skip this repo's hooks. + appendFileSync( + join(repoPath, ".git", "config"), + `[core]\n\thooksPath = ${hooksDir}\n`, + ); writeFileSync( hookPath, [ From 9e1cd851196c5e0955687fa678b01b96d181dff3 Mon Sep 17 00:00:00 2001 From: Serhii Kucherenko Date: Fri, 30 Oct 2026 22:28:37 -0700 Subject: [PATCH 2/2] test(host-service): set the fixture hooksPath with git config A raw [core] append breaks when the temp path holds # or ;, because git reads those as a comment start. git config quotes the value. --- .../pr-branch-materialize.integration.test.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/host-service/test/integration/pr-branch-materialize.integration.test.ts b/packages/host-service/test/integration/pr-branch-materialize.integration.test.ts index 0962ea72689..931db3480fc 100644 --- a/packages/host-service/test/integration/pr-branch-materialize.integration.test.ts +++ b/packages/host-service/test/integration/pr-branch-materialize.integration.test.ts @@ -1,6 +1,5 @@ import { afterEach, beforeEach, describe, expect, test } from "bun:test"; import { - appendFileSync, chmodSync, mkdtempSync, realpathSync, @@ -72,14 +71,14 @@ async function createPrScenario(prNumber: number): Promise<{ }; } -function installDirtyPostCheckoutHook(repoPath: string): void { +async function installDirtyPostCheckoutHook( + git: SimpleGit, + repoPath: string, +): Promise { const hooksDir = join(repoPath, ".git", "hooks"); const hookPath = join(hooksDir, "post-checkout"); // A machine-wide core.hooksPath would otherwise skip this repo's hooks. - appendFileSync( - join(repoPath, ".git", "config"), - `[core]\n\thooksPath = ${hooksDir}\n`, - ); + await git.addConfig("core.hooksPath", hooksDir); writeFileSync( hookPath, [ @@ -103,7 +102,10 @@ describe("materializePrBranch (real git)", () => { }); test("materialize-first worktree creation survives hooks that dirty tracked files during checkout", async () => { - installDirtyPostCheckoutHook(scenario.local.repoPath); + await installDirtyPostCheckoutHook( + scenario.local.git, + scenario.local.repoPath, + ); const materialized = await materializePrBranch({ git: scenario.local.git,