summaryrefslogtreecommitdiff
path: root/packages/cli/src/config
diff options
context:
space:
mode:
authorYuki Okita <[email protected]>2025-08-20 10:55:47 +0900
committerGitHub <[email protected]>2025-08-20 01:55:47 +0000
commit21c6480b65528a98ac0e1e3855f3c78c1f9b7cbe (patch)
tree5555ec429209e87e0c21483c9e5fddd53ac01dbc /packages/cli/src/config
parent1049d388451120587a8643a401fd71430a8cd5fe (diff)
Refac: Centralize storage file management (#4078)
Co-authored-by: Taylor Mullen <[email protected]>
Diffstat (limited to 'packages/cli/src/config')
-rw-r--r--packages/cli/src/config/config.test.ts62
-rw-r--r--packages/cli/src/config/extension.test.ts3
-rw-r--r--packages/cli/src/config/extension.ts10
-rw-r--r--packages/cli/src/config/settings.ts18
4 files changed, 51 insertions, 42 deletions
diff --git a/packages/cli/src/config/config.test.ts b/packages/cli/src/config/config.test.ts
index 727f6fe1..c9426379 100644
--- a/packages/cli/src/config/config.test.ts
+++ b/packages/cli/src/config/config.test.ts
@@ -6,7 +6,6 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import * as os from 'os';
-import * as fs from 'fs';
import * as path from 'path';
import { ShellTool, EditTool, WriteFileTool } from '@google/gemini-cli-core';
import { loadCliConfig, parseArguments } from './config.js';
@@ -19,6 +18,38 @@ vi.mock('./trustedFolders.js', () => ({
isWorkspaceTrusted: vi.fn(),
}));
+vi.mock('fs', async (importOriginal) => {
+ const actualFs = await importOriginal<typeof import('fs')>();
+ const pathMod = await import('path');
+ const mockHome = '/mock/home/user';
+ const MOCK_CWD1 = process.cwd();
+ const MOCK_CWD2 = pathMod.resolve(pathMod.sep, 'home', 'user', 'project');
+
+ const mockPaths = new Set([
+ MOCK_CWD1,
+ MOCK_CWD2,
+ pathMod.resolve(pathMod.sep, 'cli', 'path1'),
+ pathMod.resolve(pathMod.sep, 'settings', 'path1'),
+ pathMod.join(mockHome, 'settings', 'path2'),
+ pathMod.join(MOCK_CWD2, 'cli', 'path2'),
+ pathMod.join(MOCK_CWD2, 'settings', 'path3'),
+ ]);
+
+ return {
+ ...actualFs,
+ mkdirSync: vi.fn(),
+ writeFileSync: vi.fn(),
+ existsSync: vi.fn((p) => mockPaths.has(p.toString())),
+ statSync: vi.fn((p) => {
+ if (mockPaths.has(p.toString())) {
+ return { isDirectory: () => true } as unknown as import('fs').Stats;
+ }
+ return (actualFs as typeof import('fs')).statSync(p as unknown as string);
+ }),
+ realpathSync: vi.fn((p) => p),
+ };
+});
+
vi.mock('os', async (importOriginal) => {
const actualOs = await importOriginal<typeof os>();
return {
@@ -1441,35 +1472,6 @@ describe('loadCliConfig folderTrust', () => {
});
});
-vi.mock('fs', async () => {
- const actualFs = await vi.importActual<typeof fs>('fs');
- const MOCK_CWD1 = process.cwd();
- const MOCK_CWD2 = path.resolve(path.sep, 'home', 'user', 'project');
-
- const mockPaths = new Set([
- MOCK_CWD1,
- MOCK_CWD2,
- path.resolve(path.sep, 'cli', 'path1'),
- path.resolve(path.sep, 'settings', 'path1'),
- path.join(os.homedir(), 'settings', 'path2'),
- path.join(MOCK_CWD2, 'cli', 'path2'),
- path.join(MOCK_CWD2, 'settings', 'path3'),
- ]);
-
- return {
- ...actualFs,
- existsSync: vi.fn((p) => mockPaths.has(p.toString())),
- statSync: vi.fn((p) => {
- if (mockPaths.has(p.toString())) {
- return { isDirectory: () => true };
- }
- // Fallback for other paths if needed, though the test should be specific.
- return actualFs.statSync(p);
- }),
- realpathSync: vi.fn((p) => p),
- };
-});
-
describe('loadCliConfig with includeDirectories', () => {
const originalArgv = process.argv;
diff --git a/packages/cli/src/config/extension.test.ts b/packages/cli/src/config/extension.test.ts
index 85852bd7..ece36916 100644
--- a/packages/cli/src/config/extension.test.ts
+++ b/packages/cli/src/config/extension.test.ts
@@ -10,7 +10,6 @@ import * as os from 'os';
import * as path from 'path';
import {
EXTENSIONS_CONFIG_FILENAME,
- EXTENSIONS_DIRECTORY_NAME,
annotateActiveExtensions,
loadExtensions,
} from './extension.js';
@@ -23,6 +22,8 @@ vi.mock('os', async (importOriginal) => {
};
});
+const EXTENSIONS_DIRECTORY_NAME = path.join('.gemini', 'extensions');
+
describe('loadExtensions', () => {
let tempWorkspaceDir: string;
let tempHomeDir: string;
diff --git a/packages/cli/src/config/extension.ts b/packages/cli/src/config/extension.ts
index 1922f55a..46fd32fc 100644
--- a/packages/cli/src/config/extension.ts
+++ b/packages/cli/src/config/extension.ts
@@ -4,12 +4,15 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { MCPServerConfig, GeminiCLIExtension } from '@google/gemini-cli-core';
+import {
+ MCPServerConfig,
+ GeminiCLIExtension,
+ Storage,
+} from '@google/gemini-cli-core';
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
-export const EXTENSIONS_DIRECTORY_NAME = path.join('.gemini', 'extensions');
export const EXTENSIONS_CONFIG_FILENAME = 'gemini-extension.json';
export interface Extension {
@@ -43,7 +46,8 @@ export function loadExtensions(workspaceDir: string): Extension[] {
}
function loadExtensionsFromDir(dir: string): Extension[] {
- const extensionsDir = path.join(dir, EXTENSIONS_DIRECTORY_NAME);
+ const storage = new Storage(dir);
+ const extensionsDir = storage.getExtensionsDir();
if (!fs.existsSync(extensionsDir)) {
return [];
}
diff --git a/packages/cli/src/config/settings.ts b/packages/cli/src/config/settings.ts
index 414caf11..3df98d95 100644
--- a/packages/cli/src/config/settings.ts
+++ b/packages/cli/src/config/settings.ts
@@ -11,6 +11,7 @@ import * as dotenv from 'dotenv';
import {
GEMINI_CONFIG_DIR as GEMINI_DIR,
getErrorMessage,
+ Storage,
} from '@google/gemini-cli-core';
import stripJsonComments from 'strip-json-comments';
import { DefaultLight } from '../ui/themes/default-light.js';
@@ -20,8 +21,9 @@ import { Settings, MemoryImportFormat } from './settingsSchema.js';
export type { Settings, MemoryImportFormat };
export const SETTINGS_DIRECTORY_NAME = '.gemini';
-export const USER_SETTINGS_DIR = path.join(homedir(), SETTINGS_DIRECTORY_NAME);
-export const USER_SETTINGS_PATH = path.join(USER_SETTINGS_DIR, 'settings.json');
+
+export const USER_SETTINGS_PATH = Storage.getGlobalSettingsPath();
+export const USER_SETTINGS_DIR = path.dirname(USER_SETTINGS_PATH);
export const DEFAULT_EXCLUDED_ENV_VARS = ['DEBUG', 'DEBUG_MODE'];
export function getSystemSettingsPath(): string {
@@ -37,10 +39,6 @@ export function getSystemSettingsPath(): string {
}
}
-export function getWorkspaceSettingsPath(workspaceDir: string): string {
- return path.join(workspaceDir, SETTINGS_DIRECTORY_NAME, 'settings.json');
-}
-
export type { DnsResolutionOrder } from './settingsSchema.js';
export enum SettingScope {
@@ -269,7 +267,9 @@ export function loadEnvironment(settings?: Settings): void {
// If no settings provided, try to load workspace settings for exclusions
let resolvedSettings = settings;
if (!resolvedSettings) {
- const workspaceSettingsPath = getWorkspaceSettingsPath(process.cwd());
+ const workspaceSettingsPath = new Storage(
+ process.cwd(),
+ ).getWorkspaceSettingsPath();
try {
if (fs.existsSync(workspaceSettingsPath)) {
const workspaceContent = fs.readFileSync(
@@ -342,7 +342,9 @@ export function loadSettings(workspaceDir: string): LoadedSettings {
// We expect homedir to always exist and be resolvable.
const realHomeDir = fs.realpathSync(resolvedHomeDir);
- const workspaceSettingsPath = getWorkspaceSettingsPath(workspaceDir);
+ const workspaceSettingsPath = new Storage(
+ workspaceDir,
+ ).getWorkspaceSettingsPath();
// Load system settings
try {