mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-06-13 12:57:12 -07:00
Fix bug where linked extensions can't be found
This commit is contained in:
@@ -438,6 +438,19 @@ Would you like to attempt to install via "git clone" instead?`,
|
||||
extensionIdentifier.toLowerCase(),
|
||||
);
|
||||
if (!extension) {
|
||||
const extensionDir = path.join(
|
||||
ExtensionStorage.getUserExtensionsDir(),
|
||||
extensionIdentifier,
|
||||
);
|
||||
if (fs.existsSync(extensionDir)) {
|
||||
await fs.promises.rm(extensionDir, { recursive: true, force: true });
|
||||
this.extensionEnablementManager.remove(extensionIdentifier);
|
||||
coreEvents.emitFeedback(
|
||||
'info',
|
||||
`Uninstalled broken extension '${extensionIdentifier}'.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
throw new Error(`Extension not found.`);
|
||||
}
|
||||
await this.unloadExtension(extension);
|
||||
@@ -515,25 +528,24 @@ Would you like to attempt to install via "git clone" instead?`,
|
||||
extensionDir: string,
|
||||
): Promise<GeminiCLIExtension | null> {
|
||||
this.loadedExtensions ??= [];
|
||||
if (!fs.statSync(extensionDir).isDirectory()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const installMetadata = loadInstallMetadata(extensionDir);
|
||||
let effectiveExtensionPath = extensionDir;
|
||||
if (
|
||||
(installMetadata?.type === 'git' ||
|
||||
installMetadata?.type === 'github-release') &&
|
||||
this.settings.security.blockGitExtensions
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (installMetadata?.type === 'link') {
|
||||
effectiveExtensionPath = installMetadata.source;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!fs.statSync(extensionDir).isDirectory()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const installMetadata = loadInstallMetadata(extensionDir);
|
||||
if (
|
||||
(installMetadata?.type === 'git' ||
|
||||
installMetadata?.type === 'github-release') &&
|
||||
this.settings.security.blockGitExtensions
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (installMetadata?.type === 'link') {
|
||||
effectiveExtensionPath = installMetadata.source;
|
||||
}
|
||||
let config = await this.loadExtensionConfig(effectiveExtensionPath);
|
||||
if (
|
||||
this.getExtensions().find((extension) => extension.name === config.name)
|
||||
|
||||
@@ -1769,6 +1769,39 @@ ${INSTALL_WARNING_MESSAGE}`,
|
||||
});
|
||||
|
||||
describe('uninstallExtension', () => {
|
||||
it('should uninstall a broken extension without crashing', async () => {
|
||||
const badExtDir = path.join(userExtensionsDir, 'broken-ext');
|
||||
fs.mkdirSync(badExtDir);
|
||||
// Malformed JSON to simulate a broken extension
|
||||
fs.writeFileSync(
|
||||
path.join(badExtDir, EXTENSIONS_CONFIG_FILENAME),
|
||||
'{ "name": "broken-ext"',
|
||||
);
|
||||
|
||||
// Attempt to load extensions. The broken one should be skipped.
|
||||
const consoleErrorSpy = vi
|
||||
.spyOn(console, 'error')
|
||||
.mockImplementation(() => {});
|
||||
await extensionManager.loadExtensions();
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining(
|
||||
`Warning: Skipping extension in ${badExtDir}: Failed to load extension config from ${badExtDir}/gemini-extension.json`,
|
||||
),
|
||||
);
|
||||
consoleErrorSpy.mockRestore();
|
||||
|
||||
// Ensure no extensions were loaded
|
||||
expect(extensionManager.getExtensions()).toHaveLength(0);
|
||||
|
||||
// Attempt to uninstall the broken extension by its name.
|
||||
await expect(
|
||||
extensionManager.uninstallExtension('broken-ext', false),
|
||||
).resolves.toBeUndefined(); // Should resolve, not throw an error
|
||||
|
||||
// Verify the directory is removed
|
||||
expect(fs.existsSync(badExtDir)).toBe(false);
|
||||
});
|
||||
|
||||
it('should uninstall an extension by name', async () => {
|
||||
const sourceExtDir = createExtension({
|
||||
extensionsDir: userExtensionsDir,
|
||||
|
||||
Reference in New Issue
Block a user