mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-16 00:51:25 -07:00
Reinstate support for updating locally-installed extensions (#8833)
This commit is contained in:
@@ -20,7 +20,6 @@ interface InstallArgs {
|
||||
export async function handleInstall(args: InstallArgs) {
|
||||
try {
|
||||
let installMetadata: ExtensionInstallMetadata;
|
||||
|
||||
if (args.source) {
|
||||
const { source } = args;
|
||||
if (
|
||||
|
||||
@@ -35,28 +35,7 @@ export async function handleUpdate(args: UpdateArgs) {
|
||||
allExtensions.map((e) => e.config.name),
|
||||
workingDir,
|
||||
);
|
||||
|
||||
if (args.all) {
|
||||
try {
|
||||
let updateInfos = await updateAllUpdatableExtensions(
|
||||
workingDir,
|
||||
extensions,
|
||||
await checkForAllExtensionUpdates(extensions, new Map(), (_) => {}),
|
||||
() => {},
|
||||
);
|
||||
updateInfos = updateInfos.filter(
|
||||
(info) => info.originalVersion !== info.updatedVersion,
|
||||
);
|
||||
if (updateInfos.length === 0) {
|
||||
console.log('No extensions to update.');
|
||||
return;
|
||||
}
|
||||
console.log(updateInfos.map((info) => updateOutput(info)).join('\n'));
|
||||
} catch (error) {
|
||||
console.error(getErrorMessage(error));
|
||||
}
|
||||
}
|
||||
if (args.name)
|
||||
if (args.name) {
|
||||
try {
|
||||
const extension = extensions.find(
|
||||
(extension) => extension.name === args.name,
|
||||
@@ -99,10 +78,31 @@ export async function handleUpdate(args: UpdateArgs) {
|
||||
} catch (error) {
|
||||
console.error(getErrorMessage(error));
|
||||
}
|
||||
}
|
||||
if (args.all) {
|
||||
try {
|
||||
let updateInfos = await updateAllUpdatableExtensions(
|
||||
workingDir,
|
||||
extensions,
|
||||
await checkForAllExtensionUpdates(extensions, new Map(), (_) => {}),
|
||||
() => {},
|
||||
);
|
||||
updateInfos = updateInfos.filter(
|
||||
(info) => info.originalVersion !== info.updatedVersion,
|
||||
);
|
||||
if (updateInfos.length === 0) {
|
||||
console.log('No extensions to update.');
|
||||
return;
|
||||
}
|
||||
console.log(updateInfos.map((info) => updateOutput(info)).join('\n'));
|
||||
} catch (error) {
|
||||
console.error(getErrorMessage(error));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const updateCommand: CommandModule = {
|
||||
command: 'update [--all] [name]',
|
||||
command: 'update [<name>] [--all]',
|
||||
describe:
|
||||
'Updates all extensions or a named extension to the latest version.',
|
||||
builder: (yargs) =>
|
||||
|
||||
@@ -128,7 +128,7 @@ describe('git extension helpers', () => {
|
||||
version: '1.0.0',
|
||||
isActive: true,
|
||||
installMetadata: {
|
||||
type: 'local',
|
||||
type: 'link',
|
||||
source: '',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -16,6 +16,7 @@ import * as https from 'node:https';
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import { execSync } from 'node:child_process';
|
||||
import { loadExtension } from '../extension.js';
|
||||
|
||||
function getGitHubToken(): string | undefined {
|
||||
return process.env['GITHUB_TOKEN'];
|
||||
@@ -115,9 +116,29 @@ async function fetchFromGithub(
|
||||
export async function checkForExtensionUpdate(
|
||||
extension: GeminiCLIExtension,
|
||||
setExtensionUpdateState: (updateState: ExtensionUpdateState) => void,
|
||||
cwd: string = process.cwd(),
|
||||
): Promise<void> {
|
||||
setExtensionUpdateState(ExtensionUpdateState.CHECKING_FOR_UPDATES);
|
||||
const installMetadata = extension.installMetadata;
|
||||
if (installMetadata?.type === 'local') {
|
||||
const newExtension = loadExtension({
|
||||
extensionDir: installMetadata.source,
|
||||
workspaceDir: cwd,
|
||||
});
|
||||
if (!newExtension) {
|
||||
console.error(
|
||||
`Failed to check for update for local extension "${extension.name}". Could not load extension from source path: ${installMetadata.source}`,
|
||||
);
|
||||
setExtensionUpdateState(ExtensionUpdateState.ERROR);
|
||||
return;
|
||||
}
|
||||
if (newExtension.config.version !== extension.version) {
|
||||
setExtensionUpdateState(ExtensionUpdateState.UPDATE_AVAILABLE);
|
||||
return;
|
||||
}
|
||||
setExtensionUpdateState(ExtensionUpdateState.UP_TO_DATE);
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!installMetadata ||
|
||||
(installMetadata.type !== 'git' &&
|
||||
|
||||
@@ -341,17 +341,24 @@ describe('update tests', () => {
|
||||
expect(result).toBe(ExtensionUpdateState.UP_TO_DATE);
|
||||
});
|
||||
|
||||
it('should return NotUpdatable for a non-git extension', async () => {
|
||||
const extensionDir = createExtension({
|
||||
it('should return UpToDate for a local extension with no updates', async () => {
|
||||
const localExtensionSourcePath = path.join(tempHomeDir, 'local-source');
|
||||
const sourceExtensionDir = createExtension({
|
||||
extensionsDir: localExtensionSourcePath,
|
||||
name: 'my-local-ext',
|
||||
version: '1.0.0',
|
||||
});
|
||||
|
||||
const installedExtensionDir = createExtension({
|
||||
extensionsDir: userExtensionsDir,
|
||||
name: 'local-extension',
|
||||
version: '1.0.0',
|
||||
installMetadata: { source: '/local/path', type: 'local' },
|
||||
installMetadata: { source: sourceExtensionDir, type: 'local' },
|
||||
});
|
||||
const extension = annotateActiveExtensions(
|
||||
[
|
||||
loadExtension({
|
||||
extensionDir,
|
||||
extensionDir: installedExtensionDir,
|
||||
workspaceDir: tempWorkspaceDir,
|
||||
})!,
|
||||
],
|
||||
@@ -369,9 +376,51 @@ describe('update tests', () => {
|
||||
extensionState = newState;
|
||||
}
|
||||
},
|
||||
tempWorkspaceDir,
|
||||
);
|
||||
const result = results.get('local-extension');
|
||||
expect(result).toBe(ExtensionUpdateState.NOT_UPDATABLE);
|
||||
expect(result).toBe(ExtensionUpdateState.UP_TO_DATE);
|
||||
});
|
||||
|
||||
it('should return UpdateAvailable for a local extension with updates', async () => {
|
||||
const localExtensionSourcePath = path.join(tempHomeDir, 'local-source');
|
||||
const sourceExtensionDir = createExtension({
|
||||
extensionsDir: localExtensionSourcePath,
|
||||
name: 'my-local-ext',
|
||||
version: '1.1.0',
|
||||
});
|
||||
|
||||
const installedExtensionDir = createExtension({
|
||||
extensionsDir: userExtensionsDir,
|
||||
name: 'local-extension',
|
||||
version: '1.0.0',
|
||||
installMetadata: { source: sourceExtensionDir, type: 'local' },
|
||||
});
|
||||
const extension = annotateActiveExtensions(
|
||||
[
|
||||
loadExtension({
|
||||
extensionDir: installedExtensionDir,
|
||||
workspaceDir: tempWorkspaceDir,
|
||||
})!,
|
||||
],
|
||||
[],
|
||||
process.cwd(),
|
||||
)[0];
|
||||
let extensionState = new Map();
|
||||
const results = await checkForAllExtensionUpdates(
|
||||
[extension],
|
||||
extensionState,
|
||||
(newState) => {
|
||||
if (typeof newState === 'function') {
|
||||
newState(extensionState);
|
||||
} else {
|
||||
extensionState = newState;
|
||||
}
|
||||
},
|
||||
tempWorkspaceDir,
|
||||
);
|
||||
const result = results.get('local-extension');
|
||||
expect(result).toBe(ExtensionUpdateState.UPDATE_AVAILABLE);
|
||||
});
|
||||
|
||||
it('should return Error when git check fails', async () => {
|
||||
|
||||
@@ -128,6 +128,7 @@ export async function checkForAllExtensionUpdates(
|
||||
setExtensionsUpdateState: Dispatch<
|
||||
SetStateAction<Map<string, ExtensionUpdateState>>
|
||||
>,
|
||||
cwd: string = process.cwd(),
|
||||
): Promise<Map<string, ExtensionUpdateState>> {
|
||||
for (const extension of extensions) {
|
||||
const initialState = extensionsUpdateState.get(extension.name);
|
||||
@@ -143,13 +144,17 @@ export async function checkForAllExtensionUpdates(
|
||||
});
|
||||
continue;
|
||||
}
|
||||
await checkForExtensionUpdate(extension, (updatedState) => {
|
||||
setExtensionsUpdateState((prev) => {
|
||||
extensionsUpdateState = new Map(prev);
|
||||
extensionsUpdateState.set(extension.name, updatedState);
|
||||
return extensionsUpdateState;
|
||||
});
|
||||
});
|
||||
await checkForExtensionUpdate(
|
||||
extension,
|
||||
(updatedState) => {
|
||||
setExtensionsUpdateState((prev) => {
|
||||
extensionsUpdateState = new Map(prev);
|
||||
extensionsUpdateState.set(extension.name, updatedState);
|
||||
return extensionsUpdateState;
|
||||
});
|
||||
},
|
||||
cwd,
|
||||
);
|
||||
}
|
||||
}
|
||||
return extensionsUpdateState;
|
||||
|
||||
Reference in New Issue
Block a user