Merge VS Code 1.21 source code (#1067)

* Initial VS Code 1.21 file copy with patches

* A few more merges

* Post npm install

* Fix batch of build breaks

* Fix more build breaks

* Fix more build errors

* Fix more build breaks

* Runtime fixes 1

* Get connection dialog working with some todos

* Fix a few packaging issues

* Copy several node_modules to package build to fix loader issues

* Fix breaks from master

* A few more fixes

* Make tests pass

* First pass of license header updates

* Second pass of license header updates

* Fix restore dialog issues

* Remove add additional themes menu items

* fix select box issues where the list doesn't show up

* formatting

* Fix editor dispose issue

* Copy over node modules to correct location on all platforms
This commit is contained in:
Karl Burtram
2018-04-04 15:27:51 -07:00
committed by GitHub
parent 5fba3e31b4
commit dafb780987
9412 changed files with 141255 additions and 98813 deletions

View File

@@ -0,0 +1,162 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import Event, { Emitter } from 'vs/base/common/event';
import { Throttler } from 'vs/base/common/async';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import product from 'vs/platform/node/product';
import { TPromise } from 'vs/base/common/winjs.base';
import { IUpdateService, State, StateType, AvailableForDownload } from 'vs/platform/update/common/update';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ILogService } from 'vs/platform/log/common/log';
export function createUpdateURL(platform: string, quality: string): string {
return `${product.updateUrl}/api/update/${platform}/${quality}/${product.commit}`;
}
export abstract class AbstractUpdateService implements IUpdateService {
_serviceBrand: any;
private _state: State = State.Uninitialized;
private throttler: Throttler = new Throttler();
private _onStateChange = new Emitter<State>();
get onStateChange(): Event<State> { return this._onStateChange.event; }
get state(): State {
return this._state;
}
protected setState(state: State): void {
this.logService.info('update#setState', state.type);
this._state = state;
this._onStateChange.fire(state);
}
constructor(
@ILifecycleService private lifecycleService: ILifecycleService,
@IConfigurationService protected configurationService: IConfigurationService,
@IEnvironmentService private environmentService: IEnvironmentService,
@ILogService protected logService: ILogService
) {
if (this.environmentService.disableUpdates) {
this.logService.info('update#ctor - updates are disabled');
return;
}
if (!product.updateUrl || !product.commit) {
this.logService.info('update#ctor - updates are disabled');
return;
}
const quality = this.getProductQuality();
if (!quality) {
this.logService.info('update#ctor - updates are disabled');
return;
}
if (!this.setUpdateFeedUrl(quality)) {
this.logService.info('update#ctor - updates are disabled');
return;
}
this.setState({ type: StateType.Idle });
// Start checking for updates after 30 seconds
this.scheduleCheckForUpdates(30 * 1000)
.done(null, err => this.logService.error(err));
}
private getProductQuality(): string {
const quality = this.configurationService.getValue<string>('update.channel');
return quality === 'none' ? null : product.quality;
}
private scheduleCheckForUpdates(delay = 60 * 60 * 1000): TPromise<void> {
return TPromise.timeout(delay)
.then(() => this.checkForUpdates(null))
.then(update => {
if (update) {
// Update found, no need to check more
return TPromise.as(null);
}
// Check again after 1 hour
return this.scheduleCheckForUpdates(60 * 60 * 1000);
});
}
checkForUpdates(context: any): TPromise<void> {
this.logService.trace('update#checkForUpdates, state = ', this.state.type);
if (this.state.type !== StateType.Idle) {
return TPromise.as(null);
}
return this.throttler.queue(() => TPromise.as(this.doCheckForUpdates(context)));
}
downloadUpdate(): TPromise<void> {
this.logService.trace('update#downloadUpdate, state = ', this.state.type);
if (this.state.type !== StateType.AvailableForDownload) {
return TPromise.as(null);
}
return this.doDownloadUpdate(this.state);
}
protected doDownloadUpdate(state: AvailableForDownload): TPromise<void> {
return TPromise.as(null);
}
applyUpdate(): TPromise<void> {
this.logService.trace('update#applyUpdate, state = ', this.state.type);
if (this.state.type !== StateType.Downloaded) {
return TPromise.as(null);
}
return this.doApplyUpdate();
}
protected doApplyUpdate(): TPromise<void> {
return TPromise.as(null);
}
quitAndInstall(): TPromise<void> {
this.logService.trace('update#quitAndInstall, state = ', this.state.type);
if (this.state.type !== StateType.Ready) {
return TPromise.as(null);
}
this.logService.trace('update#quitAndInstall(): before lifecycle quit()');
this.lifecycleService.quit(true /* from update */).done(vetod => {
this.logService.trace(`update#quitAndInstall(): after lifecycle quit() with veto: ${vetod}`);
if (vetod) {
return;
}
this.logService.trace('update#quitAndInstall(): running raw#quitAndInstall()');
this.doQuitAndInstall();
});
return TPromise.as(null);
}
protected doQuitAndInstall(): void {
// noop
}
protected abstract setUpdateFeedUrl(quality: string): boolean;
protected abstract doCheckForUpdates(context: any): void;
}

View File

@@ -1,77 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { EventEmitter } from 'events';
import { isString } from 'vs/base/common/types';
import { Promise } from 'vs/base/common/winjs.base';
import { asJson } from 'vs/base/node/request';
import { IRequestService } from 'vs/platform/request/node/request';
import { IAutoUpdater } from 'vs/platform/update/common/update';
import product from 'vs/platform/node/product';
interface IUpdate {
url: string;
name: string;
releaseNotes?: string;
version: string;
productVersion: string;
hash: string;
}
export class LinuxAutoUpdaterImpl extends EventEmitter implements IAutoUpdater {
private url: string;
private currentRequest: Promise;
constructor(
@IRequestService private requestService: IRequestService
) {
super();
this.url = null;
this.currentRequest = null;
}
setFeedURL(url: string): void {
this.url = url;
}
checkForUpdates(): void {
if (!this.url) {
throw new Error('No feed url set.');
}
if (this.currentRequest) {
return;
}
this.emit('checking-for-update');
this.currentRequest = this.requestService.request({ url: this.url })
.then<IUpdate>(asJson)
.then(update => {
if (!update || !update.url || !update.version || !update.productVersion) {
this.emit('update-not-available');
} else {
this.emit('update-available', null, product.downloadUrl, update.productVersion);
}
})
.then(null, e => {
if (isString(e) && /^Server returned/.test(e)) {
return;
}
this.emit('update-not-available');
this.emit('error', e);
})
.then(() => this.currentRequest = null);
}
quitAndInstall(): void {
// noop
}
}

View File

@@ -1,141 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as path from 'path';
import * as pfs from 'vs/base/node/pfs';
import { checksum } from 'vs/base/node/crypto';
import { EventEmitter } from 'events';
import { tmpdir } from 'os';
import { spawn } from 'child_process';
import { isString } from 'vs/base/common/types';
import { Promise, TPromise } from 'vs/base/common/winjs.base';
import { download, asJson } from 'vs/base/node/request';
import { IRequestService } from 'vs/platform/request/node/request';
import { IAutoUpdater } from 'vs/platform/update/common/update';
import product from 'vs/platform/node/product';
interface IUpdate {
url: string;
name: string;
releaseNotes?: string;
version: string;
productVersion: string;
hash: string;
}
export class Win32AutoUpdaterImpl extends EventEmitter implements IAutoUpdater {
private url: string = null;
private currentRequest: Promise = null;
private updatePackagePath: string = null;
constructor(
@IRequestService private requestService: IRequestService
) {
super();
}
get cachePath(): TPromise<string> {
// {{SQL CARBON EDIT}}
const result = path.join(tmpdir(), `sqlops-update-${process.arch}`);
return pfs.mkdirp(result, null).then(() => result);
}
setFeedURL(url: string): void {
this.url = url;
}
checkForUpdates(): void {
if (!this.url) {
throw new Error('No feed url set.');
}
if (this.currentRequest) {
return;
}
this.emit('checking-for-update');
this.currentRequest = this.requestService.request({ url: this.url })
.then<IUpdate>(asJson)
.then(update => {
if (!update || !update.url || !update.version) {
this.emit('update-not-available');
return this.cleanup();
}
this.emit('update-available');
return this.cleanup(update.version).then(() => {
return this.getUpdatePackagePath(update.version).then(updatePackagePath => {
return pfs.exists(updatePackagePath).then(exists => {
if (exists) {
return TPromise.as(updatePackagePath);
}
const url = update.url;
const hash = update.hash;
const downloadPath = `${updatePackagePath}.tmp`;
return this.requestService.request({ url })
.then(context => download(downloadPath, context))
.then(hash ? () => checksum(downloadPath, update.hash) : () => null)
.then(() => pfs.rename(downloadPath, updatePackagePath))
.then(() => updatePackagePath);
});
}).then(updatePackagePath => {
this.updatePackagePath = updatePackagePath;
this.emit('update-downloaded',
{},
update.releaseNotes,
update.productVersion,
new Date(),
this.url
);
});
});
})
.then(null, e => {
if (isString(e) && /^Server returned/.test(e)) {
return;
}
this.emit('update-not-available');
this.emit('error', e);
})
.then(() => this.currentRequest = null);
}
private getUpdatePackagePath(version: string): TPromise<string> {
// {{SQL CARBON EDIT}}
return this.cachePath.then(cachePath => path.join(cachePath, `SqlOpsStudioSetup-${product.quality}-${version}.exe`));
}
private cleanup(exceptVersion: string = null): Promise {
const filter = exceptVersion ? one => !(new RegExp(`${product.quality}-${exceptVersion}\\.exe$`).test(one)) : () => true;
return this.cachePath
.then(cachePath => pfs.readdir(cachePath)
.then(all => Promise.join(all
.filter(filter)
.map(one => pfs.unlink(path.join(cachePath, one)).then(null, () => null))
))
);
}
quitAndInstall(): void {
if (!this.updatePackagePath) {
return;
}
spawn(this.updatePackagePath, ['/silent', '/mergetasks=runcode,!desktopicon,!quicklaunchicon'], {
detached: true,
stdio: ['ignore', 'ignore', 'ignore']
});
}
}

View File

@@ -0,0 +1,119 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as electron from 'electron';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import Event, { fromNodeEventEmitter } from 'vs/base/common/event';
import { memoize } from 'vs/base/common/decorators';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import { State, IUpdate, StateType } from 'vs/platform/update/common/update';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ILogService } from 'vs/platform/log/common/log';
import { AbstractUpdateService, createUpdateURL } from 'vs/platform/update/electron-main/abstractUpdateService';
export class DarwinUpdateService extends AbstractUpdateService {
_serviceBrand: any;
private disposables: IDisposable[] = [];
@memoize private get onRawError(): Event<string> { return fromNodeEventEmitter(electron.autoUpdater, 'error', (_, message) => message); }
@memoize private get onRawUpdateNotAvailable(): Event<void> { return fromNodeEventEmitter<void>(electron.autoUpdater, 'update-not-available'); }
@memoize private get onRawUpdateAvailable(): Event<IUpdate> { return fromNodeEventEmitter(electron.autoUpdater, 'update-available', (_, url, version) => ({ url, version, productVersion: version })); }
@memoize private get onRawUpdateDownloaded(): Event<IUpdate> { return fromNodeEventEmitter(electron.autoUpdater, 'update-downloaded', (_, releaseNotes, version, date) => ({ releaseNotes, version, productVersion: version, date })); }
constructor(
@ILifecycleService lifecycleService: ILifecycleService,
@IConfigurationService configurationService: IConfigurationService,
@ITelemetryService private telemetryService: ITelemetryService,
@IEnvironmentService environmentService: IEnvironmentService,
@ILogService logService: ILogService
) {
super(lifecycleService, configurationService, environmentService, logService);
this.onRawError(this.onError, this, this.disposables);
this.onRawUpdateAvailable(this.onUpdateAvailable, this, this.disposables);
this.onRawUpdateDownloaded(this.onUpdateDownloaded, this, this.disposables);
this.onRawUpdateNotAvailable(this.onUpdateNotAvailable, this, this.disposables);
}
private onError(err: string): void {
this.logService.error('UpdateService error: ', err);
this.setState(State.Idle);
}
protected setUpdateFeedUrl(quality: string): boolean {
try {
electron.autoUpdater.setFeedURL(createUpdateURL('darwin', quality));
} catch (e) {
// application is very likely not signed
this.logService.error('Failed to set update feed URL');
return false;
}
return true;
}
protected doCheckForUpdates(context: any): void {
this.setState(State.CheckingForUpdates(context));
electron.autoUpdater.checkForUpdates();
}
private onUpdateAvailable(update: IUpdate): void {
if (this.state.type !== StateType.CheckingForUpdates) {
return;
}
this.setState(State.Downloading(update));
}
private onUpdateDownloaded(update: IUpdate): void {
if (this.state.type !== StateType.Downloading) {
return;
}
/* __GDPR__
"update:downloaded" : {
"version" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('update:downloaded', { version: update.version });
this.setState(State.Ready(update));
}
private onUpdateNotAvailable(): void {
if (this.state.type !== StateType.CheckingForUpdates) {
return;
}
/* __GDPR__
"update:notAvailable" : {
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('update:notAvailable', { explicit: !!this.state.context });
this.setState(State.Idle);
}
protected doQuitAndInstall(): void {
// for some reason updating on Mac causes the local storage not to be flushed.
// we workaround this issue by forcing an explicit flush of the storage data.
// see also https://github.com/Microsoft/vscode/issues/172
this.logService.trace('update#quitAndInstall(): calling flushStorageData()');
electron.session.defaultSession.flushStorageData();
this.logService.trace('update#quitAndInstall(): running raw#quitAndInstall()');
electron.autoUpdater.quitAndInstall();
}
dispose(): void {
this.disposables = dispose(this.disposables);
}
}

View File

@@ -0,0 +1,91 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import product from 'vs/platform/node/product';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import { IRequestService } from 'vs/platform/request/node/request';
import { State, IUpdate, AvailableForDownload } from 'vs/platform/update/common/update';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ILogService } from 'vs/platform/log/common/log';
import { createUpdateURL, AbstractUpdateService } from 'vs/platform/update/electron-main/abstractUpdateService';
import { asJson } from 'vs/base/node/request';
import { TPromise } from 'vs/base/common/winjs.base';
import { shell } from 'electron';
export class LinuxUpdateService extends AbstractUpdateService {
_serviceBrand: any;
private url: string | undefined;
constructor(
@ILifecycleService lifecycleService: ILifecycleService,
@IConfigurationService configurationService: IConfigurationService,
@ITelemetryService private telemetryService: ITelemetryService,
@IEnvironmentService environmentService: IEnvironmentService,
@IRequestService private requestService: IRequestService,
@ILogService logService: ILogService
) {
super(lifecycleService, configurationService, environmentService, logService);
}
protected setUpdateFeedUrl(quality: string): boolean {
this.url = createUpdateURL(`linux-${process.arch}`, quality);
return true;
}
protected doCheckForUpdates(context: any): void {
if (!this.url) {
return;
}
this.setState(State.CheckingForUpdates(context));
this.requestService.request({ url: this.url })
.then<IUpdate>(asJson)
.then(update => {
if (!update || !update.url || !update.version || !update.productVersion) {
/* __GDPR__
"update:notAvailable" : {
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('update:notAvailable', { explicit: !!context });
this.setState(State.Idle);
} else {
this.setState(State.AvailableForDownload(update));
}
})
.then(null, err => {
this.logService.error(err);
/* __GDPR__
"update:notAvailable" : {
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('update:notAvailable', { explicit: !!context });
this.setState(State.Idle);
});
}
protected doDownloadUpdate(state: AvailableForDownload): TPromise<void> {
// Use the download URL if available as we don't currently detect the package type that was
// installed and the website download page is more useful than the tarball generally.
if (product.downloadUrl && product.downloadUrl.length > 0) {
shell.openExternal(product.downloadUrl);
} else {
shell.openExternal(state.update.url);
}
this.setState(State.Idle);
return TPromise.as(null);
}
}

View File

@@ -1,295 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as fs from 'original-fs';
import * as path from 'path';
import * as electron from 'electron';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import Event, { Emitter, once, filterEvent, fromNodeEventEmitter } from 'vs/base/common/event';
import { always, Throttler } from 'vs/base/common/async';
import { memoize } from 'vs/base/common/decorators';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { Win32AutoUpdaterImpl } from './auto-updater.win32';
import { LinuxAutoUpdaterImpl } from './auto-updater.linux';
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import { IRequestService } from 'vs/platform/request/node/request';
import product from 'vs/platform/node/product';
import { TPromise } from 'vs/base/common/winjs.base';
import { IUpdateService, State, IAutoUpdater, IUpdate, IRawUpdate } from 'vs/platform/update/common/update';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ILogService } from 'vs/platform/log/common/log';
export class UpdateService implements IUpdateService {
_serviceBrand: any;
private _state: State = State.Uninitialized;
private _availableUpdate: IUpdate = null;
private raw: IAutoUpdater;
private throttler: Throttler = new Throttler();
private _onError = new Emitter<any>();
get onError(): Event<any> { return this._onError.event; }
private _onCheckForUpdate = new Emitter<void>();
get onCheckForUpdate(): Event<void> { return this._onCheckForUpdate.event; }
private _onUpdateAvailable = new Emitter<{ url: string; version: string; }>();
get onUpdateAvailable(): Event<{ url: string; version: string; }> { return this._onUpdateAvailable.event; }
private _onUpdateNotAvailable = new Emitter<boolean>();
get onUpdateNotAvailable(): Event<boolean> { return this._onUpdateNotAvailable.event; }
private _onUpdateReady = new Emitter<IRawUpdate>();
get onUpdateReady(): Event<IRawUpdate> { return this._onUpdateReady.event; }
private _onStateChange = new Emitter<State>();
get onStateChange(): Event<State> { return this._onStateChange.event; }
@memoize
private get onRawError(): Event<string> {
return fromNodeEventEmitter(this.raw, 'error', (_, message) => message);
}
@memoize
private get onRawUpdateNotAvailable(): Event<void> {
return fromNodeEventEmitter<void>(this.raw, 'update-not-available');
}
@memoize
private get onRawUpdateAvailable(): Event<{ url: string; version: string; }> {
return filterEvent(fromNodeEventEmitter(this.raw, 'update-available', (_, url, version) => ({ url, version })), ({ url }) => !!url);
}
@memoize
private get onRawUpdateDownloaded(): Event<IRawUpdate> {
return fromNodeEventEmitter(this.raw, 'update-downloaded', (_, releaseNotes, version, date, url) => ({ releaseNotes, version, date }));
}
get state(): State {
return this._state;
}
set state(state: State) {
this._state = state;
this._onStateChange.fire(state);
}
get availableUpdate(): IUpdate {
return this._availableUpdate;
}
constructor(
@IRequestService requestService: IRequestService,
@ILifecycleService private lifecycleService: ILifecycleService,
@IConfigurationService private configurationService: IConfigurationService,
@ITelemetryService private telemetryService: ITelemetryService,
@IEnvironmentService private environmentService: IEnvironmentService,
@ILogService private logService: ILogService
) {
if (process.platform === 'win32') {
this.raw = new Win32AutoUpdaterImpl(requestService);
} else if (process.platform === 'linux') {
this.raw = new LinuxAutoUpdaterImpl(requestService);
} else if (process.platform === 'darwin') {
this.raw = electron.autoUpdater;
} else {
return;
}
if (this.environmentService.disableUpdates) {
return;
}
const channel = this.getUpdateChannel();
const feedUrl = this.getUpdateFeedUrl(channel);
if (!feedUrl) {
return; // updates not available
}
try {
this.raw.setFeedURL(feedUrl);
} catch (e) {
return; // application not signed
}
this.state = State.Idle;
// Start checking for updates after 30 seconds
this.scheduleCheckForUpdates(30 * 1000)
.done(null, err => this.logService.error(err));
}
private scheduleCheckForUpdates(delay = 60 * 60 * 1000): TPromise<void> {
return TPromise.timeout(delay)
.then(() => this.checkForUpdates())
.then(update => {
if (update) {
// Update found, no need to check more
return TPromise.as(null);
}
// Check again after 1 hour
return this.scheduleCheckForUpdates(60 * 60 * 1000);
});
}
checkForUpdates(explicit = false): TPromise<IUpdate> {
return this.throttler.queue(() => this._checkForUpdates(explicit))
.then(null, err => {
if (explicit) {
this._onError.fire(err);
}
return null;
});
}
private _checkForUpdates(explicit: boolean): TPromise<IUpdate> {
if (this.state !== State.Idle) {
return TPromise.as(null);
}
this._onCheckForUpdate.fire();
this.state = State.CheckingForUpdate;
const listeners: IDisposable[] = [];
const result = new TPromise<IUpdate>((c, e) => {
once(this.onRawError)(e, null, listeners);
once(this.onRawUpdateNotAvailable)(() => c(null), null, listeners);
once(this.onRawUpdateAvailable)(({ url, version }) => url && c({ url, version }), null, listeners);
once(this.onRawUpdateDownloaded)(({ version, date, releaseNotes }) => c({ version, date, releaseNotes }), null, listeners);
this.raw.checkForUpdates();
}).then(update => {
if (!update) {
this._onUpdateNotAvailable.fire(explicit);
this.state = State.Idle;
/* __GDPR__
"update:notAvailable" : {
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('update:notAvailable', { explicit });
} else if (update.url) {
const data: IUpdate = {
url: update.url,
releaseNotes: '',
version: update.version,
date: new Date()
};
this._availableUpdate = data;
this._onUpdateAvailable.fire({ url: update.url, version: update.version });
this.state = State.UpdateAvailable;
/* __GDPR__
"update:available" : {
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"version": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"currentVersion": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('update:available', { explicit, version: update.version, currentVersion: product.commit });
} else {
const data: IRawUpdate = {
releaseNotes: update.releaseNotes,
version: update.version,
date: update.date
};
this._availableUpdate = data;
this._onUpdateReady.fire(data);
this.state = State.UpdateDownloaded;
/* __GDPR__
"update:downloaded" : {
"version" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('update:downloaded', { version: update.version });
}
return update;
}, err => {
this.state = State.Idle;
return TPromise.wrapError<IUpdate>(err);
});
return always(result, () => dispose(listeners));
}
private getUpdateChannel(): string {
const channel = this.configurationService.getValue<string>('update.channel');
return channel === 'none' ? null : product.quality;
}
private getUpdateFeedUrl(channel: string): string {
if (!channel) {
return null;
}
if (process.platform === 'win32' && !fs.existsSync(path.join(path.dirname(process.execPath), 'unins000.exe'))) {
return null;
}
if (!product.updateUrl || !product.commit) {
return null;
}
const platform = this.getUpdatePlatform();
return `${product.updateUrl}/api/update/${platform}/${channel}/${product.commit}`;
}
private getUpdatePlatform(): string {
if (process.platform === 'linux') {
return `linux-${process.arch}`;
}
if (process.platform === 'win32' && process.arch === 'x64') {
return 'win32-x64';
}
return process.platform;
}
quitAndInstall(): TPromise<void> {
if (!this._availableUpdate) {
return TPromise.as(null);
}
if (this._availableUpdate.url) {
electron.shell.openExternal(this._availableUpdate.url);
return TPromise.as(null);
}
this.logService.trace('update#quitAndInstall(): before lifecycle quit()');
this.lifecycleService.quit(true /* from update */).done(vetod => {
this.logService.trace(`update#quitAndInstall(): after lifecycle quit() with veto: ${vetod}`);
if (vetod) {
return;
}
// for some reason updating on Mac causes the local storage not to be flushed.
// we workaround this issue by forcing an explicit flush of the storage data.
// see also https://github.com/Microsoft/vscode/issues/172
if (process.platform === 'darwin') {
this.logService.trace('update#quitAndInstall(): calling flushStorageData()');
electron.session.defaultSession.flushStorageData();
}
this.logService.trace('update#quitAndInstall(): running raw#quitAndInstall()');
this.raw.quitAndInstall();
});
return TPromise.as(null);
}
}

View File

@@ -0,0 +1,212 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as fs from 'original-fs';
import * as path from 'path';
import * as pfs from 'vs/base/node/pfs';
import { memoize } from 'vs/base/common/decorators';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import { IRequestService } from 'vs/platform/request/node/request';
import product from 'vs/platform/node/product';
import { TPromise, Promise } from 'vs/base/common/winjs.base';
import { State, IUpdate, StateType } from 'vs/platform/update/common/update';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ILogService } from 'vs/platform/log/common/log';
import { createUpdateURL, AbstractUpdateService } from 'vs/platform/update/electron-main/abstractUpdateService';
import { download, asJson } from 'vs/base/node/request';
import { checksum } from 'vs/base/node/crypto';
import { tmpdir } from 'os';
import { spawn } from 'child_process';
function pollUntil(fn: () => boolean, timeout = 1000): TPromise<void> {
return new TPromise<void>(c => {
const poll = () => {
if (fn()) {
c(null);
} else {
setTimeout(poll, timeout);
}
};
poll();
});
}
interface IAvailableUpdate {
packagePath: string;
updateFilePath?: string;
}
export class Win32UpdateService extends AbstractUpdateService {
_serviceBrand: any;
private url: string | undefined;
private availableUpdate: IAvailableUpdate | undefined;
@memoize
get cachePath(): TPromise<string> {
// {{SQL CARBON EDIT}}
const result = path.join(tmpdir(), `sqlops-update-${process.arch}`);
return pfs.mkdirp(result, null).then(() => result);
}
constructor(
@ILifecycleService lifecycleService: ILifecycleService,
@IConfigurationService configurationService: IConfigurationService,
@ITelemetryService private telemetryService: ITelemetryService,
@IEnvironmentService environmentService: IEnvironmentService,
@IRequestService private requestService: IRequestService,
@ILogService logService: ILogService
) {
super(lifecycleService, configurationService, environmentService, logService);
}
protected setUpdateFeedUrl(quality: string): boolean {
if (!fs.existsSync(path.join(path.dirname(process.execPath), 'unins000.exe'))) {
return false;
}
this.url = createUpdateURL(process.arch === 'x64' ? 'win32-x64' : 'win32', quality);
return true;
}
protected doCheckForUpdates(context: any): void {
if (!this.url) {
return;
}
this.setState(State.CheckingForUpdates(context));
this.requestService.request({ url: this.url })
.then<IUpdate>(asJson)
.then(update => {
if (!update || !update.url || !update.version) {
/* __GDPR__
"update:notAvailable" : {
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('update:notAvailable', { explicit: !!context });
this.setState(State.Idle);
return TPromise.as(null);
}
this.setState(State.Downloading(update));
return this.cleanup(update.version).then(() => {
return this.getUpdatePackagePath(update.version).then(updatePackagePath => {
return pfs.exists(updatePackagePath).then(exists => {
if (exists) {
return TPromise.as(updatePackagePath);
}
const url = update.url;
const hash = update.hash;
const downloadPath = `${updatePackagePath}.tmp`;
return this.requestService.request({ url })
.then(context => download(downloadPath, context))
.then(hash ? () => checksum(downloadPath, update.hash) : () => null)
.then(() => pfs.rename(downloadPath, updatePackagePath))
.then(() => updatePackagePath);
});
}).then(packagePath => {
const fastUpdatesEnabled = this.configurationService.getValue<boolean>('update.enableWindowsBackgroundUpdates');
this.availableUpdate = { packagePath };
if (fastUpdatesEnabled && update.supportsFastUpdate) {
this.setState(State.Downloaded(update));
} else {
this.setState(State.Ready(update));
}
});
});
})
.then(null, err => {
this.logService.error(err);
/* __GDPR__
"update:notAvailable" : {
"explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('update:notAvailable', { explicit: !!context });
this.setState(State.Idle);
});
}
private getUpdatePackagePath(version: string): TPromise<string> {
// {{SQL CARBON EDIT}}
return this.cachePath.then(cachePath => path.join(cachePath, `SqlOpsStudioSetup-${product.quality}-${version}.exe`));
}
private cleanup(exceptVersion: string = null): Promise {
const filter = exceptVersion ? one => !(new RegExp(`${product.quality}-${exceptVersion}\\.exe$`).test(one)) : () => true;
return this.cachePath
.then(cachePath => pfs.readdir(cachePath)
.then(all => Promise.join(all
.filter(filter)
.map(one => pfs.unlink(path.join(cachePath, one)).then(null, () => null))
))
);
}
protected doApplyUpdate(): TPromise<void> {
if (this.state.type !== StateType.Downloaded || !this.availableUpdate) {
return TPromise.as(null);
}
const update = this.state.update;
this.setState(State.Updating(update));
return this.cachePath.then(cachePath => {
this.availableUpdate.updateFilePath = path.join(cachePath, `CodeSetup-${product.quality}-${update.version}.flag`);
return pfs.writeFile(this.availableUpdate.updateFilePath, 'flag').then(() => {
const child = spawn(this.availableUpdate.packagePath, ['/verysilent', `/update="${this.availableUpdate.updateFilePath}"`, '/nocloseapplications', '/mergetasks=runcode,!desktopicon,!quicklaunchicon'], {
detached: true,
stdio: ['ignore', 'ignore', 'ignore'],
windowsVerbatimArguments: true
});
child.once('exit', () => {
this.availableUpdate = undefined;
this.setState(State.Idle);
});
const readyMutexName = `${product.win32MutexName}-ready`;
const isActive = (require.__$__nodeRequire('windows-mutex') as any).isActive;
// poll for mutex-ready
pollUntil(() => isActive(readyMutexName))
.then(() => this.setState(State.Ready(update)));
});
});
}
protected doQuitAndInstall(): void {
if (this.state.type !== StateType.Ready) {
return;
}
this.logService.trace('update#quitAndInstall(): running raw#quitAndInstall()');
if (this.state.update.supportsFastUpdate && this.availableUpdate.updateFilePath) {
fs.unlinkSync(this.availableUpdate.updateFilePath);
} else {
spawn(this.availableUpdate.packagePath, ['/silent', '/mergetasks=runcode,!desktopicon,!quicklaunchicon'], {
detached: true,
stdio: ['ignore', 'ignore', 'ignore']
});
}
}
}