VSCode 检查更新机制完整流程
梳理 VSCode(Win32)从启动加载更新服务,到检查、下载、安装更新的完整链路,供二次开发自建更新源时参考。
一、启动加载
在 CodeApplication.startup()(src/vs/code/electron-main/app.ts)中依次:初始化服务 → 初始化管道 → 打开窗口后初始化更新服务。
// Services
const appInstantiationService = await this.initServices(machineId, sharedProcess, sharedProcessReady);
// Init Channels
appInstantiationService.invokeFunction((accessor) => this.initChannels(accessor, mainProcessElectronServer, sharedProcessClient));
// Post Open Windows Tasks
appInstantiationService.invokeFunction((accessor) => this.afterWindowOpen(accessor, sharedProcess));
初始化服务
CodeApplication.initServices() 通过 process.platform 判断平台(win32),加载对应更新服务:
services.set(IUpdateService, new SyncDescriptor(Win32UpdateService));
初始化管道
CodeApplication.initChannels():
const updateChannel = new UpdateChannel(accessor.get(IUpdateService));
mainProcessElectronServer.registerChannel('update', updateChannel);
管道方法:checkForUpdates / downloadUpdate / applyUpdate / quitAndInstall / _getInitialState / isLatestVersion。
初始化更新服务
CodeApplication.afterWindowOpen() 调用 updateService.initialize()。注意非构建模式(运行源码)不会启用更新:
if (!this.environmentMainService.isBuilt) {
return; // updates are never enabled when running out of sources
}
更新源 URL 由 buildUpdateFeedUrl(quality) 构建(updateService.win32.ts),自建更新时替换为自己的服务地址(如 http://<更新服务>/api/update/win32-x64-archive/stable/...)。
protected buildUpdateFeedUrl(quality: string): string | undefined {
let platform = 'win32';
if (process.arch !== 'ia32') platform += `-${process.arch}`;
if (getUpdateType() === UpdateType.Archive) platform += '-archive';
else if (this.productService.target === 'user') platform += '-user';
return createUpdateURL(platform, quality, this.productService);
}
二、检查更新
界面触发「检查更新…」→ 命令 update.checkForVSCodeUpdate(update.ts)→ 执行 this.updateService.checkForUpdates(sessionId)(update.browser/update.ts)。
最终走到 Win32 的 doCheckForUpdates()(updateService.win32.ts):
protected doCheckForUpdates(context: any): void {
if (!this.url) return; // 无更新源直接返回
this.setState(State.CheckingForUpdates(context));
this.requestService.request({ url: this.url }, CancellationToken.None)
.then<IUpdate | null>(asJson)
.then(update => {
const updateType = getUpdateType();
// 校验更新信息是否完整
if (!update || !update.url || !update.version || !update.productVersion) {
this.setState(State.Idle(updateType));
return Promise.resolve(null);
}
// 安装包模式:仅提示可下载
if (updateType === UpdateType.Archive) {
this.setState(State.AvailableForDownload(update));
return Promise.resolve(null);
}
this.setState(State.Downloading(update));
// 清理被占用的旧安装包 → 取下载路径 → 不存在则下载 → 校验 hash → 重命名
return this.cleanup(update.version).then(() =>
this.getUpdatePackagePath(update.version).then(updatePackagePath =>
pfs.exists(updatePackagePath).then(exists => {
if (exists) return Promise.resolve(updatePackagePath);
const downloadPath = `${updatePackagePath}.tmp`;
return this.requestService.request({ url: update.url }, CancellationToken.None)
.then(context => this.fileService.writeFile(URI.file(downloadPath), context.stream))
.then(update.hash ? () => checksum(downloadPath, update.hash) : () => undefined)
.then(() => fs.promises.rename(downloadPath, updatePackagePath))
.then(() => updatePackagePath);
})
).then(packagePath => {
const fastUpdatesEnabled = this.configurationService.getValue<boolean>('update.enableWindowsBackgroundUpdates');
this.availableUpdate = { packagePath };
if (fastUpdatesEnabled && update.supportsFastUpdate) {
if (this.productService.target === 'user') this.doApplyUpdate(); // 快速更新
else this.setState(State.Downloaded(update));
} else {
this.setState(State.Ready(update)); // 提示重启安装
}
})
);
});
}
三、更新源响应格式
更新源(this.url)返回的 JSON 用于判断是否有更新(version 与本地 product.json 的 commit 字段不一致即视为有更新):
{
"url": "https://.../VSCodeUserSetup-x64-1.63.2.exe",
"name": "1.63.2",
"version": "899d46d82c4c95423fb7e10e68eba52050e30ba3",
"productVersion": "1.63.2",
"hash": "096e2827cc80f50ed8f0af8dcc3daab521f689c7",
"timestamp": 1639560982409,
"sha256hash": "e48536b5cab43e08b75b75b89b71a0923e2a28a64f51c53e1a49ec025749eb6b",
"supportsFastUpdate": true
}
其中
stable来自 product.json 的quality字段;version对应commit字段(VSCode GitHub 对应版本的 tag)。
附:安装包 hash 校验
import * as fs from 'fs';
import * as crypto from 'crypto';
function checksum(path: string) {
const input = fs.createReadStream(path);
const hash = crypto.createHash('sha1');
input.pipe(hash);
hash.once('data', (data: Buffer) => console.log(data.toString('hex')));
}
原文链接:https://www.ssssmy.com/notes/mo-dou-ide-yuan-ma-vscode-jian-cha-geng-xin-ji-zhi-wan-zheng-liu-cheng