资讯动态

MacOS DMG打包与公证全流程指南

发布时间:2026/10/26 2:42:15 来源:尧图企业网站定制
1. 为什么需要DMG打包与公证在MacOS生态中DMGDisk Image是最常见的软件分发格式之一。它本质上是一个虚拟磁盘镜像文件用户双击后会自动挂载为一个虚拟磁盘开发者可以将应用程序、说明文档等一起打包进去。相比直接分发APP文件DMG具有以下优势专业形象规范的DMG文件通常包含精心设计的背景图、应用程序快捷方式拖拽提示等给用户更专业的体验完整性保障DMG作为容器可以包含多个文件如主程序、帮助文档、许可协议等避免用户遗漏必要组件安装引导通过自定义背景图可以直观指导用户将APP拖拽到Applications文件夹压缩支持DMG支持压缩存储减小分发文件体积而公证Notarization则是苹果在macOS 10.14.5后引入的安全机制。未经公证的应用程序在首次运行时会出现安全警告甚至被系统直接阻止运行。公证过程实际上是苹果对应用程序进行恶意代码扫描和验证的过程通过后会给应用程序添加数字票据使用户可以更安全地安装和使用。重要提示从macOS 10.15 Catalina开始所有包含可执行代码的分发包包括DMG都必须经过公证才能在默认安全设置下正常运行。2. 环境准备与工具链配置2.1 基础环境要求在开始打包前需要确保开发环境满足以下条件macOS系统建议使用较新版本至少10.15以上Xcode命令行工具这是公证流程的必要组件xcode-select --install开发者账号有效的Apple Developer账号个人或公司App专用密码为公证流程生成的特殊密码非Apple ID密码2.2 关键工具介绍hdiutilmacOS内置的磁盘工具用于创建和管理DMG文件codesign苹果的代码签名工具用于对应用程序进行签名altoolXcode自带的公证上传工具位于/usr/bin/altoolstapler用于将公证结果钉到应用程序上的工具2.3 开发者证书配置登录 Apple Developer网站在Certificates, Identifiers Profiles中创建Developer ID Application证书下载并安装证书到钥匙串记下证书的完整名称通常类似Developer ID Application: Your Name (XXXXXXXXXX)3. 完整的DMG打包流程3.1 应用程序准备在打包前确保应用程序已经完成以下步骤清理构建产物删除所有调试符号和临时文件代码签名使用Developer ID证书对应用程序进行签名codesign --deep --force --verify --verbose --sign Developer ID Application: Your Name (XXXXXXXXXX) YourApp.app验证签名检查签名是否成功codesign -dv --verbose4 YourApp.app spctl -a -t exec -vv YourApp.app3.2 创建DMG文件以下是创建专业DMG的完整脚本#!/bin/bash APP_NAMEYourApp VERSION1.0 DMG_NAME${APP_NAME}_${VERSION}.dmg TEMP_DIRtemp_dmg VOLUME_NAME${APP_NAME} ${VERSION} APP_PATH./build/${APP_NAME}.app DMG_BACKGROUND_IMGbackground.png # 创建临时目录 mkdir -p ${TEMP_DIR} # 复制应用程序和资源 cp -R ${APP_PATH} ${TEMP_DIR} cp README.txt ${TEMP_DIR} cp LICENSE.txt ${TEMP_DIR} # 创建软链接到Applications ln -s /Applications ${TEMP_DIR}/Applications # 计算DMG大小增加20%余量 APP_SIZE$(du -sm ${TEMP_DIR} | cut -f1) DMG_SIZE$((APP_SIZE APP_SIZE/5)) # 创建空白DMG hdiutil create -srcfolder ${TEMP_DIR} \ -volname ${VOLUME_NAME} \ -fs HFS \ -fsargs -c c64,a16,e16 \ -format UDRW \ -size ${DMG_SIZE}m \ ${DMG_NAME}.temp # 挂载DMG DEVICE$(hdiutil attach -readwrite -noverify -noautoopen ${DMG_NAME}.temp.dmg | egrep ^/dev/ | sed 1q | awk {print $1}) MOUNT_POINT/Volumes/${VOLUME_NAME} # 设置DMG布局 echo tell application Finder tell disk ${VOLUME_NAME} open set current view of container window to icon view set toolbar visible of container window to false set statusbar visible of container window to false set the bounds of container window to {400, 100, 1000, 500} set viewOptions to the icon view options of container window set arrangement of viewOptions to not arranged set icon size of viewOptions to 72 set background picture of viewOptions to file .background:${DMG_BACKGROUND_IMG} set position of item ${APP_NAME}.app of container window to {150, 100} set position of item Applications of container window to {450, 100} set position of item README.txt of container window to {150, 250} set position of item LICENSE.txt of container window to {450, 250} update without registering applications delay 5 close end tell end tell | osascript # 设置权限 chmod -Rf go-w ${MOUNT_POINT} # 卸载DMG hdiutil detach ${DEVICE} # 压缩DMG hdiutil convert ${DMG_NAME}.temp.dmg \ -format UDZO \ -imagekey zlib-level9 \ -o ${DMG_NAME} # 清理临时文件 rm -f ${DMG_NAME}.temp.dmg rm -rf ${TEMP_DIR}3.3 DMG签名创建好的DMG也需要进行签名codesign --sign Developer ID Application: Your Name (XXXXXXXXXX) --timestamp ${DMG_NAME}4. 公证流程详解4.1 公证前的检查在提交公证前建议先进行以下检查检查签名有效性codesign -vvv --deep --strict ${DMG_NAME}验证Gatekeeper兼容性spctl -a -t install --context context:primary-signature -v ${DMG_NAME}4.2 提交公证使用altool提交公证请求xcrun altool --notarize-app \ --primary-bundle-id com.yourcompany.yourapp \ --username your_apple_idemail.com \ --password keychain:AC_PASSWORD \ --file ${DMG_NAME}注意这里的密码是App专用密码可以通过Apple ID账户页面生成。建议将密码存储在钥匙串中通过keychain:AC_PASSWORD引用而不是直接写在脚本中。4.3 检查公证状态公证通常需要5-30分钟完成可以通过以下命令检查状态xcrun altool --notarization-info RequestUUID \ --username your_apple_idemail.com \ --password keychain:AC_PASSWORD4.4 钉书公证结果公证通过后需要将公证结果钉到DMG文件上xcrun stapler staple ${DMG_NAME}验证钉书是否成功xcrun stapler validate ${DMG_NAME}5. 自动化脚本整合将上述流程整合为一个完整的自动化脚本#!/bin/bash # 配置变量 APP_NAMEYourApp VERSION1.0 BUNDLE_IDcom.yourcompany.yourapp DEVELOPER_IDDeveloper ID Application: Your Name (XXXXXXXXXX) APPLE_IDyour_apple_idemail.com DMG_BACKGROUND_IMGbackground.png # 清理旧文件 rm -rf ${APP_NAME}_${VERSION}.dmg rm -rf temp_dmg # 1. 代码签名 echo 正在签名应用程序... codesign --deep --force --verify --verbose --sign ${DEVELOPER_ID} \ --options runtime \ --timestamp \ ./build/${APP_NAME}.app # 2. 验证签名 echo 验证签名... codesign -dv --verbose4 ./build/${APP_NAME}.app spctl -a -t exec -vv ./build/${APP_NAME}.app # 3. 创建DMG echo 创建DMG文件... mkdir -p temp_dmg cp -R ./build/${APP_NAME}.app temp_dmg cp README.txt temp_dmg cp LICENSE.txt temp_dmg ln -s /Applications temp_dmg/Applications APP_SIZE$(du -sm temp_dmg | cut -f1) DMG_SIZE$((APP_SIZE APP_SIZE/5)) hdiutil create -srcfolder temp_dmg \ -volname ${APP_NAME} ${VERSION} \ -fs HFS \ -fsargs -c c64,a16,e16 \ -format UDRW \ -size ${DMG_SIZE}m \ ${APP_NAME}_${VERSION}.temp.dmg DEVICE$(hdiutil attach -readwrite -noverify -noautoopen ${APP_NAME}_${VERSION}.temp.dmg | egrep ^/dev/ | sed 1q | awk {print $1}) MOUNT_POINT/Volumes/${APP_NAME} ${VERSION} echo tell application Finder tell disk ${APP_NAME} ${VERSION} open set current view of container window to icon view set toolbar visible of container window to false set statusbar visible of container window to false set the bounds of container window to {400, 100, 1000, 500} set viewOptions to the icon view options of container window set arrangement of viewOptions to not arranged set icon size of viewOptions to 72 set background picture of viewOptions to file .background:${DMG_BACKGROUND_IMG} set position of item ${APP_NAME}.app of container window to {150, 100} set position of item Applications of container window to {450, 100} set position of item README.txt of container window to {150, 250} set position of item LICENSE.txt of container window to {450, 250} update without registering applications delay 5 close end tell end tell | osascript chmod -Rf go-w ${MOUNT_POINT} hdiutil detach ${DEVICE} hdiutil convert ${APP_NAME}_${VERSION}.temp.dmg \ -format UDZO \ -imagekey zlib-level9 \ -o ${APP_NAME}_${VERSION}.dmg rm -f ${APP_NAME}_${VERSION}.temp.dmg rm -rf temp_dmg # 4. 签名DMG echo 签名DMG文件... codesign --sign ${DEVELOPER_ID} --timestamp ${APP_NAME}_${VERSION}.dmg # 5. 提交公证 echo 提交公证... NOTARIZE_OUTPUT$(xcrun altool --notarize-app \ --primary-bundle-id ${BUNDLE_ID} \ --username ${APPLE_ID} \ --password keychain:AC_PASSWORD \ --file ${APP_NAME}_${VERSION}.dmg 21) REQUEST_UUID$(echo ${NOTARIZE_OUTPUT} | grep RequestUUID | awk {print $3}) if [ -z ${REQUEST_UUID} ]; then echo 公证提交失败: echo ${NOTARIZE_OUTPUT} exit 1 fi echo 公证已提交RequestUUID: ${REQUEST_UUID} echo 等待公证结果... # 6. 检查公证状态 while true; do NOTARIZATION_INFO$(xcrun altool --notarization-info ${REQUEST_UUID} \ --username ${APPLE_ID} \ --password keychain:AC_PASSWORD 21) STATUS$(echo ${NOTARIZATION_INFO} | grep Status: | awk -F : {print $2}) if [ ${STATUS} success ]; then echo 公证成功 break elif [ ${STATUS} in progress ]; then echo 公证仍在处理中等待30秒后重试... sleep 30 else echo 公证失败: echo ${NOTARIZATION_INFO} exit 1 fi done # 7. 钉书公证结果 echo 钉书公证结果... xcrun stapler staple ${APP_NAME}_${VERSION}.dmg xcrun stapler validate ${APP_NAME}_${VERSION}.dmg echo DMG打包及公证完成: ${APP_NAME}_${VERSION}.dmg6. 常见问题与解决方案6.1 公证失败常见原因签名无效或不完整解决方案确保使用--deep参数签名并验证所有嵌套组件都已签名codesign --deep --verify --verbose4 YourApp.app时间戳服务不可用解决方案检查网络连接确保能访问Apple的时间戳服务器ping timestamp.apple.com证书问题解决方案确认钥匙串中的证书有效且未被撤销security find-identity -v -p codesigning公证日志分析公证失败时会返回日志URL可以使用以下命令查看详细错误xcrun altool --notarization-info RequestUUID \ --username your_apple_idemail.com \ --password keychain:AC_PASSWORD6.2 DMG创建常见问题背景图片不显示确保图片已复制到.background隐藏文件夹图片格式建议使用PNG尺寸与DMG窗口大小匹配DMG挂载后图标位置错乱在osascript脚本中添加delay让Finder有足够时间刷新确保所有路径和文件名正确无误DMG文件过大使用UDZO格式并设置zlib-level9获得最佳压缩比清理应用程序中的调试符号和不需要的资源文件6.3 性能优化技巧并行处理将DMG创建和应用程序构建过程并行化增量构建仅重新构建变更的部分减少完整构建时间缓存公证结果对于未修改的版本可以直接复用之前的公证结果使用--options runtime启用Hardened Runtime这是公证的要求之一7. 进阶技巧与最佳实践7.1 自动化构建集成可以将上述脚本集成到CI/CD流程中例如GitHub Actions配置示例name: Build and Notarize on: release: types: [created] jobs: build: runs-on: macos-latest steps: - uses: actions/checkoutv2 - name: Build Application run: | # 你的构建命令 xcodebuild -project YourApp.xcodeproj -scheme YourApp -configuration Release - name: Package and Notarize env: APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_ID_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} run: | chmod x build_and_notarize.sh ./build_and_notarize.sh7.2 多架构支持随着Apple Silicon的普及建议构建通用二进制# 在Xcode构建命令中添加ARCHS参数 xcodebuild -project YourApp.xcodeproj \ -scheme YourApp \ -configuration Release \ ARCHSarm64 x86_64 \ ONLY_ACTIVE_ARCHNO7.3 版本管理策略语义化版本遵循主版本号.次版本号.修订号的格式DMG命名规范建议包含应用名称、版本号和架构信息如YourApp_1.0.0_universal.dmg版本元数据在Info.plist中正确设置CFBundleShortVersionString和CFBundleVersion7.4 安全增强措施启用Hardened Runtime在签名时添加--options runtime标志设置权限限制确保应用程序只请求必要的权限沙盒配置根据应用需求配置合适的沙盒权限库验证启用库验证以防止代码注入codesign --sign ${DEVELOPER_ID} \ --options runtime \ --timestamp \ --library-validation \ YourApp.app在实际项目中我发现将整个流程脚本化可以显著提高发布效率特别是在频繁迭代的开发周期中。一个完整的建议是创建一个Makefile来管理不同的构建目标例如.PHONY: all clean sign package notarize all: package clean: rm -rf build rm -rf *.dmg rm -rf temp_dmg build: xcodebuild -project YourApp.xcodeproj \ -scheme YourApp \ -configuration Release \ ARCHSarm64 x86_64 \ ONLY_ACTIVE_ARCHNO \ CONFIGURATION_BUILD_DIRbuild sign: build codesign --deep --force --verify --verbose \ --sign Developer ID Application: Your Name (XXXXXXXXXX) \ --options runtime \ --timestamp \ build/YourApp.app package: sign ./create_dmg.sh notarize: package xcrun altool --notarize-app \ --primary-bundle-id com.yourcompany.yourapp \ --username your_apple_idemail.com \ --password keychain:AC_PASSWORD \ --file YourApp_1.0.0.dmg这种模块化的设计使得构建流程更加清晰也便于团队协作和维护。对于大型项目还可以考虑将配置信息提取到单独的配置文件中实现脚本与配置的分离。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑