From e4226b8a3ed015a477c554836c8ebece852e9390 Mon Sep 17 00:00:00 2001 From: Billy Biggs Date: Fri, 17 Oct 2025 12:44:31 -0700 Subject: [PATCH] Only check for updates if disableUpdateNag is false (#11405) --- packages/cli/src/gemini.tsx | 2 +- packages/cli/src/ui/utils/updateCheck.test.ts | 39 ++++++++++++++----- packages/cli/src/ui/utils/updateCheck.ts | 8 +++- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/packages/cli/src/gemini.tsx b/packages/cli/src/gemini.tsx index e266fcc048..75e4795be0 100644 --- a/packages/cli/src/gemini.tsx +++ b/packages/cli/src/gemini.tsx @@ -206,7 +206,7 @@ export async function startInteractiveUI( }, ); - checkForUpdates() + checkForUpdates(settings) .then((info) => { handleAutoUpdate(info, settings, config.getProjectRoot()); }) diff --git a/packages/cli/src/ui/utils/updateCheck.test.ts b/packages/cli/src/ui/utils/updateCheck.test.ts index c7214e8b61..4a2a74c83a 100644 --- a/packages/cli/src/ui/utils/updateCheck.test.ts +++ b/packages/cli/src/ui/utils/updateCheck.test.ts @@ -4,8 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { vi, describe, it, expect, beforeEach } from 'vitest'; +import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest'; import { checkForUpdates } from './updateCheck.js'; +import type { LoadedSettings } from '../../config/settings.js'; const getPackageJson = vi.hoisted(() => vi.fn()); vi.mock('../../utils/package.js', () => ({ @@ -18,11 +19,21 @@ vi.mock('update-notifier', () => ({ })); describe('checkForUpdates', () => { + let mockSettings: LoadedSettings; + beforeEach(() => { vi.useFakeTimers(); vi.resetAllMocks(); // Clear DEV environment variable before each test delete process.env['DEV']; + + mockSettings = { + merged: { + general: { + disableUpdateNag: false, + }, + }, + } as LoadedSettings; }); afterEach(() => { @@ -30,6 +41,14 @@ describe('checkForUpdates', () => { vi.restoreAllMocks(); }); + it('should return null if disableUpdateNag is true', async () => { + mockSettings.merged.general!.disableUpdateNag = true; + const result = await checkForUpdates(mockSettings); + expect(result).toBeNull(); + expect(getPackageJson).not.toHaveBeenCalled(); + expect(updateNotifier).not.toHaveBeenCalled(); + }); + it('should return null when running from source (DEV=true)', async () => { process.env['DEV'] = 'true'; getPackageJson.mockResolvedValue({ @@ -41,7 +60,7 @@ describe('checkForUpdates', () => { .fn() .mockResolvedValue({ current: '1.0.0', latest: '1.1.0' }), }); - const result = await checkForUpdates(); + const result = await checkForUpdates(mockSettings); expect(result).toBeNull(); expect(getPackageJson).not.toHaveBeenCalled(); expect(updateNotifier).not.toHaveBeenCalled(); @@ -49,7 +68,7 @@ describe('checkForUpdates', () => { it('should return null if package.json is missing', async () => { getPackageJson.mockResolvedValue(null); - const result = await checkForUpdates(); + const result = await checkForUpdates(mockSettings); expect(result).toBeNull(); }); @@ -61,7 +80,7 @@ describe('checkForUpdates', () => { updateNotifier.mockReturnValue({ fetchInfo: vi.fn().mockResolvedValue(null), }); - const result = await checkForUpdates(); + const result = await checkForUpdates(mockSettings); expect(result).toBeNull(); }); @@ -76,7 +95,7 @@ describe('checkForUpdates', () => { .mockResolvedValue({ current: '1.0.0', latest: '1.1.0' }), }); - const result = await checkForUpdates(); + const result = await checkForUpdates(mockSettings); expect(result?.message).toContain('1.0.0 → 1.1.0'); expect(result?.update).toEqual({ current: '1.0.0', latest: '1.1.0' }); }); @@ -91,7 +110,7 @@ describe('checkForUpdates', () => { .fn() .mockResolvedValue({ current: '1.0.0', latest: '1.0.0' }), }); - const result = await checkForUpdates(); + const result = await checkForUpdates(mockSettings); expect(result).toBeNull(); }); @@ -105,7 +124,7 @@ describe('checkForUpdates', () => { .fn() .mockResolvedValue({ current: '1.1.0', latest: '1.0.0' }), }); - const result = await checkForUpdates(); + const result = await checkForUpdates(mockSettings); expect(result).toBeNull(); }); @@ -118,13 +137,13 @@ describe('checkForUpdates', () => { fetchInfo: vi.fn().mockRejectedValue(new Error('Timeout')), }); - const result = await checkForUpdates(); + const result = await checkForUpdates(mockSettings); expect(result).toBeNull(); }); it('should handle errors gracefully', async () => { getPackageJson.mockRejectedValue(new Error('test error')); - const result = await checkForUpdates(); + const result = await checkForUpdates(mockSettings); expect(result).toBeNull(); }); @@ -155,7 +174,7 @@ describe('checkForUpdates', () => { fetchInfo: () => fetchInfoMock({ pkg, distTag }), })); - const result = await checkForUpdates(); + const result = await checkForUpdates(mockSettings); expect(result?.message).toContain('1.2.3-nightly.1 → 1.2.3-nightly.2'); expect(result?.update.latest).toBe('1.2.3-nightly.2'); }); diff --git a/packages/cli/src/ui/utils/updateCheck.ts b/packages/cli/src/ui/utils/updateCheck.ts index ef45cd493e..c45bf98771 100644 --- a/packages/cli/src/ui/utils/updateCheck.ts +++ b/packages/cli/src/ui/utils/updateCheck.ts @@ -8,6 +8,7 @@ import type { UpdateInfo } from 'update-notifier'; import updateNotifier from 'update-notifier'; import semver from 'semver'; import { getPackageJson } from '../../utils/package.js'; +import type { LoadedSettings } from '../../config/settings.js'; export const FETCH_TIMEOUT_MS = 2000; @@ -39,8 +40,13 @@ function getBestAvailableUpdate( return semver.gt(stableVer, nightlyVer) ? stable : nightly; } -export async function checkForUpdates(): Promise { +export async function checkForUpdates( + settings: LoadedSettings, +): Promise { try { + if (settings.merged.general?.disableUpdateNag) { + return null; + } // Skip update check when running from source (development mode) if (process.env['DEV'] === 'true') { return null;