feat(cli): disable folder trust in headless mode (#18407)

This commit is contained in:
Gal Zahavi
2026-02-09 15:46:49 -08:00
committed by GitHub
parent 80057c5208
commit bce1caefd0
14 changed files with 587 additions and 48 deletions
+81 -2
View File
@@ -141,6 +141,22 @@ vi.mock('@google/gemini-cli-core', async () => {
defaultDecision: ServerConfig.PolicyDecision.ASK_USER,
approvalMode: ServerConfig.ApprovalMode.DEFAULT,
})),
isHeadlessMode: vi.fn((opts) => {
if (process.env['VITEST'] === 'true') {
return (
!!opts?.prompt ||
(!!process.stdin && !process.stdin.isTTY) ||
(!!process.stdout && !process.stdout.isTTY)
);
}
return (
!!opts?.prompt ||
process.env['CI'] === 'true' ||
process.env['GITHUB_ACTIONS'] === 'true' ||
(!!process.stdin && !process.stdin.isTTY) ||
(!!process.stdout && !process.stdout.isTTY)
);
}),
};
});
@@ -154,6 +170,8 @@ vi.mock('./extension-manager.js', () => {
// Global setup to ensure clean environment for all tests in this file
const originalArgv = process.argv;
const originalGeminiModel = process.env['GEMINI_MODEL'];
const originalStdoutIsTTY = process.stdout.isTTY;
const originalStdinIsTTY = process.stdin.isTTY;
beforeEach(() => {
delete process.env['GEMINI_MODEL'];
@@ -162,6 +180,18 @@ beforeEach(() => {
ExtensionManager.prototype.loadExtensions = vi
.fn()
.mockResolvedValue(undefined);
// Default to interactive mode for tests unless otherwise specified
Object.defineProperty(process.stdout, 'isTTY', {
value: true,
configurable: true,
writable: true,
});
Object.defineProperty(process.stdin, 'isTTY', {
value: true,
configurable: true,
writable: true,
});
});
afterEach(() => {
@@ -171,6 +201,16 @@ afterEach(() => {
} else {
delete process.env['GEMINI_MODEL'];
}
Object.defineProperty(process.stdout, 'isTTY', {
value: originalStdoutIsTTY,
configurable: true,
writable: true,
});
Object.defineProperty(process.stdin, 'isTTY', {
value: originalStdinIsTTY,
configurable: true,
writable: true,
});
});
describe('parseArguments', () => {
@@ -249,6 +289,16 @@ describe('parseArguments', () => {
});
describe('positional arguments and @commands', () => {
beforeEach(() => {
// Default to headless mode for these tests as they mostly expect one-shot behavior
process.stdin.isTTY = false;
Object.defineProperty(process.stdout, 'isTTY', {
value: false,
configurable: true,
writable: true,
});
});
it.each([
{
description:
@@ -379,8 +429,12 @@ describe('parseArguments', () => {
);
it('should include a startup message when converting positional query to interactive prompt', async () => {
const originalIsTTY = process.stdin.isTTY;
process.stdin.isTTY = true;
Object.defineProperty(process.stdout, 'isTTY', {
value: true,
configurable: true,
writable: true,
});
process.argv = ['node', 'script.js', 'hello'];
try {
@@ -389,7 +443,7 @@ describe('parseArguments', () => {
'Positional arguments now default to interactive mode. To run in non-interactive mode, use the --prompt (-p) flag.',
);
} finally {
process.stdin.isTTY = originalIsTTY;
// beforeEach handles resetting
}
});
});
@@ -1732,14 +1786,29 @@ describe('loadCliConfig model selection', () => {
});
describe('loadCliConfig folderTrust', () => {
let originalVitest: string | undefined;
let originalIntegrationTest: string | undefined;
beforeEach(() => {
vi.resetAllMocks();
vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
vi.stubEnv('GEMINI_API_KEY', 'test-api-key');
vi.spyOn(ExtensionManager.prototype, 'getExtensions').mockReturnValue([]);
originalVitest = process.env['VITEST'];
originalIntegrationTest = process.env['GEMINI_CLI_INTEGRATION_TEST'];
delete process.env['VITEST'];
delete process.env['GEMINI_CLI_INTEGRATION_TEST'];
});
afterEach(() => {
if (originalVitest !== undefined) {
process.env['VITEST'] = originalVitest;
}
if (originalIntegrationTest !== undefined) {
process.env['GEMINI_CLI_INTEGRATION_TEST'] = originalIntegrationTest;
}
vi.unstubAllEnvs();
vi.restoreAllMocks();
});
@@ -2779,6 +2848,16 @@ describe('Output format', () => {
describe('parseArguments with positional prompt', () => {
const originalArgv = process.argv;
beforeEach(() => {
// Default to headless mode for these tests as they mostly expect one-shot behavior
process.stdin.isTTY = false;
Object.defineProperty(process.stdout, 'isTTY', {
value: false,
configurable: true,
writable: true,
});
});
afterEach(() => {
process.argv = originalArgv;
});