From 30f3de37180ef4cc74ed474ac56b85e4d9cc96bc Mon Sep 17 00:00:00 2001 From: Abdelrahman Essawy Date: Sun, 7 Jun 2026 01:35:02 +0300 Subject: [PATCH] fix: prune leftover update artifacts from the bin dir before updating MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pre-1.3.2 updater extracted directly into the binary's directory and, when it failed on Windows, could strand a partial `update-.zip` archive there. A killed run of the current updater can likewise leave a `.rb-update-` temp dir or a half-staged `.new` binary. None are ever reused, so they just accumulate in the user's bin dir. Sweep these at the start of an update: stray `update-.{zip,tar.gz}` archives, `.rb-update-*` dirs, and `rb.new` / `rb.exe.new`. Best-effort and wrapped in try/catch so cleanup never blocks the update. `.bak` is left alone — it is the intentional one-version rollback. --- src/commands/update.ts | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/commands/update.ts b/src/commands/update.ts index 3286caf..e04446f 100644 --- a/src/commands/update.ts +++ b/src/commands/update.ts @@ -2,7 +2,7 @@ * `rb update` — Self-replace for standalone binary, print install instructions for dev mode. * Detects install method via compile-time IS_STANDALONE define. */ -import { writeFileSync, mkdirSync, existsSync, chmodSync, renameSync, unlinkSync, rmSync } from "node:fs"; +import { writeFileSync, mkdirSync, existsSync, chmodSync, renameSync, unlinkSync, rmSync, readdirSync } from "node:fs"; import { join, dirname } from "node:path"; import { homedir } from "node:os"; import { spawnSync } from "node:child_process"; @@ -150,6 +150,26 @@ async function updateBinary(latest: string): Promise { const tmpBinPath = `${binPath}.new`; const bakBinPath = `${binPath}.bak`; const archiveBase = ext === ".tar.gz" ? "rb" : "rb.exe"; + + // Sweep leftovers from interrupted updates before starting. The pre-1.3.2 + // updater extracted into binDir and, on failure, could strand a partial + // `update-.zip`/`.tar.gz` archive or a half-staged `.new` binary; a killed + // run of the current updater can strand a `.rb-update-` temp dir. None are + // reused, so clear them so they don't pile up. Never touch `.bak` — that's the + // intentional one-version rollback. + try { + for (const name of readdirSync(binDir)) { + const stale = + /^update-\d+\.(zip|tar\.gz)$/.test(name) || + name.startsWith(".rb-update-") || + name === "rb.new" || + name === "rb.exe.new"; + if (stale) rmSync(join(binDir, name), { recursive: true, force: true }); + } + } catch { + // Best-effort — never block an update on cleanup. + } + const extractDir = join(binDir, `.rb-update-${Date.now()}`); mkdirSync(extractDir, { recursive: true });