{T}

GitHub Actions 接入 macOS 公证签名、Linux 构建与打包问题修复

概述

基础发布闭环打通后,真正的工程难点才浮现:macOS 既要签名又要公证,认证凭证在云端无法直接读取本地钥匙串,Linux 虽简单但也有自己的坑。这一节把 macOS 自动化发布的两阶段拆开讲,对比课程旧方案与官方新方案在命名上的差异,说明证书与 API Key 如何安全注入,并给出矩阵构建和常见打包失败的系统排查。

学习目标

  • 区分 macOS 的代码签名与公证两个阶段
  • 对比 electron-builder-notarize + afterSign 与内置 @electron/notarize
  • 厘清 Apple API Key 的课程旧命名与官方 APPLE_* 命名
  • 掌握证书 .p12 导出、base64 与 CSC_LINK 注入
  • 用矩阵构建扩展到 macOS 与 Linux
  • 定位 files 配置遗漏与 CI 细节错误

一、签名与公证是两个独立阶段

对 macOS 而言,打包完成只是开始。代码签名(Code Signing)证明应用来自可信开发者;应用公证(Notarization)由 Apple 对产物做安全审核,消除“未知开发者”提示。两者面向的问题不同,凭证也不同。

GitHub Actions 只是执行者,复杂度集中在前置准备:证书、API Key、环境变量。只要这些名字或位置写错,整个流程就会失败。签名和公证缺任一,用户安装体验都不完整。

二、旧方案 afterSign 与新方案内置集成

课程视频常用 electron-builder-notarize 挂在 afterSign 钩子上,让签名完成后自动公证:

js
module.exports = {
  afterSign: 'electron-builder-notarize',
  mac: { hardenedRuntime: true },
}

但当前官方文档更推荐直接用 electron-builder 内置的 @electron/notarize 集成,只要提供 APPLE_* 环境变量,构建链路就会自动触发公证,不必额外挂 afterSign。两种方式都能工作,但不要把旧插件的 Secrets 命名和新环境变量混用,否则极易出错。

三、Apple API Key 的两套命名

云端 runner 读不到你本地钥匙串,所以要在 App Store Connect 创建 API Key,把关键字段存进 Secrets。这里最容易混淆的是命名归属:课程第三方 Action 常用 API_KEYAPI_KEY_IDAPI_ISSUER_ID;而官方 electron-builder 标准变量是 APPLE_API_KEYAPPLE_API_KEY_IDAPPLE_API_ISSUER

yaml
# 官方推荐
env:
  APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
  APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
  APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}

两者表达同一类信息,但不能随意互换——要看你的 workflow 读的是哪一组。按官方文档走,就优先记 APPLE_* 这组。

公证依赖 API Key,但签名依赖开发者证书。CI 远程构建读不到本地证书,需先从钥匙串导出 .p12,再转 base64 存入 Secrets:

bash
base64 -i certs.p12
yaml
env:
  CSC_LINK: ${{ secrets.CSC_LINK }}
  CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}

CSC_LINK 是 electron-builder 读取证书内容的标准变量,值通常是 base64 字符串;CSC_KEY_PASSWORD 对应导出 .p12 时设的密码。证书里应包含有效的 Developer ID Application.p12 密码必须与导出时一致,否则签名阶段直接失败。

五、矩阵构建扩展到多平台

Windows 跑通后,用矩阵一行就能扩展到 macOS 和 Linux:

yaml
strategy:
  matrix:
    os: [windows-latest, macos-latest, ubuntu-latest]

增加矩阵只是“启动更多平台任务”,不代表每个平台一定成功。macOS 更依赖 Secrets,Linux 配置通常简单但仍要保证依赖与打包目标正确。出错时进 Actions 日志看具体失败步骤,不要盲改配置。生产项目建议把 -latest 固定到明确版本标签(如 macos-15),减少镜像漂移带来的不确定性。

六、files 配置遗漏导致运行期报错

一个典型问题是:安装后启动报错找不到 main/locales 之类资源。根因不是运行时代码,而是打包阶段没把资源带进去。一旦手动配置 files,只有被包含的路径才会进入产物。

js
module.exports = {
  files: [
    'dist/**/*',
    'packages/main/locales/**/*',
  ],
}

“本地能跑、安装包运行报错”通常优先排查 filesextraResources 和资源路径解析,而不是先怀疑业务代码。

七、CI 配置问题多在细节

这类问题技术难度不高,难在繁琐。常见出错点:环境变量名与工作流引用名不一致、.p8.p12 内容复制不完整、afterSign 挂载位置错误、mac.hardenedRuntime 层级错误、files 漏掉非构建目录资源、引入不兼容依赖。

最佳实践是:先保证本地构建成功再推云端;每改一个环节就提交一次,便于回滚定位;关键字段名优先查官方文档,不凭记忆手写。

八、把 macOS 与 Linux 纳入稳定交付

把前面几节合起来:用矩阵启动三端任务,macOS job 注入 APPLE_*CSC_LINK 完成签名公证,Linux job 跑最简配置,所有产物上传到同一草稿。这样本地只需推代码,云端就产出带签名、带公证、三端齐备的发布物。

到这,macOS 公证与 Linux 构建已在 CI 中稳定。下一节我们转向交付的最后一公里:让已安装的应用自动检测到新版本。


常见问题

问题原因解决方案
macOS 任务直接失败Secrets 缺失或命名不一致确认 workflow 读的是旧命名还是官方 APPLE_*,逐项核对
公证失败未开 hardenedRuntime 或 API Key 错开启 mac.hardenedRuntime,重核 Apple Key
签名失败.p12 内容或密码错重新导出证书、base64 编码,确认密码
安装后找不到语言文件files 漏了 locales补 packages/main/locales/**/*
只有 Windows 产物矩阵只有 Windows在 matrix.os 加 macos 与 ubuntu
发布后 macOS 仍弹提示只签名未公证检查 afterSign、Apple Key 与公证日志

延伸阅读