mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-18 18:11:02 -07:00
feat(config): Support telemetry configuration via environment variables (#9113)
This commit is contained in:
@@ -2311,3 +2311,146 @@ describe('parseArguments with positional prompt', () => {
|
||||
expect(argv.prompt).toBe('test prompt');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Telemetry configuration via environment variables', () => {
|
||||
it('should prioritize GEMINI_TELEMETRY_ENABLED over settings', async () => {
|
||||
vi.stubEnv('GEMINI_TELEMETRY_ENABLED', 'true');
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const settings: Settings = { telemetry: { enabled: false } };
|
||||
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||
expect(config.getTelemetryEnabled()).toBe(true);
|
||||
});
|
||||
|
||||
it('should prioritize GEMINI_TELEMETRY_TARGET over settings', async () => {
|
||||
vi.stubEnv('GEMINI_TELEMETRY_TARGET', 'gcp');
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const settings: Settings = { telemetry: { target: 'local' } };
|
||||
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||
expect(config.getTelemetryTarget()).toBe('gcp');
|
||||
});
|
||||
|
||||
it('should throw when GEMINI_TELEMETRY_TARGET is invalid', async () => {
|
||||
vi.stubEnv('GEMINI_TELEMETRY_TARGET', 'bogus');
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const settings: Settings = { telemetry: { target: 'gcp' } };
|
||||
await expect(
|
||||
loadCliConfig(settings, [], 'test-session', argv),
|
||||
).rejects.toThrow(
|
||||
/Invalid telemetry configuration: .*Invalid telemetry target/i,
|
||||
);
|
||||
vi.unstubAllEnvs();
|
||||
});
|
||||
|
||||
it('should prioritize GEMINI_TELEMETRY_OTLP_ENDPOINT over settings and default env var', async () => {
|
||||
vi.stubEnv('OTEL_EXPORTER_OTLP_ENDPOINT', 'http://default.env.com');
|
||||
vi.stubEnv('GEMINI_TELEMETRY_OTLP_ENDPOINT', 'http://gemini.env.com');
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const settings: Settings = {
|
||||
telemetry: { otlpEndpoint: 'http://settings.com' },
|
||||
};
|
||||
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||
expect(config.getTelemetryOtlpEndpoint()).toBe('http://gemini.env.com');
|
||||
});
|
||||
|
||||
it('should prioritize GEMINI_TELEMETRY_OTLP_PROTOCOL over settings', async () => {
|
||||
vi.stubEnv('GEMINI_TELEMETRY_OTLP_PROTOCOL', 'http');
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const settings: Settings = { telemetry: { otlpProtocol: 'grpc' } };
|
||||
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||
expect(config.getTelemetryOtlpProtocol()).toBe('http');
|
||||
});
|
||||
|
||||
it('should prioritize GEMINI_TELEMETRY_LOG_PROMPTS over settings', async () => {
|
||||
vi.stubEnv('GEMINI_TELEMETRY_LOG_PROMPTS', 'false');
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const settings: Settings = { telemetry: { logPrompts: true } };
|
||||
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||
expect(config.getTelemetryLogPromptsEnabled()).toBe(false);
|
||||
});
|
||||
|
||||
it('should prioritize GEMINI_TELEMETRY_OUTFILE over settings', async () => {
|
||||
vi.stubEnv('GEMINI_TELEMETRY_OUTFILE', '/gemini/env/telemetry.log');
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const settings: Settings = {
|
||||
telemetry: { outfile: '/settings/telemetry.log' },
|
||||
};
|
||||
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||
expect(config.getTelemetryOutfile()).toBe('/gemini/env/telemetry.log');
|
||||
});
|
||||
|
||||
it('should prioritize GEMINI_TELEMETRY_USE_COLLECTOR over settings', async () => {
|
||||
vi.stubEnv('GEMINI_TELEMETRY_USE_COLLECTOR', 'true');
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const settings: Settings = { telemetry: { useCollector: false } };
|
||||
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||
expect(config.getTelemetryUseCollector()).toBe(true);
|
||||
});
|
||||
|
||||
it('should use settings value when GEMINI_TELEMETRY_ENABLED is not set', async () => {
|
||||
vi.stubEnv('GEMINI_TELEMETRY_ENABLED', undefined);
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const settings: Settings = { telemetry: { enabled: true } };
|
||||
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||
expect(config.getTelemetryEnabled()).toBe(true);
|
||||
});
|
||||
|
||||
it('should use settings value when GEMINI_TELEMETRY_TARGET is not set', async () => {
|
||||
vi.stubEnv('GEMINI_TELEMETRY_TARGET', undefined);
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const settings: Settings = { telemetry: { target: 'local' } };
|
||||
const config = await loadCliConfig(settings, [], 'test-session', argv);
|
||||
expect(config.getTelemetryTarget()).toBe('local');
|
||||
});
|
||||
|
||||
it("should treat GEMINI_TELEMETRY_ENABLED='1' as true", async () => {
|
||||
vi.stubEnv('GEMINI_TELEMETRY_ENABLED', '1');
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const config = await loadCliConfig({}, [], 'test-session', argv);
|
||||
expect(config.getTelemetryEnabled()).toBe(true);
|
||||
});
|
||||
|
||||
it("should treat GEMINI_TELEMETRY_ENABLED='0' as false", async () => {
|
||||
vi.stubEnv('GEMINI_TELEMETRY_ENABLED', '0');
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const config = await loadCliConfig(
|
||||
{ telemetry: { enabled: true } },
|
||||
[],
|
||||
'test-session',
|
||||
argv,
|
||||
);
|
||||
expect(config.getTelemetryEnabled()).toBe(false);
|
||||
});
|
||||
|
||||
it("should treat GEMINI_TELEMETRY_LOG_PROMPTS='1' as true", async () => {
|
||||
vi.stubEnv('GEMINI_TELEMETRY_LOG_PROMPTS', '1');
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const config = await loadCliConfig({}, [], 'test-session', argv);
|
||||
expect(config.getTelemetryLogPromptsEnabled()).toBe(true);
|
||||
});
|
||||
|
||||
it("should treat GEMINI_TELEMETRY_LOG_PROMPTS='false' as false", async () => {
|
||||
vi.stubEnv('GEMINI_TELEMETRY_LOG_PROMPTS', 'false');
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const config = await loadCliConfig(
|
||||
{ telemetry: { logPrompts: true } },
|
||||
[],
|
||||
'test-session',
|
||||
argv,
|
||||
);
|
||||
expect(config.getTelemetryLogPromptsEnabled()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,7 +12,6 @@ import { hideBin } from 'yargs/helpers';
|
||||
import process from 'node:process';
|
||||
import { mcpCommand } from '../commands/mcp.js';
|
||||
import type {
|
||||
TelemetryTarget,
|
||||
FileFilteringOptions,
|
||||
MCPServerConfig,
|
||||
OutputFormat,
|
||||
@@ -32,6 +31,8 @@ import {
|
||||
ShellTool,
|
||||
EditTool,
|
||||
WriteFileTool,
|
||||
resolveTelemetrySettings,
|
||||
FatalConfigError,
|
||||
} from '@google/gemini-cli-core';
|
||||
import type { Settings } from './settings.js';
|
||||
|
||||
@@ -503,6 +504,22 @@ export async function loadCliConfig(
|
||||
approvalMode = ApprovalMode.DEFAULT;
|
||||
}
|
||||
|
||||
let telemetrySettings;
|
||||
try {
|
||||
telemetrySettings = await resolveTelemetrySettings({
|
||||
argv,
|
||||
env: process.env as unknown as Record<string, string | undefined>,
|
||||
settings: settings.telemetry,
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof FatalConfigError) {
|
||||
throw new FatalConfigError(
|
||||
`Invalid telemetry configuration: ${err.message}.`,
|
||||
);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
const policyEngineConfig = createPolicyEngineConfig(settings, approvalMode);
|
||||
|
||||
// Fix: If promptWords are provided, always use non-interactive mode
|
||||
@@ -608,23 +625,7 @@ export async function loadCliConfig(
|
||||
...settings.ui?.accessibility,
|
||||
screenReader,
|
||||
},
|
||||
telemetry: {
|
||||
enabled: argv.telemetry ?? settings.telemetry?.enabled,
|
||||
target: (argv.telemetryTarget ??
|
||||
settings.telemetry?.target) as TelemetryTarget,
|
||||
otlpEndpoint:
|
||||
argv.telemetryOtlpEndpoint ??
|
||||
process.env['OTEL_EXPORTER_OTLP_ENDPOINT'] ??
|
||||
settings.telemetry?.otlpEndpoint,
|
||||
otlpProtocol: (['grpc', 'http'] as const).find(
|
||||
(p) =>
|
||||
p ===
|
||||
(argv.telemetryOtlpProtocol ?? settings.telemetry?.otlpProtocol),
|
||||
),
|
||||
logPrompts: argv.telemetryLogPrompts ?? settings.telemetry?.logPrompts,
|
||||
outfile: argv.telemetryOutfile ?? settings.telemetry?.outfile,
|
||||
useCollector: settings.telemetry?.useCollector,
|
||||
},
|
||||
telemetry: telemetrySettings,
|
||||
usageStatisticsEnabled: settings.privacy?.usageStatisticsEnabled ?? true,
|
||||
fileFiltering: settings.context?.fileFiltering,
|
||||
checkpointing:
|
||||
|
||||
Reference in New Issue
Block a user