mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-28 22:14:52 -07:00
feat(policy): support auto-add to policy by default and scoped persistence
This commit is contained in:
@@ -9,15 +9,12 @@ import { policiesCommand } from './policiesCommand.js';
|
||||
import { CommandKind } from './types.js';
|
||||
import { MessageType } from '../types.js';
|
||||
import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
|
||||
import * as fs from 'node:fs/promises';
|
||||
import {
|
||||
type Config,
|
||||
PolicyDecision,
|
||||
ApprovalMode,
|
||||
} from '@google/gemini-cli-core';
|
||||
|
||||
vi.mock('node:fs/promises');
|
||||
|
||||
describe('policiesCommand', () => {
|
||||
let mockContext: ReturnType<typeof createMockCommandContext>;
|
||||
|
||||
@@ -29,9 +26,8 @@ describe('policiesCommand', () => {
|
||||
expect(policiesCommand.name).toBe('policies');
|
||||
expect(policiesCommand.description).toBe('Manage policies');
|
||||
expect(policiesCommand.kind).toBe(CommandKind.BUILT_IN);
|
||||
expect(policiesCommand.subCommands).toHaveLength(2);
|
||||
expect(policiesCommand.subCommands).toHaveLength(1);
|
||||
expect(policiesCommand.subCommands![0].name).toBe('list');
|
||||
expect(policiesCommand.subCommands![1].name).toBe('undo');
|
||||
});
|
||||
|
||||
describe('list subcommand', () => {
|
||||
@@ -164,63 +160,4 @@ describe('policiesCommand', () => {
|
||||
expect(content).toContain('**ALLOW** tool: `shell` [Priority: 50]');
|
||||
});
|
||||
});
|
||||
|
||||
describe('undo subcommand', () => {
|
||||
it('should show error if config is missing', async () => {
|
||||
mockContext.services.config = null;
|
||||
const undoCommand = policiesCommand.subCommands![1];
|
||||
await undoCommand.action!(mockContext, '');
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Error: Config not available.',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
it('should show message if no backups found', async () => {
|
||||
const mockStorage = {
|
||||
getAutoSavedPolicyPath: vi.fn().mockReturnValue('user.toml'),
|
||||
getWorkspaceAutoSavedPolicyPath: vi.fn().mockReturnValue('ws.toml'),
|
||||
};
|
||||
mockContext.services.config = {
|
||||
storage: mockStorage,
|
||||
} as unknown as Config;
|
||||
|
||||
vi.mocked(fs.access).mockRejectedValue(new Error('no backup'));
|
||||
const undoCommand = policiesCommand.subCommands![1];
|
||||
await undoCommand.action!(mockContext, '');
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: MessageType.WARNING,
|
||||
text: 'No policy backups found to restore.',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
it('should restore backups if found', async () => {
|
||||
const mockStorage = {
|
||||
getAutoSavedPolicyPath: vi.fn().mockReturnValue('user.toml'),
|
||||
getWorkspaceAutoSavedPolicyPath: vi.fn().mockReturnValue('ws.toml'),
|
||||
};
|
||||
mockContext.services.config = {
|
||||
storage: mockStorage,
|
||||
} as unknown as Config;
|
||||
|
||||
vi.mocked(fs.access).mockResolvedValue(undefined);
|
||||
vi.mocked(fs.copyFile).mockResolvedValue(undefined);
|
||||
const undoCommand = policiesCommand.subCommands![1];
|
||||
await undoCommand.action!(mockContext, '');
|
||||
expect(fs.copyFile).toHaveBeenCalled();
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: MessageType.INFO,
|
||||
text: expect.stringContaining('Successfully restored'),
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import * as fs from 'node:fs/promises';
|
||||
import { ApprovalMode, type PolicyRule } from '@google/gemini-cli-core';
|
||||
import { CommandKind, type SlashCommand } from './types.js';
|
||||
import { MessageType } from '../types.js';
|
||||
@@ -112,66 +111,10 @@ const listPoliciesCommand: SlashCommand = {
|
||||
},
|
||||
};
|
||||
|
||||
const undoPoliciesCommand: SlashCommand = {
|
||||
name: 'undo',
|
||||
description: 'Undo the last auto-saved policy update',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
autoExecute: true,
|
||||
action: async (context) => {
|
||||
const { config } = context.services;
|
||||
if (!config) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.ERROR,
|
||||
text: 'Error: Config not available.',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const storage = config.storage;
|
||||
const paths = [
|
||||
storage.getAutoSavedPolicyPath(),
|
||||
storage.getWorkspaceAutoSavedPolicyPath(),
|
||||
];
|
||||
|
||||
let restoredCount = 0;
|
||||
for (const p of paths) {
|
||||
const bak = `${p}.bak`;
|
||||
try {
|
||||
await fs.access(bak);
|
||||
await fs.copyFile(bak, p);
|
||||
restoredCount++;
|
||||
} catch {
|
||||
// No backup or failed to restore
|
||||
}
|
||||
}
|
||||
|
||||
if (restoredCount > 0) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Successfully restored ${restoredCount} policy file(s) from backup. Please restart the CLI to apply changes.`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
} else {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.WARNING,
|
||||
text: 'No policy backups found to restore.',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export const policiesCommand: SlashCommand = {
|
||||
name: 'policies',
|
||||
description: 'Manage policies',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
autoExecute: false,
|
||||
subCommands: [listPoliciesCommand, undoPoliciesCommand],
|
||||
subCommands: [listPoliciesCommand],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user