fix(git): disable gpg signing for ktx's own commits (#299)

ktx commits under a synthetic identity (ktx <ktx@example.com>) that can
never own a GPG secret key. On a machine with commit.gpgsign=true, git
tried to sign every ktx commit and failed with "No secret key", breaking
ingest, scan, wiki, memory, and bootstrap commits.

Inject commit.gpgsign=false as a per-invocation -c override in the single
core git client factory every ktx commit flows through. This honors the
existing principle of not mutating the user's repo config, and is
locale-independent (no error-message matching).

Also harden the repo-isolation fixture helper to disable signing on its
raw commits so the suite is deterministic regardless of the contributor's
global git config.

Fixes KLO-735.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kevin Messiaen 2026-06-15 19:02:26 +07:00 committed by GitHub
parent e4e7b40c23
commit 9587049283
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 29 additions and 2 deletions

View file

@ -31,6 +31,10 @@ function sanitizedGitEnv(env: NodeJS.ProcessEnv = process.env): NodeJS.ProcessEn
* directory is an existing repo ktx did not create and the machine has no configured git
* identity (e.g. a fresh Mac with no ~/.gitconfig), without mutating the user's repo config.
* Explicit `--author` flags on individual commits still take precedence over GIT_AUTHOR_NAME.
*
* `commit.gpgsign=false` is injected as a per-invocation `-c` override so ktx's commits never
* attempt GPG signing: ktx commits under a synthetic identity that can never own a secret key, so
* a user's `commit.gpgsign=true` would otherwise fail every commit with "No secret key".
*/
export function createSimpleGit(baseDir: string, identity?: { name: string; email: string }): SimpleGit {
const env = sanitizedGitEnv();
@ -40,5 +44,5 @@ export function createSimpleGit(baseDir: string, identity?: { name: string; emai
env.GIT_COMMITTER_NAME = identity.name;
env.GIT_COMMITTER_EMAIL = identity.email;
}
return simpleGit({ baseDir, unsafe: { allowUnsafeAskPass: true } }).env(env);
return simpleGit({ baseDir, config: ['commit.gpgsign=false'], unsafe: { allowUnsafeAskPass: true } }).env(env);
}

View file

@ -94,4 +94,23 @@ describe('GitService.initialize without a configured git identity', () => {
}).trim();
expect(localName).toBe('');
});
// Regression for KLO-735: a machine with commit.gpgsign=true makes git try to GPG-sign every
// commit, but ktx commits under a synthetic identity that can never own a secret key, so signing
// fails with "No secret key". ktx commits must succeed regardless of the user's signing config.
it('commits even when the global git config forces gpg signing', async () => {
// Force signing and point gpg at a program that always fails, mirroring a machine whose
// configured signing key does not match ktx's synthetic identity.
await writeFile(
join(homeDir, '.gitconfig'),
'[user]\n\tuseConfigOnly = true\n[commit]\n\tgpgsign = true\n[gpg]\n\tprogram = false\n',
'utf-8',
);
const service = new GitService(coreConfig(repoDir));
await expect(service.onModuleInit()).resolves.toBeUndefined();
const head = await service.revParseHead();
expect(head).toMatch(/^[0-9a-f]{40}$/);
});
});

View file

@ -21,7 +21,11 @@ function coreConfig(configDir: string): KtxCoreConfig {
}
function git(cwd: string, args: string[]): string {
return execFileSync('git', args, {
// `-c commit.gpgsign=false` keeps fixture commits deterministic regardless of the host's git
// config: a contributor with commit.gpgsign=true would otherwise fail these raw commits under a
// synthetic identity that owns no secret key.
const fixtureArgs = args[0] === 'commit' ? ['-c', 'commit.gpgsign=false', ...args] : args;
return execFileSync('git', fixtureArgs, {
cwd,
encoding: 'utf-8',
env: {