Apple macOS CI/CD 与自动更新
该技能提供通过 GitHub Actions 发布已签名并公证的 macOS 应用的专家工作流,并支持自动更新。内容基于工作区中的 Cometrans(原生 Swift)与 Cometode(Electron)两个参考项目。
参考项目
| 应用 | 技术栈 | 发布触发 | 自动更新 |
|---|---|---|---|
| Cometrans | Swift CLI → .app + DMG | workflow_dispatch 并输入版本 | 构建号已为 Sparkle 就绪(尚未接入) |
| Cometode | Electron + Svelte | 推送 tag v* | electron-updater + latest-mac.yml |
实现时可参考的资料文件包括:Cometrans 模式、Cometode 模式、GitHub secrets、自动更新指南。
思考流程
第一步:确定应用类型
目标:选择正确的发布与更新技术栈。
决策矩阵:
| 应用类型 | 构建工具 | CI 运行器 | 更新器 |
|---|---|---|---|
| 原生 Swift / SwiftPM | build_dmg.sh 或 Xcode | macos-26(通过 DEVELOPER_DIR 固定 Xcode) | Sparkle + appcast |
| Electron + electron-builder | pnpm build:mac | macos-latest | electron-updater + GitHub Releases |
决策点:明确说明“这是 [原生|Electron] 应用;更新器将使用 [Sparkle|electron-updater]。”
第二步:Apple 开发者前置条件
目标:在改动 CI 之前,确保签名与公证凭据已存在。
清单:
- Apple Developer Program 会员资格
- Developer ID Application 证书(非 Mac App Store)
- 导出 .p12 并做 base64 编码以供 CI 使用
- 在 appleid.apple.com 生成 App 专用密码(供 notarytool 使用)
- 从 developer.apple.com 获取 Team ID
操作:
- 运行
bash /mnt/skills/user/apple-mac-cicd/scripts/validate-secrets.sh审计本地环境变量 - 配置 GitHub 仓库 secrets(见 github-secrets.md)
第三步:本地构建脚本
目标:一个脚本即可在本地与 CI 中产出一致且已签名的构建产物。
原生(Cometrans 模式):
- build_dmg.sh 构建 Swift release,组装 .app,使用
--options runtime进行 codesign,创建 DMG - CFBundleVersion 按 major10000 + minor100 + patch 推导,用于 Sparkle 的语义化版本比较
NOTARIZE=1时通过xcrun notarytool submit --wait+stapler staple触发 scripts/notarize.sh
Electron(Cometode 模式):
- electron-builder.yml:
hardenedRuntime: true、notarize: true、publish: github - mac 目标:dmg + zip(electron-updater 需要 zip)
- Entitlements 放在 build/entitlements.mac.plist(Electron 需要 JIT、unsigned executable memory)
第四步:GitHub Actions 发布工作流
目标:CI 完成构建、签名、公证并发布到 GitHub Releases。
原生工作流结构(Cometrans .github/workflows/release.yml):
- prepare job — 解析版本,创建/推送 tag
- build job — 将 P12 导入临时 keychain,运行 ./build_dmg.sh,上传产物
- release job — 下载产物,使用 softprops/action-gh-release 附带 DMG + sha256
Electron 工作流结构(Cometode .github/workflows/release.yml):
- 触发条件:
push: tags: ['v*'] - 从 tag 同步 package.json 版本
- 带签名环境变量运行
pnpm build:mac - 上传 dist/.dmg、dist/.zip、dist/*.blockmap、dist/latest-mac.yml(自动更新必需)
按技术栈所需的 secrets:
| Secret | 原生 | Electron |
|---|---|---|
| APPLE_DEVELOPER_ID_P12_BASE64 | ✓ | — |
| APPLE_DEVELOPER_ID_P12_PASSWORD | ✓ | — |
| KEYCHAIN_PASSWORD | ✓ | — |
| DEVELOPER_ID_APPLICATION | ✓ | — |
| CSC_LINK | — | ✓(base64 p12) |
| CSC_KEY_PASSWORD | — | ✓ |
| APPLE_ID | ✓ | ✓ |
| APPLE_APP_SPECIFIC_PASSWORD | ✓ | ✓ |
| APPLE_TEAM_ID | ✓ | ✓ |
第五步:自动更新集成
目标:用户无需手动下载 DMG 即可获得更新。
Electron(Cometode — 已完整接通):
- electron-updater 从 GitHub Releases 读取 latest-mac.yml
- electron-builder.yml 的 publish.provider: github 必须与仓库 owner/name 匹配
- 主进程:
autoUpdater.autoDownload = true,启动时以及每 4 小时检查一次 - 发布工作流必须附带 latest-mac.yml、.zip 与 .blockmap
- 仅在生产环境启用(
if (!is.dev))
原生(Cometrans — 构建号已就绪,Sparkle 待接入):
- build_dmg.sh 已设置 CFBundleVersion 以供 Sparkle 比较
- 待完成:添加 Sparkle SPM 依赖、在 Info.plist 配置 SUFeedURL、EdDSA 签名密钥
- CI 需在每次发布时生成/签名 appcast XML 并与 DMG 一起发布
详见 auto-updater.md。
第六步:PR 的 CI 工作流
目标:在发布前捕获构建中断。
- 原生:在 push/PR 触发 ci.yml — 在 macos-26 上执行
swift build -c release && swift test - Electron:添加 ci.yml,执行
pnpm typecheck && pnpm build(CI 无需签名)
第七步:发布与验证
目标:发布并端到端确认自动更新可用。
发布清单:
- 在源码中升级版本(version.txt 或 package.json)
- 打 tag vX.Y.Z(Electron)或运行 workflow_dispatch(Cometrans)
- CI 完成签名 + 公证
- GitHub Release 包含所有必需资产
- 安装旧版本,确认更新器能找到新版本
- 对下载的产物执行
spctl -a -vv -t install通过
本地验证公证:
xcrun stapler validate Cometrans-1.0.0.dmg
# 或
spctl -a -vv -t install dist/cometode-1.0.0-arm64.dmg
用法
验证本地是否已配置 secrets:
bash /mnt/skills/user/apple-mac-cicd/scripts/validate-secrets.sh
生成发布工作流脚手架:
bash /mnt/skills/user/apple-mac-cicd/scripts/scaffold-release-workflow.sh \
--type electron \
--app-name MyApp \
--output .github/workflows/release.yml
参数:
--type— native 或 electron(必填)--app-name— 产物名称主干,例如 Cometrans(必填)--output— 工作流文件路径(默认:.github/workflows/release.yml)
向用户呈现结果
协助搭建 macOS CI/CD 时,报告:
- 应用类型与所选更新器
- 仍缺失的 secrets(运行 validate 脚本输出)
- 创建/修改的文件及路径
- 发布步骤 — 如何触发首次发布
- 自动更新状态 — 已接通或剩余步骤
故障排查
CI 报“No signing certificate configured”
- 确认 APPLE_DEVELOPER_ID_P12_BASE64 / CSC_LINK secret 已设置且能解码为有效 P12
公证因凭据无效失败
- 重新生成 App 专用密码;确认 APPLE_TEAM_ID 与证书匹配
CI 成功但 Gatekeeper 仍拦截应用
- 确保原生 codesign 使用
--options runtime,或 Electron 使用hardenedRuntime: true - 确认已运行 stapler:
xcrun stapler validate <artifact>
electron-updater 找不到更新
- 发布必须包含 latest-mac.yml、.zip、.blockmap
- electron-builder.yml 中的 publish.owner / publish.repo 必须与 GitHub 仓库匹配
- 应用必须以生产模式构建(非 dev)
Sparkle 版本顺序错误
- 使用数字型 CFBundleVersion(Cometrans 公式:major10000 + minor100 + patch)
- 切勿在 CFBundleVersion 中使用 semver 字符串