fix(core,cli): enable recursive directory access for (#17094)

This commit is contained in:
Gal Zahavi
2026-01-21 09:58:23 -08:00
committed by GitHub
parent acbef4cd31
commit 45d554ae2f
17 changed files with 410 additions and 135 deletions

View File

@@ -16,10 +16,26 @@ import type { LoadedTrustedFolders } from '../../config/trustedFolders.js';
import type { MultiFolderTrustDialogProps } from '../components/MultiFolderTrustDialog.js';
vi.mock('../utils/directoryUtils.js', () => ({
expandHomeDir: (p: string) => p, // Simple pass-through for testing
loadMemoryFromDirectories: vi.fn().mockResolvedValue({ fileCount: 1 }),
}));
vi.mock('../utils/directoryUtils.js', async (importOriginal) => {
const actual =
await importOriginal<typeof import('../utils/directoryUtils.js')>();
return {
...actual,
expandHomeDir: (p: string) => p, // Simple pass-through for testing
batchAddDirectories: (
workspaceContext: WorkspaceContext,
paths: string[],
) => {
const result = workspaceContext.addDirectories(paths);
const errors: string[] = [];
for (const failure of result.failed) {
errors.push(`Error adding '${failure.path}': ${failure.error.message}`);
}
return { added: result.added, errors };
},
loadMemoryFromDirectories: vi.fn().mockResolvedValue({ fileCount: 1 }),
};
});
vi.mock('../components/MultiFolderTrustDialog.js', () => ({
MultiFolderTrustDialog: (props: MultiFolderTrustDialogProps) => (
@@ -38,6 +54,7 @@ describe('useIncludeDirsTrust', () => {
mockWorkspaceContext = {
addDirectory: vi.fn(),
addDirectories: vi.fn().mockReturnValue({ added: [], failed: [] }),
getDirectories: vi.fn().mockReturnValue([]),
onDirectoriesChangedListeners: new Set(),
onDirectoriesChanged: vi.fn(),
@@ -111,23 +128,18 @@ describe('useIncludeDirsTrust', () => {
'/dir1',
'/dir2',
]);
vi.mocked(mockWorkspaceContext.addDirectory).mockImplementation(
(path) => {
if (path === '/dir2') {
throw new Error('Test error');
}
},
);
vi.mocked(mockWorkspaceContext.addDirectories).mockReturnValue({
added: ['/dir1'],
failed: [{ path: '/dir2', error: new Error('Test error') }],
});
renderTestHook(isTrusted);
await waitFor(() => {
expect(mockWorkspaceContext.addDirectory).toHaveBeenCalledWith(
expect(mockWorkspaceContext.addDirectories).toHaveBeenCalledWith([
'/dir1',
);
expect(mockWorkspaceContext.addDirectory).toHaveBeenCalledWith(
'/dir2',
);
]);
expect(mockHistoryManager.addItem).toHaveBeenCalledWith(
expect.objectContaining({
text: expect.stringContaining("Error adding '/dir2': Test error"),
@@ -171,6 +183,11 @@ describe('useIncludeDirsTrust', () => {
return undefined;
});
vi.mocked(mockWorkspaceContext.addDirectories).mockReturnValue({
added: ['/trusted'],
failed: [],
});
renderTestHook(true);
// Opens dialog for undefined trust dir
@@ -193,15 +210,16 @@ describe('useIncludeDirsTrust', () => {
pendingDirs,
);
mockIsPathTrusted.mockReturnValue(true);
vi.mocked(mockWorkspaceContext.addDirectories).mockReturnValue({
added: pendingDirs,
failed: [],
});
renderTestHook(true);
await waitFor(() => {
expect(mockWorkspaceContext.addDirectory).toHaveBeenCalledWith(
'/trusted1',
);
expect(mockWorkspaceContext.addDirectory).toHaveBeenCalledWith(
'/trusted2',
expect(mockWorkspaceContext.addDirectories).toHaveBeenCalledWith(
pendingDirs,
);
expect(mockSetCustomDialog).not.toHaveBeenCalled();
expect(mockConfig.clearPendingIncludeDirectories).toHaveBeenCalledTimes(

View File

@@ -5,9 +5,9 @@
*/
import { useEffect } from 'react';
import type { Config } from '@google/gemini-cli-core';
import { type Config } from '@google/gemini-cli-core';
import { loadTrustedFolders } from '../../config/trustedFolders.js';
import { expandHomeDir } from '../utils/directoryUtils.js';
import { expandHomeDir, batchAddDirectories } from '../utils/directoryUtils.js';
import {
debugLogger,
refreshServerHierarchicalMemory,
@@ -79,15 +79,10 @@ export function useIncludeDirsTrust(
const added: string[] = [];
const errors: string[] = [];
const workspaceContext = config.getWorkspaceContext();
for (const pathToAdd of pendingDirs) {
try {
workspaceContext.addDirectory(expandHomeDir(pathToAdd.trim()));
added.push(pathToAdd.trim());
} catch (e) {
const error = e as Error;
errors.push(`Error adding '${pathToAdd.trim()}': ${error.message}`);
}
}
const result = batchAddDirectories(workspaceContext, pendingDirs);
added.push(...result.added);
errors.push(...result.errors);
if (added.length > 0 || errors.length > 0) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
@@ -125,14 +120,10 @@ export function useIncludeDirsTrust(
}
const workspaceContext = config.getWorkspaceContext();
for (const pathToAdd of trustedDirs) {
try {
workspaceContext.addDirectory(expandHomeDir(pathToAdd));
added.push(pathToAdd);
} catch (e) {
const error = e as Error;
errors.push(`Error adding '${pathToAdd}': ${error.message}`);
}
if (trustedDirs.length > 0) {
const result = batchAddDirectories(workspaceContext, trustedDirs);
added.push(...result.added);
errors.push(...result.errors);
}
if (undefinedTrustDirs.length > 0) {