From b8907f6d68ee9fa586b35cd8905e1acacaab87cf Mon Sep 17 00:00:00 2001 From: Evgeniy Podivilov Date: Sun, 21 Jun 2026 10:06:15 +0100 Subject: [PATCH] fix(notifier): suppress stale update notice during self-update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The update notifier registered a `process.on("exit")` handler at startup that captured the cached `latestVersion` in its closure. When the `self-update` command then upgraded the binary in the same process, the already-registered handler still printed "Update available: x → y" on exit — pointing at the very version the user just installed. Skip the notifier entirely when argv contains `self-update`. The command itself reports the upgrade outcome, so the footer notice would be redundant even on the no-op "already up to date" path. Add a SKIP_COMMANDS set alongside the existing SKIP_FLAGS so the two classes of skip-reasons stay distinct, and cover the new gate with a focused test. --- src/cli/update-notifier.test.ts | 5 +++++ src/cli/update-notifier.ts | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/cli/update-notifier.test.ts b/src/cli/update-notifier.test.ts index 7c13fdd..70d0fdd 100644 --- a/src/cli/update-notifier.test.ts +++ b/src/cli/update-notifier.test.ts @@ -71,6 +71,11 @@ describe("runUpdateNotifier", () => { await runUpdateNotifier(makeBombContainer(), "1.0.0"); }); + test("skips when argv contains self-update command", async () => { + process.argv = ["bun", "wt", "self-update"]; + await runUpdateNotifier(makeBombContainer(), "1.0.0"); + }); + test("calls checkForUpdates on normal TTY invocation", async () => { let readFileCalled = false; diff --git a/src/cli/update-notifier.ts b/src/cli/update-notifier.ts index e2cd00c..7554c58 100644 --- a/src/cli/update-notifier.ts +++ b/src/cli/update-notifier.ts @@ -8,10 +8,14 @@ import { getCacheDir } from "../shared/xdg-paths.ts"; export const UPDATE_CHECK_FILENAME = "update-check.json"; const SKIP_FLAGS = new Set(["--help", "-h", "--version", "-v"]); +// Commands for which the "update available" notice would be redundant or misleading. +// `self-update` mutates the binary mid-process; the exit-time notice captured a stale +// version in its closure and would print after a successful upgrade. +const SKIP_COMMANDS = new Set(["self-update"]); export async function runUpdateNotifier(container: Container, currentVersion: string): Promise { if (!process.stdout.isTTY) return; - if (process.argv.some((arg) => SKIP_FLAGS.has(arg))) return; + if (process.argv.some((arg) => SKIP_FLAGS.has(arg) || SKIP_COMMANDS.has(arg))) return; const cachePath = join(getCacheDir(), UPDATE_CHECK_FILENAME);