Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[draft] Attempt to install extensions in workbench that fail to install from remote code-server #238815

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ const useId = localize('useId', "Make sure you use the full extension ID, includ
type InstallVSIXInfo = { vsix: URI; installOptions: InstallOptions };
type InstallGalleryExtensionInfo = { id: string; version?: string; installOptions: InstallOptions };

export class ExtensionInstallationError extends Error {
constructor(message: string, public failed: string[], public innerError?: Error) {
super(innerError?.message ?? message);
this.name = innerError?.name ?? 'ExtensionInstallationError';
this.stack = innerError?.stack;
}
}

export class ExtensionManagementCLI {

constructor(
Expand Down Expand Up @@ -119,11 +127,19 @@ export class ExtensionManagementCLI {
}
} catch (error) {
this.logger.error(localize('error while installing extensions', "Error while installing extensions: {0}", getErrorMessage(error)));
throw error;
throw new ExtensionInstallationError(
'Error while installing extensions',
extensions.map(ext => ext instanceof URI ? ext.toString() : ext), // TODO: This could use some work
error);
}

if (failed.length) {
throw new Error(localize('installation failed', "Failed Installing Extensions: {0}", failed.join(', ')));
throw new ExtensionInstallationError(
localize(
'installation failed',
"Failed installing extensions: {0}",
failed.join(', ')),
failed);
}
}

Expand Down
1 change: 1 addition & 0 deletions src/vs/platform/remote/common/remoteExtensionsScanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ export interface IRemoteExtensionsScannerService {

whenExtensionsReady(): Promise<void>;
scanExtensions(): Promise<IExtensionDescription[]>;
failed(): Promise<string[]>;
}
21 changes: 20 additions & 1 deletion src/vs/server/node/remoteExtensionsScanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { IURITransformer, transformOutgoingURIs } from '../../base/common/uriIpc
import { IServerChannel } from '../../base/parts/ipc/common/ipc.js';
import { ContextKeyDefinedExpr, ContextKeyEqualsExpr, ContextKeyExpr, ContextKeyExpression, ContextKeyGreaterEqualsExpr, ContextKeyGreaterExpr, ContextKeyInExpr, ContextKeyNotEqualsExpr, ContextKeyNotExpr, ContextKeyNotInExpr, ContextKeyRegexExpr, ContextKeySmallerEqualsExpr, ContextKeySmallerExpr, IContextKeyExprMapper } from '../../platform/contextkey/common/contextkey.js';
import { IExtensionGalleryService, InstallOptions } from '../../platform/extensionManagement/common/extensionManagement.js';
import { ExtensionManagementCLI } from '../../platform/extensionManagement/common/extensionManagementCLI.js';
import { ExtensionInstallationError, ExtensionManagementCLI } from '../../platform/extensionManagement/common/extensionManagementCLI.js';
import { IExtensionsScannerService, toExtensionDescription } from '../../platform/extensionManagement/common/extensionsScannerService.js';
import { ExtensionType, IExtensionDescription } from '../../platform/extensions/common/extensions.js';
import { ILogService } from '../../platform/log/common/log.js';
Expand All @@ -30,6 +30,7 @@ export class RemoteExtensionsScannerService implements IRemoteExtensionsScannerS

private readonly _whenBuiltinExtensionsReady = Promise.resolve();
private readonly _whenExtensionsReady = Promise.resolve();
private failedExtensionInstallations: Set<string> = new Set(); // TODO: Should probably track a more complex type than 'string'. Could also have a VSIX uri

constructor(
private readonly _extensionManagementCLI: ExtensionManagementCLI,
Expand All @@ -51,6 +52,12 @@ export class RemoteExtensionsScannerService implements IRemoteExtensionsScannerS
_logService.trace('Finished installing builtin extensions');
}, error => {
_logService.error(error);
if (error instanceof ExtensionInstallationError) {
_logService.error(`Capturing failed builtin extension installations: ${error.failed.join(', ')}`);
for (const extension of error.failed) {
this.failedExtensionInstallations.add(extension);
}
}
});
}

Expand All @@ -67,10 +74,21 @@ export class RemoteExtensionsScannerService implements IRemoteExtensionsScannerS
_logService.trace('Finished installing extensions');
}, error => {
_logService.error(error);
if (error instanceof ExtensionInstallationError) {
_logService.error(`Capturing failed extension installations: ${error.failed.join(', ')}`);
for (const extension of error.failed) {
this.failedExtensionInstallations.add(extension);
}
}
});
}
}

async failed(): Promise<string[]> {
await this._whenExtensionsReady;
return Array.from(this.failedExtensionInstallations);
}

private _asExtensionIdOrVSIX(inputs: string[]): (string | URI)[] {
return inputs.map(input => /\.vsix$/i.test(input) ? URI.file(isAbsolute(input) ? input : join(cwd(), input)) : input);
}
Expand Down Expand Up @@ -308,6 +326,7 @@ export class RemoteExtensionsScannerChannel implements IServerChannel {
async call(context: any, command: string, args?: any): Promise<any> {
const uriTransformer = this.getUriTransformer(context);
switch (command) {
case 'failed': return this.service.failed();
case 'whenExtensionsReady': return this.service.whenExtensionsReady();
case 'scanExtensions': {
const language = args[0];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,18 @@ export class NativeExtensionService extends AbstractExtensionService implements
connection.onReconnecting(() => this._resolveAuthorityAgain());
}

// Install extensions that remote server could not install on its own
// ie: Due to lack of internet access on the remote
const failedFromRemote = await this._remoteExtensionsScannerService.failed();
if (failedFromRemote.length) {
this._logService.debug('Failed from remote', failedFromRemote);
const exts = await this._extensionGalleryService.getExtensions(failedFromRemote.map(id => ({ id })), CancellationToken.None);
for (const ext of exts) {
// TODO: These could be VSIX's
await this._extensionManagementService.installFromGallery(ext);
}
}

// fetch the remote environment
[remoteEnv, remoteExtensions] = await Promise.all([
this._remoteAgentService.getEnvironment(),
Expand Down
12 changes: 12 additions & 0 deletions src/vs/workbench/services/remote/common/remoteExtensionsScanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@ class RemoteExtensionsScannerService implements IRemoteExtensionsScannerService
);
}

async failed(): Promise<string[]> {
try {
return await this.withChannel(
channel => channel.call<string[]>('failed'),
[]
);
} catch (error) {
this.logService.error(error);
return [];
}
}

async scanExtensions(): Promise<IExtensionDescription[]> {
try {
const languagePack = await this.activeLanguagePackService.getExtensionIdProvidingCurrentLocale();
Expand Down
3 changes: 3 additions & 0 deletions src/vs/workbench/test/browser/workbenchTestServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2162,6 +2162,9 @@ export class TestRemoteAgentService implements IRemoteAgentService {
}

export class TestRemoteExtensionsScannerService implements IRemoteExtensionsScannerService {
failed(): Promise<string[]> {
throw new Error('Method not implemented.');
}
declare readonly _serviceBrand: undefined;
async whenExtensionsReady(): Promise<void> { }
scanExtensions(): Promise<IExtensionDescription[]> { throw new Error('Method not implemented.'); }
Expand Down
Loading