mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-12 21:03:05 -07:00
feat(core): Introduce release channel detection (#12257)
Co-authored-by: shishu314 <shishu_1998@yahoo.com> Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com> Co-authored-by: Shardul Natu <43422294+kiranani@users.noreply.github.com> Co-authored-by: Shnatu <snatu@google.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,159 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2025 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import { isNightly, isPreview, isStable, _clearCache } from './channel.js';
|
||||||
|
import * as packageJson from './package.js';
|
||||||
|
|
||||||
|
vi.mock('./package.js', () => ({
|
||||||
|
getPackageJson: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('channel', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.resetAllMocks();
|
||||||
|
_clearCache();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isStable', () => {
|
||||||
|
it('should return true for a stable version', async () => {
|
||||||
|
vi.spyOn(packageJson, 'getPackageJson').mockResolvedValue({
|
||||||
|
name: 'test',
|
||||||
|
version: '1.0.0',
|
||||||
|
});
|
||||||
|
await expect(isStable('/test/dir')).resolves.toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false for a nightly version', async () => {
|
||||||
|
vi.spyOn(packageJson, 'getPackageJson').mockResolvedValue({
|
||||||
|
name: 'test',
|
||||||
|
version: '1.0.0-nightly.1',
|
||||||
|
});
|
||||||
|
await expect(isStable('/test/dir')).resolves.toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false for a preview version', async () => {
|
||||||
|
vi.spyOn(packageJson, 'getPackageJson').mockResolvedValue({
|
||||||
|
name: 'test',
|
||||||
|
version: '1.0.0-preview.1',
|
||||||
|
});
|
||||||
|
await expect(isStable('/test/dir')).resolves.toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if package.json is not found', async () => {
|
||||||
|
vi.spyOn(packageJson, 'getPackageJson').mockResolvedValue(undefined);
|
||||||
|
await expect(isStable('/test/dir')).resolves.toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if version is not defined', async () => {
|
||||||
|
vi.spyOn(packageJson, 'getPackageJson').mockResolvedValue({
|
||||||
|
name: 'test',
|
||||||
|
});
|
||||||
|
await expect(isStable('/test/dir')).resolves.toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isNightly', () => {
|
||||||
|
it('should return false for a stable version', async () => {
|
||||||
|
vi.spyOn(packageJson, 'getPackageJson').mockResolvedValue({
|
||||||
|
name: 'test',
|
||||||
|
version: '1.0.0',
|
||||||
|
});
|
||||||
|
await expect(isNightly('/test/dir')).resolves.toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true for a nightly version', async () => {
|
||||||
|
vi.spyOn(packageJson, 'getPackageJson').mockResolvedValue({
|
||||||
|
name: 'test',
|
||||||
|
version: '1.0.0-nightly.1',
|
||||||
|
});
|
||||||
|
await expect(isNightly('/test/dir')).resolves.toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false for a preview version', async () => {
|
||||||
|
vi.spyOn(packageJson, 'getPackageJson').mockResolvedValue({
|
||||||
|
name: 'test',
|
||||||
|
version: '1.0.0-preview.1',
|
||||||
|
});
|
||||||
|
await expect(isNightly('/test/dir')).resolves.toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true if package.json is not found', async () => {
|
||||||
|
vi.spyOn(packageJson, 'getPackageJson').mockResolvedValue(undefined);
|
||||||
|
await expect(isNightly('/test/dir')).resolves.toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true if version is not defined', async () => {
|
||||||
|
vi.spyOn(packageJson, 'getPackageJson').mockResolvedValue({
|
||||||
|
name: 'test',
|
||||||
|
});
|
||||||
|
await expect(isNightly('/test/dir')).resolves.toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isPreview', () => {
|
||||||
|
it('should return false for a stable version', async () => {
|
||||||
|
vi.spyOn(packageJson, 'getPackageJson').mockResolvedValue({
|
||||||
|
name: 'test',
|
||||||
|
version: '1.0.0',
|
||||||
|
});
|
||||||
|
await expect(isPreview('/test/dir')).resolves.toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false for a nightly version', async () => {
|
||||||
|
vi.spyOn(packageJson, 'getPackageJson').mockResolvedValue({
|
||||||
|
name: 'test',
|
||||||
|
version: '1.0.0-nightly.1',
|
||||||
|
});
|
||||||
|
await expect(isPreview('/test/dir')).resolves.toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true for a preview version', async () => {
|
||||||
|
vi.spyOn(packageJson, 'getPackageJson').mockResolvedValue({
|
||||||
|
name: 'test',
|
||||||
|
version: '1.0.0-preview.1',
|
||||||
|
});
|
||||||
|
await expect(isPreview('/test/dir')).resolves.toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if package.json is not found', async () => {
|
||||||
|
vi.spyOn(packageJson, 'getPackageJson').mockResolvedValue(undefined);
|
||||||
|
await expect(isPreview('/test/dir')).resolves.toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if version is not defined', async () => {
|
||||||
|
vi.spyOn(packageJson, 'getPackageJson').mockResolvedValue({
|
||||||
|
name: 'test',
|
||||||
|
});
|
||||||
|
await expect(isPreview('/test/dir')).resolves.toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('memoization', () => {
|
||||||
|
it('should only call getPackageJson once for the same cwd', async () => {
|
||||||
|
const spy = vi
|
||||||
|
.spyOn(packageJson, 'getPackageJson')
|
||||||
|
.mockResolvedValue({ name: 'test', version: '1.0.0' });
|
||||||
|
|
||||||
|
await expect(isStable('/test/dir')).resolves.toBe(true);
|
||||||
|
await expect(isNightly('/test/dir')).resolves.toBe(false);
|
||||||
|
await expect(isPreview('/test/dir')).resolves.toBe(false);
|
||||||
|
|
||||||
|
expect(spy).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call getPackageJson again for a different cwd', async () => {
|
||||||
|
const spy = vi
|
||||||
|
.spyOn(packageJson, 'getPackageJson')
|
||||||
|
.mockResolvedValue({ name: 'test', version: '1.0.0' });
|
||||||
|
|
||||||
|
await expect(isStable('/test/dir1')).resolves.toBe(true);
|
||||||
|
await expect(isStable('/test/dir2')).resolves.toBe(true);
|
||||||
|
|
||||||
|
expect(spy).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2025 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { getPackageJson } from './package.js';
|
||||||
|
|
||||||
|
export enum ReleaseChannel {
|
||||||
|
NIGHTLY = 'nightly',
|
||||||
|
PREVIEW = 'preview',
|
||||||
|
STABLE = 'stable',
|
||||||
|
}
|
||||||
|
|
||||||
|
const cache = new Map<string, ReleaseChannel>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the cache for testing purposes.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
export function _clearCache() {
|
||||||
|
cache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _getReleaseChannel(cwd: string): Promise<ReleaseChannel> {
|
||||||
|
if (cache.has(cwd)) {
|
||||||
|
return cache.get(cwd)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
const packageJson = await getPackageJson(cwd);
|
||||||
|
const version = packageJson?.version ?? '';
|
||||||
|
|
||||||
|
let channel: ReleaseChannel;
|
||||||
|
if (version.includes('nightly') || version === '') {
|
||||||
|
channel = ReleaseChannel.NIGHTLY;
|
||||||
|
} else if (version.includes('preview')) {
|
||||||
|
channel = ReleaseChannel.PREVIEW;
|
||||||
|
} else {
|
||||||
|
channel = ReleaseChannel.STABLE;
|
||||||
|
}
|
||||||
|
cache.set(cwd, channel);
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function isNightly(cwd: string): Promise<boolean> {
|
||||||
|
return (await _getReleaseChannel(cwd)) === ReleaseChannel.NIGHTLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function isPreview(cwd: string): Promise<boolean> {
|
||||||
|
return (await _getReleaseChannel(cwd)) === ReleaseChannel.PREVIEW;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function isStable(cwd: string): Promise<boolean> {
|
||||||
|
return (await _getReleaseChannel(cwd)) === ReleaseChannel.STABLE;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user