mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-27 05:24:34 -07:00
[A2A] Disable checkpointing if git is not installed (#16896)
This commit is contained in:
@@ -575,7 +575,10 @@ export class Task {
|
|||||||
EDIT_TOOL_NAMES.has(request.name),
|
EDIT_TOOL_NAMES.has(request.name),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (restorableToolCalls.length > 0) {
|
if (
|
||||||
|
restorableToolCalls.length > 0 &&
|
||||||
|
this.config.getCheckpointingEnabled()
|
||||||
|
) {
|
||||||
const gitService = await this.config.getGitService();
|
const gitService = await this.config.getGitService();
|
||||||
if (gitService) {
|
if (gitService) {
|
||||||
const { checkpointsToWrite, toolCallToCheckpointMap, errors } =
|
const { checkpointsToWrite, toolCallToCheckpointMap, errors } =
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2025 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||||
|
import { loadConfig } from './config.js';
|
||||||
|
import type { ExtensionLoader } from '@google/gemini-cli-core';
|
||||||
|
import type { Settings } from './settings.js';
|
||||||
|
|
||||||
|
const {
|
||||||
|
mockLoadServerHierarchicalMemory,
|
||||||
|
mockConfigConstructor,
|
||||||
|
mockVerifyGitAvailability,
|
||||||
|
} = vi.hoisted(() => ({
|
||||||
|
mockLoadServerHierarchicalMemory: vi.fn().mockResolvedValue({
|
||||||
|
memoryContent: '',
|
||||||
|
fileCount: 0,
|
||||||
|
filePaths: [],
|
||||||
|
}),
|
||||||
|
mockConfigConstructor: vi.fn(),
|
||||||
|
mockVerifyGitAvailability: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('@google/gemini-cli-core', async () => ({
|
||||||
|
Config: class MockConfig {
|
||||||
|
constructor(params: unknown) {
|
||||||
|
mockConfigConstructor(params);
|
||||||
|
}
|
||||||
|
initialize = vi.fn();
|
||||||
|
refreshAuth = vi.fn();
|
||||||
|
},
|
||||||
|
loadServerHierarchicalMemory: mockLoadServerHierarchicalMemory,
|
||||||
|
startupProfiler: {
|
||||||
|
flush: vi.fn(),
|
||||||
|
},
|
||||||
|
FileDiscoveryService: vi.fn(),
|
||||||
|
ApprovalMode: { DEFAULT: 'default', YOLO: 'yolo' },
|
||||||
|
AuthType: {
|
||||||
|
LOGIN_WITH_GOOGLE: 'login_with_google',
|
||||||
|
USE_GEMINI: 'use_gemini',
|
||||||
|
},
|
||||||
|
GEMINI_DIR: '.gemini',
|
||||||
|
DEFAULT_GEMINI_EMBEDDING_MODEL: 'models/embedding-001',
|
||||||
|
DEFAULT_GEMINI_MODEL: 'models/gemini-1.5-flash',
|
||||||
|
PREVIEW_GEMINI_MODEL: 'models/gemini-1.5-pro-latest',
|
||||||
|
homedir: () => '/tmp',
|
||||||
|
GitService: {
|
||||||
|
verifyGitAvailability: mockVerifyGitAvailability,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('loadConfig', () => {
|
||||||
|
const mockSettings = {
|
||||||
|
checkpointing: { enabled: true },
|
||||||
|
};
|
||||||
|
const mockExtensionLoader = {
|
||||||
|
start: vi.fn(),
|
||||||
|
getExtensions: vi.fn().mockReturnValue([]),
|
||||||
|
} as unknown as ExtensionLoader;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.resetAllMocks();
|
||||||
|
process.env['GEMINI_API_KEY'] = 'test-key';
|
||||||
|
// Reset the mock return value just in case
|
||||||
|
mockLoadServerHierarchicalMemory.mockResolvedValue({
|
||||||
|
memoryContent: '',
|
||||||
|
fileCount: 0,
|
||||||
|
filePaths: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
delete process.env['GEMINI_API_KEY'];
|
||||||
|
delete process.env['CHECKPOINTING'];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should disable checkpointing if git is not installed', async () => {
|
||||||
|
mockVerifyGitAvailability.mockResolvedValue(false);
|
||||||
|
|
||||||
|
await loadConfig(
|
||||||
|
mockSettings as unknown as Settings,
|
||||||
|
mockExtensionLoader,
|
||||||
|
'test-task',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(mockConfigConstructor).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
checkpointing: false,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should enable checkpointing if git is installed', async () => {
|
||||||
|
mockVerifyGitAvailability.mockResolvedValue(true);
|
||||||
|
|
||||||
|
await loadConfig(
|
||||||
|
mockSettings as unknown as Settings,
|
||||||
|
mockExtensionLoader,
|
||||||
|
'test-task',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(mockConfigConstructor).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
checkpointing: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
startupProfiler,
|
startupProfiler,
|
||||||
PREVIEW_GEMINI_MODEL,
|
PREVIEW_GEMINI_MODEL,
|
||||||
homedir,
|
homedir,
|
||||||
|
GitService,
|
||||||
} from '@google/gemini-cli-core';
|
} from '@google/gemini-cli-core';
|
||||||
|
|
||||||
import { logger } from '../utils/logger.js';
|
import { logger } from '../utils/logger.js';
|
||||||
@@ -41,6 +42,19 @@ export async function loadConfig(
|
|||||||
settings.folderTrust === true ||
|
settings.folderTrust === true ||
|
||||||
process.env['GEMINI_FOLDER_TRUST'] === 'true';
|
process.env['GEMINI_FOLDER_TRUST'] === 'true';
|
||||||
|
|
||||||
|
let checkpointing = process.env['CHECKPOINTING']
|
||||||
|
? process.env['CHECKPOINTING'] === 'true'
|
||||||
|
: settings.checkpointing?.enabled;
|
||||||
|
|
||||||
|
if (checkpointing) {
|
||||||
|
if (!(await GitService.verifyGitAvailability())) {
|
||||||
|
logger.warn(
|
||||||
|
'[Config] Checkpointing is enabled but git is not installed. Disabling checkpointing.',
|
||||||
|
);
|
||||||
|
checkpointing = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const configParams: ConfigParameters = {
|
const configParams: ConfigParameters = {
|
||||||
sessionId: taskId,
|
sessionId: taskId,
|
||||||
model: settings.general?.previewFeatures
|
model: settings.general?.previewFeatures
|
||||||
@@ -79,9 +93,7 @@ export async function loadConfig(
|
|||||||
folderTrust,
|
folderTrust,
|
||||||
trustedFolder: true,
|
trustedFolder: true,
|
||||||
extensionLoader,
|
extensionLoader,
|
||||||
checkpointing: process.env['CHECKPOINTING']
|
checkpointing,
|
||||||
? process.env['CHECKPOINTING'] === 'true'
|
|
||||||
: settings.checkpointing?.enabled,
|
|
||||||
previewFeatures: settings.general?.previewFeatures,
|
previewFeatures: settings.general?.previewFeatures,
|
||||||
interactive: true,
|
interactive: true,
|
||||||
enableInteractiveShell: true,
|
enableInteractiveShell: true,
|
||||||
|
|||||||
@@ -147,15 +147,13 @@ describe('GitService', () => {
|
|||||||
|
|
||||||
describe('verifyGitAvailability', () => {
|
describe('verifyGitAvailability', () => {
|
||||||
it('should resolve true if git --version command succeeds', async () => {
|
it('should resolve true if git --version command succeeds', async () => {
|
||||||
const service = new GitService(projectRoot, storage);
|
await expect(GitService.verifyGitAvailability()).resolves.toBe(true);
|
||||||
await expect(service.verifyGitAvailability()).resolves.toBe(true);
|
|
||||||
expect(spawnAsync).toHaveBeenCalledWith('git', ['--version']);
|
expect(spawnAsync).toHaveBeenCalledWith('git', ['--version']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should resolve false if git --version command fails', async () => {
|
it('should resolve false if git --version command fails', async () => {
|
||||||
(spawnAsync as Mock).mockRejectedValue(new Error('git not found'));
|
(spawnAsync as Mock).mockRejectedValue(new Error('git not found'));
|
||||||
const service = new GitService(projectRoot, storage);
|
await expect(GitService.verifyGitAvailability()).resolves.toBe(false);
|
||||||
await expect(service.verifyGitAvailability()).resolves.toBe(false);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export class GitService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async initialize(): Promise<void> {
|
async initialize(): Promise<void> {
|
||||||
const gitAvailable = await this.verifyGitAvailability();
|
const gitAvailable = await GitService.verifyGitAvailability();
|
||||||
if (!gitAvailable) {
|
if (!gitAvailable) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Checkpointing is enabled, but Git is not installed. Please install Git or disable checkpointing to continue.',
|
'Checkpointing is enabled, but Git is not installed. Please install Git or disable checkpointing to continue.',
|
||||||
@@ -42,7 +42,7 @@ export class GitService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async verifyGitAvailability(): Promise<boolean> {
|
static async verifyGitAvailability(): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
await spawnAsync('git', ['--version']);
|
await spawnAsync('git', ['--version']);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
Reference in New Issue
Block a user