发版日最烦的就是手动上传这一步:打包机出包,人还得打开上传工具,选文件、填账号、盯进度条,赶上网络抖动再来一遍。更麻烦的是这类操作散在个人电脑上,换个人交接就要重新讲一遍流程。把「上传 IPA」接进 CI/CD,第一步要解决的其实是认证------让流水线里的机器以你的身份把包提交到 App Store Connect,同时不把 Apple ID 密码到处放。
给 CI 用的两类凭据
给自动化用的认证凭据有两类。一类是 App 专用密码,在苹果账号后台生成,格式 xxxx-xxxx-xxxx-xxxx,绑定 Apple ID,适合交互式工具手动填。另一类是 App Store Connect API 密钥,生成时拿到一把 .p8 私钥,外加 Key ID 和 Issuer ID 两个标识,请求时用 JWT 签名,不占密码额度、可独立吊销、权限可以控制。CI 场景下 .p8 这套更顺手:不会触发双重验证,不需要人在终端前输东西,密钥出事只影响它自己,换掉不影响别的自动化。
Mac 上的官方路线:altool 与 fastlane
苹果官方路线是把 .p8 喂给 altool。Xcode 自带的 altool 支持 --apiKey 和 --apiIssuer 参数,一条命令就能传包:
xcrun altool --upload-app -f Payload.ipa --apiKey XXXXXXXXXX --apiIssuer YYYYYYYYYY --type ios
fastlane 的 pilot 通道也吃同一套凭据,用 APP_STORE_CONNECT_API_KEY_KEY_ID、APP_STORE_CONNECT_API_KEY_ISSUER_ID、APP_STORE_CONNECT_API_KEY 三个环境变量注入,CI 里不用把密钥写进仓库。这套方案的代价在环境:altool 依赖 Xcode 工具链,fastlane 要装 ruby 依赖,Windows 和裸 Linux 构建机上基本跑不起来,密钥本身反而没有平台限制。
生成 .p8:Appuploader 的做法
如果构建机不是 Mac,或者不想在流水线里多养一套 Xcode 环境,可以看 Appuploader 这边的组合。它的账号概览页里能直接生成 App Store Connect API 密钥:填个密钥名称提交,就生成一把 ADMIN 权限、可访问账号下全部 App 的密钥,私钥(.p8)只展示这一次,页面会提示立刻复制或下载成 .json 密钥文件保存好------这份 .json 在登录页「添加账号 → API 密钥 → 密钥文件」里导入,就能免密码、免二次验证地登录同一个 App Store Connect 账号,密钥跨设备通用,吊销后由它派生的登录立刻失效,收回来也干净。
上传步骤进命令行
上传 IPA 这一步走命令行。Appuploader 安装目录的 runtime 里带着 appuploader_cli,用子命令方式调用:
appuploader_cli upload -f Payload.ipa -u dev@example.com -p abcd-efgh-ijkl-mnop --type ios
-f 指定 IPA 路径,-u 是 Apple ID,-p 是 App 专用密码,--type 默认 ios。它做的事和图形界面的「提交 App Store」按钮一致,但完全可脚本化,Windows、Linux、Mac 都能跑,整条链路不出现 Xcode,也不带 Mac 设备信息。
接到 GitHub Actions 里
接到 CI 里就是几步普通的流水线配置:构建产物出来,在 Actions 里下载 Appuploader 的 Linux 版,解压后把 runtime 目录加进 PATH,账号和专用密码放进 secrets,一个 run 步骤完成上传。还可以在传包前先跑 appuploader_cli 的 info 命令,本地分析 IPA 生成 AppStoreInfo.plist,把签名、版本这类信息先校验一道,问题提前暴露,不用等苹果那边拒绝再回头查日志------上传失败时 CLI 的日志同样会带出具体原因,版本号冲突、签名不匹配一眼能看出来。
按环境选路线
两条路线按构建环境选:Mac 上的 CI 用 altool 配 .p8 顺手,生态成熟,社区踩坑记录多;Linux 或 Windows 构建机,Appuploader 命令行是把上传 IPA 接进流水线最直接的方式,.p8 密钥解决账号侧的身份问题,登录、管证书、传包共用同一把身份。把上传从人工操作里摘出来之后,发版日要盯的只剩审核状态,省下的是每次发版那半小时和来回交接的口舌。