mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
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:
@@ -9,35 +9,71 @@ import Event, { NodeEventEmitter } from 'vs/base/common/event';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
|
||||
export enum State {
|
||||
Uninitialized,
|
||||
Idle,
|
||||
CheckingForUpdate,
|
||||
UpdateAvailable,
|
||||
UpdateDownloaded
|
||||
}
|
||||
|
||||
export enum ExplicitState {
|
||||
Implicit,
|
||||
Explicit
|
||||
}
|
||||
|
||||
export interface IRawUpdate {
|
||||
releaseNotes: string;
|
||||
version: string;
|
||||
date: Date;
|
||||
}
|
||||
|
||||
export interface IUpdate {
|
||||
version: string;
|
||||
productVersion: string;
|
||||
date?: Date;
|
||||
releaseNotes?: string;
|
||||
supportsFastUpdate?: boolean;
|
||||
url?: string;
|
||||
hash?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates are run as a state machine:
|
||||
*
|
||||
* Uninitialized
|
||||
* ↓
|
||||
* Idle
|
||||
* ↓ ↑
|
||||
* Checking for Updates → Available for Download
|
||||
* ↓
|
||||
* Downloading → Ready
|
||||
* ↓ ↑
|
||||
* Downloaded → Updating
|
||||
*
|
||||
* Available: There is an update available for download (linux).
|
||||
* Ready: Code will be updated as soon as it restarts (win32, darwin).
|
||||
* Donwloaded: There is an update ready to be installed in the background (win32).
|
||||
*/
|
||||
|
||||
export enum StateType {
|
||||
Uninitialized = 'uninitialized',
|
||||
Idle = 'idle',
|
||||
CheckingForUpdates = 'checking for updates',
|
||||
AvailableForDownload = 'available for download',
|
||||
Downloading = 'downloading',
|
||||
Downloaded = 'downloaded',
|
||||
Updating = 'updating',
|
||||
Ready = 'ready',
|
||||
}
|
||||
|
||||
export type Uninitialized = { type: StateType.Uninitialized };
|
||||
export type Idle = { type: StateType.Idle };
|
||||
export type CheckingForUpdates = { type: StateType.CheckingForUpdates, context: any };
|
||||
export type AvailableForDownload = { type: StateType.AvailableForDownload, update: IUpdate };
|
||||
export type Downloading = { type: StateType.Downloading, update: IUpdate };
|
||||
export type Downloaded = { type: StateType.Downloaded, update: IUpdate };
|
||||
export type Updating = { type: StateType.Updating, update: IUpdate };
|
||||
export type Ready = { type: StateType.Ready, update: IUpdate };
|
||||
|
||||
export type State = Uninitialized | Idle | CheckingForUpdates | AvailableForDownload | Downloading | Downloaded | Updating | Ready;
|
||||
|
||||
export const State = {
|
||||
Uninitialized: { type: StateType.Uninitialized } as Uninitialized,
|
||||
Idle: { type: StateType.Idle } as Idle,
|
||||
CheckingForUpdates: (context: any) => ({ type: StateType.CheckingForUpdates, context } as CheckingForUpdates),
|
||||
AvailableForDownload: (update: IUpdate) => ({ type: StateType.AvailableForDownload, update } as AvailableForDownload),
|
||||
Downloading: (update: IUpdate) => ({ type: StateType.Downloading, update } as Downloading),
|
||||
Downloaded: (update: IUpdate) => ({ type: StateType.Downloaded, update } as Downloaded),
|
||||
Updating: (update: IUpdate) => ({ type: StateType.Updating, update } as Updating),
|
||||
Ready: (update: IUpdate) => ({ type: StateType.Ready, update } as Ready),
|
||||
};
|
||||
|
||||
export interface IAutoUpdater extends NodeEventEmitter {
|
||||
setFeedURL(url: string): void;
|
||||
checkForUpdates(): void;
|
||||
applyUpdate?(): TPromise<void>;
|
||||
quitAndInstall(): void;
|
||||
}
|
||||
|
||||
@@ -46,13 +82,11 @@ export const IUpdateService = createDecorator<IUpdateService>('updateService');
|
||||
export interface IUpdateService {
|
||||
_serviceBrand: any;
|
||||
|
||||
readonly onError: Event<any>;
|
||||
readonly onUpdateAvailable: Event<{ url: string; version: string; }>;
|
||||
readonly onUpdateNotAvailable: Event<boolean>;
|
||||
readonly onUpdateReady: Event<IRawUpdate>;
|
||||
readonly onStateChange: Event<State>;
|
||||
readonly state: State;
|
||||
|
||||
checkForUpdates(explicit: boolean): TPromise<IUpdate>;
|
||||
checkForUpdates(context: any): TPromise<void>;
|
||||
downloadUpdate(): TPromise<void>;
|
||||
applyUpdate(): TPromise<void>;
|
||||
quitAndInstall(): TPromise<void>;
|
||||
}
|
||||
@@ -9,15 +9,12 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IChannel, eventToCall, eventFromCall } from 'vs/base/parts/ipc/common/ipc';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { IUpdateService, IRawUpdate, State, IUpdate } from './update';
|
||||
import { IUpdateService, State } from './update';
|
||||
|
||||
export interface IUpdateChannel extends IChannel {
|
||||
call(command: 'event:onError'): TPromise<void>;
|
||||
call(command: 'event:onUpdateAvailable'): TPromise<void>;
|
||||
call(command: 'event:onUpdateNotAvailable'): TPromise<void>;
|
||||
call(command: 'event:onUpdateReady'): TPromise<void>;
|
||||
call(command: 'event:onStateChange'): TPromise<void>;
|
||||
call(command: 'checkForUpdates', arg: boolean): TPromise<IUpdate>;
|
||||
call(command: 'checkForUpdates', arg: any): TPromise<void>;
|
||||
call(command: 'downloadUpdate'): TPromise<void>;
|
||||
call(command: 'applyUpdate'): TPromise<void>;
|
||||
call(command: 'quitAndInstall'): TPromise<void>;
|
||||
call(command: '_getInitialState'): TPromise<State>;
|
||||
call(command: string, arg?: any): TPromise<any>;
|
||||
@@ -29,12 +26,10 @@ export class UpdateChannel implements IUpdateChannel {
|
||||
|
||||
call(command: string, arg?: any): TPromise<any> {
|
||||
switch (command) {
|
||||
case 'event:onError': return eventToCall(this.service.onError);
|
||||
case 'event:onUpdateAvailable': return eventToCall(this.service.onUpdateAvailable);
|
||||
case 'event:onUpdateNotAvailable': return eventToCall(this.service.onUpdateNotAvailable);
|
||||
case 'event:onUpdateReady': return eventToCall(this.service.onUpdateReady);
|
||||
case 'event:onStateChange': return eventToCall(this.service.onStateChange);
|
||||
case 'checkForUpdates': return this.service.checkForUpdates(arg);
|
||||
case 'downloadUpdate': return this.service.downloadUpdate();
|
||||
case 'applyUpdate': return this.service.applyUpdate();
|
||||
case 'quitAndInstall': return this.service.quitAndInstall();
|
||||
case '_getInitialState': return TPromise.as(this.service.state);
|
||||
}
|
||||
@@ -46,19 +41,8 @@ export class UpdateChannelClient implements IUpdateService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
private _onError = eventFromCall<any>(this.channel, 'event:onError');
|
||||
get onError(): Event<any> { return this._onError; }
|
||||
|
||||
private _onUpdateAvailable = eventFromCall<{ url: string; version: string; }>(this.channel, 'event:onUpdateAvailable');
|
||||
get onUpdateAvailable(): Event<{ url: string; version: string; }> { return this._onUpdateAvailable; }
|
||||
|
||||
private _onUpdateNotAvailable = eventFromCall<boolean>(this.channel, 'event:onUpdateNotAvailable');
|
||||
get onUpdateNotAvailable(): Event<boolean> { return this._onUpdateNotAvailable; }
|
||||
|
||||
private _onUpdateReady = eventFromCall<IRawUpdate>(this.channel, 'event:onUpdateReady');
|
||||
get onUpdateReady(): Event<IRawUpdate> { return this._onUpdateReady; }
|
||||
|
||||
private _onRemoteStateChange = eventFromCall<State>(this.channel, 'event:onStateChange');
|
||||
|
||||
private _onStateChange = new Emitter<State>();
|
||||
get onStateChange(): Event<State> { return this._onStateChange.event; }
|
||||
|
||||
@@ -78,8 +62,16 @@ export class UpdateChannelClient implements IUpdateService {
|
||||
}, onUnexpectedError);
|
||||
}
|
||||
|
||||
checkForUpdates(explicit: boolean): TPromise<IUpdate> {
|
||||
return this.channel.call('checkForUpdates', explicit);
|
||||
checkForUpdates(context: any): TPromise<void> {
|
||||
return this.channel.call('checkForUpdates', context);
|
||||
}
|
||||
|
||||
downloadUpdate(): TPromise<void> {
|
||||
return this.channel.call('downloadUpdate');
|
||||
}
|
||||
|
||||
applyUpdate(): TPromise<void> {
|
||||
return this.channel.call('applyUpdate');
|
||||
}
|
||||
|
||||
quitAndInstall(): TPromise<void> {
|
||||
|
||||
162
src/vs/platform/update/electron-main/abstractUpdateService.ts
Normal file
162
src/vs/platform/update/electron-main/abstractUpdateService.ts
Normal 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;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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']
|
||||
});
|
||||
}
|
||||
}
|
||||
119
src/vs/platform/update/electron-main/updateService.darwin.ts
Normal file
119
src/vs/platform/update/electron-main/updateService.darwin.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
91
src/vs/platform/update/electron-main/updateService.linux.ts
Normal file
91
src/vs/platform/update/electron-main/updateService.linux.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
212
src/vs/platform/update/electron-main/updateService.win32.ts
Normal file
212
src/vs/platform/update/electron-main/updateService.win32.ts
Normal 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']
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
32
src/vs/platform/update/node/update.config.contribution.ts
Normal file
32
src/vs/platform/update/node/update.config.contribution.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 nls from 'vs/nls';
|
||||
import product from 'vs/platform/node/product';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
|
||||
const configurationRegistry = <IConfigurationRegistry>Registry.as(ConfigurationExtensions.Configuration);
|
||||
configurationRegistry.registerConfiguration({
|
||||
'id': 'update',
|
||||
'order': 15,
|
||||
'title': nls.localize('updateConfigurationTitle', "Update"),
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'update.channel': {
|
||||
'type': 'string',
|
||||
'enum': ['none', 'default'],
|
||||
'default': 'default',
|
||||
'description': nls.localize('updateChannel', "Configure whether you receive automatic updates from an update channel. Requires a restart after change.")
|
||||
},
|
||||
'update.enableWindowsBackgroundUpdates': {
|
||||
'type': 'boolean',
|
||||
'default': product.quality === 'insider',
|
||||
'description': nls.localize('enableWindowsBackgroundUpdates', "Enables Windows background updates.")
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user