fix(#856): remove gsd-cmd-rewrites temp dirs after install (#862)

* fix(#856): remove gsd-cmd-rewrites temp dirs after install

applyRuntimeContentRewritesForCommandsInPlace() returns a fresh
mkdtemp dir under os.tmpdir() (gsd-cmd-rewrites-*) with rewritten
command markdown. installRuntimeArtifacts() copied from it but never
removed it, leaking one temp dir per commands kind per install — on
tmpfs /tmp hosts these accumulate and consume RAM-backed storage.

Wrap the per-kind copy in try/finally and rmSync the temp dir (only
when it differs from the staged source, i.e. the commands kind) once
the copy completes or fails. dest creation moved inside the try so a
mkdir failure still triggers cleanup.

Regression test isolates os.tmpdir() to a private TMPDIR root and
asserts no gsd-cmd-rewrites-* dir survives the install.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(#856): add changeset for installer temp-dir cleanup

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit ac56672dba)
This commit is contained in:
Tom Boucher
2026-06-08 10:41:54 -04:00
committed by github-actions[bot]
parent 47fad118a8
commit d02e29259a
3 changed files with 95 additions and 47 deletions

View File

@@ -135,6 +135,41 @@ describe('enh-790 — installRuntimeArtifacts augment emits both commands and sk
});
});
describe('enh-790 — installRuntimeArtifacts does not leak temp dirs', () => {
test('install cleans up gsd-cmd-rewrites-* temp dirs (no leak) — #856', (t) => {
const { resolveProfile } = require('../gsd-core/bin/lib/install-profiles.cjs');
const RESOLVED_FULL = resolveProfile({ modes: ['full'], manifest: MANIFEST });
// Isolate os.tmpdir() to a private root so parallel test processes can't race
// on the shared system temp dir. os.tmpdir() resolves $TMPDIR/$TEMP/$TMP per call.
const isolatedTmp = createTempDir('gsd-enh790-tmproot-');
const prev = { TMPDIR: process.env.TMPDIR, TEMP: process.env.TEMP, TMP: process.env.TMP };
process.env.TMPDIR = isolatedTmp;
process.env.TEMP = isolatedTmp;
process.env.TMP = isolatedTmp;
const configDir = createTempDir('gsd-enh790-leak-');
t.after(() => {
for (const k of ['TMPDIR', 'TEMP', 'TMP']) {
if (prev[k] === undefined) delete process.env[k];
else process.env[k] = prev[k];
}
cleanup(configDir);
cleanup(isolatedTmp);
});
installRuntimeArtifacts('augment', configDir, 'global', RESOLVED_FULL);
// The install creates its gsd-cmd-rewrites-* temp dirs under the isolated root;
// after the fix none must remain.
const leaked = fs.readdirSync(isolatedTmp).filter(n => n.startsWith('gsd-cmd-rewrites-'));
assert.ok(
leaked.length === 0,
`installer must not leak gsd-cmd-rewrites-* temp dirs; leaked: ${leaked.join(', ')}`
);
});
});
// ─── Uninstall contract ──────────────────────────────────────────────────────
describe('enh-790 — uninstallRuntimeArtifacts removes augment commands', () => {