macos-release
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRelease macOS App
发布macOS应用
This skill covers the full release pipeline for distributing a native macOS app outside the Mac App Store via GitHub Releases with Sparkle auto-update support.
此技能覆盖了完整的发布流程,用于在Mac App Store之外,通过GitHub Releases分发带有Sparkle自动更新支持的原生macOS应用。
Release Pipeline Overview
发布流程概览
Bump version → Archive → Notarize → Export → Create DMG → Sign DMG → Update appcast.xml → Git push → GitHub ReleaseBump version → Archive → Notarize → Export → Create DMG → Sign DMG → Update appcast.xml → Git push → GitHub ReleasePrerequisites
前置条件
The user needs these tools installed:
| Tool | Install | Purpose |
|---|---|---|
| | Creates the DMG installer |
| | Creates GitHub releases |
| Built-in | Pushes appcast changes |
Sparkle | Built automatically when the project is built with Sparkle | EdDSA-signs the DMG |
The binary lives in DerivedData after building the project in Xcode:
sign_updatebash
find ~/Library/Developer/Xcode/DerivedData -name "sign_update" -type f 2>/dev/null | head -1用户需要安装以下工具:
| 工具 | 安装方式 | 用途 |
|---|---|---|
| | 制作DMG安装包 |
| | 创建GitHub releases |
| 系统自带 | 推送appcast变更 |
Sparkle | 项目使用Sparkle构建时自动生成 | 对DMG进行EdDSA签名 |
sign_updatebash
find ~/Library/Developer/Xcode/DerivedData -name "sign_update" -type f 2>/dev/null | head -1Step-by-Step Release Guide
分步发布指南
1. Bump Version Numbers
1. 提升版本号
In Xcode, update:
- (e.g.,
MARKETING_VERSION) -- the user-facing version1.2 - (e.g.,
CURRENT_PROJECT_VERSION) -- the build number (must be unique per release)5
Or via command line:
bash
undefined在Xcode中,更新以下内容:
- (例如
MARKETING_VERSION)—— 用户可见的版本号1.2 - (例如
CURRENT_PROJECT_VERSION)—— 构建号(每个版本必须唯一)5
也可以通过命令行操作:
bash
undefinedCheck current values
Check current values
grep -E "MARKETING_VERSION|CURRENT_PROJECT_VERSION" YourApp.xcodeproj/project.pbxproj | head -4
undefinedgrep -E "MARKETING_VERSION|CURRENT_PROJECT_VERSION" YourApp.xcodeproj/project.pbxproj | head -4
undefined2. Archive in Xcode
2. 在Xcode中归档
Product > Archive. This creates a release build with the proper signing identity.
Product > Archive。这会使用正确的签名身份创建发布构建版本。
3. Notarize and Export
3. 公证与导出
In the Archives organizer:
- Select the archive > Distribute App
- Choose "Direct Distribution" (or "Developer ID" for notarization)
- Wait for notarization to complete
- Export to
~/Downloads/YourApp.app
在归档管理器中:
- 选中归档文件 > Distribute App
- 选择「Direct Distribution」(或用于公证的「Developer ID」)
- 等待公证完成
- 导出到
~/Downloads/YourApp.app
4. Create DMG
4. 制作DMG
bash
create-dmg \
--volname "YourApp" \
--window-pos 200 120 \
--window-size 660 400 \
--icon-size 160 \
--icon "YourApp.app" 180 170 \
--app-drop-link 480 170 \
--hide-extension "YourApp.app" \
~/Downloads/YourApp.dmg \
~/Downloads/YourApp.appbash
create-dmg \
--volname "YourApp" \
--window-pos 200 120 \
--window-size 660 400 \
--icon-size 160 \
--icon "YourApp.app" 180 170 \
--app-drop-link 480 170 \
--hide-extension "YourApp.app" \
~/Downloads/YourApp.dmg \
~/Downloads/YourApp.app5. Sign DMG with Sparkle
5. 使用Sparkle对DMG签名
bash
/path/to/sign_update ~/Downloads/YourApp.dmgThis outputs the EdDSA signature and file length:
sparkle:edSignature="BASE64..." length="12345"Save both values for the appcast.
bash
/path/to/sign_update ~/Downloads/YourApp.dmg这会输出EdDSA签名和文件长度:
sparkle:edSignature="BASE64..." length="12345"保存这两个值,用于后续更新appcast。
6. Update appcast.xml
6. 更新appcast.xml
Add a new at the top of the in your :
<item><channel>appcast.xmlxml
<item>
<title>Version 1.2 (Build 5)</title>
<pubDate>Mon, 26 May 2026 12:00:00 +0000</pubDate>
<sparkle:version>5</sparkle:version>
<sparkle:shortVersionString>1.2</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>14.0</sparkle:minimumSystemVersion>
<description><![CDATA[<ul><li>Feature one</li><li>Bug fix two</li></ul>]]></description>
<enclosure url="https://github.com/OWNER/REPO/releases/download/v1.2/YourApp.dmg"
type="application/octet-stream"
sparkle:edSignature="THE_SIGNATURE_FROM_STEP_5"
length="THE_LENGTH_FROM_STEP_5" />
</item>The should be RFC 2822 format. Generate it:
pubDatebash
date -R在你的的顶部添加一个新的:
appcast.xml<channel><item>xml
<item>
<title>Version 1.2 (Build 5)</title>
<pubDate>Mon, 26 May 2026 12:00:00 +0000</pubDate>
<sparkle:version>5</sparkle:version>
<sparkle:shortVersionString>1.2</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>14.0</sparkle:minimumSystemVersion>
<description><![CDATA[<ul><li>Feature one</li><li>Bug fix two</li></ul>]]></description>
<enclosure url="https://github.com/OWNER/REPO/releases/download/v1.2/YourApp.dmg"
type="application/octet-stream"
sparkle:edSignature="THE_SIGNATURE_FROM_STEP_5"
length="THE_LENGTH_FROM_STEP_5" />
</item>pubDatebash
date -R7. Commit and Push Appcast
7. 提交并推送appcast
bash
git add appcast.xml
git commit -m "Release v1.2 appcast"
git push origin mainbash
git add appcast.xml
git commit -m "Release v1.2 appcast"
git push origin main8. Create GitHub Release
8. 创建GitHub Release
bash
gh release create v1.2 \
~/Downloads/YourApp.dmg \
--title "v1.2" \
--notes "- Feature one
- Bug fix two"bash
gh release create v1.2 \
~/Downloads/YourApp.dmg \
--title "v1.2" \
--notes "- Feature one
- Bug fix two"Automating the Pipeline
流程自动化
For frequent releases, build a CLI tool that automates steps 4-8. See for a template Go CLI that handles DMG creation, signing, appcast updates, and GitHub release creation in one command.
references/release-pipeline.mdThe CLI should:
- Find in DerivedData automatically
sign_update - Read version/build from the exported app's Info.plist via
plutil - Parse and update appcast.xml (preserving existing entries)
- Interactively collect release notes
- Show a summary and ask for confirmation before proceeding
- Create the GitHub release with the DMG attached
对于频繁发布的场景,可以构建一个CLI工具来自动化第4-8步。参考中的Go CLI模板,它可以在一条命令中完成DMG制作、签名、appcast更新和GitHub Release创建。
references/release-pipeline.md该CLI应具备以下功能:
- 自动在DerivedData中查找
sign_update - 通过从导出应用的Info.plist中读取版本/构建号
plutil - 解析并更新appcast.xml(保留现有条目)
- 交互式收集发布说明
- 显示摘要并在执行前请求确认
- 创建附带DMG的GitHub Release
Release CLI Tool
发布CLI工具
This repo includes a Go CLI that automates steps 4-8 (DMG creation through GitHub release) in a single command. It reads configuration from a file in your project root.
release.json此仓库包含一个Go CLI,可在单条命令中自动化第4-8步(从DMG制作到GitHub Release)。它从项目根目录的文件中读取配置。
release.jsonSetup
配置步骤
- Create a in your project root (see
release.json):cli/release.example.json
json
{
"app_name": "MyApp",
"github_repo": "owner/myapp"
}Only and are required. Everything else has sensible defaults:
app_namegithub_repo| Field | Default | Description |
|---|---|---|
| | The .app bundle filename |
| | Output DMG filename |
| | Branch to push appcast to |
| | Sparkle minimum macOS version |
| | Appcast filename in repo root |
| | DerivedData prefixes to search for |
- Run the CLI from your project directory:
bash
go run github.com/fayazara/macos-app-skills/release/cli@latestOr clone this repo and run locally:
bash
go run ./release/cliThe CLI is interactive -- it prompts for release notes and asks for confirmation before proceeding. It must be run in a terminal the user can interact with.
- 在项目根目录创建(参考
release.json):cli/release.example.json
json
{
"app_name": "MyApp",
"github_repo": "owner/myapp"
}仅需填写和,其余配置均有合理默认值:
app_namegithub_repo| 字段 | 默认值 | 描述 |
|---|---|---|
| | .app包的文件名 |
| | 输出的DMG文件名 |
| | 推送appcast的分支 |
| | Sparkle要求的最低macOS版本 |
| | 仓库根目录下的appcast文件名 |
| | 用于搜索 |
- 在项目目录中运行CLI:
bash
go run github.com/fayazara/macos-app-skills/release/cli@latest或者克隆此仓库后本地运行:
bash
go run ./release/cli该CLI是交互式的——它会提示输入发布说明,并在执行前请求确认。必须在用户可交互的终端中运行。
What the CLI Does
CLI的功能
- Finds by walking up from the current directory
release.json - Checks that ,
create-dmg,gh, and Sparkle'sgitare availablesign_update - Validates the exported app in (reads version, build, Sparkle keys from Info.plist)
~/Downloads/ - Warns if the build number already exists in the appcast
- Collects release notes interactively (one bullet per line, empty line to finish)
- Shows a release summary and asks for confirmation
- Creates the DMG via
create-dmg - Signs the DMG with Sparkle's (EdDSA)
sign_update - Updates with the new release entry
appcast.xml - Commits and pushes the appcast
- Creates a GitHub release with the DMG attached
- 从当前目录向上查找
release.json - 检查、
create-dmg、gh和Sparkle的git是否可用sign_update - 验证中导出的应用(从Info.plist读取版本、构建号、Sparkle密钥)
~/Downloads/ - 如果构建号已存在于appcast中则发出警告
- 交互式收集发布说明(每行一个要点,空行结束输入)
- 显示发布摘要并请求确认
- 通过制作DMG
create-dmg - 使用Sparkle的(EdDSA)对DMG签名
sign_update - 用新的发布条目更新
appcast.xml - 提交并推送appcast
- 创建附带DMG的GitHub Release
Common Issues
常见问题
| Problem | Solution |
|---|---|
| Build the project in Xcode first so DerivedData has the Sparkle artifacts |
| Run |
| Duplicate build number in appcast | Bump |
| Notarization fails | Check signing identity, entitlements, and hardened runtime settings |
| DMG is too large | Check for debug symbols or unnecessary frameworks in the export |
| App won't update | Verify |
| 问题 | 解决方案 |
|---|---|
找不到 | 先在Xcode中构建项目,确保DerivedData中有Sparkle构建产物 |
| 运行 |
| appcast中存在重复构建号 | 在归档前提升 |
| 公证失败 | 检查签名身份、entitlements和hardened runtime设置 |
| DMG体积过大 | 检查导出的内容中是否包含调试符号或不必要的框架 |
| 应用无法更新 | 确认 |
Appcast Hosting
Appcast托管
The simplest hosting: commit to your GitHub repo and use the raw URL:
appcast.xmlhttps://raw.githubusercontent.com/OWNER/REPO/main/appcast.xmlThis must match in your app's Info.plist.
SUFeedURL最简单的托管方式:将提交到GitHub仓库,使用原始文件URL:
appcast.xmlhttps://raw.githubusercontent.com/OWNER/REPO/main/appcast.xml该URL必须与应用Info.plist中的一致。
SUFeedURL