mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-23 04:21:31 -07:00
fix(cli): Exit CLI when trust save unsuccessful during launch (#11968)
This commit is contained in:
@@ -148,10 +148,11 @@ describe('PermissionsModifyTrustDialog', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should commit, restart, and exit on `r` keypress', async () => {
|
||||
it('should commit and restart `r` keypress', async () => {
|
||||
const mockRelaunchApp = vi
|
||||
.spyOn(processUtils, 'relaunchApp')
|
||||
.mockResolvedValue(undefined);
|
||||
mockCommitTrustLevelChange.mockReturnValue(true);
|
||||
vi.mocked(usePermissionsModifyTrust).mockReturnValue({
|
||||
cwd: '/test/dir',
|
||||
currentTrustLevel: TrustLevel.DO_NOT_TRUST,
|
||||
@@ -175,7 +176,6 @@ describe('PermissionsModifyTrustDialog', () => {
|
||||
await waitFor(() => {
|
||||
expect(mockCommitTrustLevelChange).toHaveBeenCalled();
|
||||
expect(mockRelaunchApp).toHaveBeenCalled();
|
||||
expect(onExit).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
mockRelaunchApp.mockRestore();
|
||||
|
||||
@@ -62,9 +62,12 @@ export function PermissionsModifyTrustDialog({
|
||||
onExit();
|
||||
}
|
||||
if (needsRestart && key.name === 'r') {
|
||||
commitTrustLevelChange();
|
||||
relaunchApp();
|
||||
onExit();
|
||||
const success = commitTrustLevelChange();
|
||||
if (success) {
|
||||
relaunchApp();
|
||||
} else {
|
||||
onExit();
|
||||
}
|
||||
}
|
||||
},
|
||||
{ isActive: true },
|
||||
|
||||
@@ -14,8 +14,10 @@ import { FolderTrustChoice } from '../components/FolderTrustDialog.js';
|
||||
import type { LoadedTrustedFolders } from '../../config/trustedFolders.js';
|
||||
import { TrustLevel } from '../../config/trustedFolders.js';
|
||||
import * as trustedFolders from '../../config/trustedFolders.js';
|
||||
import { coreEvents } from '@google/gemini-cli-core';
|
||||
|
||||
const mockedCwd = vi.hoisted(() => vi.fn());
|
||||
const mockedExit = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock('node:process', async () => {
|
||||
const actual =
|
||||
@@ -23,6 +25,7 @@ vi.mock('node:process', async () => {
|
||||
return {
|
||||
...actual,
|
||||
cwd: mockedCwd,
|
||||
exit: mockedExit,
|
||||
platform: 'linux',
|
||||
};
|
||||
});
|
||||
@@ -35,6 +38,7 @@ describe('useFolderTrust', () => {
|
||||
let addItem: Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
mockSettings = {
|
||||
merged: {
|
||||
security: {
|
||||
@@ -60,6 +64,7 @@ describe('useFolderTrust', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
@@ -260,4 +265,30 @@ describe('useFolderTrust', () => {
|
||||
expect(result.current.isRestarting).toBe(false);
|
||||
expect(result.current.isFolderTrustDialogOpen).toBe(false); // Dialog should close
|
||||
});
|
||||
|
||||
it('should emit feedback on failure to set value', () => {
|
||||
isWorkspaceTrustedSpy.mockReturnValue({
|
||||
isTrusted: undefined,
|
||||
source: undefined,
|
||||
});
|
||||
(mockTrustedFolders.setValue as Mock).mockImplementation(() => {
|
||||
throw new Error('test error');
|
||||
});
|
||||
const emitFeedbackSpy = vi.spyOn(coreEvents, 'emitFeedback');
|
||||
const { result } = renderHook(() =>
|
||||
useFolderTrust(mockSettings, onTrustChange, addItem),
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.handleFolderTrustSelect(FolderTrustChoice.TRUST_FOLDER);
|
||||
});
|
||||
|
||||
vi.runAllTimers();
|
||||
|
||||
expect(emitFeedbackSpy).toHaveBeenCalledWith(
|
||||
'error',
|
||||
'Failed to save trust settings. Exiting Gemini CLI.',
|
||||
);
|
||||
expect(mockedExit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
} from '../../config/trustedFolders.js';
|
||||
import * as process from 'node:process';
|
||||
import { type HistoryItemWithoutId, MessageType } from '../types.js';
|
||||
import { coreEvents } from '@google/gemini-cli-core';
|
||||
|
||||
export const useFolderTrust = (
|
||||
settings: LoadedSettings,
|
||||
@@ -67,7 +68,19 @@ export const useFolderTrust = (
|
||||
return;
|
||||
}
|
||||
|
||||
trustedFolders.setValue(cwd, trustLevel);
|
||||
try {
|
||||
trustedFolders.setValue(cwd, trustLevel);
|
||||
} catch (_e) {
|
||||
coreEvents.emitFeedback(
|
||||
'error',
|
||||
'Failed to save trust settings. Exiting Gemini CLI.',
|
||||
);
|
||||
setTimeout(() => {
|
||||
process.exit(1);
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
const currentIsTrusted =
|
||||
trustLevel === TrustLevel.TRUST_FOLDER ||
|
||||
trustLevel === TrustLevel.TRUST_PARENT;
|
||||
|
||||
@@ -19,6 +19,7 @@ import { usePermissionsModifyTrust } from './usePermissionsModifyTrust.js';
|
||||
import { TrustLevel } from '../../config/trustedFolders.js';
|
||||
import type { LoadedSettings } from '../../config/settings.js';
|
||||
import type { LoadedTrustedFolders } from '../../config/trustedFolders.js';
|
||||
import { coreEvents } from '@google/gemini-cli-core';
|
||||
|
||||
// Hoist mocks
|
||||
const mockedCwd = vi.hoisted(() => vi.fn());
|
||||
@@ -259,4 +260,70 @@ describe('usePermissionsModifyTrust', () => {
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
it('should emit feedback when setValue throws in updateTrustLevel', () => {
|
||||
const mockSetValue = vi.fn().mockImplementation(() => {
|
||||
throw new Error('test error');
|
||||
});
|
||||
mockedLoadTrustedFolders.mockReturnValue({
|
||||
user: { config: {} },
|
||||
setValue: mockSetValue,
|
||||
} as unknown as LoadedTrustedFolders);
|
||||
|
||||
mockedIsWorkspaceTrusted.mockReturnValue({
|
||||
isTrusted: true,
|
||||
source: 'file',
|
||||
});
|
||||
|
||||
const emitFeedbackSpy = vi.spyOn(coreEvents, 'emitFeedback');
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
usePermissionsModifyTrust(mockOnExit, mockAddItem),
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.updateTrustLevel(TrustLevel.TRUST_PARENT);
|
||||
});
|
||||
|
||||
expect(emitFeedbackSpy).toHaveBeenCalledWith(
|
||||
'error',
|
||||
'Failed to save trust settings. Your changes may not persist.',
|
||||
);
|
||||
expect(mockOnExit).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should emit feedback when setValue throws in commitTrustLevelChange', () => {
|
||||
const mockSetValue = vi.fn().mockImplementation(() => {
|
||||
throw new Error('test error');
|
||||
});
|
||||
mockedLoadTrustedFolders.mockReturnValue({
|
||||
user: { config: {} },
|
||||
setValue: mockSetValue,
|
||||
} as unknown as LoadedTrustedFolders);
|
||||
|
||||
mockedIsWorkspaceTrusted
|
||||
.mockReturnValueOnce({ isTrusted: false, source: 'file' })
|
||||
.mockReturnValueOnce({ isTrusted: true, source: 'file' });
|
||||
|
||||
const emitFeedbackSpy = vi.spyOn(coreEvents, 'emitFeedback');
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
usePermissionsModifyTrust(mockOnExit, mockAddItem),
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.updateTrustLevel(TrustLevel.TRUST_FOLDER);
|
||||
});
|
||||
|
||||
act(() => {
|
||||
const success = result.current.commitTrustLevelChange();
|
||||
expect(success).toBe(false);
|
||||
});
|
||||
|
||||
expect(emitFeedbackSpy).toHaveBeenCalledWith(
|
||||
'error',
|
||||
'Failed to save trust settings. Your changes may not persist.',
|
||||
);
|
||||
expect(result.current.needsRestart).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,6 +16,7 @@ import { useSettings } from '../contexts/SettingsContext.js';
|
||||
import { MessageType } from '../types.js';
|
||||
import { type UseHistoryManagerReturn } from './useHistoryManager.js';
|
||||
import type { LoadedSettings } from '../../config/settings.js';
|
||||
import { coreEvents } from '@google/gemini-cli-core';
|
||||
|
||||
interface TrustState {
|
||||
currentTrustLevel: TrustLevel | undefined;
|
||||
@@ -101,7 +102,14 @@ export const usePermissionsModifyTrust = (
|
||||
setNeedsRestart(true);
|
||||
} else {
|
||||
const folders = loadTrustedFolders();
|
||||
folders.setValue(cwd, trustLevel);
|
||||
try {
|
||||
folders.setValue(cwd, trustLevel);
|
||||
} catch (_e) {
|
||||
coreEvents.emitFeedback(
|
||||
'error',
|
||||
'Failed to save trust settings. Your changes may not persist.',
|
||||
);
|
||||
}
|
||||
onExit();
|
||||
}
|
||||
},
|
||||
@@ -111,8 +119,20 @@ export const usePermissionsModifyTrust = (
|
||||
const commitTrustLevelChange = useCallback(() => {
|
||||
if (pendingTrustLevel) {
|
||||
const folders = loadTrustedFolders();
|
||||
folders.setValue(cwd, pendingTrustLevel);
|
||||
try {
|
||||
folders.setValue(cwd, pendingTrustLevel);
|
||||
return true;
|
||||
} catch (_e) {
|
||||
coreEvents.emitFeedback(
|
||||
'error',
|
||||
'Failed to save trust settings. Your changes may not persist.',
|
||||
);
|
||||
setNeedsRestart(false);
|
||||
setPendingTrustLevel(undefined);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}, [cwd, pendingTrustLevel]);
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user