appuploader-cli 使用教程:在 Windows 上用命令行把 IPA 上传到 App Store
一条命令就能把打好的 IPA 传到 App Store Connect,不用 Mac,也不用装 Xcode:
bash
appuploader-cli upload -f Payload.ipa -u dev@example.com -p abcd-efgh-ijkl-mnop
appuploader-cli 是「开心上架(AppUploader)」自带的命令行工具,装好软件它就在安装目录里。下面从零走一遍:怎么找到它、第一次上传要准备什么、团队和流水线里该换成哪种登录方式、传不上去时怎么定位问题。
什么时候需要用命令行
图形界面点两下就能提交,多数人其实用不上命令行。真正需要它的是这三种情况:
- 构建是自动出来的。 每次提交代码 CI 自动打包,打完还要人工去点一下上传,这一步就该顺手接上。
- 上传要交给别人跑。 外包、代运营、多个 App 轮流发版,写成脚本比口头交代点哪个按钮靠谱。
- 在没有图形界面的机器上。 比如公司的构建服务器,只有一个远程终端。
如果只是偶尔手动发一版,图形界面点两下反而更快,命令行的价值在于重复执行。
开始之前
一、你得先有一个已签名的包。 appuploader-cli 负责的是"把包交给苹果"这一步,不负责编译和签名。手里得先有打好的 .ipa(iOS)或 .pkg(macOS)。
二、找到这个命令。 装好开心上架之后,它已经在安装目录里了:
| 系统 | 位置 |
|---|---|
| Windows | 主程序 AppUploader.exe 同目录下的 appuploader-cli.exe |
| macOS | AppUploader.app/Contents/Resources/appuploader-cli |
在这个目录里打开终端就能直接用。嫌每次都要 cd 过去麻烦,把这个目录加进系统 PATH 就行(Windows 上:设置 → 系统 → 关于 → 高级系统设置 → 环境变量 → 选中 Path → 编辑 → 新建,粘贴目录路径,然后重开一个终端才生效)。
先敲一条确认能用:
bash
appuploader-cli --help
能列出 upload、info 就说明找对了。
第一次上传:用 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
三个参数:-f 是包的路径(也可以省掉 -f,直接把路径写在命令最后),-u 是 Apple ID 邮箱,-p 就是刚才那个专用密码。
-p一定填专用密码,不是你平时登录 Apple ID 用的密码。专用密码固定是 16 个字母、中间三个连字符,长得和登录密码完全不一样,对一眼就知道有没有填错。
回车之后,进度会一行行打出来,几分钟后看到这一行就成功了:
markdown
> upload finished successfully
失败的话是 > upload finished with error:,后面直接跟苹果给的原因------版本号重复、签名不匹配、Info.plist 缺字段之类,照着改就行。
写脚本时不用去读日志:失败时这条命令的退出码是非 0,判断退出码就够了。
传完先别急着去 App Store Connect 找构建版本------苹果收下包之后还要处理几分钟到几十分钟,处理完它才会出现在「构建版本」列表里,那之后才能选来提审。
传 macOS / tvOS / visionOS 的包
默认按 iOS 处理,其他平台加 --type:
bash
appuploader-cli upload -u dev@example.com -p abcd-efgh-ijkl-mnop --type osx App.pkg
可选的就四个值:ios(默认)、osx、appletvos、xros(visionOS)。
团队和 CI:换成 API 密钥登录
上一节的做法有个问题:命令里带着某个人的 Apple ID 和密码。这个人离职、改密码、或者干脆不愿意把自己的账号交给流水线,整条自动化就断了。
团队场景更合适的是 App Store Connect API 密钥:它属于团队而不是个人,可以单独吊销,也不涉及双重验证。
去 appstoreconnect.apple.com/access/api 生成一把(需要 Account Holder 或 Admin 权限),拿到三样东西:
- 密钥 ID(一串大写字母数字)
- Issuer ID (页面上方,形如
69a6de78-4459-47e3-e053-5b8c7c11a4d1) .p8私钥文件 ------只能下载这一次,苹果不给第二次机会,立刻存好
然后把 -u -p 换成这三个参数:
bash
appuploader-cli upload -f Payload.ipa \
--api-key UK29KBAX9X \
--api-issuer 69a6de78-4459-47e3-e053-5b8c7c11a4d1 \
--private-key AuthKey_UK29KBAX9X.p8
两种登录方式只能选一种,混着写会被直接拒绝。
用 API 密钥前确认两件事:
- 只能传
.ipa。 这种方式要靠包里的 Bundle ID 去 App Store Connect 上找到对应的 App,.pkg里取不到。macOS 的包还是用上一节的 App 专用密码。 - 这个 Bundle ID 得先在 App Store Connect 里建过 App。 没建的话会提示
no App Store Connect app found for bundle ID,先去建 App 再传。
写进流水线
思路就一句话:构建产出 IPA 之后加一步 upload,账号密码走 CI 自带的 Secret,别明文写进脚本。
GitHub Actions(把三样东西存成仓库 Secret):
yaml
- name: 上传到 App Store Connect
env:
ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
ASC_P8: ${{ secrets.ASC_P8 }}
run: |
printf '%s' "$ASC_P8" > AuthKey.p8
appuploader-cli upload -f build/App.ipa \
--api-key "$ASC_KEY_ID" \
--api-issuer "$ASC_ISSUER_ID" \
--private-key AuthKey.p8
rm -f AuthKey.p8
最后那行 rm 别省,私钥不要留在 Runner 的工作目录里。
Jenkins / 通用 Shell:
bash
set -e # 上传失败就中断整条流水线
appuploader-cli upload -f "$WORKSPACE/build/App.ipa" \
-u "$APPLE_ID" -p "$APP_SPECIFIC_PASSWORD"
Windows 批处理 / PowerShell:
powershell
& "C:\Program Files\AppUploader\appuploader-cli.exe" upload `
-f .\build\App.ipa -u $env:APPLE_ID -p $env:APP_PASSWORD
if ($LASTEXITCODE -ne 0) { throw "上传失败" }
整个过程不需要 Mac,构建机是 Windows 也照样跑得完这一步。
顺带一提:只想检查包,不想上传
还有个 info 命令,不联网上传,只在本地读包,把 App Store 需要的资源描述文件 AppStoreInfo.plist 生成出来:
bash
appuploader-cli info -u dev@example.com Payload.ipa -o AppStoreInfo.plist
两个用处:上传前先确认包本身没问题(生成成功会打印出包的 Bundle ID 和大小,可以顺手核对传的是不是那个包);或者你的流程里用的是别的上传工具,只缺这份 plist。
不加 -o 就直接打印到终端,方便接管道。info 只支持 -u 这一种账号写法,没有 API 密钥模式。