GitHub Actions上传macOS App Bundle为产物后下载无法执行
问题根因
问题和签名、公证流程无关,核心是actions/upload-artifact的默认打包逻辑破坏了.app bundle的结构完整性:
actions/upload-artifact@v3及更早版本默认开启符号链接跟随,会将.app包内的所有相对符号链接替换为指向的实体文件副本,同时不会完整保留Unix文件权限位、macOS文件系统扩展属性。Avalonia应用的.app bundle、以及codesign写入的签名元数据强依赖包内的目录结构、符号链接关系和扩展属性,一旦结构被破坏,codesign校验时就会判定嵌套组件签名丢失或无效。- upload-artifact内置的zip打包实现未适配macOS应用签名的存储规则:macOS的部分签名信息存储在文件扩展属性中,普通zip打包逻辑如果没有专门做兼容,解压后签名信息会直接丢失。
- 本地流程正常是因为本地操作不会经过这套非原生的打包解压流程,签名结构始终完整;执行chmod添加可执行权限无效也符合预期,因为问题本质是bundle结构损坏,不是权限缺失。
可落地修复方案
按优先级尝试以下方案:
- 升级upload-artifact到v4及以上版本,关闭符号链接跟随配置,让动作尽可能保留文件原始结构
配置参考:- name: 上传签名完成的App Bundle uses: actions/upload-artifact@v4 with: name: signed-macos-app path: ./path/to/your/AppName.app follow-symlinks: false retention-days: 30 - 如果升级动作后问题仍然存在,直接绕开upload-artifact的内置打包逻辑:上传前先用macOS原生工具打包成完整保留签名结构的zip包,再上传这个zip作为产物
在上传步骤前增加打包命令:
后续upload-artifact直接指定上传生成的# 使用macOS原生ditto打包,完整保留资源分支、符号链接、权限、扩展属性和签名信息 ditto -c -k --sequesterRsrc --keepParent AppName.app AppName_signed_notarized.zipAppName_signed_notarized.zip即可,下载后用系统默认解压工具打开,得到的.app签名、公证、钉装信息全部完整,可以正常启动。 - 避坑:不要将.app放到多层嵌套的子文件夹后再让upload-artifact扫描整个文件夹打包,直接将.app或预打包好的zip作为path参数传入,减少文件遍历过程中破坏结构的概率。
流程校验方法
在工作流的上传步骤前增加本地校验环节,先确认runner环境内构建签名完成的应用本身是正常的,避免误判问题环节:
# 校验代码签名完整性 codesign -vvv --deep --strict AppName.app # 校验公证有效性 spctl -a -vvv AppName.app # 校验钉装状态 xcrun stapler validate AppName.app
如果以上命令全部执行通过,下载解压后校验失败,就可以确定是上传打包环节的结构破坏问题,直接用ditto预打包的方案即可100%解决。
内容的提问来源于stack exchange,提问作者sergevm
相关产品推荐
相关产品推荐

