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 专用密码 。它是苹果专门为第三方工具准备的,不会触发双重验证------不用每传一次就去手机上点一次确认,这对脚本和无人值守的构建机是刚需。

account.apple.com/account/man... 登录,找到「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 密钥:它属于团队而不是个人,可以单独吊销,同样不涉及双重验证。

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)。

怎么判断到底传成功没有

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

markdown 复制代码
> upload finished successfully

失败则是:

vbnet 复制代码
> 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 的。

相关推荐
iFlyCai1 小时前
iOS中的单例模式详解
ios·单例模式·objective-c·多线程·swift
凌涘1 小时前
JWT 登录鉴权 Demo 拆解
后端
JimmtButler1 小时前
从 Java 到 JS:我终于把闭包想明白了
javascript·后端
掘金者阿豪2 小时前
Codex 怎么突然变慢了?一个需求跑几十分钟,我才发现它的工作方式已经变了
前端·后端
Zadig2 小时前
Zadig 全面支持 CRD,至此所有 K8s 资源类型均可一键发布!
后端·devops
得物技术2 小时前
EP-Harness:从个人 AI Coding 到团队级 Agent 工作流|得物技术
后端·程序员·架构
用户125758524362 小时前
对象存储 URL 为什么别到处拼:后台附件预览要验这一层
后端·go·ai编程
步行cgn2 小时前
MyBatis 一对多关联映射详解
java·后端
阿弱2 小时前
pi 扩展机制:加载、执行与能力
后端·llm·agent