fix(install): prune old version bundles instead of piling them up (#1074) (#1075)

install.sh kept each release in its own versions/<v> dir (~50 MB with the
vendored Node runtime) and only moved the `current` symlink, so old versions
accumulated forever across upgrades. Keep only the just-installed version and
delete the rest; `codegraph upgrade` re-runs install.sh, so this covers
upgrades too. The npm-shim self-heal cache (~/.codegraph/bundles/) prunes the
same way. Windows installs overwrite a single dir in place and were never
affected.

Validated real-world on macOS, Linux (Docker/dash), and Windows (VM): a
v1.1.2 -> v1.1.4 install leaves only the latest behind.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-06-29 23:35:50 -05:00
committed by GitHub
co-authored by Claude Opus 4.8
parent 9684b3b5a5
commit 31a58070c8
5 changed files with 209 additions and 2 deletions
+23 -1
View File
@@ -105,7 +105,7 @@ async function selfHealBundle() {
// Already downloaded by a previous run? Use it even when downloads are
// disabled — CODEGRAPH_NO_DOWNLOAD blocks fetching, not a cached bundle.
var cached = launcherIn(dest);
if (cached) return cached;
if (cached) { pruneOldBundles(bundlesDir, dest); return cached; }
if (process.env.CODEGRAPH_NO_DOWNLOAD) {
fail('the network fallback is disabled (CODEGRAPH_NO_DOWNLOAD is set).');
@@ -149,6 +149,7 @@ async function selfHealBundle() {
var ready = launcherIn(dest);
if (!ready) fail('downloaded bundle is missing its launcher under ' + dest + '.');
pruneOldBundles(bundlesDir, dest);
process.stderr.write('codegraph: bundle ready.\n');
return ready;
}
@@ -230,6 +231,27 @@ function rmrf(p) {
try { fs.rmSync(p, { recursive: true, force: true }); } catch (e) { /* best effort */ }
}
// Drop sibling bundles for OTHER versions of this same platform target, keeping
// only keepDir. The self-heal cache otherwise accumulates a full ~50 MB bundle
// per version forever (issue #1074). Best-effort: a locked/busy dir (a
// concurrent run still mapping an older node.exe on Windows) just stays — rmrf
// already swallows its own errors, and the readdir is guarded — so cleanup can
// never break a working command. Only this target's "<target>-<version>" dirs
// are touched; other platforms' bundles and the ".dl-*" staging dirs are left
// alone.
function pruneOldBundles(bundlesDir, keepDir) {
var keep = path.basename(keepDir);
try {
var names = fs.readdirSync(bundlesDir);
for (var i = 0; i < names.length; i++) {
var name = names[i];
if (name === keep) continue;
if (name.indexOf(target + '-') !== 0) continue;
rmrf(path.join(bundlesDir, name));
}
} catch (e) { /* best effort — never break a working run over cleanup */ }
}
function fail(reason) {
process.stderr.write(
'codegraph: no prebuilt bundle for ' + target + '.\n' +