Skip to content

Commit 2d406ff

Browse files
hritanTaneja Hriday
andauthored
fix(cli): uninstall extensions using their source URL (#8692)
Co-authored-by: Taneja Hriday <hridayt@google.com>
1 parent 0534ca7 commit 2d406ff

3 files changed

Lines changed: 47 additions & 10 deletions

File tree

packages/cli/src/commands/extensions/uninstall.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { uninstallExtension } from '../../config/extension.js';
99
import { getErrorMessage } from '../../utils/errors.js';
1010

1111
interface UninstallArgs {
12-
name: string;
12+
name: string; // can be extension name or source URL.
1313
}
1414

1515
export async function handleUninstall(args: UninstallArgs) {
@@ -28,7 +28,7 @@ export const uninstallCommand: CommandModule = {
2828
builder: (yargs) =>
2929
yargs
3030
.positional('name', {
31-
describe: 'The name of the extension to uninstall.',
31+
describe: 'The name or source path of the extension to uninstall.',
3232
type: 'string',
3333
})
3434
.check((argv) => {

packages/cli/src/config/extension.test.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -715,7 +715,7 @@ describe('extension tests', () => {
715715

716716
it('should throw an error if the extension does not exist', async () => {
717717
await expect(uninstallExtension('nonexistent-extension')).rejects.toThrow(
718-
'Extension "nonexistent-extension" not found.',
718+
'Extension not found.',
719719
);
720720
});
721721

@@ -733,6 +733,40 @@ describe('extension tests', () => {
733733
new ExtensionUninstallEvent('my-local-extension', 'success'),
734734
);
735735
});
736+
737+
it('should uninstall an extension by its source URL', async () => {
738+
const gitUrl = 'https://github.com/google/gemini-sql-extension.git';
739+
const sourceExtDir = createExtension({
740+
extensionsDir: userExtensionsDir,
741+
name: 'gemini-sql-extension',
742+
version: '1.0.0',
743+
installMetadata: {
744+
source: gitUrl,
745+
type: 'git',
746+
},
747+
});
748+
749+
await uninstallExtension(gitUrl);
750+
751+
expect(fs.existsSync(sourceExtDir)).toBe(false);
752+
const logger = ClearcutLogger.getInstance({} as Config);
753+
expect(logger?.logExtensionUninstallEvent).toHaveBeenCalledWith(
754+
new ExtensionUninstallEvent('gemini-sql-extension', 'success'),
755+
);
756+
});
757+
758+
it('should fail to uninstall by URL if an extension has no install metadata', async () => {
759+
createExtension({
760+
extensionsDir: userExtensionsDir,
761+
name: 'no-metadata-extension',
762+
version: '1.0.0',
763+
// No installMetadata provided
764+
});
765+
766+
await expect(
767+
uninstallExtension('https://github.com/google/no-metadata-extension'),
768+
).rejects.toThrow('Extension not found.');
769+
});
736770
});
737771

738772
describe('performWorkspaceExtensionMigration', () => {

packages/cli/src/config/extension.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -573,17 +573,20 @@ export async function loadExtensionConfig(
573573
}
574574

575575
export async function uninstallExtension(
576-
extensionName: string,
576+
extensionIdentifier: string,
577577
cwd: string = process.cwd(),
578578
): Promise<void> {
579579
const logger = getClearcutLogger(cwd);
580580
const installedExtensions = loadUserExtensions();
581-
if (
582-
!installedExtensions.some(
583-
(installed) => installed.config.name === extensionName,
584-
)
585-
) {
586-
throw new Error(`Extension "${extensionName}" not found.`);
581+
const extensionName = installedExtensions.find(
582+
(installed) =>
583+
installed.config.name.toLowerCase() ===
584+
extensionIdentifier.toLowerCase() ||
585+
installed.installMetadata?.source.toLowerCase() ===
586+
extensionIdentifier.toLowerCase(),
587+
)?.config.name;
588+
if (!extensionName) {
589+
throw new Error(`Extension not found.`);
587590
}
588591
const manager = new ExtensionEnablementManager(
589592
ExtensionStorage.getUserExtensionsDir(),

0 commit comments

Comments
 (0)