mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-16 09:01:17 -07:00
fix: handle missing local extension config and skip hooks when disabled (#14744)
This commit is contained in:
@@ -514,10 +514,13 @@ export class ExtensionManager extends ExtensionLoader {
|
||||
)
|
||||
.filter((contextFilePath) => fs.existsSync(contextFilePath));
|
||||
|
||||
const hooks = await this.loadExtensionHooks(effectiveExtensionPath, {
|
||||
extensionPath: effectiveExtensionPath,
|
||||
workspacePath: this.workspaceDir,
|
||||
});
|
||||
let hooks: { [K in HookEventName]?: HookDefinition[] } | undefined;
|
||||
if (this.settings.tools?.enableHooks) {
|
||||
hooks = await this.loadExtensionHooks(effectiveExtensionPath, {
|
||||
extensionPath: effectiveExtensionPath,
|
||||
workspacePath: this.workspaceDir,
|
||||
});
|
||||
}
|
||||
|
||||
const extension = {
|
||||
name: config.name,
|
||||
|
||||
@@ -749,6 +749,17 @@ describe('extension tests', () => {
|
||||
JSON.stringify(hooksConfig),
|
||||
);
|
||||
|
||||
const settings = loadSettings(tempWorkspaceDir).merged;
|
||||
if (!settings.tools) settings.tools = {};
|
||||
settings.tools.enableHooks = true;
|
||||
|
||||
extensionManager = new ExtensionManager({
|
||||
workspaceDir: tempWorkspaceDir,
|
||||
requestConsent: mockRequestConsent,
|
||||
requestSetting: mockPromptForSettings,
|
||||
settings,
|
||||
});
|
||||
|
||||
const extensions = await extensionManager.loadExtensions();
|
||||
expect(extensions).toHaveLength(1);
|
||||
const extension = extensions[0];
|
||||
@@ -760,6 +771,36 @@ describe('extension tests', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should not load hooks if enableHooks is false', async () => {
|
||||
const extDir = createExtension({
|
||||
extensionsDir: userExtensionsDir,
|
||||
name: 'hook-extension-disabled',
|
||||
version: '1.0.0',
|
||||
});
|
||||
|
||||
const hooksDir = path.join(extDir, 'hooks');
|
||||
fs.mkdirSync(hooksDir);
|
||||
fs.writeFileSync(
|
||||
path.join(hooksDir, 'hooks.json'),
|
||||
JSON.stringify({ hooks: { BeforeTool: [] } }),
|
||||
);
|
||||
|
||||
const settings = loadSettings(tempWorkspaceDir).merged;
|
||||
if (!settings.tools) settings.tools = {};
|
||||
settings.tools.enableHooks = false;
|
||||
|
||||
extensionManager = new ExtensionManager({
|
||||
workspaceDir: tempWorkspaceDir,
|
||||
requestConsent: mockRequestConsent,
|
||||
requestSetting: mockPromptForSettings,
|
||||
settings,
|
||||
});
|
||||
|
||||
const extensions = await extensionManager.loadExtensions();
|
||||
expect(extensions).toHaveLength(1);
|
||||
expect(extensions[0].hooks).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should warn about hooks during installation', async () => {
|
||||
const requestConsentSpy = vi.fn().mockResolvedValue(true);
|
||||
extensionManager.setRequestConsent(requestConsentSpy);
|
||||
|
||||
@@ -43,6 +43,7 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
||||
debugLogger: {
|
||||
error: vi.fn(),
|
||||
log: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -263,6 +264,25 @@ describe('github.ts', () => {
|
||||
ExtensionUpdateState.UP_TO_DATE,
|
||||
);
|
||||
});
|
||||
|
||||
it('should return NOT_UPDATABLE if local extension config cannot be loaded', async () => {
|
||||
vi.mocked(mockExtensionManager.loadExtensionConfig).mockImplementation(
|
||||
() => {
|
||||
throw new Error('Config not found');
|
||||
},
|
||||
);
|
||||
|
||||
const ext = {
|
||||
name: 'local-ext',
|
||||
version: '1.0.0',
|
||||
path: '/path/to/installed/ext',
|
||||
installMetadata: { type: 'local', source: '/path/to/source/ext' },
|
||||
} as unknown as GeminiCLIExtension;
|
||||
|
||||
expect(await checkForExtensionUpdate(ext, mockExtensionManager)).toBe(
|
||||
ExtensionUpdateState.NOT_UPDATABLE,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('downloadFromGitHubRelease', () => {
|
||||
|
||||
@@ -19,6 +19,7 @@ import * as path from 'node:path';
|
||||
import * as tar from 'tar';
|
||||
import extract from 'extract-zip';
|
||||
import { fetchJson, getGitHubToken } from './github_fetch.js';
|
||||
import type { ExtensionConfig } from '../extension.js';
|
||||
import type { ExtensionManager } from '../extension-manager.js';
|
||||
import { EXTENSIONS_CONFIG_FILENAME } from './variables.js';
|
||||
|
||||
@@ -172,14 +173,23 @@ export async function checkForExtensionUpdate(
|
||||
): Promise<ExtensionUpdateState> {
|
||||
const installMetadata = extension.installMetadata;
|
||||
if (installMetadata?.type === 'local') {
|
||||
const latestConfig = extensionManager.loadExtensionConfig(
|
||||
installMetadata.source,
|
||||
);
|
||||
let latestConfig: ExtensionConfig | undefined;
|
||||
try {
|
||||
latestConfig = extensionManager.loadExtensionConfig(
|
||||
installMetadata.source,
|
||||
);
|
||||
} catch (e) {
|
||||
debugLogger.warn(
|
||||
`Failed to check for update for local extension "${extension.name}". Could not load extension from source path: ${installMetadata.source}. Error: ${getErrorMessage(e)}`,
|
||||
);
|
||||
return ExtensionUpdateState.NOT_UPDATABLE;
|
||||
}
|
||||
|
||||
if (!latestConfig) {
|
||||
debugLogger.error(
|
||||
debugLogger.warn(
|
||||
`Failed to check for update for local extension "${extension.name}". Could not load extension from source path: ${installMetadata.source}`,
|
||||
);
|
||||
return ExtensionUpdateState.ERROR;
|
||||
return ExtensionUpdateState.NOT_UPDATABLE;
|
||||
}
|
||||
if (latestConfig.version !== extension.version) {
|
||||
return ExtensionUpdateState.UPDATE_AVAILABLE;
|
||||
|
||||
Reference in New Issue
Block a user