mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-26 04:54:25 -07:00
Re-request consent if necessary when updating extensions (#9517)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
@@ -16,8 +16,10 @@ import {
|
||||
enableExtension,
|
||||
installExtension,
|
||||
loadExtension,
|
||||
loadExtensionConfig,
|
||||
loadExtensions,
|
||||
performWorkspaceExtensionMigration,
|
||||
requestConsentNonInteractive,
|
||||
uninstallExtension,
|
||||
type Extension,
|
||||
} from './extension.js';
|
||||
@@ -258,10 +260,13 @@ describe('extension tests', () => {
|
||||
});
|
||||
fs.writeFileSync(path.join(sourceExtDir, 'context.md'), 'linked context');
|
||||
|
||||
const extensionName = await installExtension({
|
||||
source: sourceExtDir,
|
||||
type: 'link',
|
||||
});
|
||||
const extensionName = await installExtension(
|
||||
{
|
||||
source: sourceExtDir,
|
||||
type: 'link',
|
||||
},
|
||||
async (_) => true,
|
||||
);
|
||||
|
||||
expect(extensionName).toEqual('my-linked-extension');
|
||||
const extensions = loadExtensions();
|
||||
@@ -627,7 +632,10 @@ describe('extension tests', () => {
|
||||
const targetExtDir = path.join(userExtensionsDir, 'my-local-extension');
|
||||
const metadataPath = path.join(targetExtDir, INSTALL_METADATA_FILENAME);
|
||||
|
||||
await installExtension({ source: sourceExtDir, type: 'local' });
|
||||
await installExtension(
|
||||
{ source: sourceExtDir, type: 'local' },
|
||||
async (_) => true,
|
||||
);
|
||||
|
||||
expect(fs.existsSync(targetExtDir)).toBe(true);
|
||||
expect(fs.existsSync(metadataPath)).toBe(true);
|
||||
@@ -645,9 +653,15 @@ describe('extension tests', () => {
|
||||
name: 'my-local-extension',
|
||||
version: '1.0.0',
|
||||
});
|
||||
await installExtension({ source: sourceExtDir, type: 'local' });
|
||||
await installExtension(
|
||||
{ source: sourceExtDir, type: 'local' },
|
||||
async (_) => true,
|
||||
);
|
||||
await expect(
|
||||
installExtension({ source: sourceExtDir, type: 'local' }),
|
||||
installExtension(
|
||||
{ source: sourceExtDir, type: 'local' },
|
||||
async (_) => true,
|
||||
),
|
||||
).rejects.toThrow(
|
||||
'Extension "my-local-extension" is already installed. Please uninstall it first.',
|
||||
);
|
||||
@@ -659,7 +673,10 @@ describe('extension tests', () => {
|
||||
const configPath = path.join(sourceExtDir, EXTENSIONS_CONFIG_FILENAME);
|
||||
|
||||
await expect(
|
||||
installExtension({ source: sourceExtDir, type: 'local' }),
|
||||
installExtension(
|
||||
{ source: sourceExtDir, type: 'local' },
|
||||
async (_) => true,
|
||||
),
|
||||
).rejects.toThrow(`Configuration file not found at ${configPath}`);
|
||||
|
||||
const targetExtDir = path.join(userExtensionsDir, 'bad-extension');
|
||||
@@ -673,7 +690,10 @@ describe('extension tests', () => {
|
||||
fs.writeFileSync(configPath, '{ "name": "bad-json", "version": "1.0.0"'); // Malformed JSON
|
||||
|
||||
await expect(
|
||||
installExtension({ source: sourceExtDir, type: 'local' }),
|
||||
installExtension(
|
||||
{ source: sourceExtDir, type: 'local' },
|
||||
async (_) => true,
|
||||
),
|
||||
).rejects.toThrow(
|
||||
new RegExp(
|
||||
`^Failed to load extension config from ${configPath.replace(
|
||||
@@ -695,7 +715,10 @@ describe('extension tests', () => {
|
||||
fs.writeFileSync(configPath, JSON.stringify({ version: '1.0.0' }));
|
||||
|
||||
await expect(
|
||||
installExtension({ source: sourceExtDir, type: 'local' }),
|
||||
installExtension(
|
||||
{ source: sourceExtDir, type: 'local' },
|
||||
async (_) => true,
|
||||
),
|
||||
).rejects.toThrow(
|
||||
`Invalid configuration in ${configPath}: missing "name"`,
|
||||
);
|
||||
@@ -718,7 +741,10 @@ describe('extension tests', () => {
|
||||
});
|
||||
mockGit.getRemotes.mockResolvedValue([{ name: 'origin' }]);
|
||||
|
||||
await installExtension({ source: gitUrl, type: 'git' });
|
||||
await installExtension(
|
||||
{ source: gitUrl, type: 'git' },
|
||||
async (_) => true,
|
||||
);
|
||||
|
||||
expect(fs.existsSync(targetExtDir)).toBe(true);
|
||||
expect(fs.existsSync(metadataPath)).toBe(true);
|
||||
@@ -740,7 +766,10 @@ describe('extension tests', () => {
|
||||
const metadataPath = path.join(targetExtDir, INSTALL_METADATA_FILENAME);
|
||||
const configPath = path.join(targetExtDir, EXTENSIONS_CONFIG_FILENAME);
|
||||
|
||||
await installExtension({ source: sourceExtDir, type: 'link' });
|
||||
await installExtension(
|
||||
{ source: sourceExtDir, type: 'link' },
|
||||
async (_) => true,
|
||||
);
|
||||
|
||||
expect(fs.existsSync(targetExtDir)).toBe(true);
|
||||
expect(fs.existsSync(metadataPath)).toBe(true);
|
||||
@@ -762,7 +791,10 @@ describe('extension tests', () => {
|
||||
version: '1.0.0',
|
||||
});
|
||||
|
||||
await installExtension({ source: sourceExtDir, type: 'local' });
|
||||
await installExtension(
|
||||
{ source: sourceExtDir, type: 'local' },
|
||||
async (_) => true,
|
||||
);
|
||||
|
||||
expect(mockLogExtensionInstallEvent).toHaveBeenCalled();
|
||||
});
|
||||
@@ -789,7 +821,10 @@ describe('extension tests', () => {
|
||||
mockQuestion.mockImplementation((_query, callback) => callback('y'));
|
||||
|
||||
await expect(
|
||||
installExtension({ source: sourceExtDir, type: 'local' }, true),
|
||||
installExtension(
|
||||
{ source: sourceExtDir, type: 'local' },
|
||||
requestConsentNonInteractive,
|
||||
),
|
||||
).resolves.toBe('my-local-extension');
|
||||
|
||||
expect(consoleInfoSpy).toHaveBeenCalledWith(
|
||||
@@ -817,7 +852,10 @@ This extension will run the following MCP servers:
|
||||
mockQuestion.mockImplementation((_query, callback) => callback('y'));
|
||||
|
||||
await expect(
|
||||
installExtension({ source: sourceExtDir, type: 'local' }, true),
|
||||
installExtension(
|
||||
{ source: sourceExtDir, type: 'local' },
|
||||
requestConsentNonInteractive,
|
||||
),
|
||||
).resolves.toBe('my-local-extension');
|
||||
|
||||
expect(mockQuestion).toHaveBeenCalledWith(
|
||||
@@ -842,8 +880,11 @@ This extension will run the following MCP servers:
|
||||
mockQuestion.mockImplementation((_query, callback) => callback('n'));
|
||||
|
||||
await expect(
|
||||
installExtension({ source: sourceExtDir, type: 'local' }, true),
|
||||
).rejects.toThrow('Installation cancelled by user.');
|
||||
installExtension(
|
||||
{ source: sourceExtDir, type: 'local' },
|
||||
requestConsentNonInteractive,
|
||||
),
|
||||
).rejects.toThrow('Installation cancelled.');
|
||||
|
||||
expect(mockQuestion).toHaveBeenCalledWith(
|
||||
expect.stringContaining('Do you want to continue? [Y/n]: '),
|
||||
@@ -860,11 +901,14 @@ This extension will run the following MCP servers:
|
||||
const targetExtDir = path.join(userExtensionsDir, 'my-local-extension');
|
||||
const metadataPath = path.join(targetExtDir, INSTALL_METADATA_FILENAME);
|
||||
|
||||
await installExtension({
|
||||
source: sourceExtDir,
|
||||
type: 'local',
|
||||
autoUpdate: true,
|
||||
});
|
||||
await installExtension(
|
||||
{
|
||||
source: sourceExtDir,
|
||||
type: 'local',
|
||||
autoUpdate: true,
|
||||
},
|
||||
async (_) => true,
|
||||
);
|
||||
|
||||
expect(fs.existsSync(targetExtDir)).toBe(true);
|
||||
expect(fs.existsSync(metadataPath)).toBe(true);
|
||||
@@ -890,9 +934,22 @@ This extension will run the following MCP servers:
|
||||
},
|
||||
});
|
||||
|
||||
const mockRequestConsent = vi.fn();
|
||||
|
||||
await expect(
|
||||
installExtension({ source: sourceExtDir, type: 'local' }, false),
|
||||
installExtension(
|
||||
{ source: sourceExtDir, type: 'local' },
|
||||
mockRequestConsent,
|
||||
process.cwd(),
|
||||
// Provide its own existing config as the previous config.
|
||||
await loadExtensionConfig({
|
||||
extensionDir: sourceExtDir,
|
||||
workspaceDir: process.cwd(),
|
||||
}),
|
||||
),
|
||||
).resolves.toBe('my-local-extension');
|
||||
|
||||
expect(mockRequestConsent).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1028,12 +1085,15 @@ This extension will run the following MCP servers:
|
||||
version: '1.0.0',
|
||||
});
|
||||
|
||||
await performWorkspaceExtensionMigration([
|
||||
loadExtension({
|
||||
extensionDir: ext1Path,
|
||||
workspaceDir: tempWorkspaceDir,
|
||||
})!,
|
||||
]);
|
||||
await performWorkspaceExtensionMigration(
|
||||
[
|
||||
loadExtension({
|
||||
extensionDir: ext1Path,
|
||||
workspaceDir: tempWorkspaceDir,
|
||||
})!,
|
||||
],
|
||||
async (_) => true,
|
||||
);
|
||||
|
||||
const userExtensionsDir = path.join(
|
||||
tempHomeDir,
|
||||
@@ -1051,12 +1111,15 @@ This extension will run the following MCP servers:
|
||||
version: '1.0.0',
|
||||
});
|
||||
|
||||
await performWorkspaceExtensionMigration([
|
||||
loadExtension({
|
||||
extensionDir: ext1Path,
|
||||
workspaceDir: tempWorkspaceDir,
|
||||
})!,
|
||||
]);
|
||||
await performWorkspaceExtensionMigration(
|
||||
[
|
||||
loadExtension({
|
||||
extensionDir: ext1Path,
|
||||
workspaceDir: tempWorkspaceDir,
|
||||
})!,
|
||||
],
|
||||
async (_) => true,
|
||||
);
|
||||
const extensions = loadExtensions();
|
||||
|
||||
expect(extensions).toEqual([]);
|
||||
@@ -1084,8 +1147,10 @@ This extension will run the following MCP servers:
|
||||
workspaceDir: tempWorkspaceDir,
|
||||
})!,
|
||||
];
|
||||
const failed =
|
||||
await performWorkspaceExtensionMigration(extensionsToMigrate);
|
||||
const failed = await performWorkspaceExtensionMigration(
|
||||
extensionsToMigrate,
|
||||
async (_) => true,
|
||||
);
|
||||
|
||||
expect(failed).toEqual([]);
|
||||
|
||||
@@ -1126,7 +1191,10 @@ This extension will run the following MCP servers:
|
||||
},
|
||||
];
|
||||
|
||||
const failed = await performWorkspaceExtensionMigration(extensions);
|
||||
const failed = await performWorkspaceExtensionMigration(
|
||||
extensions,
|
||||
async (_) => true,
|
||||
);
|
||||
expect(failed).toEqual(['ext2']);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user