update - move to browser (#80806)

* web update - first cut service

* web update - move update contribution to browser

* update - tweak settings
This commit is contained in:
Benjamin Pasero 2019-09-12 17:53:35 +02:00 committed by GitHub
parent 5e417f3d22
commit f6df256c60
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 631 additions and 541 deletions

View file

@ -12,7 +12,7 @@ import { WindowsService } from 'vs/platform/windows/electron-main/windowsService
import { ILifecycleService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import { getShellEnvironment } from 'vs/code/node/shellEnv';
import { IUpdateService } from 'vs/platform/update/common/update';
import { UpdateChannel } from 'vs/platform/update/node/updateIpc';
import { UpdateChannel } from 'vs/platform/update/electron-main/updateIpc';
import { Server as ElectronIPCServer } from 'vs/base/parts/ipc/electron-main/ipc.electron-main';
import { Client } from 'vs/base/parts/ipc/common/ipc.net';
import { Server, connect } from 'vs/base/parts/ipc/node/ipc.net';

View file

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/platform/update/node/update.config.contribution';
import 'vs/platform/update/common/update.config.contribution';
import { app, dialog } from 'electron';
import { assign } from 'vs/base/common/objects';
import * as platform from 'vs/base/common/platform';

View file

@ -12,7 +12,7 @@ const product = { /*BUILD->INSERT_PRODUCT_CONFIGURATION*/ } as IProductConfigura
// Running out of sources
if (Object.keys(product).length === 0) {
assign(product, {
version: '1.38.0-dev',
version: '1.39.0-dev',
nameLong: 'Visual Studio Code Web Dev',
nameShort: 'VSCode Web Dev'
});

View file

@ -1,20 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import product from 'vs/platform/product/node/product';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
export async function addGAParameters(telemetryService: ITelemetryService, environmentService: IEnvironmentService, uri: URI, origin: string, experiment = '1'): Promise<URI> {
if (environmentService.isBuilt && !environmentService.isExtensionDevelopment && !environmentService.args['disable-telemetry'] && !!product.enableTelemetry) {
if (uri.scheme === 'https' && uri.authority === 'code.visualstudio.com') {
const info = await telemetryService.getTelemetryInfo();
return uri.with({ query: `${uri.query ? uri.query + '&' : ''}utm_source=VsCode&utm_medium=${encodeURIComponent(origin)}&utm_campaign=${encodeURIComponent(info.instanceId)}&utm_content=${encodeURIComponent(experiment)}` });
}
}
return uri;
}

View file

@ -6,7 +6,7 @@
import { Registry } from 'vs/platform/registry/common/platform';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { localize } from 'vs/nls';
import { isWindows } from 'vs/base/common/platform';
import { isWindows, isWeb } from 'vs/base/common/platform';
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
configurationRegistry.registerConfiguration({
@ -42,7 +42,7 @@ configurationRegistry.registerConfiguration({
scope: ConfigurationScope.APPLICATION,
title: localize('enableWindowsBackgroundUpdatesTitle', "Enable Background Updates on Windows"),
description: localize('enableWindowsBackgroundUpdates', "Enable to download and install new VS Code Versions in the background on Windows"),
included: isWindows
included: isWindows && !isWeb
},
'update.showReleaseNotes': {
type: 'boolean',

View file

@ -9,8 +9,6 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
export interface IUpdate {
version: string;
productVersion: string;
date?: Date;
releaseNotes?: string;
supportsFastUpdate?: boolean;
url?: string;
hash?: string;

View file

@ -10,7 +10,6 @@ import { Event } from 'vs/base/common/event';
import { ILogService } from 'vs/platform/log/common/log';
import { Disposable } from 'vs/base/common/lifecycle';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IUpdateService, State } from 'vs/platform/update/common/update';
import { IWindowService, INativeOpenDialogOptions, IEnterWorkspaceResult, IURIToOpen, IMessageBoxResult, IWindowsService, IOpenSettings, IWindowSettings } from 'vs/platform/windows/common/windows';
import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceFolderCreationData, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
import { IRecentlyOpened, IRecent, isRecentFile, isRecentFolder } from 'vs/platform/history/common/history';
@ -34,40 +33,6 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService
// tslint:disable-next-line: import-patterns
import { IWorkspaceStatsService, Tags } from 'vs/workbench/contrib/stats/common/workspaceStats';
//#region Update
export class SimpleUpdateService implements IUpdateService {
_serviceBrand: undefined;
onStateChange = Event.None;
state!: State;
checkForUpdates(context: any): Promise<void> {
return Promise.resolve(undefined);
}
downloadUpdate(): Promise<void> {
return Promise.resolve(undefined);
}
applyUpdate(): Promise<void> {
return Promise.resolve(undefined);
}
quitAndInstall(): Promise<void> {
return Promise.resolve(undefined);
}
isLatestVersion(): Promise<boolean> {
return Promise.resolve(true);
}
}
registerSingleton(IUpdateService, SimpleUpdateService);
//#endregion
//#region Window
export class SimpleWindowService extends Disposable implements IWindowService {
@ -377,6 +342,7 @@ export class SimpleWindowsService implements IWindowsService {
@IClipboardService private readonly clipboardService: IClipboardService
) {
}
isFocused(_windowId: number): Promise<boolean> {
return Promise.resolve(true);
}
@ -595,7 +561,7 @@ export class SimpleWindowsService implements IWindowsService {
}
getActiveWindowId(): Promise<number | undefined> {
return Promise.resolve(undefined);
return Promise.resolve(0);
}
// This needs to be handled from browser process to prevent

View file

Before

Width:  |  Height:  |  Size: 559 B

After

Width:  |  Height:  |  Size: 559 B

View file

@ -18,7 +18,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IRequestService, asText } from 'vs/platform/request/common/request';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { addGAParameters } from 'vs/platform/telemetry/node/telemetryNodeUtils';
import { IProductService } from 'vs/platform/product/common/product';
import { IWebviewEditorService } from 'vs/workbench/contrib/webview/browser/webviewEditorService';
import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { WebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput';
@ -45,7 +45,8 @@ export class ReleaseNotesManager {
@IEditorService private readonly _editorService: IEditorService,
@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService,
@IWebviewEditorService private readonly _webviewEditorService: IWebviewEditorService,
@IExtensionService private readonly _extensionService: IExtensionService
@IExtensionService private readonly _extensionService: IExtensionService,
@IProductService private readonly _productService: IProductService
) {
TokenizationRegistry.onDidChange(async () => {
if (!this._currentReleaseNotes || !this._lastText) {
@ -161,11 +162,22 @@ export class ReleaseNotesManager {
}
private onDidClickLink(uri: URI) {
addGAParameters(this._telemetryService, this._environmentService, uri, 'ReleaseNotes')
this.addGAParameters(uri, 'ReleaseNotes')
.then(updated => this._openerService.open(updated))
.then(undefined, onUnexpectedError);
}
private async addGAParameters(uri: URI, origin: string, experiment = '1'): Promise<URI> {
if (this._environmentService.isBuilt && !this._environmentService.isExtensionDevelopment && !this._environmentService.args['disable-telemetry'] && !!this._productService.enableTelemetry) {
if (uri.scheme === 'https' && uri.authority === 'code.visualstudio.com') {
const info = await this._telemetryService.getTelemetryInfo();
return uri.with({ query: `${uri.query ? uri.query + '&' : ''}utm_source=VsCode&utm_medium=${encodeURIComponent(origin)}&utm_campaign=${encodeURIComponent(info.instanceId)}&utm_content=${encodeURIComponent(experiment)}` });
}
}
return uri;
}
private async renderBody(text: string) {
const content = await this.renderContent(text);
const colorMap = TokenizationRegistry.getColorMap();

View file

@ -0,0 +1,21 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/platform/update/common/update.config.contribution';
import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { ShowCurrentReleaseNotesAction, ProductContribution, UpdateContribution } from 'vs/workbench/contrib/update/browser/update';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
const workbench = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
workbench.registerWorkbenchContribution(ProductContribution, LifecyclePhase.Restored);
workbench.registerWorkbenchContribution(UpdateContribution, LifecyclePhase.Restored);
// Editor
Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions)
.registerWorkbenchAction(new SyncActionDescriptor(ShowCurrentReleaseNotesAction, ShowCurrentReleaseNotesAction.ID, ShowCurrentReleaseNotesAction.LABEL), 'Show Release Notes');

View file

@ -0,0 +1,474 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import severity from 'vs/base/common/severity';
import { Action } from 'vs/base/common/actions';
import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { IActivityService, NumberBadge, IBadge, ProgressBadge } from 'vs/workbench/services/activity/common/activity';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { GLOBAL_ACTIVITY_ID } from 'vs/workbench/common/activity';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IUpdateService, State as UpdateState, StateType, IUpdate } from 'vs/platform/update/common/update';
import * as semver from 'semver-umd';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { ReleaseNotesManager } from './releaseNotesEditor';
import { isWindows } from 'vs/base/common/platform';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { FalseContext } from 'vs/platform/contextkey/common/contextkeys';
import { ShowCurrentReleaseNotesActionId } from 'vs/workbench/contrib/update/common/update';
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
import { IProductService } from 'vs/platform/product/common/product';
const CONTEXT_UPDATE_STATE = new RawContextKey<string>('updateState', StateType.Uninitialized);
let releaseNotesManager: ReleaseNotesManager | undefined = undefined;
function showReleaseNotes(instantiationService: IInstantiationService, version: string) {
if (!releaseNotesManager) {
releaseNotesManager = instantiationService.createInstance(ReleaseNotesManager);
}
return instantiationService.invokeFunction(accessor => releaseNotesManager!.show(accessor, version));
}
export class OpenLatestReleaseNotesInBrowserAction extends Action {
constructor(
@IOpenerService private readonly openerService: IOpenerService,
@IProductService private readonly productService: IProductService
) {
super('update.openLatestReleaseNotes', nls.localize('releaseNotes', "Release Notes"), undefined, true);
}
run(): Promise<any> {
if (this.productService.releaseNotesUrl) {
const uri = URI.parse(this.productService.releaseNotesUrl);
return this.openerService.open(uri);
}
return Promise.resolve(false);
}
}
export abstract class AbstractShowReleaseNotesAction extends Action {
constructor(
id: string,
label: string,
private version: string,
@IInstantiationService private readonly instantiationService: IInstantiationService
) {
super(id, label, undefined, true);
}
run(): Promise<boolean> {
if (!this.enabled) {
return Promise.resolve(false);
}
this.enabled = false;
return showReleaseNotes(this.instantiationService, this.version)
.then(undefined, () => {
const action = this.instantiationService.createInstance(OpenLatestReleaseNotesInBrowserAction);
return action.run().then(() => false);
});
}
}
export class ShowReleaseNotesAction extends AbstractShowReleaseNotesAction {
constructor(
version: string,
@IInstantiationService instantiationService: IInstantiationService
) {
super('update.showReleaseNotes', nls.localize('releaseNotes', "Release Notes"), version, instantiationService);
}
}
export class ShowCurrentReleaseNotesAction extends AbstractShowReleaseNotesAction {
static readonly ID = ShowCurrentReleaseNotesActionId;
static LABEL = nls.localize('showReleaseNotes', "Show Release Notes");
constructor(
id = ShowCurrentReleaseNotesAction.ID,
label = ShowCurrentReleaseNotesAction.LABEL,
@IInstantiationService instantiationService: IInstantiationService,
@IProductService productService: IProductService
) {
super(id, label, productService.version, instantiationService);
}
}
export class ProductContribution implements IWorkbenchContribution {
private static readonly KEY = 'releaseNotes/lastVersion';
constructor(
@IStorageService storageService: IStorageService,
@IInstantiationService instantiationService: IInstantiationService,
@INotificationService notificationService: INotificationService,
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
@IOpenerService openerService: IOpenerService,
@IConfigurationService configurationService: IConfigurationService,
@IWindowService windowService: IWindowService,
@IWindowsService windowsService: IWindowsService,
@IProductService productService: IProductService
) {
windowsService.getActiveWindowId().then(async windowId => {
if (windowId !== windowService.windowId) {
return;
}
const lastVersion = storageService.get(ProductContribution.KEY, StorageScope.GLOBAL, '');
const shouldShowReleaseNotes = configurationService.getValue<boolean>('update.showReleaseNotes');
// was there an update? if so, open release notes
const releaseNotesUrl = productService.releaseNotesUrl;
if (shouldShowReleaseNotes && !environmentService.skipReleaseNotes && releaseNotesUrl && lastVersion && productService.version !== lastVersion) {
showReleaseNotes(instantiationService, productService.version)
.then(undefined, () => {
notificationService.prompt(
severity.Info,
nls.localize('read the release notes', "Welcome to {0} v{1}! Would you like to read the Release Notes?", productService.nameLong, productService.version),
[{
label: nls.localize('releaseNotes', "Release Notes"),
run: () => {
const uri = URI.parse(releaseNotesUrl);
openerService.open(uri);
}
}],
{ sticky: true }
);
});
}
// should we show the new license?
if (productService.licenseUrl && lastVersion && semver.satisfies(lastVersion, '<1.0.0') && semver.satisfies(productService.version, '>=1.0.0')) {
notificationService.info(nls.localize('licenseChanged', "Our license terms have changed, please click [here]({0}) to go through them.", productService.licenseUrl));
}
storageService.store(ProductContribution.KEY, productService.version, StorageScope.GLOBAL);
});
}
}
export class UpdateContribution extends Disposable implements IWorkbenchContribution {
private state: UpdateState;
private readonly badgeDisposable = this._register(new MutableDisposable());
private updateStateContextKey: IContextKey<string>;
constructor(
@IStorageService private readonly storageService: IStorageService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@INotificationService private readonly notificationService: INotificationService,
@IDialogService private readonly dialogService: IDialogService,
@IUpdateService private readonly updateService: IUpdateService,
@IActivityService private readonly activityService: IActivityService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IProductService private readonly productService: IProductService
) {
super();
this.state = updateService.state;
this.updateStateContextKey = CONTEXT_UPDATE_STATE.bindTo(this.contextKeyService);
this._register(updateService.onStateChange(this.onUpdateStateChange, this));
this.onUpdateStateChange(this.updateService.state);
/*
The `update/lastKnownVersion` and `update/updateNotificationTime` storage keys are used in
combination to figure out when to show a message to the user that he should update.
This message should appear if the user has received an update notification but hasn't
updated since 5 days.
*/
const currentVersion = this.productService.commit;
const lastKnownVersion = this.storageService.get('update/lastKnownVersion', StorageScope.GLOBAL);
// if current version != stored version, clear both fields
if (currentVersion !== lastKnownVersion) {
this.storageService.remove('update/lastKnownVersion', StorageScope.GLOBAL);
this.storageService.remove('update/updateNotificationTime', StorageScope.GLOBAL);
}
this.registerGlobalActivityActions();
}
private onUpdateStateChange(state: UpdateState): void {
this.updateStateContextKey.set(state.type);
switch (state.type) {
case StateType.Idle:
if (state.error) {
this.onError(state.error);
} else if (this.state.type === StateType.CheckingForUpdates && this.state.context && this.state.context.windowId === this.environmentService.configuration.windowId) {
this.onUpdateNotAvailable();
}
break;
case StateType.AvailableForDownload:
this.onUpdateAvailable(state.update);
break;
case StateType.Downloaded:
this.onUpdateDownloaded(state.update);
break;
case StateType.Updating:
this.onUpdateUpdating(state.update);
break;
case StateType.Ready:
this.onUpdateReady(state.update);
break;
}
let badge: IBadge | undefined = undefined;
let clazz: string | undefined;
if (state.type === StateType.AvailableForDownload || state.type === StateType.Downloaded || state.type === StateType.Ready) {
badge = new NumberBadge(1, () => nls.localize('updateIsReady', "New {0} update available.", this.productService.nameShort));
} else if (state.type === StateType.CheckingForUpdates || state.type === StateType.Downloading || state.type === StateType.Updating) {
badge = new ProgressBadge(() => nls.localize('updateIsReady', "New {0} update available.", this.productService.nameShort));
clazz = 'progress-badge';
}
this.badgeDisposable.clear();
if (badge) {
this.badgeDisposable.value = this.activityService.showActivity(GLOBAL_ACTIVITY_ID, badge, clazz);
}
this.state = state;
}
private onError(error: string): void {
error = error.replace(/See https:\/\/github\.com\/Squirrel\/Squirrel\.Mac\/issues\/182 for more information/, 'See [this link](https://github.com/Microsoft/vscode/issues/7426#issuecomment-425093469) for more information');
this.notificationService.notify({
severity: Severity.Error,
message: error,
source: nls.localize('update service', "Update Service"),
});
}
private onUpdateNotAvailable(): void {
this.dialogService.show(
severity.Info,
nls.localize('noUpdatesAvailable', "There are currently no updates available."),
[nls.localize('ok', "OK")]
);
}
// linux
private onUpdateAvailable(update: IUpdate): void {
if (!this.shouldShowNotification()) {
return;
}
this.notificationService.prompt(
severity.Info,
nls.localize('thereIsUpdateAvailable', "There is an available update."),
[{
label: nls.localize('download update', "Download Update"),
run: () => this.updateService.downloadUpdate()
}, {
label: nls.localize('later', "Later"),
run: () => { }
}, {
label: nls.localize('releaseNotes', "Release Notes"),
run: () => {
const action = this.instantiationService.createInstance(ShowReleaseNotesAction, update.productVersion);
action.run();
action.dispose();
}
}],
{ sticky: true }
);
}
// windows fast updates (target === system)
private onUpdateDownloaded(update: IUpdate): void {
if (!this.shouldShowNotification()) {
return;
}
this.notificationService.prompt(
severity.Info,
nls.localize('updateAvailable', "There's an update available: {0} {1}", this.productService.nameLong, update.productVersion),
[{
label: nls.localize('installUpdate', "Install Update"),
run: () => this.updateService.applyUpdate()
}, {
label: nls.localize('later', "Later"),
run: () => { }
}, {
label: nls.localize('releaseNotes', "Release Notes"),
run: () => {
const action = this.instantiationService.createInstance(ShowReleaseNotesAction, update.productVersion);
action.run();
action.dispose();
}
}],
{ sticky: true }
);
}
// windows fast updates
private onUpdateUpdating(update: IUpdate): void {
if (isWindows && this.productService.target === 'user') {
return;
}
// windows fast updates (target === system)
this.notificationService.prompt(
severity.Info,
nls.localize('updateInstalling', "{0} {1} is being installed in the background; we'll let you know when it's done.", this.productService.nameLong, update.productVersion),
[],
{
neverShowAgain: { id: 'neverShowAgain:update/win32-fast-updates', isSecondary: true }
}
);
}
// windows and mac
private onUpdateReady(update: IUpdate): void {
if (!(isWindows && this.productService.target !== 'user') && !this.shouldShowNotification()) {
return;
}
const actions = [{
label: nls.localize('updateNow', "Update Now"),
run: () => this.updateService.quitAndInstall()
}, {
label: nls.localize('later', "Later"),
run: () => { }
}];
// TODO@joao check why snap updates send `update` as falsy
if (update.productVersion) {
actions.push({
label: nls.localize('releaseNotes', "Release Notes"),
run: () => {
const action = this.instantiationService.createInstance(ShowReleaseNotesAction, update.productVersion);
action.run();
action.dispose();
}
});
}
// windows user fast updates and mac
this.notificationService.prompt(
severity.Info,
nls.localize('updateAvailableAfterRestart', "Restart {0} to apply the latest update.", this.productService.nameLong),
actions,
{ sticky: true }
);
}
private shouldShowNotification(): boolean {
const currentVersion = this.productService.commit;
const currentMillis = new Date().getTime();
const lastKnownVersion = this.storageService.get('update/lastKnownVersion', StorageScope.GLOBAL);
// if version != stored version, save version and date
if (currentVersion !== lastKnownVersion) {
this.storageService.store('update/lastKnownVersion', currentVersion!, StorageScope.GLOBAL);
this.storageService.store('update/updateNotificationTime', currentMillis, StorageScope.GLOBAL);
}
const updateNotificationMillis = this.storageService.getNumber('update/updateNotificationTime', StorageScope.GLOBAL, currentMillis);
const diffDays = (currentMillis - updateNotificationMillis) / (1000 * 60 * 60 * 24);
return diffDays > 5;
}
private registerGlobalActivityActions(): void {
CommandsRegistry.registerCommand('update.check', () => this.updateService.checkForUpdates({ windowId: this.environmentService.configuration.windowId }));
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
group: '5_update',
command: {
id: 'update.check',
title: nls.localize('checkForUpdates', "Check for Updates...")
},
when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.Idle)
});
CommandsRegistry.registerCommand('update.checking', () => { });
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
group: '5_update',
command: {
id: 'update.checking',
title: nls.localize('checkingForUpdates', "Checking for Updates..."),
precondition: FalseContext
},
when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.CheckingForUpdates)
});
CommandsRegistry.registerCommand('update.downloadNow', () => this.updateService.downloadUpdate());
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
group: '5_update',
command: {
id: 'update.downloadNow',
title: nls.localize('download update', "Download Update")
},
when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.AvailableForDownload)
});
CommandsRegistry.registerCommand('update.downloading', () => { });
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
group: '5_update',
command: {
id: 'update.downloading',
title: nls.localize('DownloadingUpdate', "Downloading Update..."),
precondition: FalseContext
},
when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.Downloading)
});
CommandsRegistry.registerCommand('update.install', () => this.updateService.applyUpdate());
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
group: '5_update',
command: {
id: 'update.install',
title: nls.localize('installUpdate...', "Install Update...")
},
when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.Downloaded)
});
CommandsRegistry.registerCommand('update.updating', () => { });
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
group: '5_update',
command: {
id: 'update.updating',
title: nls.localize('installingUpdate', "Installing Update..."),
precondition: FalseContext
},
when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.Updating)
});
CommandsRegistry.registerCommand('update.restart', () => this.updateService.quitAndInstall());
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
group: '5_update',
command: {
id: 'update.restart',
title: nls.localize('restartToUpdate', "Restart to Update")
},
when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.Ready)
});
}
}

View file

@ -3,27 +3,16 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/platform/update/node/update.config.contribution';
import * as platform from 'vs/base/common/platform';
import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { ShowCurrentReleaseNotesAction, ProductContribution, UpdateContribution, Win3264BitContribution } from './update';
import { Win3264BitContribution } from 'vs/workbench/contrib/update/electron-browser/update';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
const workbench = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
workbench.registerWorkbenchContribution(ProductContribution, LifecyclePhase.Restored);
if (platform.isWindows) {
if (process.arch === 'ia32') {
workbench.registerWorkbenchContribution(Win3264BitContribution, LifecyclePhase.Restored);
}
}
workbench.registerWorkbenchContribution(UpdateContribution, LifecyclePhase.Restored);
// Editor
Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions)
.registerWorkbenchAction(new SyncActionDescriptor(ShowCurrentReleaseNotesAction, ShowCurrentReleaseNotesAction.ID, ShowCurrentReleaseNotesAction.LABEL), 'Show Release Notes');

View file

@ -5,163 +5,10 @@
import * as nls from 'vs/nls';
import severity from 'vs/base/common/severity';
import { Action } from 'vs/base/common/actions';
import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
import pkg from 'vs/platform/product/node/package';
import product from 'vs/platform/product/node/product';
import { URI } from 'vs/base/common/uri';
import { IActivityService, NumberBadge, IBadge, ProgressBadge } from 'vs/workbench/services/activity/common/activity';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { GLOBAL_ACTIVITY_ID } from 'vs/workbench/common/activity';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IUpdateService, State as UpdateState, StateType, IUpdate } from 'vs/platform/update/common/update';
import * as semver from 'semver-umd';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { ReleaseNotesManager } from './releaseNotesEditor';
import { isWindows } from 'vs/base/common/platform';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { FalseContext } from 'vs/platform/contextkey/common/contextkeys';
import { ShowCurrentReleaseNotesActionId } from 'vs/workbench/contrib/update/common/update';
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
const CONTEXT_UPDATE_STATE = new RawContextKey<string>('updateState', StateType.Uninitialized);
let releaseNotesManager: ReleaseNotesManager | undefined = undefined;
function showReleaseNotes(instantiationService: IInstantiationService, version: string) {
if (!releaseNotesManager) {
releaseNotesManager = instantiationService.createInstance(ReleaseNotesManager);
}
return instantiationService.invokeFunction(accessor => releaseNotesManager!.show(accessor, version));
}
export class OpenLatestReleaseNotesInBrowserAction extends Action {
constructor(
@IOpenerService private readonly openerService: IOpenerService
) {
super('update.openLatestReleaseNotes', nls.localize('releaseNotes', "Release Notes"), undefined, true);
}
run(): Promise<any> {
if (product.releaseNotesUrl) {
const uri = URI.parse(product.releaseNotesUrl);
return this.openerService.open(uri);
}
return Promise.resolve(false);
}
}
export abstract class AbstractShowReleaseNotesAction extends Action {
constructor(
id: string,
label: string,
private version: string,
@IInstantiationService private readonly instantiationService: IInstantiationService
) {
super(id, label, undefined, true);
}
run(): Promise<boolean> {
if (!this.enabled) {
return Promise.resolve(false);
}
this.enabled = false;
return showReleaseNotes(this.instantiationService, this.version)
.then(undefined, () => {
const action = this.instantiationService.createInstance(OpenLatestReleaseNotesInBrowserAction);
return action.run().then(() => false);
});
}
}
export class ShowReleaseNotesAction extends AbstractShowReleaseNotesAction {
constructor(
version: string,
@IInstantiationService instantiationService: IInstantiationService
) {
super('update.showReleaseNotes', nls.localize('releaseNotes', "Release Notes"), version, instantiationService);
}
}
export class ShowCurrentReleaseNotesAction extends AbstractShowReleaseNotesAction {
static readonly ID = ShowCurrentReleaseNotesActionId;
static LABEL = nls.localize('showReleaseNotes', "Show Release Notes");
constructor(
id = ShowCurrentReleaseNotesAction.ID,
label = ShowCurrentReleaseNotesAction.LABEL,
@IInstantiationService instantiationService: IInstantiationService
) {
super(id, label, pkg.version, instantiationService);
}
}
export class ProductContribution implements IWorkbenchContribution {
private static readonly KEY = 'releaseNotes/lastVersion';
constructor(
@IStorageService storageService: IStorageService,
@IInstantiationService instantiationService: IInstantiationService,
@INotificationService notificationService: INotificationService,
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
@IOpenerService openerService: IOpenerService,
@IConfigurationService configurationService: IConfigurationService,
@IWindowService windowService: IWindowService,
@IWindowsService windowsService: IWindowsService
) {
windowsService.getActiveWindowId().then(async windowId => {
if (windowId !== windowService.windowId) {
return;
}
const lastVersion = storageService.get(ProductContribution.KEY, StorageScope.GLOBAL, '');
const shouldShowReleaseNotes = configurationService.getValue<boolean>('update.showReleaseNotes');
// was there an update? if so, open release notes
const releaseNotesUrl = product.releaseNotesUrl;
if (shouldShowReleaseNotes && !environmentService.skipReleaseNotes && releaseNotesUrl && lastVersion && pkg.version !== lastVersion) {
showReleaseNotes(instantiationService, pkg.version)
.then(undefined, () => {
notificationService.prompt(
severity.Info,
nls.localize('read the release notes', "Welcome to {0} v{1}! Would you like to read the Release Notes?", product.nameLong, pkg.version),
[{
label: nls.localize('releaseNotes', "Release Notes"),
run: () => {
const uri = URI.parse(releaseNotesUrl);
openerService.open(uri);
}
}],
{ sticky: true }
);
});
}
// should we show the new license?
if (product.licenseUrl && lastVersion && semver.satisfies(lastVersion, '<1.0.0') && semver.satisfies(pkg.version, '>=1.0.0')) {
notificationService.info(nls.localize('licenseChanged', "Our license terms have changed, please click [here]({0}) to go through them.", product.licenseUrl));
}
storageService.store(ProductContribution.KEY, pkg.version, StorageScope.GLOBAL);
});
}
}
import { INotificationService } from 'vs/platform/notification/common/notification';
export class Win3264BitContribution implements IWorkbenchContribution {
@ -169,7 +16,6 @@ export class Win3264BitContribution implements IWorkbenchContribution {
private static readonly INSIDER_URL = 'https://github.com/Microsoft/vscode-docs/blob/vnext/release-notes/v1_15.md#windows-64-bit';
constructor(
@IStorageService storageService: IStorageService,
@INotificationService notificationService: INotificationService,
@IEnvironmentService environmentService: IEnvironmentService
) {
@ -192,311 +38,3 @@ export class Win3264BitContribution implements IWorkbenchContribution {
);
}
}
export class UpdateContribution extends Disposable implements IWorkbenchContribution {
private state: UpdateState;
private readonly badgeDisposable = this._register(new MutableDisposable());
private updateStateContextKey: IContextKey<string>;
constructor(
@IStorageService private readonly storageService: IStorageService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@INotificationService private readonly notificationService: INotificationService,
@IDialogService private readonly dialogService: IDialogService,
@IUpdateService private readonly updateService: IUpdateService,
@IActivityService private readonly activityService: IActivityService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IContextKeyService private readonly contextKeyService: IContextKeyService
) {
super();
this.state = updateService.state;
this.updateStateContextKey = CONTEXT_UPDATE_STATE.bindTo(this.contextKeyService);
this._register(updateService.onStateChange(this.onUpdateStateChange, this));
this.onUpdateStateChange(this.updateService.state);
/*
The `update/lastKnownVersion` and `update/updateNotificationTime` storage keys are used in
combination to figure out when to show a message to the user that he should update.
This message should appear if the user has received an update notification but hasn't
updated since 5 days.
*/
const currentVersion = product.commit;
const lastKnownVersion = this.storageService.get('update/lastKnownVersion', StorageScope.GLOBAL);
// if current version != stored version, clear both fields
if (currentVersion !== lastKnownVersion) {
this.storageService.remove('update/lastKnownVersion', StorageScope.GLOBAL);
this.storageService.remove('update/updateNotificationTime', StorageScope.GLOBAL);
}
this.registerGlobalActivityActions();
}
private onUpdateStateChange(state: UpdateState): void {
this.updateStateContextKey.set(state.type);
switch (state.type) {
case StateType.Idle:
if (state.error) {
this.onError(state.error);
} else if (this.state.type === StateType.CheckingForUpdates && this.state.context && this.state.context.windowId === this.environmentService.configuration.windowId) {
this.onUpdateNotAvailable();
}
break;
case StateType.AvailableForDownload:
this.onUpdateAvailable(state.update);
break;
case StateType.Downloaded:
this.onUpdateDownloaded(state.update);
break;
case StateType.Updating:
this.onUpdateUpdating(state.update);
break;
case StateType.Ready:
this.onUpdateReady(state.update);
break;
}
let badge: IBadge | undefined = undefined;
let clazz: string | undefined;
if (state.type === StateType.AvailableForDownload || state.type === StateType.Downloaded || state.type === StateType.Ready) {
badge = new NumberBadge(1, () => nls.localize('updateIsReady', "New {0} update available.", product.nameShort));
} else if (state.type === StateType.CheckingForUpdates || state.type === StateType.Downloading || state.type === StateType.Updating) {
badge = new ProgressBadge(() => nls.localize('updateIsReady', "New {0} update available.", product.nameShort));
clazz = 'progress-badge';
}
this.badgeDisposable.clear();
if (badge) {
this.badgeDisposable.value = this.activityService.showActivity(GLOBAL_ACTIVITY_ID, badge, clazz);
}
this.state = state;
}
private onError(error: string): void {
error = error.replace(/See https:\/\/github\.com\/Squirrel\/Squirrel\.Mac\/issues\/182 for more information/, 'See [this link](https://github.com/Microsoft/vscode/issues/7426#issuecomment-425093469) for more information');
this.notificationService.notify({
severity: Severity.Error,
message: error,
source: nls.localize('update service', "Update Service"),
});
}
private onUpdateNotAvailable(): void {
this.dialogService.show(
severity.Info,
nls.localize('noUpdatesAvailable', "There are currently no updates available."),
[nls.localize('ok', "OK")]
);
}
// linux
private onUpdateAvailable(update: IUpdate): void {
if (!this.shouldShowNotification()) {
return;
}
this.notificationService.prompt(
severity.Info,
nls.localize('thereIsUpdateAvailable', "There is an available update."),
[{
label: nls.localize('download update', "Download Update"),
run: () => this.updateService.downloadUpdate()
}, {
label: nls.localize('later', "Later"),
run: () => { }
}, {
label: nls.localize('releaseNotes', "Release Notes"),
run: () => {
const action = this.instantiationService.createInstance(ShowReleaseNotesAction, update.productVersion);
action.run();
action.dispose();
}
}],
{ sticky: true }
);
}
// windows fast updates (target === system)
private onUpdateDownloaded(update: IUpdate): void {
if (!this.shouldShowNotification()) {
return;
}
this.notificationService.prompt(
severity.Info,
nls.localize('updateAvailable', "There's an update available: {0} {1}", product.nameLong, update.productVersion),
[{
label: nls.localize('installUpdate', "Install Update"),
run: () => this.updateService.applyUpdate()
}, {
label: nls.localize('later', "Later"),
run: () => { }
}, {
label: nls.localize('releaseNotes', "Release Notes"),
run: () => {
const action = this.instantiationService.createInstance(ShowReleaseNotesAction, update.productVersion);
action.run();
action.dispose();
}
}],
{ sticky: true }
);
}
// windows fast updates
private onUpdateUpdating(update: IUpdate): void {
if (isWindows && product.target === 'user') {
return;
}
// windows fast updates (target === system)
this.notificationService.prompt(
severity.Info,
nls.localize('updateInstalling', "{0} {1} is being installed in the background; we'll let you know when it's done.", product.nameLong, update.productVersion),
[],
{
neverShowAgain: { id: 'neverShowAgain:update/win32-fast-updates', isSecondary: true }
}
);
}
// windows and mac
private onUpdateReady(update: IUpdate): void {
if (!(isWindows && product.target !== 'user') && !this.shouldShowNotification()) {
return;
}
const actions = [{
label: nls.localize('updateNow', "Update Now"),
run: () => this.updateService.quitAndInstall()
}, {
label: nls.localize('later', "Later"),
run: () => { }
}];
// TODO@joao check why snap updates send `update` as falsy
if (update.productVersion) {
actions.push({
label: nls.localize('releaseNotes', "Release Notes"),
run: () => {
const action = this.instantiationService.createInstance(ShowReleaseNotesAction, update.productVersion);
action.run();
action.dispose();
}
});
}
// windows user fast updates and mac
this.notificationService.prompt(
severity.Info,
nls.localize('updateAvailableAfterRestart', "Restart {0} to apply the latest update.", product.nameLong),
actions,
{ sticky: true }
);
}
private shouldShowNotification(): boolean {
const currentVersion = product.commit;
const currentMillis = new Date().getTime();
const lastKnownVersion = this.storageService.get('update/lastKnownVersion', StorageScope.GLOBAL);
// if version != stored version, save version and date
if (currentVersion !== lastKnownVersion) {
this.storageService.store('update/lastKnownVersion', currentVersion!, StorageScope.GLOBAL);
this.storageService.store('update/updateNotificationTime', currentMillis, StorageScope.GLOBAL);
}
const updateNotificationMillis = this.storageService.getNumber('update/updateNotificationTime', StorageScope.GLOBAL, currentMillis);
const diffDays = (currentMillis - updateNotificationMillis) / (1000 * 60 * 60 * 24);
return diffDays > 5;
}
private registerGlobalActivityActions(): void {
CommandsRegistry.registerCommand('update.check', () => this.updateService.checkForUpdates({ windowId: this.environmentService.configuration.windowId }));
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
group: '5_update',
command: {
id: 'update.check',
title: nls.localize('checkForUpdates', "Check for Updates...")
},
when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.Idle)
});
CommandsRegistry.registerCommand('update.checking', () => { });
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
group: '5_update',
command: {
id: 'update.checking',
title: nls.localize('checkingForUpdates', "Checking for Updates..."),
precondition: FalseContext
},
when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.CheckingForUpdates)
});
CommandsRegistry.registerCommand('update.downloadNow', () => this.updateService.downloadUpdate());
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
group: '5_update',
command: {
id: 'update.downloadNow',
title: nls.localize('download update', "Download Update")
},
when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.AvailableForDownload)
});
CommandsRegistry.registerCommand('update.downloading', () => { });
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
group: '5_update',
command: {
id: 'update.downloading',
title: nls.localize('DownloadingUpdate', "Downloading Update..."),
precondition: FalseContext
},
when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.Downloading)
});
CommandsRegistry.registerCommand('update.install', () => this.updateService.applyUpdate());
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
group: '5_update',
command: {
id: 'update.install',
title: nls.localize('installUpdate...', "Install Update...")
},
when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.Downloaded)
});
CommandsRegistry.registerCommand('update.updating', () => { });
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
group: '5_update',
command: {
id: 'update.updating',
title: nls.localize('installingUpdate', "Installing Update..."),
precondition: FalseContext
},
when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.Updating)
});
CommandsRegistry.registerCommand('update.restart', () => this.updateService.quitAndInstall());
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
group: '5_update',
command: {
id: 'update.restart',
title: nls.localize('restartToUpdate', "Restart to Update")
},
when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.Ready)
});
}
}

View file

@ -0,0 +1,95 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import { IUpdateService, State, UpdateType } from 'vs/platform/update/common/update';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { Disposable } from 'vs/base/common/lifecycle';
export interface IUpdate {
version: string;
}
export interface IUpdateProvider {
/**
* Should return with the `IUpdate` object if an update is
* available or `null` otherwise to signal that there are
* no updates.
*/
checkForUpdate(): Promise<IUpdate | null>;
}
export class UpdateService extends Disposable implements IUpdateService {
_serviceBrand: undefined;
private _onStateChange = this._register(new Emitter<State>());
readonly onStateChange: Event<State> = this._onStateChange.event;
private _state: State = State.Uninitialized;
get state(): State { return this._state; }
set state(state: State) {
this._state = state;
this._onStateChange.fire(state);
}
constructor(
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IWindowService private readonly windowService: IWindowService
) {
super();
this.checkForUpdates();
}
async isLatestVersion(): Promise<boolean> {
const update = await this.doCheckForUpdates();
return !!update;
}
async checkForUpdates(): Promise<void> {
await this.doCheckForUpdates();
}
private async doCheckForUpdates(): Promise<IUpdate | null> {
if (this.environmentService.options && this.environmentService.options.updateProvider) {
const updateProvider = this.environmentService.options.updateProvider;
// State -> Checking for Updates
this.state = State.CheckingForUpdates(null);
const update = await updateProvider.checkForUpdate();
if (update) {
// State -> Downloaded
this.state = State.Ready({ version: update.version, productVersion: update.version });
} else {
// State -> Idle
this.state = State.Idle(UpdateType.Archive);
}
return update;
}
return null; // no update provider to ask
}
async downloadUpdate(): Promise<void> {
// no-op
}
async applyUpdate(): Promise<void> {
this.windowService.reloadWindow();
}
async quitAndInstall(): Promise<void> {
this.windowService.reloadWindow();
}
}
registerSingleton(IUpdateService, UpdateService);

View file

@ -219,6 +219,9 @@ import 'vs/workbench/contrib/format/browser/format.contribution';
// Themes
import 'vs/workbench/contrib/themes/browser/themes.contribution';
// Update
import 'vs/workbench/contrib/update/browser/update.contribution';
// Watermark
import 'vs/workbench/contrib/watermark/browser/watermark';

View file

@ -12,6 +12,7 @@ import { ICredentialsProvider } from 'vs/workbench/services/credentials/browser/
import { IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { IURLCallbackProvider } from 'vs/workbench/services/url/browser/urlService';
import { LogLevel } from 'vs/platform/log/common/log';
import { IUpdateProvider } from 'vs/workbench/services/update/browser/updateService';
export interface IWorkbenchConstructionOptions {
@ -82,6 +83,11 @@ export interface IWorkbenchConstructionOptions {
* Current logging level. Default is `LogLevel.Info`.
*/
logLevel?: LogLevel;
/**
* Experimental: Support for update reporting.
*/
updateProvider?: IUpdateProvider;
}
/**

View file

@ -38,6 +38,7 @@ import 'vs/workbench/services/telemetry/browser/telemetryService';
import 'vs/workbench/services/configurationResolver/browser/configurationResolverService';
import 'vs/workbench/services/credentials/browser/credentialsService';
import 'vs/workbench/services/url/browser/urlService';
import 'vs/workbench/services/update/browser/updateService';
import 'vs/workbench/browser/web.simpleservices';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';

View file

@ -549,6 +549,13 @@
"**/vs/**"
]
},
{
"target": "**/vs/workbench/contrib/update/browser/update.ts",
"restrictions": [
"semver-umd",
"**/vs/**"
]
},
{
"target": "**/vs/code/node/**",
"restrictions": [