2025-06-17 08:24:07 -07:00
|
|
|
/**
|
|
|
|
|
* @license
|
|
|
|
|
* Copyright 2025 Google LLC
|
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
*/
|
|
|
|
|
|
2025-10-24 14:23:39 -07:00
|
|
|
import latestVersion from 'latest-version';
|
2025-06-30 20:03:16 -07:00
|
|
|
import semver from 'semver';
|
2025-10-29 13:23:35 -07:00
|
|
|
import { getPackageJson, debugLogger } from '@google/gemini-cli-core';
|
2025-10-17 12:44:31 -07:00
|
|
|
import type { LoadedSettings } from '../../config/settings.js';
|
2025-10-29 13:23:35 -07:00
|
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
|
import path from 'node:path';
|
|
|
|
|
|
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
|
|
|
const __dirname = path.dirname(__filename);
|
2025-06-17 08:24:07 -07:00
|
|
|
|
2025-07-29 20:11:15 -04:00
|
|
|
export const FETCH_TIMEOUT_MS = 2000;
|
|
|
|
|
|
2025-10-24 14:23:39 -07:00
|
|
|
// Replicating the bits of UpdateInfo we need from update-notifier
|
|
|
|
|
export interface UpdateInfo {
|
|
|
|
|
latest: string;
|
|
|
|
|
current: string;
|
|
|
|
|
name: string;
|
|
|
|
|
type?: semver.ReleaseType;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-28 17:56:52 -07:00
|
|
|
export interface UpdateObject {
|
|
|
|
|
message: string;
|
|
|
|
|
update: UpdateInfo;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-01 20:17:32 -07:00
|
|
|
/**
|
2025-10-24 14:23:39 -07:00
|
|
|
* From a nightly and stable version, determines which is the "best" one to offer.
|
2025-08-01 20:17:32 -07:00
|
|
|
* The rule is to always prefer nightly if the base versions are the same.
|
|
|
|
|
*/
|
|
|
|
|
function getBestAvailableUpdate(
|
2025-10-24 14:23:39 -07:00
|
|
|
nightly?: string,
|
|
|
|
|
stable?: string,
|
|
|
|
|
): string | null {
|
2025-08-01 20:17:32 -07:00
|
|
|
if (!nightly) return stable || null;
|
|
|
|
|
if (!stable) return nightly || null;
|
|
|
|
|
|
2025-10-24 14:23:39 -07:00
|
|
|
if (semver.coerce(stable)?.version === semver.coerce(nightly)?.version) {
|
2025-08-01 20:17:32 -07:00
|
|
|
return nightly;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-24 14:23:39 -07:00
|
|
|
return semver.gt(stable, nightly) ? stable : nightly;
|
2025-08-01 20:17:32 -07:00
|
|
|
}
|
|
|
|
|
|
2025-10-17 12:44:31 -07:00
|
|
|
export async function checkForUpdates(
|
|
|
|
|
settings: LoadedSettings,
|
|
|
|
|
): Promise<UpdateObject | null> {
|
2025-06-20 00:30:30 -07:00
|
|
|
try {
|
2025-10-17 12:44:31 -07:00
|
|
|
if (settings.merged.general?.disableUpdateNag) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2025-07-18 09:44:45 +09:00
|
|
|
// Skip update check when running from source (development mode)
|
2025-08-17 12:43:21 -04:00
|
|
|
if (process.env['DEV'] === 'true') {
|
2025-07-18 09:44:45 +09:00
|
|
|
return null;
|
|
|
|
|
}
|
2025-10-29 13:23:35 -07:00
|
|
|
const packageJson = await getPackageJson(__dirname);
|
2025-06-24 17:26:50 -07:00
|
|
|
if (!packageJson || !packageJson.name || !packageJson.version) {
|
2025-06-20 00:30:30 -07:00
|
|
|
return null;
|
|
|
|
|
}
|
2025-08-01 20:17:32 -07:00
|
|
|
|
|
|
|
|
const { name, version: currentVersion } = packageJson;
|
|
|
|
|
const isNightly = currentVersion.includes('nightly');
|
|
|
|
|
|
|
|
|
|
if (isNightly) {
|
2025-10-24 14:23:39 -07:00
|
|
|
const [nightlyUpdate, latestUpdate] = await Promise.all([
|
|
|
|
|
latestVersion(name, { version: 'nightly' }),
|
|
|
|
|
latestVersion(name),
|
2025-08-01 20:17:32 -07:00
|
|
|
]);
|
|
|
|
|
|
2025-10-24 14:23:39 -07:00
|
|
|
const bestUpdate = getBestAvailableUpdate(nightlyUpdate, latestUpdate);
|
2025-08-01 20:17:32 -07:00
|
|
|
|
2025-10-24 14:23:39 -07:00
|
|
|
if (bestUpdate && semver.gt(bestUpdate, currentVersion)) {
|
|
|
|
|
const message = `A new version of Gemini CLI is available! ${currentVersion} → ${bestUpdate}`;
|
|
|
|
|
const type = semver.diff(bestUpdate, currentVersion) || undefined;
|
2025-08-01 20:17:32 -07:00
|
|
|
return {
|
|
|
|
|
message,
|
2025-10-24 14:23:39 -07:00
|
|
|
update: {
|
|
|
|
|
latest: bestUpdate,
|
|
|
|
|
current: currentVersion,
|
|
|
|
|
name,
|
|
|
|
|
type,
|
|
|
|
|
},
|
2025-08-01 20:17:32 -07:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2025-10-24 14:23:39 -07:00
|
|
|
const latestUpdate = await latestVersion(name);
|
2025-08-01 20:17:32 -07:00
|
|
|
|
2025-10-24 14:23:39 -07:00
|
|
|
if (latestUpdate && semver.gt(latestUpdate, currentVersion)) {
|
|
|
|
|
const message = `Gemini CLI update available! ${currentVersion} → ${latestUpdate}`;
|
|
|
|
|
const type = semver.diff(latestUpdate, currentVersion) || undefined;
|
2025-08-01 20:17:32 -07:00
|
|
|
return {
|
|
|
|
|
message,
|
2025-10-24 14:23:39 -07:00
|
|
|
update: {
|
|
|
|
|
latest: latestUpdate,
|
|
|
|
|
current: currentVersion,
|
|
|
|
|
name,
|
|
|
|
|
type,
|
|
|
|
|
},
|
2025-08-01 20:17:32 -07:00
|
|
|
};
|
|
|
|
|
}
|
2025-06-20 00:30:30 -07:00
|
|
|
}
|
2025-06-17 08:24:07 -07:00
|
|
|
|
2025-06-20 00:30:30 -07:00
|
|
|
return null;
|
|
|
|
|
} catch (e) {
|
2025-10-21 16:35:22 -04:00
|
|
|
debugLogger.warn('Failed to check for updates: ' + e);
|
2025-06-20 00:30:30 -07:00
|
|
|
return null;
|
|
|
|
|
}
|
2025-06-17 08:24:07 -07:00
|
|
|
}
|