Skip to content

Commit 07e98b3

Browse files
committed
fix(cli): stop leaking temporary gpg agents
1 parent 1733e58 commit 07e98b3

2 files changed

Lines changed: 59 additions & 0 deletions

File tree

packages/cli/src/daemon/dispatcher.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ export function setupGnupgHome(armoredPrivateKey: string): string {
8484

8585
export function cleanupGnupgHome(gnupgHome: string | null): void {
8686
if (!gnupgHome) return;
87+
execBoundary("gpg-kill-agent", () =>
88+
execFileSync("gpgconf", ["--kill", "gpg-agent"], {
89+
env: { ...process.env, GNUPGHOME: gnupgHome },
90+
stdio: "pipe",
91+
}),
92+
);
8793
fsSync("rm-gnupghome", () => rmSync(gnupgHome, { recursive: true, force: true }));
8894
}
8995

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// @vitest-environment node
2+
3+
import { beforeEach, describe, expect, it, vi } from "vitest";
4+
5+
const execFileSyncMock = vi.fn();
6+
const rmSyncMock = vi.fn();
7+
8+
vi.mock("node:child_process", () => ({
9+
execFileSync: execFileSyncMock,
10+
}));
11+
12+
vi.mock("node:fs", async () => {
13+
const actual = await vi.importActual<typeof import("node:fs")>("node:fs");
14+
return {
15+
...actual,
16+
rmSync: rmSyncMock,
17+
};
18+
});
19+
20+
describe("cleanupGnupgHome", () => {
21+
beforeEach(() => {
22+
execFileSyncMock.mockReset();
23+
rmSyncMock.mockReset();
24+
vi.resetModules();
25+
});
26+
27+
it("kills the gpg-agent for the temp GNUPGHOME before removing the directory", async () => {
28+
const { cleanupGnupgHome } = await import("../packages/cli/src/daemon/dispatcher.js");
29+
const gnupgHome = "/tmp/ak-gpg-test";
30+
31+
cleanupGnupgHome(gnupgHome);
32+
33+
expect(execFileSyncMock).toHaveBeenCalledWith(
34+
"gpgconf",
35+
["--kill", "gpg-agent"],
36+
expect.objectContaining({
37+
stdio: "pipe",
38+
env: expect.objectContaining({ GNUPGHOME: gnupgHome }),
39+
}),
40+
);
41+
expect(rmSyncMock).toHaveBeenCalledWith(gnupgHome, { recursive: true, force: true });
42+
expect(execFileSyncMock.mock.invocationCallOrder[0]).toBeLessThan(rmSyncMock.mock.invocationCallOrder[0]);
43+
});
44+
45+
it("does nothing when GNUPGHOME is null", async () => {
46+
const { cleanupGnupgHome } = await import("../packages/cli/src/daemon/dispatcher.js");
47+
48+
cleanupGnupgHome(null);
49+
50+
expect(execFileSyncMock).not.toHaveBeenCalled();
51+
expect(rmSyncMock).not.toHaveBeenCalled();
52+
});
53+
});

0 commit comments

Comments
 (0)