前语
关于 macOS App 开发者来说,咱们一般情况下可能会挑选在网络上分发 App。可是,站在运用者的角度,假如下载的 App 没有经过 Apple Notary Service 公证(Notarizate)过,这在装置的时分体系则会提示“无法翻开 xxx App,因为无法验证开发者”:
那么,这个时分的解决方法就是修正体系偏好——>安全性与隐私的设置,挑选仍要翻开该 App:
虽然,这样能够让运用者装置 App,可是,这并不是真正在解决这个问题的实质。从根上解决应该是让咱们要分发到网络上的 App 经过 Apple Notary Service 公证(Notarizate),这样一来他人下载装置咱们运用的时分则不会出现无法翻开的提示,而是:
所以,今天本文也将环绕「macOS App 公证」打开怎么经过手动或许自动化(Shell、东西)完结公证(Notarizate)进程。
1. 手动公证
首要,咱们先了解下怎么手动公证?手动公证进程能够经过 Xcode 提供的 GUI 界面操作完结。同样地,首要咱们需求构建 .xarchive 文件:
构建完后,Xcode 会弹出窗口让你挑选 Distribute App 或 Validate App,这儿咱们挑选前者:
接着,不同于之前分发 App Store,咱们需求挑选 Developer ID,它表明的是在 App Store 之外分发 App:
然后,咱们需求挑选 Upload ——> Development Team -> Manually manage signing,此刻需求挑选 Develop ID 对应的 Distribution 证书和 Provisioning Profile:
终究,则挑选 Next ——> Upload,然后则会将咱们的 App 上传到 Apple Notary Service 进行公证,经过则会提示咱们能够分发 App。
假如,了解过 App Store 分发相关的同学应该了解这个手动操作的进程,因为运用 Xcode 公证 App 的进程和 App Store 分发大同小异。所以,在经过简单了解运用 Xcode 手动完结公证进程后,接下来,咱们来知道下怎么自动化完结公证的进程?
2. 自动化公证
自动化公证则指的是咱们经过代码完结前面介绍到的运用 GUI 手动公证的进程。那么,这儿我列出了 4 种现在社区中完结的能够对 macOS App 自动化公证的方法:
- altool –notarize-app 运用的旧的 Apple Notary Service,适用于 Xcode 12 以及更早的版别,可是需求留意的是Apple 将会在 2023 年秋季停止对它的支撑
-
notarytool 运用的新的 Apple Notary Service,只适用于 Xcode 13 及以上的版别,相比较
altool --notarize-app
快了 10 倍 -
electron-notarize 用 JavaScript 完结的用于公证的东西,支撑
notarytool
和altool --notarize-app
等 2 种公证方法 - fastlane 用 Ruby 完结的一个能够快捷地帮你完结证书办理、代码签名和发布等相关的东西,适用于 iOS、macOS 和 Android 运用
当然,更进一步的话咱们能够把这个自动化公证也加入到 CI/CD 进程中,有爱好的同学能够自行了解相关完结。所以,接着下面将会对这 4 个的运用做对应的打开介绍,首要是 altool --notarize-app
。
2.1 altool –notarize-app
altool 是一个内置于 Xcode 中的指令行东西,用于验证 App 的二进制文件并将其上传至 App Store 或许对 App 进行公证(Notarize)。而 altool --notarize-app
也是最早咱们运用的完结自动化公证运用的方法,这在社区中也能够看到大量依据它的实践。
altool --notarize-app
则主要是将运用上传到 Apple Notary Service,可是并不会奉告你公证成功与否,所以一般需求结合 altool --notarization-info
指令一起运用(核对公证成功与否),整个公证的进程会是这样:
能够看到,首要咱们需求运用 altool --notarize-app
将运用上传公证,然后获取本次公证的 UUID:
xcrun altool --notarize-app \
# .app 的压缩包或 .dmg
--file ./Output/Apps/FEKit.zip \
--primary-bundle-id "com.xxxx.xxxx" \
# Apple ID
--username "xxxxx" \
# 运用专用暗码,这能够在 https://appleid.apple.com/account/manage 申请
--password "xxxxx" \
然后,终端中则会输出本次公证上传操作的信息和 RequestUUID
,前者用于表明本次操作履行是否成功,后者能够用于 altool --notarization-info
指令查询本次公证进程的信息:
No errors uploading 'Output/Apps/FEKit.zip'.
RequestUUID = xxxxxxx-xxx-xxxx-xxxx-xxxxx
进行完公证上传操作后,Apple Notary Service 则会对本次公证履行相关的操作,而这需求一定的时刻,所以咱们需求(守时)轮询履行 altool --notarization-info
指令实时地获取公证成功失败与否的信息:
# 加载 Apple ID($user)和 App 专用暗码($pwd)
source "./Build/app_store_user_pwd.sh"
# 标识公证履行进程成功失败与否,失败 1,0 成功
success=1
i=0
while true; do
let i+=1
echo "Checking notarize progress...$i"
# 获取 altool --notarization-info 履行的输出信息
process=$(xrun altool --notarization-info $uuid --username $user --password $pwd 2>&1)
echo "${progress}"
# 假如前次指令履行成果 $? 不等于 0(表明失败),或许指令输出信息中包括 Invalid
if [ $? -ne 0 ] || [[ "${progress}" =~ "Invalid"]] then
echo "Error with notarization. Exiting"
break
if
# 假如指令输出信息中包括 success 表明成功
if [[ "${progress}" =~ "success"]]; then
success=0
break
else
echo "Not completed yet. Sleeping for 30 seconds.\n"
fi
sleep 30
done
if [ $success -eq 0 ]; then
echo "Notarize successed."
if
其间,$uuid
则是履行 altool --notarize-app
指令后获取的返回成果:
echo xcrun altool --notarize-app \
--file xxxx \
--primary-bundle-id "com.xxxx.xxxx" \
--username $user \
--password $pwd \
2>&1 | grep RequestUUID | awk '{print $3}'
这儿咱们来看下咱们比较生疏的 2>&1
、grep RequestUUID
、awk '{print $3}'
等 3 个指令的效果:
-
2>&1
是为了将规范过错stderr
输出重定向到规范输出stdout
-
grep RequestUUID
匹配规范输出中所有包括RequestUUID
的行 -
awk '{print $3}'
打印出第 3 列的成果,在RequestUUID = xxxxxxx-xxx-xxxx-xxxx-xxxxx
就是RequestUUID
的值xxxxxxx-xxx-xxxx-xxxx-xxxxx
-
|
管道,用于将上个指令的输出经过管道输入到下一个指令
2.2 notarytool
相比较 altool --notarization-info
而言,notarytool
运用起来心智负担少一些,并且快于前者许多,咱们只需求记忆一些 Option,运用一行指令 xcrun notarytool
则能够完结上传公证和进程信息获取的进程:
xcrun notarytool submit ./Output/Apps/FEKit.zip \
# Apple ID
--apple-id $user \
--team-id $teamId \
# 运用专用暗码
--password $pwd \
-v \
-f "json"
其间,--team-id
指的是用户 ID(由数字和字母组成),这能够在本地 KeyChain 的证书中查看(或许 Apple 证书后台),-v
是 --verbose
的缩写,指的是输出公证进程的信息,-f "json"
则是表明终究成果以 JSON 的格局输出,例如:
{
"path":"\/Users\/wujingchang\/Documents\/project\/demo\/FEKit\/Output\/Apps\/FEKit.zip",
"message":"Successfully uploaded file",
"id":"xxxxxxxxxxxxxxxxxxxx"
}
需求留意的是这儿运用的是 Appple ID 和运用专用暗码的方法做与公证服务的恳求认证 Authentication,此外你还能够经过以下 3 种 Option 来进行认证:
-
--keychain-profile <keychain-profile>
,运用xcrun notarytool store-credentials
预先在本地钥匙串中新建一个运用程序暗码,例如叫AC_PASSWORD
,那么在运用notarytool submit
指令的时分则能够直接运用--keychain-profile AC_PASSWORD
替代之前的--apple-id $user --team-id $teamId --password $pwd
-
--keychain <keychain>
,不同于前者--keychain-profile
这儿是输入的AC_PASSWORD
文件所在的位置 -
--key <key-id> --key-id <key-id> --issuer <issuer>
,这运用的是 App Store Connect API keys 的方法进行认证,实质上是生成一个和 App Store Connect 约定好的 JWT,然后每次恳求的时分携带上它,然后经过认证
2.3 electron-notarize
electron-notarize 则是一个用 JavaScript 完结的公证东西,它的原理则是运用的 child_process
履行前面咱们提及的 altool --notarization-info
和 notarytool
这 2 个指令。
electron-notarize
具名导出了 notarize
函数,咱们只需求运用它以及指定的 Option 则能够完结公证的进程,这儿咱们来看下其函数的完结(伪代码):
// src/index.ts
export async function notarize({ appPath, ...otherOptions }: NotarizeOptions) {
if (otherOptions.tool === 'notarytool') {
// ...
await notarizeAndWaitForNotaryTool({
appPath,
...otherOptions,
});
} else {
// ....
const { uuid } = await startLegacyNotarize({
appPath,
...otherOptions,
});
// ...
await delay(10000);
// 获取公证进程信息
await waitForLegacyNotarize({ uuid, ...otherOptions });
}
await stapleApp({ appPath });
}
能够看到,notarize
函数会依据 Option 中传入的 tool
为 notarytool
或 legacy
来履行不同的指令来完结公证,这儿前者是 notarytool
后者则是 altool --notarization-info
。所以,假如咱们要用 notarytool
的方法进行公证会是这样:
import { notarize } from "electron-notarize"
await notarize({
appPath: "./Output/Apps/FEKit.zip",
// Apple ID
appleId: "xxxxxx",
// 运用专用暗码
appleIdPassword: "xxxxxxxx",
teamId: "xxxxxxxxxxx",
tool: "notarytool"
})
其间,假如你不希望暗码直接明文暴露在代码中的话,electron-notarize
也支撑了前面咱们说的 --keychain
、--keychain-profile
等 3 个 Option,你能够依据自己的需求挑选对应的认证方法。
2.4 fastlane
fastlane 是一个能够快捷地帮你完结证书办理、代码签名和发布等相关的东西,适用于 iOS、macOS 和 Android 运用。那么,咱们也就能够运用 fastlane 来完结 macOS App 的公证。
首要,肯定是装置 fastlane,关于这方面的介绍官方文档解说的很是翔实,这儿就不重复论说。而当你装置好 fastlane,则能够在运用项目的根目录履行 fastlane init
来初始化它相关的装备,在初始化的进程会让你挑选运用 fastlane 的方法,这儿咱们挑选手动装备即可。
然后,它会在项目根目录下创立一个 fastlane/Fastfile
目录和文件,后续咱们在履行 fastlane xxx
指令的时分则会依据该文件的代码完结履行详细的操作,默认生成的 Fastfile 文件的装备会是这样:
default_platform(:ios)
platform :ios do
desc "Description of what the lane does"
lane :custome_lane do
# add actions here: https://docs.fastlane.tools/actions
end
end
其间,default_platform
用于定义一个默认的渠道 Platform,例如当咱们有 2 个渠道(iOS 和 macOS)的时分,它的的装备需求这样:
default_platform(:ios)
platform :ios do
desc "Description of what the lane does"
lane :custome_lane do
# add actions here: https://docs.fastlane.tools/actions
end
end
platform :mac do
desc "Description of what the lane does"
lane :custome_lane do
# add actions here: https://docs.fastlane.tools/actions
end
end
此刻,假如咱们履行 fastlane custome_lane
,因为这儿渠道默认为 ios
,所以则会履行 platorm:ios
下的 custome_lane
,反之履行 fastlane mac custome_lane
,则是 platform :mac
下的 custome_lane
。那么,关于前面咱们这个例子而言只需求 platform:mac
:
default_platform(:ios)
platform :mac do
desc "Description of what the lane does"
lane :custome_lane do
# add actions here: https://docs.fastlane.tools/actions
end
end
接着,则能够在 platform:mac
写咱们需求完结的自动化分发 App Store 相关的代码。fastlane 快捷之处在于它完结了许多开箱即用的 Action,这儿咱们需求运用 notarize 这个 Action,它能够用于完结 macOS App 的公证:
default_platform(:mac)
platform :mac do
desc "Notarizes a macOS app"
lane :notarize_app do
notarize(
package: "./Output/Apps/FEKit.zip",
use_notarytool: "xcrun notarytool",
bundle_id: "com.xxxx.xxxx",
username: "xxxxxxxxxxxxxx",
verbose: true,
)
end
end
其间,在运用 notarize
的时分需求留意的是,这儿仅仅声明了你 App Store 的用户名 username
,而专用暗码需求预先在体系环境变量中增加 FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD
(其他认证方法,有爱好的同学能够自行了解),然后 fastlane 在履行 notarize_app
Action 时会去读取该环境变量,然后进行并完结后续的公证进程。
结语
看到这儿,我想可能会有同学会问:“这几种完结公证的东西,挑选哪个比较好?”,这儿比较建议的是挑选 fastlane,因为,除开前面提及它的运用方法非常快捷的长处,它具备的才能也许多,不仅仅能够做 App 公证,还能够做 App Store 分发、证书和版别办理等,所以,挑选 fastlane 将来也能够支撑咱们其他诉求,何乐不为呢?
终究,假如文中存在表达不妥或过错的当地,欢迎各位同学提 Issue ~
点赞
经过阅读本篇文章,假如有收获的话,能够点个赞,这将会成为我持续共享的动力,感谢~
我是五柳,喜爱创新、捣鼓源码,专注于源码(Vue 3、Vite)、前端工程化、跨端等技术学习和共享,欢迎关注我的微信大众号 Code center 或 GitHub。