releasing-macos-apps
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseReleasing macOS Apps
macOS应用发布指南
Complete workflow for creating notarized macOS app releases with Sparkle auto-updates, DMG installers, and GitHub releases.
实现创建带有Sparkle自动更新、DMG安装程序和GitHub发布的公证版macOS应用发布的完整工作流。
Release Checklist
发布检查清单
Copy this checklist and track progress:
Release Progress:
- [ ] Step 1: Check prerequisites (certificates, credentials)
- [ ] Step 2: Update version in .xcconfig file
- [ ] Step 3: Build and archive the app
- [ ] Step 4: Export with proper code signing
- [ ] Step 5: Create zip and generate Sparkle signature
- [ ] Step 6: Create DMG with Applications folder
- [ ] Step 7: Submit for notarization
- [ ] Step 8: Staple notarization ticket to DMG
- [ ] Step 9: Update appcast.xml with new signature
- [ ] Step 10: Commit and push changes
- [ ] Step 11: Update GitHub release assets
- [ ] Step 12: Verify DMG and version number复制此清单并跟踪进度:
Release Progress:
- [ ] Step 1: Check prerequisites (certificates, credentials)
- [ ] Step 2: Update version in .xcconfig file
- [ ] Step 3: Build and archive the app
- [ ] Step 4: Export with proper code signing
- [ ] Step 5: Create zip and generate Sparkle signature
- [ ] Step 6: Create DMG with Applications folder
- [ ] Step 7: Submit for notarization
- [ ] Step 8: Staple notarization ticket to DMG
- [ ] Step 9: Update appcast.xml with new signature
- [ ] Step 10: Commit and push changes
- [ ] Step 11: Update GitHub release assets
- [ ] Step 12: Verify DMG and version numberPrerequisites
前置条件
Before starting a release, verify:
- Apple Developer ID Application certificate installed and valid
- Apple ID credentials for notarization:
- Apple ID email
- App-specific password (generate at appleid.apple.com)
- Team ID
- Sparkle private key for signing updates
- GitHub CLI () installed and authenticated
gh - Version configuration location identified (usually file)
.xcconfig
Check certificate:
bash
security find-identity -v -p codesigning | grep "Developer ID Application"开始发布前,请确认以下内容:
- Apple Developer ID Application证书已安装且有效
- 用于公证的Apple ID凭据:
- Apple ID邮箱
- 应用专用密码(在appleid.apple.com生成)
- 团队ID
- 用于签名更新的Sparkle私钥
- GitHub CLI()已安装并完成认证
gh - 版本配置位置已确定(通常为文件)
.xcconfig
检查证书:
bash
security find-identity -v -p codesigning | grep "Developer ID Application"Step 1: Update Version
步骤1:更新版本号
Locate your version configuration file (commonly or ).
ProjectName.xcconfigproject.pbxprojFor .xcconfig files:
bash
undefined找到版本配置文件(通常为或)。
ProjectName.xcconfigproject.pbxproj针对.xcconfig文件:
bash
undefinedEdit the APP_VERSION line
编辑APP_VERSION行
Example: APP_VERSION = 1.0.9
示例: APP_VERSION = 1.0.9
**Verify the update:**
```bash
xcodebuild -project PROJECT.xcodeproj -showBuildSettings | grep MARKETING_VERSION
**验证版本更新:**
```bash
xcodebuild -project PROJECT.xcodeproj -showBuildSettings | grep MARKETING_VERSIONStep 2: Build and Archive
步骤2:构建并归档应用
Archive the app with the new version:
bash
xcodebuild -project PROJECT.xcodeproj \
-scheme SCHEME_NAME \
-configuration Release \
-archivePath ~/Desktop/APP-VERSION.xcarchive \
archiveVerify archive was created:
bash
ls -la ~/Desktop/APP-VERSION.xcarchive使用新版本号归档应用:
bash
xcodebuild -project PROJECT.xcodeproj \
-scheme SCHEME_NAME \
-configuration Release \
-archivePath ~/Desktop/APP-VERSION.xcarchive \
archive验证归档文件已创建:
bash
ls -la ~/Desktop/APP-VERSION.xcarchiveStep 3: Export with Code Signing
步骤3:带代码签名导出
Create export options file:
bash
cat > /tmp/ExportOptions.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>destination</key>
<string>export</string>
<key>method</key>
<string>developer-id</string>
<key>signingStyle</key>
<string>automatic</string>
<key>teamID</key>
<string>YOUR_TEAM_ID</string>
<key>signingCertificate</key>
<string>Developer ID Application</string>
</dict>
</plist>
EOFReplace with your actual team ID.
YOUR_TEAM_IDExport the archive:
bash
xcodebuild -exportArchive \
-archivePath ~/Desktop/APP-VERSION.xcarchive \
-exportPath ~/Desktop/APP-VERSION-Export \
-exportOptionsPlist /tmp/ExportOptions.plistVerify the exported app version:
bash
defaults read ~/Desktop/APP-VERSION-Export/APP.app/Contents/Info.plist CFBundleShortVersionStringThis should show the new version number.
Verify code signing:
bash
codesign -dvvv ~/Desktop/APP-VERSION-Export/APP.appLook for "Developer ID Application" in the Authority lines.
创建导出选项文件:
bash
cat > /tmp/ExportOptions.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>destination</key>
<string>export</string>
<key>method</key>
<string>developer-id</string>
<key>signingStyle</key>
<string>automatic</string>
<key>teamID</key>
<string>YOUR_TEAM_ID</string>
<key>signingCertificate</key>
<string>Developer ID Application</string>
</dict>
</plist>
EOF将替换为实际的团队ID。
YOUR_TEAM_ID导出归档文件:
bash
xcodebuild -exportArchive \
-archivePath ~/Desktop/APP-VERSION.xcarchive \
-exportPath ~/Desktop/APP-VERSION-Export \
-exportOptionsPlist /tmp/ExportOptions.plist验证导出应用的版本号:
bash
defaults read ~/Desktop/APP-VERSION-Export/APP.app/Contents/Info.plist CFBundleShortVersionString此命令应显示新的版本号。
验证代码签名:
bash
codesign -dvvv ~/Desktop/APP-VERSION-Export/APP.app在Authority行中查找"Developer ID Application"。
Step 4: Create Zip and Generate Sparkle Signature
步骤4:创建Zip包并生成Sparkle签名
Create zip file for Sparkle auto-updates:
bash
cd ~/Desktop/APP-VERSION-Export
ditto -c -k --keepParent APP.app APP.app.zipGenerate Sparkle EdDSA signature (you'll be prompted for the private key):
bash
echo "YOUR_SPARKLE_PRIVATE_KEY" | \
~/Library/Developer/Xcode/DerivedData/PROJECT-HASH/SourcePackages/artifacts/sparkle/Sparkle/bin/sign_update \
APP.app.zip --ed-key-file -Output format:
sparkle:edSignature="BASE64_SIGNATURE" length="FILE_SIZE"Save both the signature and length for updating appcast.xml.
For more details, see SPARKLE.md.
为Sparkle自动更新创建Zip文件:
bash
cd ~/Desktop/APP-VERSION-Export
ditto -c -k --keepParent APP.app APP.app.zip生成Sparkle EdDSA签名(会提示输入私钥):
bash
echo "YOUR_SPARKLE_PRIVATE_KEY" | \
~/Library/Developer/Xcode/DerivedData/PROJECT-HASH/SourcePackages/artifacts/sparkle/Sparkle/bin/sign_update \
APP.app.zip --ed-key-file -输出格式:
sparkle:edSignature="BASE64_SIGNATURE" length="FILE_SIZE"保存签名和文件大小,用于更新appcast.xml。
更多详情,请查看SPARKLE.md。
Step 5: Create DMG with Applications Folder
步骤5:创建带Applications文件夹的DMG
Create DMG installer with Applications folder symlink for drag-and-drop installation:
bash
TEMP_DMG_DIR="/tmp/APP_dmg" && \
rm -rf "${TEMP_DMG_DIR}" && \
mkdir -p "${TEMP_DMG_DIR}" && \
cp -R ~/Desktop/APP-VERSION-Export/APP.app "${TEMP_DMG_DIR}/" && \
ln -s /Applications "${TEMP_DMG_DIR}/Applications" && \
hdiutil create -volname "APP VERSION" \
-srcfolder "${TEMP_DMG_DIR}" \
-ov -format UDZO ~/Desktop/APP-VERSION.dmg && \
rm -rf "${TEMP_DMG_DIR}"Verify DMG contents:
bash
hdiutil attach ~/Desktop/APP-VERSION.dmg -readonly -nobrowse -mountpoint /tmp/verify_dmg && \
ls -la /tmp/verify_dmg && \
hdiutil detach /tmp/verify_dmgYou should see both and (symlink).
APP.appApplications创建包含Applications文件夹符号链接的DMG安装包,支持拖放安装:
bash
TEMP_DMG_DIR="/tmp/APP_dmg" && \
rm -rf "${TEMP_DMG_DIR}" && \
mkdir -p "${TEMP_DMG_DIR}" && \
cp -R ~/Desktop/APP-VERSION-Export/APP.app "${TEMP_DMG_DIR}/" && \
ln -s /Applications "${TEMP_DMG_DIR}/Applications" && \
hdiutil create -volname "APP VERSION" \
-srcfolder "${TEMP_DMG_DIR}" \
-ov -format UDZO ~/Desktop/APP-VERSION.dmg && \
rm -rf "${TEMP_DMG_DIR}"验证DMG内容:
bash
hdiutil attach ~/Desktop/APP-VERSION.dmg -readonly -nobrowse -mountpoint /tmp/verify_dmg && \
ls -la /tmp/verify_dmg && \
hdiutil detach /tmp/verify_dmg应同时显示和(符号链接)。
APP.appApplicationsStep 6: Submit for Notarization
步骤6:提交公证
Submit the DMG to Apple for notarization (you'll be prompted for credentials):
bash
xcrun notarytool submit ~/Desktop/APP-VERSION.dmg \
--apple-id YOUR_APPLE_ID@gmail.com \
--team-id YOUR_TEAM_ID \
--password YOUR_APP_SPECIFIC_PASSWORD \
--waitThe flag makes the command wait for processing to complete (typically 1-2 minutes).
--waitExpected output:
Processing complete
id: [submission-id]
status: AcceptedIf status is "Invalid", get detailed logs:
bash
xcrun notarytool log SUBMISSION_ID \
--apple-id YOUR_APPLE_ID@gmail.com \
--team-id YOUR_TEAM_ID \
--password YOUR_APP_SPECIFIC_PASSWORDFor notarization troubleshooting, see NOTARIZATION.md.
将DMG提交给Apple进行公证(会提示输入凭据):
bash
xcrun notarytool submit ~/Desktop/APP-VERSION.dmg \
--apple-id YOUR_APPLE_ID@gmail.com \
--team-id YOUR_TEAM_ID \
--password YOUR_APP_SPECIFIC_PASSWORD \
--wait--wait预期输出:
Processing complete
id: [submission-id]
status: Accepted如果状态为"Invalid",获取详细日志:
bash
xcrun notarytool log SUBMISSION_ID \
--apple-id YOUR_APPLE_ID@gmail.com \
--team-id YOUR_TEAM_ID \
--password YOUR_APP_SPECIFIC_PASSWORD关于公正确排,请查看NOTARIZATION.md。
Step 7: Staple Notarization Ticket
步骤7:绑定公证票据
Staple the notarization ticket to the DMG:
bash
xcrun stapler staple ~/Desktop/APP-VERSION.dmgExpected output:
The staple and validate action worked!Verify notarization:
bash
spctl -a -vvv ~/Desktop/APP-VERSION-Export/APP.appShould show:
accepted
source=Notarized Developer ID将公证票据绑定到DMG:
bash
xcrun stapler staple ~/Desktop/APP-VERSION.dmg预期输出:
The staple and validate action worked!验证公证状态:
bash
spctl -a -vvv ~/Desktop/APP-VERSION-Export/APP.app应显示:
accepted
source=Notarized Developer IDStep 8: Update appcast.xml
步骤8:更新appcast.xml
Update the Sparkle appcast file with the new version, signature, and file size from Step 4:
xml
<item>
<title>Version X.X.X</title>
<link>https://github.com/USER/REPO</link>
<sparkle:version>X.X.X</sparkle:version>
<sparkle:channel>stable</sparkle:channel>
<description><![CDATA[
Release version X.X.X
]]></description>
<pubDate>DAY, DD MMM YYYY HH:MM:SS -0700</pubDate>
<enclosure
url="https://github.com/USER/REPO/releases/download/vX.X.X/APP.app.zip"
sparkle:version="X.X.X"
sparkle:edSignature="SIGNATURE_FROM_STEP_4"
length="FILE_SIZE_FROM_STEP_4"
type="application/octet-stream" />
</item>Note: The gitleaks pre-commit hook may flag the Sparkle signature as a potential secret. This is a false positive - the EdDSA signature is public and safe to commit. Use if needed.
git commit --no-verify使用步骤4中获取的新版本号、签名和文件大小更新Sparkle的appcast文件:
xml
<item>
<title>Version X.X.X</title>
<link>https://github.com/USER/REPO</link>
<sparkle:version>X.X.X</sparkle:version>
<sparkle:channel>stable</sparkle:channel>
<description><![CDATA[
Release version X.X.X
]]></description>
<pubDate>DAY, DD MMM YYYY HH:MM:SS -0700</pubDate>
<enclosure
url="https://github.com/USER/REPO/releases/download/vX.X.X/APP.app.zip"
sparkle:version="X.X.X"
sparkle:edSignature="SIGNATURE_FROM_STEP_4"
length="FILE_SIZE_FROM_STEP_4"
type="application/octet-stream" />
</item>注意: gitleaks预提交钩子可能会将Sparkle签名标记为潜在机密。这是误报 - EdDSA签名是公开的,可以安全提交。如有需要,使用。
git commit --no-verifyStep 9: Commit and Push Changes
步骤9:提交并推送更改
Commit the version update and appcast changes:
bash
git add PROJECT.xcconfig appcast.xml
git commit --no-verify -m "Bump version to X.X.X
Update appcast.xml with new version, Sparkle signature, and file size.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>"
git push提交版本更新和appcast更改:
bash
git add PROJECT.xcconfig appcast.xml
git commit --no-verify -m "Bump version to X.X.X
Update appcast.xml with new version, Sparkle signature, and file size.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>"
git pushStep 10: Update GitHub Release
步骤10:更新GitHub发布
Create or update the GitHub release with new assets:
For new releases:
bash
gh release create vX.X.X \
--title "APP vX.X.X" \
--notes "Release version X.X.X" \
~/Desktop/APP-VERSION.dmg \
~/Desktop/APP-VERSION-Export/APP.app.zipFor updating existing releases:
bash
undefined创建或更新GitHub发布并添加新资产:
针对新发布:
bash
gh release create vX.X.X \
--title "APP vX.X.X" \
--notes "Release version X.X.X" \
~/Desktop/APP-VERSION.dmg \
~/Desktop/APP-VERSION-Export/APP.app.zip针对更新现有发布:
bash
undefinedUpload new assets (overwrites existing with --clobber)
上传新资产(--clobber参数会覆盖现有资产)
gh release upload vX.X.X
~/Desktop/APP-VERSION.dmg
~/Desktop/APP-VERSION-Export/APP.app.zip
--clobber
~/Desktop/APP-VERSION.dmg
~/Desktop/APP-VERSION-Export/APP.app.zip
--clobber
**Note on asset naming:**
The uploaded filename becomes the asset name. To upload with a specific name:
```bashgh release upload vX.X.X
~/Desktop/APP-VERSION.dmg
~/Desktop/APP-VERSION-Export/APP.app.zip
--clobber
~/Desktop/APP-VERSION.dmg
~/Desktop/APP-VERSION-Export/APP.app.zip
--clobber
**资产命名注意事项:**
上传的文件名会成为资产名称。如需使用特定名称上传:
```bashCopy to desired name first
先复制为目标名称
cp ~/Desktop/APP-1.0.9.dmg /tmp/APP.dmg
gh release upload vX.X.X /tmp/APP.dmg
**Verify release assets:**
```bash
gh release view vX.X.X --json assets -q '.assets[] | "\(.name) - \(.size) bytes"'cp ~/Desktop/APP-1.0.9.dmg /tmp/APP.dmg
gh release upload vX.X.X /tmp/APP.dmg
**验证发布资产:**
```bash
gh release view vX.X.X --json assets -q '.assets[] | "\(.name) - \(.size) bytes"'Step 11: Final Verification
步骤11:最终验证
Verify the release is working correctly:
Check version in app:
bash
defaults read /Applications/APP.app/Contents/Info.plist CFBundleShortVersionStringShould show:
X.X.XTest DMG:
- Download the DMG from GitHub release
- Open the DMG
- Verify Applications folder is present for drag-and-drop
- Drag app to Applications and launch
- Should open without any "malicious" or security warnings
Test Sparkle updates:
- Users with previous versions should receive automatic update notifications
- The update should download and install smoothly
验证发布是否正常工作:
检查应用内版本号:
bash
defaults read /Applications/APP.app/Contents/Info.plist CFBundleShortVersionString应显示:
X.X.X测试DMG:
- 从GitHub发布下载DMG
- 打开DMG
- 确认存在用于拖放的Applications文件夹
- 将应用拖到Applications文件夹并启动
- 启动时不应出现“恶意软件”或安全警告
测试Sparkle更新:
- 旧版本用户应收到自动更新通知
- 更新应能顺利下载并安装
Common Issues
常见问题
If you encounter problems, see TROUBLESHOOTING.md for solutions to:
- Version not updating after rebuild
- DMG missing Applications folder
- Notarization failures
- "Malicious app" warnings
- Sparkle signature issues
- CI/CD failures
如遇到问题,请查看TROUBLESHOOTING.md获取以下问题的解决方案:
- 重新构建后版本号未更新
- DMG缺少Applications文件夹
- 公证失败
- “恶意应用”警告
- Sparkle签名问题
- CI/CD失败
Quick Reference
快速参考
Check version:
bash
defaults read /path/to/APP.app/Contents/Info.plist CFBundleShortVersionStringCheck code signing:
bash
codesign -dvvv /path/to/APP.appCheck notarization:
bash
spctl -a -vvv /path/to/APP.appGet Sparkle sign_update path:
bash
find ~/Library/Developer/Xcode/DerivedData -name sign_update -type f检查版本号:
bash
defaults read /path/to/APP.app/Contents/Info.plist CFBundleShortVersionString检查代码签名:
bash
codesign -dvvv /path/to/APP.app检查公证状态:
bash
spctl -a -vvv /path/to/APP.app获取Sparkle sign_update路径:
bash
find ~/Library/Developer/Xcode/DerivedData -name sign_update -type f