Skip to content

Commit

Permalink
#194188 Implement auto update by extension
Browse files Browse the repository at this point in the history
  • Loading branch information
sandy081 committed Nov 24, 2023
1 parent 5d73488 commit 8a24b3b
Show file tree
Hide file tree
Showing 13 changed files with 658 additions and 79 deletions.
1 change: 1 addition & 0 deletions src/vs/base/common/product.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ export interface IProductConfiguration {
readonly nlsBaseUrl: string;
};

readonly publishersByOrganisation?: IStringDictionary<string[]>;
readonly extensionRecommendations?: IStringDictionary<IExtensionRecommendations>;
readonly configBasedExtensionTips?: IStringDictionary<IConfigBasedExtensionTip>;
readonly exeBasedExtensionTips?: IStringDictionary<IExeBasedExtensionTip>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ export type InstallOptions = {
isBuiltin?: boolean;
isMachineScoped?: boolean;
isApplicationScoped?: boolean;
pinned?: boolean;
donotIncludePackAndDependencies?: boolean;
installGivenVersion?: boolean;
installPreReleaseVersion?: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -884,7 +884,7 @@ export class InstallGalleryExtensionTask extends InstallExtensionTask {
updated: !!existingExtension,
isPreReleaseVersion: this.gallery.properties.isPreReleaseVersion,
installedTimestamp: Date.now(),
pinned: this.options.installGivenVersion ? true : existingExtension?.pinned,
pinned: this.options.installGivenVersion ? true : (this.options.pinned ?? existingExtension?.pinned),
preRelease: this.gallery.properties.isPreReleaseVersion ||
(isBoolean(this.options.installPreReleaseVersion)
? this.options.installPreReleaseVersion /* Respect the passed flag */
Expand Down Expand Up @@ -955,7 +955,7 @@ class InstallVSIXTask extends InstallExtensionTask {
isMachineScoped: this.options.isMachineScoped || existing?.isMachineScoped,
isBuiltin: this.options.isBuiltin || existing?.isBuiltin,
installedTimestamp: Date.now(),
pinned: this.options.installGivenVersion ? true : undefined,
pinned: this.options.installGivenVersion ? true : (this.options.pinned ?? existing?.pinned),
};

if (existing) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ import {
SetFileIconThemeAction,
SetLanguageAction,
SetProductIconThemeAction,
SkipUpdateAction,
ToggleAutoUpdateForExtensionAction,
ToggleAutoUpdatesForPublisherAction,
SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction,
ToggleSyncExtensionAction,
UninstallAction,
Expand Down Expand Up @@ -344,7 +345,7 @@ export class ExtensionEditor extends EditorPane {
this.instantiationService.createInstance(ReloadAction),
this.instantiationService.createInstance(ExtensionStatusLabelAction),
this.instantiationService.createInstance(ActionWithDropDownAction, 'extensions.updateActions', '',
[[this.instantiationService.createInstance(UpdateAction, true)], [this.instantiationService.createInstance(SkipUpdateAction)]]),
[[this.instantiationService.createInstance(UpdateAction, true)], [this.instantiationService.createInstance(ToggleAutoUpdateForExtensionAction), this.instantiationService.createInstance(ToggleAutoUpdatesForPublisherAction)]]),
this.instantiationService.createInstance(SetColorThemeAction),
this.instantiationService.createInstance(SetFileIconThemeAction),
this.instantiationService.createInstance(SetProductIconThemeAction),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import { EnablementState, IExtensionManagementServerService, IWorkbenchExtension
import { IExtensionIgnoredRecommendationsService, IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, WORKSPACE_RECOMMENDATIONS_VIEW_ID, IWorkspaceRecommendedExtensionsView, AutoUpdateConfigurationKey, HasOutdatedExtensionsContext, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, ExtensionEditorTab, THEME_ACTIONS_GROUP, INSTALL_ACTIONS_GROUP, OUTDATED_EXTENSIONS_VIEW_ID, CONTEXT_HAS_GALLERY, IExtension, extensionsSearchActionsMenu } from 'vs/workbench/contrib/extensions/common/extensions';
import { ReinstallAction, InstallSpecificVersionOfExtensionAction, ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, PromptExtensionInstallFailureAction, SearchExtensionsAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, SetColorThemeAction, SetFileIconThemeAction, SetProductIconThemeAction, ClearLanguageAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, WORKSPACE_RECOMMENDATIONS_VIEW_ID, IWorkspaceRecommendedExtensionsView, AutoUpdateConfigurationKey, HasOutdatedExtensionsContext, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, ExtensionEditorTab, THEME_ACTIONS_GROUP, INSTALL_ACTIONS_GROUP, OUTDATED_EXTENSIONS_VIEW_ID, CONTEXT_HAS_GALLERY, IExtension, extensionsSearchActionsMenu, UPDATE_ACTIONS_GROUP } from 'vs/workbench/contrib/extensions/common/extensions';
import { ReinstallAction, InstallSpecificVersionOfExtensionAction, ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, PromptExtensionInstallFailureAction, SearchExtensionsAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, SetColorThemeAction, SetFileIconThemeAction, SetProductIconThemeAction, ClearLanguageAction, ToggleAutoUpdateForExtensionAction, ToggleAutoUpdatesForPublisherAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput';
import { ExtensionEditor } from 'vs/workbench/contrib/extensions/browser/extensionEditor';
import { StatusUpdater, MaliciousExtensionChecker, ExtensionsViewletViewsContribution, ExtensionsViewPaneContainer, BuiltInExtensionsContext, SearchMarketplaceExtensionsContext, RecommendedExtensionsContext, DefaultViewsContext, ExtensionsSortByContext, SearchHasTextContext } from 'vs/workbench/contrib/extensions/browser/extensionsViewlet';
Expand Down Expand Up @@ -130,15 +130,17 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
type: 'object',
properties: {
'extensions.autoUpdate': {
enum: [true, 'onlyEnabledExtensions', false,],
enum: [true, 'onlyEnabledExtensions', 'onlySelectedExtensions', false,],
enumItemLabels: [
localize('all', "All Extensions"),
localize('enabled', "Only Enabled Extensions"),
localize('selected', "Only Selected Extensions"),
localize('none', "None"),
],
enumDescriptions: [
localize('extensions.autoUpdate.true', 'Download and install updates automatically for all extensions except for those updates are ignored.'),
localize('extensions.autoUpdate.enabled', 'Download and install updates automatically only for enabled extensions except for those updates are ignored. Disabled extensions are not updated automatically.'),
localize('extensions.autoUpdate.selected', 'Download and install updates automatically only for selected extensions.'),
localize('extensions.autoUpdate.false', 'Extensions are not automatically updated.'),
],
description: localize('extensions.autoUpdate', "Controls the automatic update behavior of extensions. The updates are fetched from a Microsoft online service."),
Expand Down Expand Up @@ -620,7 +622,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi
this.registerExtensionAction({
id: 'configureExtensionsAutoUpdate.all',
title: localize('configureExtensionsAutoUpdate.all', "All Extensions"),
toggled: ContextKeyExpr.and(ContextKeyExpr.has(`config.${AutoUpdateConfigurationKey}`), ContextKeyExpr.notEquals(`config.${AutoUpdateConfigurationKey}`, 'onlyEnabledExtensions')),
toggled: ContextKeyExpr.and(ContextKeyExpr.has(`config.${AutoUpdateConfigurationKey}`), ContextKeyExpr.notEquals(`config.${AutoUpdateConfigurationKey}`, 'onlyEnabledExtensions'), ContextKeyExpr.notEquals(`config.${AutoUpdateConfigurationKey}`, 'onlySelectedExtensions')),
menu: [{
id: autoUpdateExtensionsSubMenu,
order: 1,
Expand All @@ -630,7 +632,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi

this.registerExtensionAction({
id: 'configureExtensionsAutoUpdate.enabled',
title: localize('configureExtensionsAutoUpdate.enabled', "Only Enabled Extensions"),
title: localize('configureExtensionsAutoUpdate.enabled', "Enabled Extensions"),
toggled: ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlyEnabledExtensions'),
menu: [{
id: autoUpdateExtensionsSubMenu,
Expand All @@ -639,6 +641,17 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi
run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, 'onlyEnabledExtensions')
});

this.registerExtensionAction({
id: 'configureExtensionsAutoUpdate.selected',
title: localize('configureExtensionsAutoUpdate.selected', "Selected Extensions"),
toggled: ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlySelectedExtensions'),
menu: [{
id: autoUpdateExtensionsSubMenu,
order: 2,
}],
run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, 'onlySelectedExtensions')
});

this.registerExtensionAction({
id: 'configureExtensionsAutoUpdate.none',
title: localize('configureExtensionsAutoUpdate.none', "None"),
Expand Down Expand Up @@ -1314,6 +1327,50 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi
}
});

this.registerExtensionAction({
id: ToggleAutoUpdateForExtensionAction.ID,
title: { value: ToggleAutoUpdateForExtensionAction.LABEL, original: 'Auto Update' },
category: ExtensionsLocalizedLabel,
menu: {
id: MenuId.ExtensionContext,
group: UPDATE_ACTIONS_GROUP,
order: 1,
when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension'), ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlySelectedExtensions'),)
},
run: async (accessor: ServicesAccessor, id: string) => {
const instantiationService = accessor.get(IInstantiationService);
const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService);
const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id }));
if (extension) {
const action = instantiationService.createInstance(ToggleAutoUpdateForExtensionAction);
action.extension = extension;
return action.run();
}
}
});

this.registerExtensionAction({
id: ToggleAutoUpdatesForPublisherAction.ID,
title: { value: ToggleAutoUpdatesForPublisherAction.LABEL, original: 'Auto Update (Publisher)' },
category: ExtensionsLocalizedLabel,
menu: {
id: MenuId.ExtensionContext,
group: UPDATE_ACTIONS_GROUP,
order: 2,
when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension'), ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlySelectedExtensions'),)
},
run: async (accessor: ServicesAccessor, id: string) => {
const instantiationService = accessor.get(IInstantiationService);
const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService);
const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id }));
if (extension) {
const action = instantiationService.createInstance(ToggleAutoUpdatesForPublisherAction);
action.extension = extension;
return action.run();
}
}
});

this.registerExtensionAction({
id: SwitchToPreReleaseVersionAction.ID,
title: SwitchToPreReleaseVersionAction.TITLE,
Expand Down
87 changes: 76 additions & 11 deletions src/vs/workbench/contrib/extensions/browser/extensionsActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { Emitter, Event } from 'vs/base/common/event';
import * as json from 'vs/base/common/json';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { disposeIfDisposable } from 'vs/base/common/lifecycle';
import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewPaneContainer, IExtensionContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, THEME_ACTIONS_GROUP, INSTALL_ACTIONS_GROUP } from 'vs/workbench/contrib/extensions/common/extensions';
import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewPaneContainer, IExtensionContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, THEME_ACTIONS_GROUP, INSTALL_ACTIONS_GROUP, UPDATE_ACTIONS_GROUP } from 'vs/workbench/contrib/extensions/common/extensions';
import { ExtensionsConfigurationInitialContent } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate';
import { IGalleryExtension, IExtensionGalleryService, ILocalExtension, InstallOptions, InstallOperation, TargetPlatformToString, ExtensionManagementErrorCode } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
Expand Down Expand Up @@ -826,15 +826,61 @@ export class UpdateAction extends AbstractUpdateAction {
}
}

export class SkipUpdateAction extends AbstractUpdateAction {
export class ToggleAutoUpdateForExtensionAction extends AbstractUpdateAction {

static readonly ID = 'workbench.extensions.action.ignoreUpdates';
static readonly LABEL = localize('ignoreUpdates', "Ignore Updates");
static readonly ID = 'workbench.extensions.action.toggleAutoUpdateForExtension';
static readonly LABEL = localize('enableAutoUpdateLabel', "Auto Update");

constructor(
@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService
) {
super(SkipUpdateAction.ID, SkipUpdateAction.LABEL, extensionsWorkbenchService);
super(ToggleAutoUpdateForExtensionAction.ID, ToggleAutoUpdateForExtensionAction.LABEL, extensionsWorkbenchService);
}

override update() {
this.enabled = false;
if (!this.extension) {
return;
}
if (this.extension.isBuiltin) {
return;
}
if (!this.extensionsWorkbenchService.isAutoUpdateEnabled()) {
return;
}
super.update();
this._checked = this.extensionsWorkbenchService.isAutoUpdateEnabledFor(this.extension);
}

override async run(): Promise<any> {
if (!this.extension) {
return;
}

const enableAutoUpdate = !this.extensionsWorkbenchService.isAutoUpdateEnabledFor(this.extension);
await this.extensionsWorkbenchService.updateAutoUpdateEnablementFor(this.extension, enableAutoUpdate);

if (enableAutoUpdate) {
alert(localize('enableAutoUpdate', "Enabled auto updates for", this.extension.displayName));
} else {
alert(localize('disableAutoUpdate', "Disabled auto updates for", this.extension.displayName));
}
}
}

export class ToggleAutoUpdatesForPublisherAction extends AbstractUpdateAction {

static readonly ID = 'workbench.extensions.action.toggleAutoUpdatesForPublisher';
static readonly LABEL = localize('toggleAutoUpdatesForPublisherLabel', "Auto Update (Publisher)");

static getLabel(extension: IExtension): string {
return localize('toggleAutoUpdatesForPublisherLabel2', "Auto Update All from {0} (Publisher)", extension.publisherDisplayName);
}

constructor(
@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService
) {
super(ToggleAutoUpdatesForPublisherAction.ID, ToggleAutoUpdatesForPublisherAction.LABEL, extensionsWorkbenchService);
}

override update() {
Expand All @@ -845,17 +891,27 @@ export class SkipUpdateAction extends AbstractUpdateAction {
this.enabled = false;
return;
}
if (this.extensionsWorkbenchService.getAutoUpdateValue() !== 'onlySelectedExtensions') {
this.enabled = false;
return;
}
super.update();
this._checked = this.extension.pinned;
this._checked = this.extensionsWorkbenchService.isAutoUpdateEnabledFor(this.extension.publisher);
this.label = ToggleAutoUpdatesForPublisherAction.getLabel(this.extension);
}

override async run(): Promise<any> {
if (!this.extension) {
return;
}
alert(localize('ignoreExtensionUpdate', "Ignoring {0} updates", this.extension.displayName));
const newIgnoresAutoUpdates = !this.extension.pinned;
await this.extensionsWorkbenchService.pinExtension(this.extension, newIgnoresAutoUpdates);
alert(localize('ignoreExtensionUpdatePublisher', "Ignoring updates published by {0}.", this.extension.publisherDisplayName));
const enableAutoUpdate = !this.extensionsWorkbenchService.isAutoUpdateEnabledFor(this.extension.publisher);
await this.extensionsWorkbenchService.updateAutoUpdateEnablementFor(this.extension.publisher, enableAutoUpdate);
if (enableAutoUpdate) {
alert(localize('enableAutoUpdate', "Enabled auto updates for", this.extension.displayName));
} else {
alert(localize('disableAutoUpdate', "Disabled auto updates for", this.extension.displayName));
}
}
}

Expand Down Expand Up @@ -1013,7 +1069,6 @@ async function getContextMenuActionsGroups(extension: IExtension | undefined | n
cksOverlay.push(['galleryExtensionIsPreReleaseVersion', !!extension.gallery?.properties.isPreReleaseVersion]);
cksOverlay.push(['extensionHasPreReleaseVersion', extension.hasPreReleaseVersion]);
cksOverlay.push(['extensionHasReleaseVersion', extension.hasReleaseVersion]);
cksOverlay.push(['isExtensionPinned', extension.pinned]);

const [colorThemes, fileIconThemes, productIconThemes] = await Promise.all([workbenchThemeService.getColorThemes(), workbenchThemeService.getFileIconThemes(), workbenchThemeService.getProductIconThemes()]);
cksOverlay.push(['extensionHasColorThemes', colorThemes.some(theme => isThemeFromExtension(theme, extension))]);
Expand Down Expand Up @@ -1073,10 +1128,12 @@ export class ManageExtensionAction extends ExtensionDropDownAction {
async getActionGroups(): Promise<IAction[][]> {
const groups: IAction[][] = [];
const contextMenuActionsGroups = await getContextMenuActionsGroups(this.extension, this.contextKeyService, this.instantiationService);
const themeActions: IAction[] = [], installActions: IAction[] = [], otherActionGroups: IAction[][] = [];
const themeActions: IAction[] = [], installActions: IAction[] = [], updateActions: IAction[] = [], otherActionGroups: IAction[][] = [];
for (const [group, actions] of contextMenuActionsGroups) {
if (group === INSTALL_ACTIONS_GROUP) {
installActions.push(...toActions([[group, actions]], this.instantiationService)[0]);
} else if (group === UPDATE_ACTIONS_GROUP) {
updateActions.push(...toActions([[group, actions]], this.instantiationService)[0]);
} else if (group === THEME_ACTIONS_GROUP) {
themeActions.push(...toActions([[group, actions]], this.instantiationService)[0]);
} else {
Expand All @@ -1096,6 +1153,9 @@ export class ManageExtensionAction extends ExtensionDropDownAction {
this.instantiationService.createInstance(DisableGloballyAction),
this.instantiationService.createInstance(DisableForWorkspaceAction)
]);
if (updateActions.length) {
groups.push(updateActions);
}
groups.push([
...(installActions.length ? installActions : []),
this.instantiationService.createInstance(InstallAnotherVersionAction),
Expand Down Expand Up @@ -1169,6 +1229,11 @@ export class MenuItemExtensionAction extends ExtensionAction {
}
if (this.action.id === TOGGLE_IGNORE_EXTENSION_ACTION_ID) {
this.checked = !this.extensionsWorkbenchService.isExtensionIgnoredToSync(this.extension);
} else if (this.action.id === ToggleAutoUpdateForExtensionAction.ID) {
this.checked = this.extensionsWorkbenchService.isAutoUpdateEnabledFor(this.extension);
} else if (this.action.id === ToggleAutoUpdatesForPublisherAction.ID) {
this.label = ToggleAutoUpdatesForPublisherAction.getLabel(this.extension);
this.checked = this.extensionsWorkbenchService.isAutoUpdateEnabledFor(this.extension.publisher);
} else {
this.checked = this.action.checked;
}
Expand Down
Loading

0 comments on commit 8a24b3b

Please sign in to comment.