From 484acd1d9b0b69578a76bede2d2f1794a2719aad Mon Sep 17 00:00:00 2001 From: Sawyer Date: Tue, 15 Sep 2026 09:15:51 -0700 Subject: [PATCH 1/4] test(seeding): red proof that operator commit.gpgsign breaks the seed push (CL-7492) --- packages/seeding/src/workflow-push.test.ts | 41 +++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/packages/seeding/src/workflow-push.test.ts b/packages/seeding/src/workflow-push.test.ts index fe62ce00c8..75574e9a7c 100644 --- a/packages/seeding/src/workflow-push.test.ts +++ b/packages/seeding/src/workflow-push.test.ts @@ -71,7 +71,7 @@ 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], { + const child = Bun.spawn(["git", "-c", "core.hooksPath=", "-c", "commit.gpgsign=false", ...args], { cwd, env, stdout: "pipe", @@ -229,6 +229,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; From bd1f4d2f982b802b7518eddd463dfa1cf73a7f94 Mon Sep 17 00:00:00 2001 From: Sawyer Date: Tue, 15 Sep 2026 09:16:00 -0700 Subject: [PATCH 2/4] fix(seeding): seed commits opt out of operator commit.gpgsign (CL-7492) --- packages/seeding/src/workflow-push.ts | 6 ++++++ 1 file changed, 6 insertions(+) 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", From 04a6ab9b8fae23601d5e86416923604cfad1c0d5 Mon Sep 17 00:00:00 2001 From: Sawyer Date: Tue, 15 Sep 2026 09:25:50 -0700 Subject: [PATCH 3/4] style(seeding): prettier reflow for gpgsign test helper (CL-7492) --- packages/seeding/src/workflow-push.test.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/seeding/src/workflow-push.test.ts b/packages/seeding/src/workflow-push.test.ts index 75574e9a7c..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=", "-c", "commit.gpgsign=false", ...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(), From 7b028afe1c58c87f0f296e5e74bb7d91125e1089 Mon Sep 17 00:00:00 2001 From: Sawyer Date: Tue, 15 Sep 2026 09:28:24 -0700 Subject: [PATCH 4/4] test(hub): git fixtures opt out of operator commit.gpgsign (CL-7492) --- apps/hub/test/git-fixture.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) 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()}`,