@sussudio/platform
Version:
Internal APIs for VS Code's service injection the base services.
195 lines (194 loc) • 7.09 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
var __decorate =
(this && this.__decorate) ||
function (decorators, target, key, desc) {
var c = arguments.length,
r = c < 3 ? target : desc === null ? (desc = Object.getOwnPropertyDescriptor(target, key)) : desc,
d;
if (typeof Reflect === 'object' && typeof Reflect.decorate === 'function')
r = Reflect.decorate(decorators, target, key, desc);
else
for (var i = decorators.length - 1; i >= 0; i--)
if ((d = decorators[i])) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __param =
(this && this.__param) ||
function (paramIndex, decorator) {
return function (target, key) {
decorator(target, key, paramIndex);
};
};
import { spawn } from 'child_process';
import { realpath, watch } from 'fs';
import { timeout } from '@sussudio/base/common/async.mjs';
import { Emitter, Event } from '@sussudio/base/common/event.mjs';
import * as path from '@sussudio/base/common/path.mjs';
import { IEnvironmentMainService } from '../../environment/electron-main/environmentMainService.mjs';
import { ILifecycleMainService } from '../../lifecycle/electron-main/lifecycleMainService.mjs';
import { ILogService } from '../../log/common/log.mjs';
import { ITelemetryService } from '../../telemetry/common/telemetry.mjs';
import { State } from '../common/update.mjs';
let AbstractUpdateService = class AbstractUpdateService {
lifecycleMainService;
logService;
_state = State.Uninitialized;
_onStateChange = new Emitter();
onStateChange = this._onStateChange.event;
get state() {
return this._state;
}
setState(state) {
this.logService.info('update#setState', state.type);
this._state = state;
this._onStateChange.fire(state);
}
constructor(lifecycleMainService, environmentMainService, logService) {
this.lifecycleMainService = lifecycleMainService;
this.logService = logService;
if (environmentMainService.disableUpdates) {
this.logService.info('update#ctor - updates are disabled');
return;
}
this.setState(State.Idle(this.getUpdateType()));
// Start checking for updates after 30 seconds
this.scheduleCheckForUpdates(30 * 1000).then(undefined, (err) => this.logService.error(err));
}
scheduleCheckForUpdates(delay = 60 * 60 * 1000) {
return timeout(delay)
.then(() => this.checkForUpdates(false))
.then(() => {
// Check again after 1 hour
return this.scheduleCheckForUpdates(60 * 60 * 1000);
});
}
async checkForUpdates(explicit) {
this.logService.trace('update#checkForUpdates, state = ', this.state.type);
if (this.state.type !== 'idle' /* StateType.Idle */) {
return;
}
this.doCheckForUpdates(explicit);
}
async downloadUpdate() {
this.logService.trace('update#downloadUpdate, state = ', this.state.type);
if (this.state.type !== 'available for download' /* StateType.AvailableForDownload */) {
return;
}
await this.doDownloadUpdate(this.state);
}
doDownloadUpdate(state) {
return Promise.resolve(undefined);
}
async applyUpdate() {
this.logService.trace('update#applyUpdate, state = ', this.state.type);
if (this.state.type !== 'downloaded' /* StateType.Downloaded */) {
return;
}
await this.doApplyUpdate();
}
doApplyUpdate() {
return Promise.resolve(undefined);
}
quitAndInstall() {
this.logService.trace('update#quitAndInstall, state = ', this.state.type);
if (this.state.type !== 'ready' /* StateType.Ready */) {
return Promise.resolve(undefined);
}
this.logService.trace('update#quitAndInstall(): before lifecycle quit()');
this.lifecycleMainService.quit(true /* will restart */).then((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 Promise.resolve(undefined);
}
getUpdateType() {
return 2 /* UpdateType.Snap */;
}
doQuitAndInstall() {
// noop
}
async _applySpecificUpdate(packagePath) {
// noop
}
};
AbstractUpdateService = __decorate(
[__param(0, ILifecycleMainService), __param(1, IEnvironmentMainService), __param(2, ILogService)],
AbstractUpdateService,
);
let SnapUpdateService = class SnapUpdateService extends AbstractUpdateService {
snap;
snapRevision;
telemetryService;
constructor(snap, snapRevision, lifecycleMainService, environmentMainService, logService, telemetryService) {
super(lifecycleMainService, environmentMainService, logService);
this.snap = snap;
this.snapRevision = snapRevision;
this.telemetryService = telemetryService;
const watcher = watch(path.dirname(this.snap));
const onChange = Event.fromNodeEventEmitter(watcher, 'change', (_, fileName) => fileName);
const onCurrentChange = Event.filter(onChange, (n) => n === 'current');
const onDebouncedCurrentChange = Event.debounce(onCurrentChange, (_, e) => e, 2000);
const listener = onDebouncedCurrentChange(() => this.checkForUpdates(false));
lifecycleMainService.onWillShutdown(() => {
listener.dispose();
watcher.close();
});
}
doCheckForUpdates() {
this.setState(State.CheckingForUpdates(false));
this.isUpdateAvailable().then(
(result) => {
if (result) {
this.setState(State.Ready({ version: 'something', productVersion: 'something' }));
} else {
this.telemetryService.publicLog2('update:notAvailable', { explicit: false });
this.setState(State.Idle(2 /* UpdateType.Snap */));
}
},
(err) => {
this.logService.error(err);
this.telemetryService.publicLog2('update:notAvailable', { explicit: false });
this.setState(State.Idle(2 /* UpdateType.Snap */, err.message || err));
},
);
}
doQuitAndInstall() {
this.logService.trace('update#quitAndInstall(): running raw#quitAndInstall()');
// Allow 3 seconds for VS Code to close
spawn('sleep 3 && ' + path.basename(process.argv[0]), {
shell: true,
detached: true,
stdio: 'ignore',
});
}
async isUpdateAvailable() {
const resolvedCurrentSnapPath = await new Promise((c, e) =>
realpath(`${path.dirname(this.snap)}/current`, (err, r) => (err ? e(err) : c(r))),
);
const currentRevision = path.basename(resolvedCurrentSnapPath);
return this.snapRevision !== currentRevision;
}
isLatestVersion() {
return this.isUpdateAvailable().then(undefined, (err) => {
this.logService.error('update#checkForSnapUpdate(): Could not get realpath of application.');
return undefined;
});
}
};
SnapUpdateService = __decorate(
[
__param(2, ILifecycleMainService),
__param(3, IEnvironmentMainService),
__param(4, ILogService),
__param(5, ITelemetryService),
],
SnapUpdateService,
);
export { SnapUpdateService };