Electron on macOS: 揭秘 MacUpdater 如何实现无缝自动更新?

简介: 本文首发于微信公众号“前端徐徐”,详细探讨了 Electron 应用在 macOS 平台上的更新原理。文章分析了 `MacUpdater` 类的实现,包括与 Electron 原生更新器的集成、更新检测和下载、代理服务器管理、环境适配、安全性保障、错误处理和日志记录、更新安装流程控制以及缓存管理等关键功能。通过这些技术细节,展示了如何在 macOS 上实现高效、安全的 Electron 应用更新。

本文首发微信公众号:前端徐徐。

大家好,我是徐徐。今天我们讲讲 electron 应用在 mac 中的更新原理。

前言

前面分析了 electron-updater 的整体流程,但是那是针对所有平台的流程,其实在各个平台的更新流程里面还有很多细节是需要探索的,这篇文章主要是探讨 Electron 在 macOS 平台上的更新流程,让我们一起来解读解读。

源码位置

https://github1s.com/electron-userland/electron-builder/blob/master/packages/electron-updater/src/MacUpdater.ts

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 应用能够安全、高效地保持最新状态。

相关文章
|
2月前
|
前端开发 开发者 UED
你真的了解 Electron 的自动更新吗?揭秘AppUpdater 类的内部工作原理
本文由前端徐徐首发,深入探讨了 Electron 的自动更新工作原理,特别是 `electron-builder` 中 `AppUpdater` 类的源码分析,涵盖配置更新源、检查更新、下载更新、安装更新及事件通知等核心功能,帮助开发者更好地理解和使用 Electron 的自动更新机制。
180 0
你真的了解 Electron 的自动更新吗?揭秘AppUpdater 类的内部工作原理
|
2月前
|
安全 前端开发 iOS开发
揭秘 electron-builder:macOS 应用打包背后到底发生了什么?
本文详细介绍了 Electron 应用在 macOS 平台上的打包流程,涵盖配置文件、打包步骤、签名及 notarization 等关键环节。通过剖析 `electron-builder` 的源码,展示了如何处理多架构应用、执行签名,并解决常见问题。适合希望深入了解 macOS 打包细节的开发者。
111 2
|
应用服务中间件 API nginx
分享下我近期研究, Electron 的自动更新机制
Electron的自动更新机制并不算复杂,但团队内似乎没有相关文档,正好笔者搞明白了,就简单说明一下,以MacOS的arm平台为例说明,具体代码可以参考Postcat的相关配置,本篇文章就不以具体代码举例了。
分享下我近期研究, Electron 的自动更新机制
|
1月前
|
JSON JavaScript 前端开发
开发桌面程序-Electron入门
【10月更文挑战第16天】Electron 是一个使用 JavaScript、HTML 和 CSS 构建跨平台桌面应用的框架,嵌入了 Chromium 和 Node.js。本文介绍了如何搭建 Electron 开发环境,包括安装 Node.js、创建项目、配置 main.js 和打包应用。通过简单的步骤,你可以快速创建并运行一个基本的 Electron 应用程序。
开发桌面程序-Electron入门
|
4月前
|
JavaScript 开发工具
Electron 开发过程中主进程的无法看到 console.log 输出怎么办
Electron 开发过程中主进程的无法看到 console.log 输出怎么办
|
5月前
|
前端开发
PC端01,桌面端,electron的开发,electron的开发的系列课程,软件开发必备流程,electron的讲解,electron的开发,vitepress博主的gitee链接,PC端效率软件
PC端01,桌面端,electron的开发,electron的开发的系列课程,软件开发必备流程,electron的讲解,electron的开发,vitepress博主的gitee链接,PC端效率软件
PC端01,桌面端,electron的开发,electron的开发的系列课程,软件开发必备流程,electron的讲解,electron的开发,vitepress博主的gitee链接,PC端效率软件
|
7月前
|
移动开发 开发框架 JavaScript
Vue3 Vite electron 开发桌面程序
Vue3 Vite electron 开发桌面程序
355 0
|
前端开发 算法 JavaScript
从零开始开发图床工具:使用 Gitee 和 Electron 实现上传、管理和分享(下)
从零开始开发图床工具:使用 Gitee 和 Electron 实现上传、管理和分享(下)
201 0
|
存储 Web App开发 JavaScript
从零开始开发图床工具:使用 Gitee 和 Electron 实现上传、管理和分享(上)
从零开始开发图床工具:使用 Gitee 和 Electron 实现上传、管理和分享(上)
251 0
|
Web App开发 资源调度 前端开发
基于NeteaseCloudMusicApi和electron-vue开发网易云音乐--electron-vue初始化
基于NeteaseCloudMusicApi和electron-vue开发网易云音乐--electron-vue初始化
153 0