diff --git a/apps/hub/test/git-fixture.ts b/apps/hub/test/git-fixture.ts index 48c9175176..e12732e7c4 100644 --- a/apps/hub/test/git-fixture.ts +++ b/apps/hub/test/git-fixture.ts @@ -67,12 +67,19 @@ export function gitFixture(cwd: string, args: readonly string[]): void { env["GIT_AUTHOR_EMAIL"] = "safety@test"; env["GIT_COMMITTER_NAME"] = "safety-test"; env["GIT_COMMITTER_EMAIL"] = "safety@test"; - const result = Bun.spawnSync(["git", "-c", "core.hooksPath=", ...args], { - cwd, - stdout: "pipe", - stderr: "pipe", - env, - }); + // Test fixtures author their own throwaway identities and must never + // inherit the operator's ambient config: `core.hooksPath=` above, and + // `commit.gpgsign=false` here so a global `commit.gpgsign = true` can't + // hang (or sign) a fixture commit (CL-7492). + const result = Bun.spawnSync( + ["git", "-c", "core.hooksPath=", "-c", "commit.gpgsign=false", ...args], + { + cwd, + stdout: "pipe", + stderr: "pipe", + env, + }, + ); if (result.exitCode !== 0) { throw new Error( `git ${args.join(" ")} failed: ${result.stderr.toString()}`, diff --git a/packages/seeding/src/workflow-push.test.ts b/packages/seeding/src/workflow-push.test.ts index fe62ce00c8..436b4e64c0 100644 --- a/packages/seeding/src/workflow-push.test.ts +++ b/packages/seeding/src/workflow-push.test.ts @@ -71,12 +71,15 @@ async function git(args: string[], cwd: string): Promise { delete env["GIT_DIR"]; delete env["GIT_WORK_TREE"]; delete env["GIT_INDEX_FILE"]; - const child = Bun.spawn(["git", "-c", "core.hooksPath=", ...args], { - cwd, - env, - stdout: "pipe", - stderr: "pipe", - }); + const child = Bun.spawn( + ["git", "-c", "core.hooksPath=", "-c", "commit.gpgsign=false", ...args], + { + cwd, + env, + stdout: "pipe", + stderr: "pipe", + }, + ); const [stdout, stderr, code] = await Promise.all([ new Response(child.stdout).text(), new Response(child.stderr).text(), @@ -229,6 +232,45 @@ describe("createGitWorkflowPusher", () => { } }); + test("commits the seed tree even when the operator's global config enables commit signing", async () => { + const work = await scratchWorkDir("workflow-push-gpgsign-"); + const previousGlobal = process.env.GIT_CONFIG_GLOBAL; + try { + // Seed commits are authored as seed@workbench.localhost and must + // never inherit the operator's signing identity: with + // `commit.gpgsign = true` globally (and a gpg program that fails + // closed) the push still succeeds (CL-7492). + const globalConfig = join(work, "gitconfig"); + await writeFile( + globalConfig, + "[commit]\n\tgpgsign = true\n[gpg]\n\tprogram = /bin/false\n", + "utf-8", + ); + process.env.GIT_CONFIG_GLOBAL = globalConfig; + + const remoteDir = join(work, "remote.git"); + await git(["init", "--bare", "--initial-branch=main", remoteDir], work); + + const pusher = createGitWorkflowPusher(); + const outcome = await pusher({ + remoteUrl: `file://${remoteDir}`, + tokenSecret: "unused-for-file-transport", + workflowJson: '{"v":1}', + packageName: "@workbench-seed/test", + }); + + expect(outcome.outcome).toBe("pushed"); + expect(outcome.commitSha).toMatch(/^[0-9a-f]{40}$/); + } finally { + if (previousGlobal === undefined) { + delete process.env.GIT_CONFIG_GLOBAL; + } else { + process.env.GIT_CONFIG_GLOBAL = previousGlobal; + } + await rm(work, { recursive: true, force: true }); + } + }); + test("does not commit onto an inherited GIT_DIR pointing at another work tree", async () => { const work = await scratchWorkDir("workflow-push-git-dir-"); const previousGitDir = process.env.GIT_DIR; diff --git a/packages/seeding/src/workflow-push.ts b/packages/seeding/src/workflow-push.ts index 7ba0aa3804..b87ae101ac 100644 --- a/packages/seeding/src/workflow-push.ts +++ b/packages/seeding/src/workflow-push.ts @@ -147,6 +147,12 @@ export function createGitWorkflowPusher(): WorkflowPusher { { label: "commit", args: [ + // Seed commits are authored as seed@workbench.localhost and + // must never inherit the operator's signing identity + // (CL-7492): a global `commit.gpgsign = true` would fail the + // commit — or worse, sign a bot commit with a personal key. + "-c", + "commit.gpgsign=false", "commit", "--no-verify", "-m",