Mac应用发布错误:ITMS-91109
问题
今天进行 时光本 macOS端 更新的时候,上传安装包到 App Store Connect 成功后,但是在 App Store Connect 一直无法看到相应的构架包,查看邮件后发现了一个警告邮件:Action needed: The uploaded build for 时光本-日记本·笔记本·记事本·备忘录 has one or more issues.
App Store Connect
Hello,
We noticed one or more issues with a recent delivery for the following app:
时光本-日记本·笔记本·记事本·备忘录 Version 1.2.9 Build 129 Please correct the following issues and upload a new binary to App Store Connect.
ITMS-91109: Invalid package contents - The package contains one or more files with the com.apple.quarantine extended file attribute, such as “com.gorpeln.xxx.pkg/Payload/GPNotesForMac.app/Contents/Resources/zh-Hans.lproj/Main.strings”. This attribute isn’t permitted in macOS apps distributed on TestFlight or the App Store. Please remove the attribute from all files within your app and upload again.
Apple Developer Relations
在网上查找这个错误提示表明,提交到 App Store 的 macOS 应用程序包中包含一个或多个带有com.apple.quarantine扩展文件属性的文件,比如com.gorpeln.xxx.pkg/Payload/GPNotesForMac.app/Contents/Resources/zh-Hans.lproj/Main.strings,而这种属性是不被允许的。
com.apple.quarantine属性是 macOS 的一个安全特性,用于标记从不受信任的来源获取的文件,例如通过互联网下载或 AirDrop 接收的文件,以防止潜在的恶意软件。要解决这个问题,需要移除应用程序中所有文件的 com.apple.quarantine 属性。
看到网上的结果后知道是安装包中某些文件被添加了com.apple.quarantine 属性,具体可能导致相关错误的原因也很多:
- 下载来源的影响
- Xcode 构建过程问题
- 系统安全机制
- 第三方软件干扰
但是具体怎么被添加的就不得而知了,于是就进行了漫长的排查解决过程,最后结果让自己很无奈。
解决思路
步骤一: 解压安装包
首先导出安装包,按照提示路径,找到对应文件,使用 xattr 查找该文件是否有 com.apple.quarantine ,查找结果显示的确有,并且有标记是keka解压软件,因为我是用的keka进行的安装包解压,想着keka的标记应该是该流程添加,并不是原来 com.apple.quarantine 属性添加的直接原因,于是就尝试其他方法解决该问题。
步骤二:更新 Xcode
为什么要更新 Xcode 呢,因为在提交 macOS 版本前先提交了 iOS 版本,iOS 版本提交后,提交成功页面显示有警告内容,提示马上要强制使用 Xcode 16 进行开发发布了,所以就想着是不是当前 Xcode 版本太低了导致的呢,于是就进行了更新操作,经过漫长的下载安装,问题依然存在,使用新的 Mac 用户也不行,只能想其他方法了。
步骤三:操作导出的安装包
Xcode 更新不行,想着提示有具体的安装包错误内容路径,就操作删除相关内容的 com.apple.quarantine 属性,网上也有相应的操作流程xattr -rd com.apple.quarantine /path/to/YourApp.app
,操作后继续提交,还是不行,还是同样的错误。
分析这个步骤只是进行简单的安装包操作,没有对安装包里面的内容遍历操作,于是想着进行一下遍历删除操作,但是该操作又无法直接遍历安装包内部内容,于是就进行相应的解压,遍历删除相关属性,操作还算顺利,并且重新打包上传,上传直接就报错了,提示证书问题,确实重新打包没有使用证书,重新查找资料,配置相关重新打包证书,完成打包成功上传,但是苹果接着发来邮件提示证书还是有问题。
Hello,
We noticed one or more issues with a recent delivery for the following app:
时光本-日记本·笔记本·记事本·备忘录 Version 1.3.4 Build 134 Please correct the following issues and upload a new binary to App Store Connect.
ITMS-90237: The product archive package’s signature is invalid. Ensure that it is signed with your ‘3rd Party Mac Developer Installer’ certificate.
Apple Developer Relations
查找 ITMS-90237 问题的解决方法,正确配置相关证书后依然不行,感觉这个方法不太靠谱,虽然能够成功删除 com.apple.quarantine 属性,但是重新将解压的安装包打包十分复杂,想着还是换个方法。
该步骤相关操作代码:
import os
import subprocess
import shutil
def remove_quarantine_attribute(pkg_path):
extract_dir = os.path.expanduser('~/Desktop/unpacked_pkg')
if os.path.exists(extract_dir):
try:
shutil.rmtree(extract_dir)
print(f"已删除现有的 {extract_dir} 目录。")
except Exception as e:
print(f"删除 {extract_dir} 目录时出错: {e}")
return
try:
os.makedirs(extract_dir)
print(f"成功创建 {extract_dir} 目录。")
except Exception as e:
print(f"创建 {extract_dir} 目录时出错: {e}")
return
try:
# 尝试使用 xar 解压
print("尝试使用 xar 解压 pkg 文件...")
result = subprocess.run(['xar', '-xf', pkg_path, '-C', extract_dir], capture_output=True, text=True)
if result.returncode != 0:
print(f"使用 xar 解压失败: {result.stderr}")
return
else:
print("使用 xar 解压成功。")
# 打印解压后的目录内容,便于检查
print(f"解压后的目录 {extract_dir} 内容:")
for item in os.listdir(extract_dir):
print(item)
pkg_inner_dir = os.path.join(extract_dir, 'com.gorpeln.xxx.pkg')
payload_path = os.path.join(pkg_inner_dir, 'Payload')
if os.path.exists(pkg_inner_dir) and os.path.exists(payload_path):
# 进入 Payload 所在目录并进行进一步处理
os.chdir(pkg_inner_dir)
try:
# 执行 cat Payload | gunzip -dc | cpio -i 操作
print("正在对 Payload 进行进一步解压...")
command = 'cat Payload | gunzip -dc | cpio -i'
result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=60)
if result.returncode == 0:
print("Payload 进一步解压成功。")
else:
print(f"对 Payload 进一步解压时出错: {result.stderr}")
except subprocess.TimeoutExpired:
print("对 Payload 进一步解压时超时,请检查 Payload 文件是否正常。")
except subprocess.CalledProcessError as e:
print(f"对 Payload 进一步解压时出错: {e.stderr}")
finally:
# 切换回上级目录
os.chdir('..')
# 遍历所有文件并移除属性
for root, dirs, files in os.walk(extract_dir):
for file in files:
file_path = os.path.join(root, file)
try:
subprocess.run(['xattr', '-d', 'com.apple.quarantine', file_path], check=True)
print(f"成功移除 {file_path} 的 com.apple.quarantine 属性。")
except subprocess.CalledProcessError:
# 如果文件没有该属性,忽略错误
pass
# 找到 .app 文件
app_path = None
payload_extracted_dir = os.path.join(pkg_inner_dir)
for item in os.listdir(payload_extracted_dir):
if item.endswith('.app'):
app_path = os.path.join(payload_extracted_dir, item)
break
if app_path is None:
print("未找到有效的 .app 应用程序包。")
return
# 签名 .app 包内的所有文件
certificate_name_app = "3rd Party Mac Developer Application: xxxxxxxx (xxxxxxxxx)"
find_command = f'find "{app_path}" -type f -exec codesign --force --deep --sign "{certificate_name_app}" \{\{\}} \;'
subprocess.run(find_command, shell=True)
# 签名 .app 包本身
codesign_command = f'codesign --force --deep --sign "{certificate_name_app}" "{app_path}"'
subprocess.run(codesign_command, shell=True)
# 重新打包成 pkg 文件
new_pkg_path = pkg_path.replace('.pkg', '_cleaned.pkg')
install_path = '/Applications'
certificate_name_installer = "3rd Party Mac Developer Installer: xxxxxxxx (xxxxxxxxx)"
productbuild_command = [
'productbuild',
'--component', app_path, install_path,
'--sign', certificate_name_installer,
new_pkg_path
]
result = subprocess.run(productbuild_command, capture_output=True, text=True)
if result.returncode != 0:
print(f"重新打包并签名 pkg 文件时出错:{result.stderr}")
else:
print(f"处理完成,新的签名 pkg 文件已生成: {new_pkg_path}")
except Exception as e:
print(f"处理过程中出现错误: {e}")
finally:
if os.path.exists(extract_dir):
try:
shutil.rmtree(extract_dir)
print(f"已成功删除临时目录 {extract_dir}。")
except Exception as e:
print(f"删除临时目录 {extract_dir} 时出错: {e}")
# 使用示例
pkg_path = '/Users/gorpeln/Desktop/123/GPNotesForMac.pkg'
remove_quarantine_attribute(pkg_path)
步骤四:删除 Mac 破解软件
怀疑是电脑上安装的某些破解软件导致的,于是进行删除操作,包括删除了keka解压软件,再次尝试打包,问题依然没有解决。想想上个版本到现在电脑上也没有新安装什么应用,应该不是这个原因,只能想其他方法。
步骤五:删除代码中的相关文件
尝试直接删除提示有问题的文件,该文件在项目中是语言适配文件,做本地化处理的,删除后虽然对应用体验有一定的影响,但是也没有好的办法,只能先删了提交后再慢慢看具体问题了。删除后提交,居然又发邮件提错误了。。。
App Store Connect
Hello,
We noticed one or more issues with a recent delivery for the following app:
时光本-日记本·笔记本·记事本·备忘录 Version 1.3.8 Build 138 Please correct the following issues and upload a new binary to App Store Connect.
ITMS-91109: Invalid package contents - The package contains one or more files with the com.apple.quarantine extended file attribute, such as “com.gorpeln.xxx.pkg/Payload/GPNotesForMac.app/Contents/Resources/MLHudAlertInfo.png”. This attribute isn’t permitted in macOS apps distributed on TestFlight or the App Store. Please remove the attribute from all files within your app and upload again.
Apple Developer Relations
看到这个错误后心凉半截,删除了一个文件,但是同样错误又显示了一个新的文件,肯定不能再删除了,因为还不知道到底有多少文件被标记了 com.apple.quarantine 属性,其中被标记的还可能是核心代码,删除后影响项目运行的。并且标记的这个文件应该是一个第三方库文件,想着应该是第三方库有问题,输入搜索后没有发现对应文件,猜想可能是pod导入导致的错误。
步骤五:固定和更新第三方库
查看 Podfile 文件中第三方库都没有固定版本,想着可能是第三方库更新到新版本后出现了相关问题,于是就设置了所有库的对应版本并进行更新,提交上传,问题依旧。迷茫了,不知道该怎么办了。
步骤六:降级 Xcode 版本
脑子一昏,想着是不是 Xcode 版本高的问题,用比较旧的版本会不会好呢,于是就下载安装了。显而易见,并不会。因为在app上个版本到现在我除了这次升级,并没有操作 Xcode 版本,正常不会是 Xcode 版本问题的。
步骤七:删除源码中的相关属性
不知不觉,这个问题搞了一天了。没想法了,想着还是直接删除提示的对应文件吧,再次搜索提示的MLHudAlertInfo.png,居然看到了这个相关文件,进入查看具体内容,查找在Finder中的内容,确实存在MLHudAlertInfo.png文件。那这就好办了呀,直接查看这个文件是否包含 com.apple.quarantine 属性就行了呀,使用 xattr 查看后,的确存在,我感觉有点无语,基本确认是源码问题,直接使用 xattr 遍历删除项目文件夹下所有文件,重新提交上传,问题完美解决了。
总结
具体该问题是怎么导致的呢?
- 静下来想想,无轮提示的 Main.strings 还是 MLHudAlertInfo.png,都是最近甚至多个版本都没有修改过,甚至 MLHudAlert 是导入后只使用,完全没有修改过的,再根据过程中提示到的 keka 解压软件标记,我推测大概率文件相关 com.apple.quarantine 的属性在相关文件下载后使用 keka 解压的时候就已经被标记了,跟随项目多个版本,但由于苹果官方最近收紧对 com.apple.quarantine 属性的检测导致构架包一直无法通过。
总结这次问题,为什么耗费了这么长时间呢?
- macOS 软件开发较少,可参考的资料也较少,只能自己摸索
- 解决问题思路问题:一直想的是前面的版本没问题,这次不行,应该是这次上次到这次提交之间修改某些文件导致的,没想到被苹果背刺了。
- 第一次查找MLHudAlertInfo时,可能是因为手动输入错误导致没有成功搜索到相关文件
最后,如果感兴趣可以去查看使用一下【时光本】应用,是一款专注效率与记录的笔记工具。可以帮助你整理各种信息,包括便签、清单、图片、纪念日、地址、链接、银行卡、名片、账号、密码等