I am LAZY bones?
AN ancient AND boring SITE

2026年 07月 的归档

fastlane——App Store Connect CLI(非官方)

我的打鼾监测 App NightSnore 支持 7 种语言(简中、英、日、韩、德、法、阿拉伯语)。这带来一个每次发版都要经历的痛苦环节:在 App Store Connect 后台,把 What’s New(新功能介绍)逐个语言粘贴进去——切语言、粘贴、保存,再切下一个,七遍。要是 Promotional Text(推广文本)也更新了,那就是十四遍。发布过APP的朋友,肯定对此就深有体会了。

这次发 2.3.0 的时候我终于忍不住了:这玩意儿就没有 CLI 能自动化吗?

还真有,而且就是 fastlane。有意思的是,fastlane 我其实早就装了——之前一直拿它给 App Store 截图加设备边框(frameit),我一直以为它就是个截图美化工具,哈哈。这次才发现,截图加框只是它十八般武艺里最不起眼的一样。

fastlane 到底是什么

fastlane 的定位是”把 iOS/Android 发布流程的每个环节都变成可脚本化的命令”。它其实是一整套工具的集合,每个工具管一段:

1. deliver:上传元数据(What’s New、描述、关键词、截图)到 App Store Connect,本文主角。
2. snapshot:跑 UI 测试自动截图,能覆盖每种语言 × 每种设备尺寸。
3. frameit:给截图加设备边框,我之前唯一用过的那个。
4. gym / pilot:打包上传、TestFlight 分发和测试员管理。
5. match / cert / sigh:证书和描述文件的团队共享管理。
6. precheck:上传前扫描文案里的审核高危词。

单人开发、Xcode 自动签名的话,match 这类团队工具基本用不上;但 deliver 对多语言 App 来说是刚需级的效率工具。

deliver:把 ASC 表单变成本地文件

deliver 的思路很直接:ASC 后台的每个表单字段,对应本地一个文本文件,目录按语言组织:

一个很贴心的设计是:目录里有什么文件,它就只上传什么。我只放了 release_notes.txt 和 promotional_text.txt,那么描述、关键词、截图这些都不会被碰。配置文件 Deliverfile 里再把二进制和截图明确跳过:

搭一条 What’s New 流水线

我的 App Store 文案一直维护在仓库的 AppStore/*.md 里(七个语言各一个文件),每次发版往里追加一段”## What’s New (X.Y.Z)”。这个 Markdown 就是唯一数据源,所以流水线只需要一个提取脚本:从 md 里抠出指定版本的段落,写到 deliver 要的 metadata 目录去。再包一个 fastlane lane 串起来:

提取脚本里顺手做了两层校验:七个语言缺任何一个对应版本的段落就直接报错(强制多语言同步,防漏),超过 ASC 的字符上限(What’s New 4000 字符、Promotional Text 170 字符)也直接拦下。以后发版就是一条命令:

从跑命令到 ASC 七个语言全部填好,9 秒。之前手动粘贴至少十分钟,还得祈祷别粘串了语言。

API Key,和一个专门坑你的格式问题

deliver 走的是 App Store Connect API,需要一个 API 密钥:ASC 后台”用户和访问 → 集成 → App Store Connect API”里创建一个团队密钥,角色选 App Manager,会得到一个 .p8 私钥文件(只能下载一次)加 Key ID 和 Issuer ID。

然后我就结结实实踩了个坑。deliver 支持用一个 JSON 文件传密钥,我很自然地写成了指向 .p8 文件路径的形式,结果:

查了才知道:fastlane 的 Fastfile 里有个 app_store_connect_api_key 这个 action,它支持 key_filepath 参数指向 .p8 文件;但 deliver 的 api_key_path 参数指向的 JSON 文件,只认内联的 key 字段——你得把 .p8 的 PEM 内容整个塞进 JSON 字符串里(换行转成 \n)。同一个工具链里两种密钥写法长得几乎一样但互不兼容,这不纯纯挖坑嘛。正确格式长这样:

这个文件含私钥,务必进 .gitignore,待遇跟你的其他 secrets 一样。

踩坑与注意事项

1. deliver 只能写”可编辑状态”的版本(准备提交、被拒等),已在审核中或已上架的版本改不了。所以要在上传 build 之后、点提交审核之前跑它;版本还没建也没关系,deliver 会自动创建。

2. locale 代码不都带地区后缀:日语是 ja 不是 ja-JP,韩语是 ko,但英语是 en-US、德语是 de-DE。写映射表的时候留意。

3. metadata 目录里有什么就传什么,这既是特性也是风险:目录里残留一个过期的 description.txt,就会把线上描述覆盖掉。我的做法是 metadata 目录整个 gitignore,每次由脚本从 md 重新生成,保证它永远是纯派生产物。

用 skill 操作,vibe coding 更顺畅

还有一层我觉得比工具本身更有意思:这整条流水线,从功能开发、写七语言文案,到搭 fastlane、踩坑、修好,都是在 Claude Code 里完成的。搭好之后我顺手让它把整个发版流程沉淀成一个项目 skill——仓库里的一个 .claude/skills/release/SKILL.md 文件,把九个步骤写成清单:bump 版本号 → 编译验证 → 七语言 What’s New / Promotional Text → commit → xcodebuild 归档上传 → 打 tag → fastlane 同步 ASC 表单,连”deliver 的 JSON 只认内联 key”这种坑位说明都写在里面。

下次发版,我只需要说一句 /release 2.4.0,AI 就照着清单把整条链路跑完,唯一剩下的手动操作是去 ASC 点提交审核。skill 跟着仓库走,clone 下来就有,等于把发版的”部落知识”固化成了可执行的文档。对 vibe coding 来说这很关键:写代码交给 AI 大家都会了,但发布环节往往还是人肉在各个后台之间点来点去——把这段也纳入对话式工作流,从开发到上架才算真正闭环。

下一个目标

尝到甜头之后我看了一圈 fastlane 工具箱,对我这种多语言独立开发场景,下一个最值得上的是 snapshot:七个语言的商店截图现在还是手动截的,每次界面大改就是一下午。snapshot 跑 UI 测试自动出全语言截图,再接上我已经会用的 frameit 加框、deliver 上传,理论上截图这条线也能变成一条命令。挖个坑,做完再写。

发布流程自动化这件事,本质上是把”每次发版都要凭记忆和手感重复一遍的操作”变成”写一次、以后白嫖”的脚本。对独立开发者来说,省的那十几分钟是小事,真正值钱的是不再需要担心”这次是不是又漏了哪个语言”——机器不会漏。

就此,完毕。