Skip to content

Commit 04953d6

Browse files
authored
Introduce system defaults (vs system overrides) (google-gemini#6724)
1 parent 1918f44 commit 04953d6

9 files changed

Lines changed: 241 additions & 26 deletions

File tree

docs/cli/configuration.md

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,29 @@ Gemini CLI offers several ways to configure its behavior, including environment
77
Configuration is applied in the following order of precedence (lower numbers are overridden by higher numbers):
88

99
1. **Default values:** Hardcoded defaults within the application.
10-
2. **User settings file:** Global settings for the current user.
11-
3. **Project settings file:** Project-specific settings.
12-
4. **System settings file:** System-wide settings.
13-
5. **Environment variables:** System-wide or session-specific variables, potentially loaded from `.env` files.
14-
6. **Command-line arguments:** Values passed when launching the CLI.
10+
2. **System defaults file:** System-wide default settings that can be overridden by other settings files.
11+
3. **User settings file:** Global settings for the current user.
12+
4. **Project settings file:** Project-specific settings.
13+
5. **System settings file:** System-wide settings that override all other settings files.
14+
6. **Environment variables:** System-wide or session-specific variables, potentially loaded from `.env` files.
15+
7. **Command-line arguments:** Values passed when launching the CLI.
1516

1617
## Settings files
1718

18-
Gemini CLI uses `settings.json` files for persistent configuration. There are three locations for these files:
19+
Gemini CLI uses JSON settings files for persistent configuration. There are four locations for these files:
1920

21+
- **System defaults file:**
22+
- **Location:** `/etc/gemini-cli/system-defaults.json` (Linux), `C:\ProgramData\gemini-cli\system-defaults.json` (Windows) or `/Library/Application Support/GeminiCli/system-defaults.json` (macOS). The path can be overridden using the `GEMINI_CLI_SYSTEM_DEFAULTS_PATH` environment variable.
23+
- **Scope:** Provides a base layer of system-wide default settings. These settings have the lowest precedence and are intended to be overridden by user, project, or system override settings.
2024
- **User settings file:**
2125
- **Location:** `~/.gemini/settings.json` (where `~` is your home directory).
22-
- **Scope:** Applies to all Gemini CLI sessions for the current user.
26+
- **Scope:** Applies to all Gemini CLI sessions for the current user. User settings override system defaults.
2327
- **Project settings file:**
2428
- **Location:** `.gemini/settings.json` within your project's root directory.
25-
- **Scope:** Applies only when running Gemini CLI from that specific project. Project settings override user settings.
29+
- **Scope:** Applies only when running Gemini CLI from that specific project. Project settings override user settings and system defaults.
2630
- **System settings file:**
2731
- **Location:** `/etc/gemini-cli/settings.json` (Linux), `C:\ProgramData\gemini-cli\settings.json` (Windows) or `/Library/Application Support/GeminiCli/settings.json` (macOS). The path can be overridden using the `GEMINI_CLI_SYSTEM_SETTINGS_PATH` environment variable.
28-
- **Scope:** Applies to all Gemini CLI sessions on the system, for all users. System settings override user and project settings. May be useful for system administrators at enterprises to have controls over users' Gemini CLI setups.
32+
- **Scope:** Applies to all Gemini CLI sessions on the system, for all users. System settings act as overrides, taking precedence over all other settings files. May be useful for system administrators at enterprises to have controls over users' Gemini CLI setups.
2933

3034
**Note on environment variables in settings:** String values within your `settings.json` files can reference environment variables using either `$VAR_NAME` or `${VAR_NAME}` syntax. These variables will be automatically resolved when the settings are loaded. For example, if you have an environment variable `MY_API_TOKEN`, you could use it in `settings.json` like this: `"apiKey": "$MY_API_TOKEN"`.
3135

docs/cli/enterprise.md

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,27 @@ This document outlines configuration patterns and best practices for deploying a
66
77
## Centralized Configuration: The System Settings File
88

9-
The most powerful tool for enterprise administration is the system-wide `settings.json` file. This file allows you to define a baseline configuration that applies to all users on a machine. For a complete overview of configuration options, see the [Configuration documentation](./configuration.md). Settings from system, user, and project-level `settings.json` files are merged together. For most settings, the system-wide configuration takes precedence, overriding any conflicting user or project-level settings. However, some settings, like `customThemes`, `mcpServers`, and `includeDirectories`, are merged from all configuration files, and if there are conflicting values (e.g., both workspace and system settings have a 'github' MCP server defined), the system value will take precedence.
9+
The most powerful tools for enterprise administration are the system-wide settings files. These files allow you to define a baseline configuration (`system-defaults.json`) and a set of overrides (`settings.json`) that apply to all users on a machine. For a complete overview of configuration options, see the [Configuration documentation](./configuration.md).
10+
11+
Settings are merged from four files. The precedence order for single-value settings (like `theme`) is:
12+
13+
1. System Defaults (`system-defaults.json`)
14+
2. User Settings (`~/.gemini/settings.json`)
15+
3. Workspace Settings (`<project>/.gemini/settings.json`)
16+
4. System Overrides (`settings.json`)
17+
18+
This means the System Overrides file has the final say. For settings that are arrays (`includeDirectories`) or objects (`mcpServers`), the values are merged.
1019

1120
**Example of Merging and Precedence:**
1221

1322
Here is how settings from different levels are combined.
1423

15-
- **System `settings.json`:**
24+
- **System Defaults `system-defaults.json`:**
1625

1726
```json
1827
{
19-
"theme": "system-enforced-theme",
20-
"mcpServers": {
21-
"corp-server": {
22-
"command": "/usr/local/bin/corp-server-prod"
23-
}
24-
},
25-
"includeDirectories": ["/etc/gemini-cli/global-context"]
28+
"theme": "default-corporate-theme",
29+
"includeDirectories": ["/etc/gemini-cli/common-context"]
2630
}
2731
```
2832

@@ -44,6 +48,7 @@ Here is how settings from different levels are combined.
4448
```
4549

4650
- **Workspace `settings.json` (`<project>/.gemini/settings.json`):**
51+
4752
```json
4853
{
4954
"theme": "project-specific-light-theme",
@@ -56,6 +61,19 @@ Here is how settings from different levels are combined.
5661
}
5762
```
5863

64+
- **System Overrides `settings.json`:**
65+
```json
66+
{
67+
"theme": "system-enforced-theme",
68+
"mcpServers": {
69+
"corp-server": {
70+
"command": "/usr/local/bin/corp-server-prod"
71+
}
72+
},
73+
"includeDirectories": ["/etc/gemini-cli/global-context"]
74+
}
75+
```
76+
5977
This results in the following merged configuration:
6078

6179
- **Final Merged Configuration:**
@@ -74,18 +92,19 @@ This results in the following merged configuration:
7492
}
7593
},
7694
"includeDirectories": [
77-
"/etc/gemini-cli/global-context",
95+
"/etc/gemini-cli/common-context",
7896
"~/gemini-context",
79-
"./project-context"
97+
"./project-context",
98+
"/etc/gemini-cli/global-context"
8099
]
81100
}
82101
```
83102

84103
**Why:**
85104

86-
- **`theme`**: The value from the system settings is used, overriding both user and workspace settings.
87-
- **`mcpServers`**: The objects are merged. The `corp-server` definition from the system settings takes precedence over the user's definition. The unique `user-tool` and `project-tool` are included.
88-
- **`includeDirectories`**: The arrays are concatenated in the order of System, User, and then Workspace.
105+
- **`theme`**: The value from the system overrides (`system-enforced-theme`) is used, as it has the highest precedence.
106+
- **`mcpServers`**: The objects are merged. The `corp-server` definition from the system overrides takes precedence over the user's definition. The unique `user-tool` and `project-tool` are included.
107+
- **`includeDirectories`**: The arrays are concatenated in the order of System Defaults, User, Workspace, and then System Overrides.
89108

90109
- **Location**:
91110
- **Linux**: `/etc/gemini-cli/settings.json`

packages/cli/src/config/settings.test.ts

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import {
5353
loadSettings,
5454
USER_SETTINGS_PATH, // This IS the mocked path.
5555
getSystemSettingsPath,
56+
getSystemDefaultsPath,
5657
SETTINGS_DIRECTORY_NAME, // This is from the original module, but used by the mock.
5758
SettingScope,
5859
} from './settings.js';
@@ -317,6 +318,68 @@ describe('Settings Loading and Merging', () => {
317318
});
318319
});
319320

321+
it('should merge all settings files with the correct precedence', () => {
322+
(mockFsExistsSync as Mock).mockReturnValue(true);
323+
const systemDefaultsContent = {
324+
theme: 'default-theme',
325+
sandbox: true,
326+
telemetry: true,
327+
includeDirectories: ['/system/defaults/dir'],
328+
};
329+
const userSettingsContent = {
330+
theme: 'user-theme',
331+
contextFileName: 'USER_CONTEXT.md',
332+
includeDirectories: ['/user/dir1', '/user/dir2'],
333+
};
334+
const workspaceSettingsContent = {
335+
sandbox: false,
336+
contextFileName: 'WORKSPACE_CONTEXT.md',
337+
includeDirectories: ['/workspace/dir'],
338+
};
339+
const systemSettingsContent = {
340+
theme: 'system-theme',
341+
telemetry: false,
342+
includeDirectories: ['/system/dir'],
343+
};
344+
345+
(fs.readFileSync as Mock).mockImplementation(
346+
(p: fs.PathOrFileDescriptor) => {
347+
if (p === getSystemDefaultsPath())
348+
return JSON.stringify(systemDefaultsContent);
349+
if (p === getSystemSettingsPath())
350+
return JSON.stringify(systemSettingsContent);
351+
if (p === USER_SETTINGS_PATH)
352+
return JSON.stringify(userSettingsContent);
353+
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
354+
return JSON.stringify(workspaceSettingsContent);
355+
return '';
356+
},
357+
);
358+
359+
const settings = loadSettings(MOCK_WORKSPACE_DIR);
360+
361+
expect(settings.systemDefaults.settings).toEqual(systemDefaultsContent);
362+
expect(settings.system.settings).toEqual(systemSettingsContent);
363+
expect(settings.user.settings).toEqual(userSettingsContent);
364+
expect(settings.workspace.settings).toEqual(workspaceSettingsContent);
365+
expect(settings.merged).toEqual({
366+
theme: 'system-theme',
367+
sandbox: false,
368+
telemetry: false,
369+
contextFileName: 'WORKSPACE_CONTEXT.md',
370+
customThemes: {},
371+
mcpServers: {},
372+
includeDirectories: [
373+
'/system/defaults/dir',
374+
'/user/dir1',
375+
'/user/dir2',
376+
'/workspace/dir',
377+
'/system/dir',
378+
],
379+
chatCompression: {},
380+
});
381+
});
382+
320383
it('should ignore folderTrust from workspace settings', () => {
321384
(mockFsExistsSync as Mock).mockReturnValue(true);
322385
const userSettingsContent = {
@@ -806,6 +869,9 @@ describe('Settings Loading and Merging', () => {
806869
const systemSettingsContent = {
807870
includeDirectories: ['/system/dir'],
808871
};
872+
const systemDefaultsContent = {
873+
includeDirectories: ['/system/defaults/dir'],
874+
};
809875
const userSettingsContent = {
810876
includeDirectories: ['/user/dir1', '/user/dir2'],
811877
};
@@ -817,6 +883,8 @@ describe('Settings Loading and Merging', () => {
817883
(p: fs.PathOrFileDescriptor) => {
818884
if (p === getSystemSettingsPath())
819885
return JSON.stringify(systemSettingsContent);
886+
if (p === getSystemDefaultsPath())
887+
return JSON.stringify(systemDefaultsContent);
820888
if (p === USER_SETTINGS_PATH)
821889
return JSON.stringify(userSettingsContent);
822890
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
@@ -828,10 +896,11 @@ describe('Settings Loading and Merging', () => {
828896
const settings = loadSettings(MOCK_WORKSPACE_DIR);
829897

830898
expect(settings.merged.includeDirectories).toEqual([
831-
'/system/dir',
899+
'/system/defaults/dir',
832900
'/user/dir1',
833901
'/user/dir2',
834902
'/workspace/dir',
903+
'/system/dir',
835904
]);
836905
});
837906

@@ -1290,6 +1359,11 @@ describe('Settings Loading and Merging', () => {
12901359

12911360
expect(loadedSettings.system.settings.theme).toBe('ocean');
12921361
expect(loadedSettings.merged.theme).toBe('ocean');
1362+
1363+
// SystemDefaults theme is overridden by user, workspace, and system themes
1364+
loadedSettings.setValue(SettingScope.SystemDefaults, 'theme', 'default');
1365+
expect(loadedSettings.systemDefaults.settings.theme).toBe('default');
1366+
expect(loadedSettings.merged.theme).toBe('ocean');
12931367
});
12941368
});
12951369

0 commit comments

Comments
 (0)