本文首发微信公众号:前端徐徐。
大家好,我是徐徐。今天我们讲讲 electron 应用在 mac 中的更新原理。
前言
前面分析了 electron-updater 的整体流程,但是那是针对所有平台的流程,其实在各个平台的更新流程里面还有很多细节是需要探索的,这篇文章主要是探讨 Electron 在 macOS 平台上的更新流程,让我们一起来解读解读。
源码位置
MacUpdater类概述
MacUpdater
类继承自AppUpdater
,专门用于处理macOS平台的更新。它利用Electron的原生autoUpdater
模块,并结合自定义的HTTP服务器来实现更新过程。
它包含大概下面的一些职责:
- macOS 特定更新管理
- 与 Electron 原生更新器的集成
- 更新检测和下载
- 代理服务器管理
- 环境适配
- 安全性保障
- 错误处理和日志记录
- 更新安装流程控制
- 缓存管理
macOS 特定更新管理
MacUpdater 类的主要职责是处理 Electron 应用在 macOS 平台上的自动更新过程。它封装了所有与 macOS 相关的更新逻辑,包括与 Squirrel.Mac 的交互。在这里需要了解一下 Squirrel.Mac,它是一个用于macOS应用自动更新的框架,它处理了更新包的下载、验证和安装过程,它了解并遵循 macOS 的特定要求和最佳实践,确保更新过程符合 Apple 的指南。具体可参考:https://github.com/Squirrel/Squirrel.Mac
export class MacUpdater extends AppUpdater { private readonly nativeUpdater: AutoUpdater = require("electron").autoUpdater // ... }
与 Electron 原生更新器的集成
通过 nativeUpdater
属性,MacUpdater 直接与 Electron 的原生 autoUpdater 模块集成。这允许它利用 Electron 提供的底层更新功能,同时在此基础上构建更复杂的逻辑。
constructor(options?: AllPublishOptions, app?: AppAdapter) { super(options, app) this.nativeUpdater.on("error", it => { this._logger.warn(it) this.emit("error", it) }) this.nativeUpdater.on("update-downloaded", () => { this.squirrelDownloadedUpdate = true this.debug("nativeUpdater.update-downloaded") }) }
更新检测和下载
负责检查是否有可用的更新,并管理更新包的下载过程。这包括支持差异化下载,以优化网络使用和提高更新速度。
protected async doDownloadUpdate(downloadUpdateOptions: DownloadUpdateOptions): Promise<Array<string>> { // ... 环境检测代码 ... const zipFileInfo = findFile(files, "zip", ["pkg", "dmg"]) return this.executeDownload({ fileExtension: "zip", fileInfo: zipFileInfo, downloadUpdateOptions, task: async (destinationFile, downloadOptions) => { // ... 差异化下载逻辑 ... if (differentialDownloadFailed) { await this.httpExecutor.download(zipFileInfo.url, destinationFile, downloadOptions) } }, // ... }) }
代理服务器管理
MacUpdater 创建和管理一个本地 HTTP 代理服务器。这个服务器作为 Squirrel.Mac 和实际更新文件之间的中间层,提供更精细的控制更新过程。
private async updateDownloaded(zipFileInfo: ResolvedUpdateFileInfo, event: UpdateDownloadedEvent): Promise<Array<string>> { // ... this.server = createServer() this.server.on("request", (request: IncomingMessage, response: ServerResponse) => { // ... 处理请求逻辑 ... }) // ... }
环境适配
负责检测和适应不同的 macOS 环境,如 ARM64 架构或 Rosetta 模拟。这确保了更新包与用户的系统配置兼容。
let isRosetta = false try { const result = execFileSync("sysctl", [sysctlRosettaInfoKey], { encoding: "utf8" }) isRosetta = result.includes(`${sysctlRosettaInfoKey}: 1`) log.info(`Checked for macOS Rosetta environment (isRosetta=${isRosetta})`) } catch (e: any) { log.warn(`sysctl shell command to check for macOS Rosetta environment failed: ${e}`) } let isArm64Mac = false // ... 检测 ARM64 ...
安全性保障
实现基本的认证机制,确保更新过程的安全性,防止未授权访问更新服务器。
const pass = randomBytes(64).toString("base64").replace(/\//g, "_").replace(/\+/g, "-") const authInfo = Buffer.from(`autoupdater:${pass}`, "ascii") // ... 在请求处理中 ... if (!request.headers.authorization || request.headers.authorization.indexOf("Basic ") === -1) { response.statusCode = 401 response.statusMessage = "Invalid Authentication Credentials" response.end() return }
错误处理和日志记录
全面管理更新过程中可能出现的错误,并提供详细的日志记录,便于调试和问题追踪。
private debug(message: string): void { if (this._logger.debug != null) { this._logger.debug(message) } } // 在各个方法中 this._logger.warn(`Unable to copy file for caching for future differential downloads: ${error.message}`)
更新安装流程控制
管理更新的安装过程,包括决定何时安装更新,以及如何与应用的生命周期(如退出和重启)协调。
quitAndInstall(): void { if (this.squirrelDownloadedUpdate) { this.nativeUpdater.quitAndInstall() this.closeServerIfExists() } else { // ... 等待下载完成后安装 ... } }
缓存管理
处理更新文件的缓存,支持未来的差异化下载,优化长期的更新性能。
if (!downloadUpdateOptions.disableDifferentialDownload) { try { const cachedUpdateFilePath = path.join(this.downloadedUpdateHelper!.cacheDir, CURRENT_MAC_APP_ZIP_FILE_NAME) copyFileSync(event.downloadedFile, cachedUpdateFilePath) } catch (error: any) { this._logger.warn(`Unable to copy file for caching for future differential downloads: ${error.message}`) } }
结语
MacUpdater 类是一个专门为 macOS 平台设计的 Electron 应用更新管理器,它巧妙地结合了 Electron 的原生 autoUpdater 和 Squirrel.Mac 框架的功能。
通过创建本地代理服务器、处理差异化下载、管理更新生命周期、适应不同的 macOS 环境(如 ARM64 和 Rosetta)以及实现安全认证机制,MacUpdater 提供了一个全面而灵活的更新解决方案。它不仅处理了复杂的技术细节,如版本检查、文件下载和安装协调,还考虑到了性能优化、错误处理和用户体验等方面。
这个实现展示了如何在保持与 macOS 系统深度集成的同时,为开发者提供高度可定制的更新流程控制,从而确保 Electron 应用能够安全、高效地保持最新状态。