知行札记
专题技术实践桌面应用实践Electron 桌面应用

打包、签名与分发

连接构建产物、平台格式、签名、公证和资源布局。

本页整合 Electron 归档分稿中的机制、接口和接线。归档使用 Electron 44 系列作为其版本讨论背景;本文接口依据官方文档及 breaking changes 核查,实际采用时仍需固定项目版本。包版本、维护状态和原稿实测均是对应日期的历史信息。本次未启动 Electron、构建安装包、签名、公证或验证更新。代码块按各自标注区分片段与组合示例。

调研时间:2026-10-04。文中版本号、默认值均来自原调研当日记录(npm registry)与所列官方文档原文;无法核实处已标注「待核实」。

8.1 原理与机制:Electron 应用如何变成安装包

Electron 应用 = Electron 运行时(Chromium + Node.js)+ 你的应用代码。打包工具做的事情本质上是四步:

  1. 复制与重命名运行时:把对应平台的 Electron 预编译产物取出,替换图标与 Info.plist/版本资源,做成「你的」.app/.exe/AppImage;
  2. 打包应用代码进 asar:应用源码默认打进 app.asar 归档(见 8.5),native 模块的 .node 文件等留在 app.asar.unpacked;
  3. 代码签名:macOS 用 codesign(Developer ID),Windows 用 Authenticode/Azure Artifact Signing;
  4. 封装安装器:macOS 封 dmg/pkg,Windows 封 NSIS/MSI/MSIX,Linux 封 AppImage/deb/rpm/snap 等,并生成自动更新所需的元数据与 blockmap。

代码签名把发布者身份与产物完整性接入平台的安全检查。面向普通用户分发时,需要按目标系统和渠道准备签名;缺少签名、公证或足够信誉,可能触发 Gatekeeper、SmartScreen 的提示或组织策略阻止。具体结果受来源、系统版本、证书与管理策略影响。macOS 公证检查已知恶意内容,也需要与签名、下载隔离属性一起验证(见 8.4)。

两个事实在 2025-2026 发生了变化,直接影响打包策略(来源:@electron/packager README):从 Electron 44.0.0-alpha.4 起,官方不再发布 Windows ia32 与 Linux armv7l 构建(仅 Electron ≤ 43 提供);且 @electron/packager 要求 Node.js ≥ 22.12.0。另外,macOS 的 .app 只能在 macOS 主机上签名(同来源),CI 需 macOS runner。

8.2 electron-builder vs Electron Forge(2026)

官方定位:Electron 官方文档主推 Forge,并把 electron-builder 列为社区维护的第三方工具——"These tools are maintained by members of the Electron community, and do not come with official support from the Electron project"、"electron-builder replaces features and modules used by the Electron maintainers (such as the auto-updater) with custom ones"(electronjs.org forge-overview)。Forge 自己的对比页则指出哲学差异:"Forge focuses on combining existing first-party tools into a single build pipeline, while Builder rewrites its own in-house logic",并认为 Forge 优势在于新特性第一时间跟进(如 ASAR integrity、universal macOS builds)与多包架构更易扩展(electronforge.io)。

维度Electron Forgeelectron-builder
维护方Electron 官方组织electron-userland 社区(netlify 维护者为主)
版本(2026-10-04 实测)@electron-forge/cli 8.0.1npm latest 26.15.3、v26 tag 26.17.0;next 27.0.0-alpha.9(v27 仍为 alpha)
底层组装 @electron/packager + @electron/osx-sign + @electron/notarize + @electron/windows-sign自有 app-builder-lib 流水线
配置形态forge.config.js(makers/publishers/plugins)package.json 的 build 字段或 electron-builder.yml
脚手架npm init electron-app 官方模板(Vite/webpack)无(自带构建体系)
Windows 产物Squirrel.Windows、MSI(WiX)为主NSIS/NSIS-web/portable/MSI/MSI-wrapped/AppX/MSIX(beta)/Squirrel(官方标注 legacy)
Linux 产物zip、deb、rpm、snap、flatpak(视 maker)AppImage/deb/rpm/snap/flatpak/pacman(beta)/apk/freebsd/p5p/zip/tar.gz
自动更新官方 autoUpdater(Squirrel)+ update.electronjs.orgelectron-updater(blockmap 差量下载、generic/S3 等私有 feed)
签名统一 windowsSign / osxSign / osxNotarize自有签名实现 + win.azureSignOptions

两者现已可混合:electron-builder 发布了 Forge maker 包(electron-forge-maker-nsis、-nsis-web、-appimage、-snap),但官方明确其限制——"Publishing, Auto Update, and Code Signing are only available when using electron-builder as your primary build tool"(electron.build 文档)。

electron-builder 的典型配置(electron-builder.yml,集中体现签名、公证、压缩与产物选择):

appId: com.acme.app
compression: normal            # store | normal(默认) | maximum
mac:
  target: [{ target: dmg, arch: [arm64, x64] }, { target: zip, arch: [arm64, x64] }]
  notarize: true               # 公证开关;签名项 v27 起移入 mac.sign
  sign:
    hardenedRuntime: true
    entitlements: build/entitlements.mac.plist
win:
  target: [nsis]               # 默认;企业场景另出 msi
  azureSignOptions: { certificateProfileName: acme }   # Azure Artifact Signing
nsis:
  oneClick: true               # 默认值
  differentialPackage: true    # 默认值;可选 "store-asar"
linux:
  target: [AppImage, deb, rpm]
electronLanguages: [zh-CN, en-US]   # 裁剪 Electron locale
publish: { provider: generic, url: https://cdn.example.com/update/ }

本分册采用:新项目、希望与官方工具链(含 Electron 安全公告、fuses、ASAR integrity)保持同步、能接受官方 autoUpdater 模型 → Forge;需要最全的安装器矩阵、私有化更新 feed、blockmap 差量下载、深度定制 NSIS/dmg → electron-builder(consciously 接受"非官方支持"这一前提,这也是 Fora Soft 等外包团队的普遍选择)。存量大型项目(VS Code、Slack、Discord 等)多有自研管线,不必对齐任一工具。

8.3 各平台产物格式与选择

macOS:dmg / pkg / zip / mas / universal

  • dmg:消费级分发标准,拖拽安装;zip:不是给用户的,是 electron-updater 的更新 payload(比 dmg 小);pkg:需要装系统级组件(内核扩展、launch daemon、/Applications 之外文件)时用,需要单独的 Developer ID Installer 证书;mas/mas-dev:Mac App Store 提交用(electron.build targets 指南)。
  • universal binary:@electron/universal 的 makeUniversalApp() 把 x64 与 arm64 两个 .app 粘合(lipo),可用 mergeASARs: true 合并两架构 asar 以减小体积,singleArchFiles(minimatch)声明仅存在于单一架构的文件——"If @electron/universal detects an architecture-unique file that isn't covered by the singleArchFiles rule, an error will be thrown"(GitHub README)。未合并时 universal 体积近似两倍,应权衡:分架构包小但下载页需要按 CPU 分流。

Windows:NSIS(默认)/ nsis-web / portable / MSI / MSIX

electron-builder targets 指南的官方口径:NSIS 是默认与多数应用的最佳选择(支持 per-user 免管理员安装;oneClick 默认 true,perMachine 默认 false,differentialPackage 默认 true);nsis-web 适合超大应用——生成小 stub 安装器、安装时下载 payload;portable 免安装;MSI 面向企业 SCCM/Intune/GPO;appx 为 Store 遗留格式,msix 是"modern successor to AppX"但官方标注 beta;squirrel.windows 标注 Legacy (not recommended)。Microsoft Store 路线:上架后由微软重签,"Users will never see a SmartScreen warning for a Store-installed app"(Microsoft Learn)。Electron 官方 windows-store-guide 仍讲的是 electron-windows-store 打 appx 的老流程。

Linux:AppImage / deb / rpm / snap / flatpak

electron-builder 默认构建 AppImage + snap,AppImage 是官方推荐的跨发行版首选:单文件、免安装、免 root,且支持内嵌 blockmap 的差量更新;deb/rpm 覆盖 Debian/Red Hat 系包管理器;snap 走 Snap Store(严格沙箱、需 snapd);flatpak 跨发行版沙箱;pacman(Arch)为 beta。electron-builder 文档给出的对比要点:AppImage 无沙箱、Snap/Flatpak 有;deb/rpm 需要 root 且不跨发行版。Electron 官方另有 snapcraft.md 指南。

8.4 代码签名与公证全流程

macOS:Developer ID + Hardened Runtime + notarization

自 macOS Catalina 起官方分发要求签名 + 公证(Electron 官方 code-signing 文档:先 codesign 再上传 Apple 做 "notarization",通过后 staple 票据)。流水线:

  1. Apple Developer Program 付费账号 + Developer ID Application 证书(pkg 另需 Developer ID Installer);
  2. 以 Hardened Runtime(codesign --options runtime)签名并附带 entitlements——Electron 最低需要 com.apple.security.cs.allow-jit 与 com.apple.security.cs.allow-unsigned-executable-memory(electron-builder notarization 文档);
  3. xcrun notarytool submit ... --wait 提交公证(altool 已于 2023-11-01 被移除,二手来源 Fora Soft 2026 博客确认所有现代管线均用 notarytool——待向 Apple 官方页面核实);
  4. xcrun stapler staple 钉票据,spctl -a -vv 验证。

Forge 配置(osxSign / osxNotarize)示例:

// forge.config.js
module.exports = {
  packagerConfig: {
    osxSign: { identity: 'Developer ID Application: Acme (TEAMID)' },
    osxNotarize: {
      tool: 'notarytool',
      appleId: process.env.APPLE_ID,
      appleIdPassword: process.env.APPLE_APP_SPECIFIC_PASSWORD,
      teamId: process.env.APPLE_TEAM_ID,
    },
  },
};

CI 认证推荐 App Store Connect API Key(.p8) 方案:官方文档指出 API key"don't expire and don't require two-factor authentication, making them ideal for CI",而 Apple ID + app-specific password 方式在 CI 上易受 2FA 干扰(electron-builder notarization 文档给出 Option A/B/C 三种认证)。验证环节可用 codesign -dv 检查 flags=0x10000(runtime) 确认 Hardened Runtime 生效、TeamIdentifier 非空、bundle identifier 不是默认的 com.github.Electron(artmann.co 实践)。另注意 app 与 dmg 要"双层级"签名公证——只公证 .app 不公证 .dmg(或相反)都会留下 Gatekeeper 拦截面(sushi.dev 坑清单)。注意官方文档列出的依赖签名的 macOS API:safeStorage、app.setLoginItemSettings()、cookieEncryption fuse、以及 Squirrel.Mac 的 autoUpdater("requires the app to be signed for automatic updates to work at all")。

Windows:证书类型、SmartScreen 与 Azure Artifact Signing

2025-2026 的关键变化,两份官方文档表述存在张力,如实并列:

  • Electron 官方文档(electronjs.org code-signing):"since June 2023, Microsoft requires software to be signed with an 'extended validation' certificate… These simpler certificates no longer provide benefits: Windows will treat your app as completely unsigned"。
  • Microsoft Learn(SmartScreen reputation for Windows app developers):"EV certificates no longer bypass SmartScreen… EV certificates may matter for enterprise procurement, but they no longer impact SmartScreen behavior. Paying a premium for EV solely to avoid SmartScreen warnings is no longer justified"。

实践结论(ToDesktop 2026 PSA):EV 不再带来即时信誉,OV 与 EV 对 SmartScreen 效果等同;SmartScreen 信誉按文件哈希累积,新版本哈希重新计分;2026-03-26 前后 Azure CA 轮换曾导致大量已签应用触发告警——因此避免在发版临近时轮换签名身份。

Azure Artifact Signing(原 Trusted Signing) 是 Electron 官方文档与 Microsoft Learn 共同推荐的最便宜方案,"gets rid of SmartScreen warnings",但有地域资格限制(Electron Forge 文档:截至 2025-10,仅面向美/加且具 3 年以上可验证经营历史的组织及美/加个人开发者,政策可能变化);Linux/macOS 上可用 jsign 跨平台签名。EV 证书须存于 FIPS 140 Level 2 硬件或云 HSM,因此 CI 场景普遍用云签名(Electron 官方自身用 DigiCert KeyLocker)。生态统一层:@electron/windows-sign 的 windowsSign 配置可跨 Forge、packager、electron-winstaller、electron-wix-msi 复用;electron-builder 有自己的 win.azureSignOptions。

Linux 签名现状

Electron 官方 code-signing 文档只有 macOS 与 Windows 两章,没有 Linux 章节(本调研核对了文档全文)——即官方工具链不提供 Linux 代码签名。现状:AppImage 无内建签名机制,惯例是发布 SHA-256 校验和(细节待核实);Snap 由 Snap Store 审核并签名分发;Flatpak 依托 GPG 签名的 OSTree 仓库与 Flathub(待核实)。Linux 桌面生态整体不依赖签名做安装门禁。

8.5 asar、代码保护与解包风险

机制:官方文档说明 asar 归档的意义是缓解 Windows 长路径问题、加速 require、以及 "conceal your source code from cursory inspection"——注意措辞:只防"随手翻看",不防逆向(electronjs.org asar-archives)。asar 是只读虚拟文件系统;依赖真实路径的系统调用(如 native addon 的部分行为)会触发解包,因此 .node 等文件用 --unpack 留在 app.asar.unpacked 一起分发,反病毒软件有时会被"API 触发的临时解包"行为惊动(同文档)。

解包风险:npx asar extract app.asar out 一条命令即可还原全部 JS 源码;安全研究者还演示过 extract → 修改 → repack → 重算 header hash → 改二进制内嵌哈希的完整篡改链(InfoSec Write-ups)。历史上多个 ASAR integrity 绕过 CVE:CVE-2023-44402(macOS filetype confusion,影响 ≤27.0.0-alpha6)、CVE-2024-46992(Windows,Electron 30/31 beta)、CVE-2025-55305(仅影响同时开启 embeddedAsarIntegrityValidation 与 onlyLoadAppFromAsar 两个 fuse 且攻击者对 resources 目录有写权限的应用;修复于 35.7.5 / 36.8.1 / 37.3.1 / 38.0.0-beta.6,GitLab Advisory Database)。

ASAR Integrity:macOS 自 Electron ≥ 16、Windows 自 ≥ 30 支持;默认关闭,需在构建期开启 EnableEmbeddedAsarIntegrityValidation fuse 并配合 OnlyLoadAppFromAsar,运行时校验 asar header 哈希、不匹配即强制退出(electronjs.org asar-integrity)。electron-builder 通过 electronFuses 配置,Forge 用 @electron-forge/plugin-fuses。MAS 构建官方"recommended as a best practice"。

更强的保护是 V8 bytecode(把入口编译为字节码,源码不再以明文存在于 asar),electron-vite 官方指南给出 "V8 Bytecode + ASAR Integrity" 组合方案,并强调"没有客户端保护是绝对的",核心逻辑仍应放服务端。

8.6 安装包体积优化

  • 压缩级别:electron-builder 顶层 compression 支持 store/normal/maximum,默认 normal;官方文档明确 "maximum doesn't lead to noticeable size difference, but increase build time"。
  • 裁剪:files glob 排除多余文件(带 ! 前缀);electronLanguages 只保留所需 locale(默认打包全部 Electron 语言包);native 依赖按平台/架构拆分。
  • 按需下载:Windows 大应用用 nsis-web(stub 安装器安装时拉取 payload);应用内资源(模型、素材)首启后下载而非全打进安装包。
  • 差量更新(比首装更重要):electron-builder 为 NSIS 产物生成 .blockmap,electron-updater 据此做块级差量下载;NSIS differentialPackage 默认 true,可选 "store-asar"(asar 不压缩、差分更小、安装器更大);AppImage 则把 blockmap 内嵌进二进制。客户端接入只需:
// 应用内(electron-builder + electron-updater)
const { autoUpdater } = require('electron-updater');
autoUpdater.checkForUpdatesAndNotify();
// 差量逻辑由 electron-updater 内部完成:比对新旧 blockmap,
// 仅下载内容变化的块;失败则回退整包下载

坑:差量失败会静默回退全量下载(issue #6935 的 "Cannot parse blockmap… fallback to full download");2026-01 的 issue #9498 直接质询三平台差量更新是否被官方稳定支持,被 close as not planned,文档确实薄弱——上线前应自行验证实际下载量。

  • 更新 feed 与灰度:electron-builder 构建时会生成更新元数据——"latest.yml (or latest-mac.yml for macOS, or latest-linux.yml for Linux) will be generated and uploaded for all providers",客户端侧的 app-update.yml 也随构建自动生成;官方文档另确认 "Download progress and staged rollouts supported on all platforms",即三平台均支持下载进度与灰度放量,这对国内大用户量分发(先放量 1% 观察)很关键(官方 auto-update 文档)。
  • macOS universal:mergeASARs 合并双架构 asar,避免体积×2(见 8.3)。
  • 更新 payload 用 zip 而非 dmg(electron-updater 约定)。

8.7 国内网络环境的特殊考虑

  • 构建期:@electron/get 支持镜像:环境变量 ELECTRON_MIRROR(及 ELECTRON_NIGHTLY_MIRROR、ELECTRON_CUSTOM_DIR 等)或 API mirrorOptions.mirror(GitHub README 实测核对);阿里 npmmirror 提供 https://npmmirror.com/mirrors/electron/ 与 electron-builder 二进制镜像,国内项目惯例是在 .npmrc 中固定:
# .npmrc —— 国内构建环境常见配置(实践惯例,二进制镜像本身由 npmmirror 官方维护)
electron_mirror=https://npmmirror.com/mirrors/electron/
electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/

注意 electron-builder v27 起配置键由 electronDownload 改为 electronGet,镜像参数移入 mirrorOptions.mirror(官方 Configuration 文档);旧文档常见的 ELECTRON_MIRROR 环境变量写法对 @electron/get 5.x 依然有效。此外打包依赖(app-builder 二进制、winCodeSign、nsis 等)默认从 GitHub 拉取,同样受网络影响,镜像变量需在 CI 与本地同时配置。

  • 分发期:GitHub Releases 直链在国内不稳,更新 feed 建议用 provider: generic 指向国内 CDN/OSS;blockmap 差量可显著降低带宽成本(但见 8.6 的回退坑)。国产头部 Electron 应用(钉钉、QQ NT、飞书等)普遍自建更新服务,公开工程资料有限——待核实。

8.8 常见坑与反模式(清单)

  1. 只在 CI 有证书的机器上才能构建:应像 artmann.co 指南那样以 APPLE_TEAM_ID 等环境变量做门控,无证书时跳过签名,保证贡献者本地可构建。
  2. .p12 导出时漏掉私钥,CI 报错难定位;.p8 只能下载一次。
  3. 导出 .p12 未含私钥、签名 identity 与证书 Common Name 不匹配、忘记 --timestamp:公证会被拒(sushi.dev 常见坑清单)。
  4. 用 Apple Development 证书而非 Developer ID Application 分发。
  5. Hardened Runtime 首次签名后 native module 崩溃未做冒烟测试——签名后的构建是这些代码路径第一次真实运行(artmann.co)。
  6. 开 ASAR integrity 却不开 OnlyLoadAppFromAsar(可经 app 目录绕过),或 Electron 版本落后于 CVE-2025-55305 修复线。
  7. 以为 asar 能保护源码/密钥——必须 V8 bytecode + 服务端逻辑。
  8. 为「过 SmartScreen」单独买昂贵 EV 证书(2026 年已无此收益,Microsoft Learn),或发版前轮换签名证书。
  9. Electron ≥ 44 还在为 win-ia32 / linux-armv7l 出包(官方构建已停发)。
  10. 差量更新「配了就生效」——必须实测下载流量,blockmap 解析失败会静默回退全量。
  11. 公证通过却忘记 xcrun stapler staple:票据不落地,Gatekeeper 离线首启仍可能拦(sushi.dev:staple 的意义是"so Gatekeeper can check it offline")。发布脚本应把公证、钉票据、验证三步串成一条不可跳过的流水线。

参考来源

最后更新于

本页目录