Windows / Linux / Mac 上不用 Xcode 把 IPA 上传到 App Store,upload 命令详解

手里有打好签好的 IPA,一条命令就能交给苹果,全程不需要 Mac、不需要 Xcode:

bash 复制代码
appuploader-cli upload -f Payload.ipa -u dev@example.com -p abcd-efgh-ijkl-mnop

appuploader-cli 是「开心上架(AppUploader)」自带的命令行工具,upload 命令做的就是把一个已签名的 .ipa(iOS)或 .pkg(macOS)提交到 App Store Connect------和在图形界面里点「提交 App Store」是同一件事,只是可以写进脚本。

下面把这条命令的用法过一遍:两种登录方式怎么选、非 iOS 的包怎么传、怎么判断到底成没成功、传不上去时按什么顺序排查。

找到这个命令

安装「开心上架」之后,命令行工具已经跟着装好了:

系统 位置
Windows 主程序 AppUploader.exe 同目录下的 appuploader-cli.exe
macOS AppUploader.app/Contents/Resources/appuploader-cli
Linux 主程序 AppUploader 同目录下的 appuploader-cli

在这个目录里开终端就能用。想在任意目录都能敲,把它加进 PATH:Windows 走「设置 → 系统 → 关于 → 高级系统设置 → 环境变量 → Path」,加完重开终端 才生效;macOS / Linux 在 ~/.zshrc~/.bashrc 里加一行 export PATH="$PATH:/你的安装目录"

另外准备一个已经签好名的包 ------upload 不负责编译和签名,它的输入就是打好的 .ipa.pkg

登录方式一:App 专用密码

命令行上传最省事的凭据是 App 专用密码 。它是苹果专门为第三方工具准备的,不会触发双重验证------不用每传一次就去手机上点一次确认,这对脚本和无人值守的构建机是刚需。

https://account.apple.com/account/manage 登录,找到「App 专用密码」新建一个,会得到形如 abcd-efgh-ijkl-mnop 的密码。它只显示这一次,当场存好。

然后:

bash 复制代码
appuploader-cli upload -f Payload.ipa -u dev@example.com -p abcd-efgh-ijkl-mnop

-u 是 Apple ID 邮箱,-p 是刚生成的专用密码,两个必须一起给。包的路径用 -f 指定,也可以省掉 -f 直接写在命令最后:

bash 复制代码
appuploader-cli upload -u dev@example.com -p abcd-efgh-ijkl-mnop Payload.ipa

最常见的错误就是 -p 填成了 Apple ID 的登录密码。专用密码固定是 16 个字母、中间三个连字符,和登录密码长得完全不一样,填之前对一眼。

登录方式二:App Store Connect API 密钥

如果这条命令要跑在团队的构建机或流水线上,用某个人的 Apple ID 就不太合适------人一走、密码一改,整条自动化就断了。这种场景改用 App Store Connect API 密钥:它属于团队而不是个人,可以单独吊销,同样不涉及双重验证。

https://appstoreconnect.apple.com/access/api 生成一把(需要 Account Holder 或 Admin 权限),拿三样东西:密钥 ID 、页面上方的 Issuer ID 、以及 .p8 私钥文件.p8 只能下载这一次,苹果不给第二次,立刻存进密码管理器或者 CI 的密钥仓库。

bash 复制代码
appuploader-cli upload -f Payload.ipa \
  --api-key UK29KBAX9X \
  --api-issuer 69a6de78-4459-47e3-e053-5b8c7c11a4d1 \
  --private-key AuthKey_UK29KBAX9X.p8

三个参数缺一不可。两种登录方式只能选一种-u -p 和这三个参数混在一起写会被直接拒绝------从密码方式改成密钥方式时,记得把原来的 -u -p 删干净。

用密钥方式前确认两件事:

  1. 只能传 .ipa 这种方式要靠包里的 Bundle ID 去 App Store Connect 上找到对应的那个 App,.pkg 里取不到。macOS 的 .pkg 请用上面的专用密码方式。
  2. 这个 Bundle ID 必须已经在 App Store Connect 里建过 App。 还没建的话会提示找不到对应的 App,先去建再传。

该选哪种

一句话:自己一个人手动发版,用 App 专用密码;多人协作或者要写进流水线,用 API 密钥。

专用密码胜在简单,两个参数就能跑;但它是挂在个人 Apple ID 下的,交给流水线意味着把个人凭据交出去。API 密钥多几步配置,换来的是跟人解绑、能单独吊销、出问题只需要换这一把。

上传非 iOS 的包

默认按 iOS 处理,别的平台加 --type

bash 复制代码
# macOS 应用
appuploader-cli upload -u dev@example.com -p abcd-efgh-ijkl-mnop --type osx App.pkg

# tvOS
appuploader-cli upload -f App.ipa -u dev@example.com -p abcd-efgh-ijkl-mnop --type appletvos

一共四个值:ios(默认)、osxappletvosxros(visionOS)。

怎么判断到底传成功没有

回车之后进度会一行行实时打出来,包越大跑得越久。看最后一行:

复制代码
> upload finished successfully

失败则是:

复制代码
> upload finished with error: <苹果给出的具体原因>

如果是写脚本,不要去 grep 这些日志------命令失败时退出码是非 0,判断退出码就够了,也不会因为日志文案变化而失灵:

bash 复制代码
set -e
appuploader-cli upload -f build/App.ipa -u "$APPLE_ID" -p "$APP_PASSWORD"

Windows PowerShell 里:

powershell 复制代码
& appuploader-cli.exe upload -f .\build\App.ipa -u $env:APPLE_ID -p $env:APP_PASSWORD
if ($LASTEXITCODE -ne 0) { throw "上传失败" }

还有一点容易误解:命令返回成功,只表示苹果收下了这个包并通过了初步校验。苹果那边还要处理几分钟到几十分钟,处理完才会出现在 App Store Connect 的「构建版本」列表里,那之后才能选来提审。刚传完看不到属于正常。

传不上去,按这个顺序排查

第一类:命令还没跑起来就退出了。 这类问题在本机,改完立刻能验证。

  • 提示 file not found:路径不对。在 CI 里最常见的原因是当前工作目录不是你以为的那个,脚本里尽量用绝对路径。
  • 提示两种登录方式不能混用:-u/-p--api-key/--api-issuer/--private-key 只能留一组。
  • 提示 not a valid PEM fileexpected PKCS8--private-key 指的不是苹果给的那个 .p8,或者文件内容坏了。CI 里最常见的原因是把 .p8 存进 Secret 时丢了换行,重新完整复制原文件再存一遍。
  • 提示 the ipa has no CFBundleIdentifier:用密钥方式传了非 IPA 的包,换回专用密码方式。

第二类:包传上去了,但被苹果退回来。 这类原因都写在 upload finished with error: 后面,常见的几种:

  • 版本号/构建号重复 :同一个 CFBundleShortVersionString + CFBundleVersion 组合已经传过一次。苹果不允许覆盖,改构建号重新打包。
  • 签名不匹配:包用的证书或描述文件不是 App Store 分发用的。检查打包时选的是不是 App Store 类型的描述文件。
  • Info.plist 缺字段 :常见的是缺图标、缺必需的用途说明(相机、相册、定位等的 NSxxxUsageDescription)、或者缺少某些设备能力声明。按提示补齐重新打包。
  • 找不到对应的 App:这个 Bundle ID 还没在 App Store Connect 里创建 App。

这条命令配好之后基本不用再管,发版时敲一次或者交给 CI 跑就行。同一个工具在 Windows 上还能签证书、建 Bundle ID、生成描述文件,我这边整条 iOS 发布链路就是这么绕开 Mac 的。

相关推荐
秦少游在淮海1 小时前
id - Android、iOS
android·ios
恋猫de小郭1 小时前
超好用 R8 Configuration Analyzer, 优化你的 App 大小和内存
android·前端·flutter
又见情义2 小时前
Android 13 系统应用裁剪:RK3568平台下的精简之道
android
苦瓜花2 小时前
【Android】LiveData
android
风月说与山鬼2 小时前
八、uni-app页面调用接口
uni-app
格林威2 小时前
C#图像处理:使用imagemagick实现像素放大水印转换等多种功能
android·开发语言·图像处理·人工智能·计算机视觉·c#·视觉检测
雨白9 小时前
深入理解 Java/Kotlin 反射、注解与泛型:从原理到实战
android·架构
喜欢打篮球的普通人13 小时前
LLVM Backend Lowering 从入门到实战:把 IR 变成机器码的完整链路
android·java·数据库
萝卜er17 小时前
BroadcastReceiver 静态注册与动态注册-《Android深水区(七)》
android