fix(cli): Prevent stdout/stderr patching for extension commands (#13600)

Co-authored-by: jacob314 <jacob314@gmail.com>
This commit is contained in:
christine betts
2025-11-21 21:08:06 -05:00
committed by GitHub
parent 5e218a5630
commit bdf80ea7c0
30 changed files with 83 additions and 14 deletions

View File

@@ -56,6 +56,9 @@ vi.mock('../../config/extensions/consent.js', () => ({
vi.mock('../../config/extensions/extensionSettings.js', () => ({
promptForSetting: vi.fn(),
}));
vi.mock('../utils.js', () => ({
exitCli: vi.fn(),
}));
describe('extensions disable command', () => {
const mockLoadSettings = vi.mocked(loadSettings);

View File

@@ -11,6 +11,7 @@ import { debugLogger } from '@google/gemini-cli-core';
import { ExtensionManager } from '../../config/extension-manager.js';
import { requestConsentNonInteractive } from '../../config/extensions/consent.js';
import { promptForSetting } from '../../config/extensions/extensionSettings.js';
import { exitCli } from '../utils.js';
interface DisableArgs {
name: string;
@@ -81,5 +82,6 @@ export const disableCommand: CommandModule = {
name: argv['name'] as string,
scope: argv['scope'] as string,
});
await exitCli();
},
};

View File

@@ -58,6 +58,9 @@ vi.mock('../../config/extension-manager.js');
vi.mock('../../config/settings.js');
vi.mock('../../config/extensions/consent.js');
vi.mock('../../config/extensions/extensionSettings.js');
vi.mock('../utils.js', () => ({
exitCli: vi.fn(),
}));
describe('extensions enable command', () => {
const mockLoadSettings = vi.mocked(loadSettings);

View File

@@ -14,6 +14,7 @@ import {
getErrorMessage,
} from '@google/gemini-cli-core';
import { promptForSetting } from '../../config/extensions/extensionSettings.js';
import { exitCli } from '../utils.js';
interface EnableArgs {
name: string;
@@ -86,5 +87,6 @@ export const enableCommand: CommandModule = {
name: argv['name'] as string,
scope: argv['scope'] as string,
});
await exitCli();
},
};

View File

@@ -48,6 +48,10 @@ vi.mock('node:fs/promises', () => ({
},
}));
vi.mock('../utils.js', () => ({
exitCli: vi.fn(),
}));
describe('extensions install command', () => {
it('should fail if no source is provided', () => {
const validationParser = yargs([]).command(installCommand).fail(false);

View File

@@ -18,6 +18,7 @@ import {
import { ExtensionManager } from '../../config/extension-manager.js';
import { loadSettings } from '../../config/settings.js';
import { promptForSetting } from '../../config/extensions/extensionSettings.js';
import { exitCli } from '../utils.js';
interface InstallArgs {
source: string;
@@ -130,5 +131,6 @@ export const installCommand: CommandModule = {
allowPreRelease: argv['pre-release'] as boolean | undefined,
consent: argv['consent'] as boolean | undefined,
});
await exitCli();
},
};

View File

@@ -52,6 +52,9 @@ vi.mock('../../config/extensions/consent.js', () => ({
vi.mock('../../config/extensions/extensionSettings.js', () => ({
promptForSetting: vi.fn(),
}));
vi.mock('../utils.js', () => ({
exitCli: vi.fn(),
}));
describe('extensions link command', () => {
const mockLoadSettings = vi.mocked(loadSettings);

View File

@@ -15,6 +15,7 @@ import { requestConsentNonInteractive } from '../../config/extensions/consent.js
import { ExtensionManager } from '../../config/extension-manager.js';
import { loadSettings } from '../../config/settings.js';
import { promptForSetting } from '../../config/extensions/extensionSettings.js';
import { exitCli } from '../utils.js';
interface InstallArgs {
path: string;
@@ -60,5 +61,6 @@ export const linkCommand: CommandModule = {
await handleLink({
path: argv['path'] as string,
});
await exitCli();
},
};

View File

@@ -44,6 +44,9 @@ vi.mock('../../config/extensions/consent.js', () => ({
vi.mock('../../config/extensions/extensionSettings.js', () => ({
promptForSetting: vi.fn(),
}));
vi.mock('../utils.js', () => ({
exitCli: vi.fn(),
}));
describe('extensions list command', () => {
const mockLoadSettings = vi.mocked(loadSettings);

View File

@@ -11,6 +11,7 @@ import { ExtensionManager } from '../../config/extension-manager.js';
import { requestConsentNonInteractive } from '../../config/extensions/consent.js';
import { loadSettings } from '../../config/settings.js';
import { promptForSetting } from '../../config/extensions/extensionSettings.js';
import { exitCli } from '../utils.js';
export async function handleList() {
try {
@@ -45,5 +46,6 @@ export const listCommand: CommandModule = {
builder: (yargs) => yargs,
handler: async () => {
await handleList();
await exitCli();
},
};

View File

@@ -11,6 +11,9 @@ import * as fsPromises from 'node:fs/promises';
import path from 'node:path';
vi.mock('node:fs/promises');
vi.mock('../utils.js', () => ({
exitCli: vi.fn(),
}));
const mockedFs = vi.mocked(fsPromises);

View File

@@ -9,6 +9,7 @@ import { join, dirname, basename } from 'node:path';
import type { CommandModule } from 'yargs';
import { fileURLToPath } from 'node:url';
import { debugLogger } from '@google/gemini-cli-core';
import { exitCli } from '../utils.js';
interface NewArgs {
path: string;
@@ -100,5 +101,6 @@ export const newCommand: CommandModule = {
path: args['path'] as string,
template: args['template'] as string | undefined,
});
await exitCli();
},
};

View File

@@ -75,6 +75,9 @@ vi.mock('../../config/extensions/consent.js', () => ({
vi.mock('../../config/extensions/extensionSettings.js', () => ({
promptForSetting: vi.fn(),
}));
vi.mock('../utils.js', () => ({
exitCli: vi.fn(),
}));
describe('extensions uninstall command', () => {
const mockLoadSettings = vi.mocked(loadSettings);

View File

@@ -11,6 +11,7 @@ import { requestConsentNonInteractive } from '../../config/extensions/consent.js
import { ExtensionManager } from '../../config/extension-manager.js';
import { loadSettings } from '../../config/settings.js';
import { promptForSetting } from '../../config/extensions/extensionSettings.js';
import { exitCli } from '../utils.js';
interface UninstallArgs {
names: string[]; // can be extension names or source URLs.
@@ -72,5 +73,6 @@ export const uninstallCommand: CommandModule = {
await handleUninstall({
names: argv['names'] as string[],
});
await exitCli();
},
};

View File

@@ -56,6 +56,9 @@ vi.mock('../../config/extensions/consent.js', () => ({
vi.mock('../../config/extensions/extensionSettings.js', () => ({
promptForSetting: vi.fn(),
}));
vi.mock('../utils.js', () => ({
exitCli: vi.fn(),
}));
describe('extensions update command', () => {
const mockLoadSettings = vi.mocked(loadSettings);

View File

@@ -19,6 +19,7 @@ import { ExtensionManager } from '../../config/extension-manager.js';
import { requestConsentNonInteractive } from '../../config/extensions/consent.js';
import { loadSettings } from '../../config/settings.js';
import { promptForSetting } from '../../config/extensions/extensionSettings.js';
import { exitCli } from '../utils.js';
interface UpdateArgs {
name?: string;
@@ -144,5 +145,6 @@ export const updateCommand: CommandModule = {
name: argv['name'] as string | undefined,
all: argv['all'] as boolean | undefined,
});
await exitCli();
},
};

View File

@@ -13,6 +13,10 @@ import path from 'node:path';
import * as os from 'node:os';
import { debugLogger } from '@google/gemini-cli-core';
vi.mock('../utils.js', () => ({
exitCli: vi.fn(),
}));
describe('extensions validate command', () => {
it('should fail if no path is provided', () => {
const validationParser = yargs([]).command(validateCommand).fail(false);

View File

@@ -15,6 +15,7 @@ import { ExtensionManager } from '../../config/extension-manager.js';
import { requestConsentNonInteractive } from '../../config/extensions/consent.js';
import { promptForSetting } from '../../config/extensions/extensionSettings.js';
import { loadSettings } from '../../config/settings.js';
import { exitCli } from '../utils.js';
interface ValidateArgs {
path: string;
@@ -101,5 +102,6 @@ export const validateCommand: CommandModule = {
await handleValidate({
path: args['path'] as string,
});
await exitCli();
},
};