EAS+Fastlane:打造多端自动化打包发布流水线

发布时间:2026/9/19 6:53:35
EAS+Fastlane:打造多端自动化打包发布流水线 做移动端的人应该都有过这种体验打包这件事看着简单真要到“发布”这一步琐碎得能让你怀疑人生。证书要配、签名要搞、版本号要手动改、上传商店还得开个浏览器慢慢点更别提同时维护 iOS 和 Android 两套流程。我早年还是老老实实本地跑xcodebuild和gradlew的时候一个版本发完半天时间就没了中间还得伺候各种“偶发性”失败。后来我把这套流程彻底交给了 EAS 和 Fastlane整个多端自动化打包发布才算是真正收口。这篇文章不打算讲那些花里胡哨的概念就实打实聊聊我搭建这套流水线的完整思路、关键配置、踩过的坑以及为什么说它是工程化里比较值得做的一笔投资。如果你正在用 React Native / Expo或者手里同时管着几个跨平台 App这篇应该能帮你省下不少时间。1. 打包发布这件事为什么值得认真搞成流水线先说一个很多人容易忽略的点多端工程化的难点其实不在“代码怎么写”而在“版本怎么出”。代码写完了只能算完成了一半剩下的分发、签名、上传、审核任何一个环节卡住整个迭代节奏就废了。1.1 手工打包到底浪费了多少时间我算过一笔账。一个同时支持 iOS 和 Android 的项目手工走完一遍完整发布流程大概包括本地拉最新代码、装依赖、跑测试、配置开发/生产环境变量、处理 iOS 证书和描述文件、处理 Android Keystore、修改 buildNumber 和 versionCode、构建两个平台的包、上传 TestFlight / App Store Connect、上传 Google Play Console、填写版本更新说明、再截图上传商店素材。这一套下来熟练工也得一个小时往上如果是大版本加多语言包时间直接翻倍。问题还不止是“慢”。手工操作最大的隐患是“不稳定”今天你记得改版本号明天可能就忘了换一台电脑证书又找不到了同一个包在不同机器上构建产物可能还不一致。这些偶发问题比单纯耗时间更折磨人因为它们会出现在你最不想出问题的节点——发版当天。1.2 自动化能带来什么实际收益把打包发布做成流水线之后最直观的变化是从“人工操作”变成“触发行为”。代码推到指定分支云端自动开始构建构建完自动签名签名完自动上传商店上传完还能自动把版本号和更新日志推给团队。人只需要在最后看一眼结果或者在关键时刻点一下“发布确认”。收益有三个维度。第一是时间一次发版从一小时压缩到十几分钟而且这十几分钟里你不需要盯着屏幕可以去干别的第二是稳定性每次构建都在干净的环境中执行不依赖个人电脑的本地状态成功率和可复现性都大幅提升第三是可追踪性每一次构建都有日志、有产物、有版本记录出了问题可以回查是哪个环节造成的不用靠回忆。1.3 为什么把 EAS 和 Fastlane 放一起用这里要先说清楚工具定位。EASExpo Application Services是 Expo 官方推出的一套应用服务核心是云构建、云签名和自动上传Fastlane 是社区老牌自动化工具专门用来编排“发版过程中的那些琐碎操作”比如上传 TestFlight、上传应用商店、管理证书、跑 meta 数据同步等等。很多人会问既然 EAS 里也有 Build 和 Submit是不是用了 EAS 就不用 Fastlane 了我的答案是可以但没必要。EAS 的长处是把“Expo 项目的构建和分发”做到极致而 Fastlane 的强项是生态广、插件多、自由度大。比如 Google Play 的“内部测试轨道”发布、截图素材上传、版本说明自动生成这类需求Fastlane 的成熟插件直接就能用又比如你手里除了 Expo 项目还有几个原生 RN 项目或者 Flutter 项目那 Fastlane 反而是你打通多项目的“公约数”。所以我的建议是EAS 负责云端构建和签名Fastlane 负责构建完之后的收尾分发两边各干各擅长的。这也是这篇博文的主线思路。2. 先把两个工具的角色和边界搞明白这章不写配置先讲清楚“为什么是这两个工具”以及“它们各自边界在哪”。工具选型这种事最忌讳看别人用就跟着上最后变成“全家桶堆砌”维护成本比手工还高。2.1 EAS 到底解决了什么问题EAS 全称是 Expo Application Services它解决的是 React Native / Expo 项目从代码到应用包的全链路问题。整套服务里有几个模块EAS Build 负责云构建EAS Submit 负责上传商店EAS Update 负责 OTA 热更新EAS Metadata 负责同步商店元数据。我重点说 EAS Build。它是整个自动化流程的基石因为它的设计思路很“粗暴”你不需要本地装 Xcode、不需要配 Android SDK、不需要手工管理证书只要把项目推到 EAS 的云端它会起一台干净的虚拟机帮你装好依赖、配好环境、执行构建最后把产物.ipa或.aab交还给你。这个过程中iOS 证书和 Android Keystore 也都由它接管里边的签名流程是自动化的前提是你首次构建时在本地把凭证信息交给它托管。对于多端项目来说EAS Build 最大的价值是环境一致性。你的构建结果跟本地 Mac 是 Windows 还是 Linux 无关跟系统里有没有装某个依赖无关只要项目配置对了云端产物几乎是唯一确定的。这种可复现性是工程化流水线的地基。2.2 EAS 云端构建免费吗额度怎么算被问得最多的一句话“EAS 云端构建免费吗”答案是有免费档但有额度限制个人项目通常够用团队项目大概率要付费。EAS Build 的定价模式是按“构建时长”计量的官方叫 fair use policy合理使用政策。免费档每个月会给你一定的构建分钟数具体额度以官方页面为准它不算多但如果你只是个人开发、每次改动不频繁触发构建通常够用。需要注意两个容易踩的坑免费档只支持有限的并发数如果你的团队同时好几个人推代码触发构建会排队。iOS 的云端构建成本比 Android 高因为跑的是 macOS 虚拟机消耗额度也更多控制台里能看到每次构建的实际耗时和费用换算。我的建议是先把免费额度用起来日常开发、提交 PR 后自动跑 preview 构建用完了就升级到按量付费或者固定套餐。别一上来就充年费建好了量化基线再付费比较理性。2.3 Fastlane 在流水线里负责哪一段Fastlane 解决的问题和 EAS 有重叠但侧重完全不同。它是一个用 Ruby 写的自动化工具核心概念是一个叫 “lane” 的脚本你可以在里面定义“一系列动作”比如“先跑测试再构建再上传 TestFlight”每个动作都有对应的 action 插件。在整套流水线里我让 Fastlane 承担两件事。第一件是EAS Build 之后的“收尾动作”。比如 EAS 构建完 iOS 包后我可以用 Fastlane 把它传到 TestFlight 或 App Store Connect构建完 Android 包后用 Fastlane 传到 Google Play 的某个轨道然后自动把更新说明同步上去。这一步如果只用 EAS Submit 也能做但 Fastlane 的轨道控制和发布状态管理会更灵活。第二件是处理那些 EAS 管不了或者不好管的“边缘场景”。比如批量上传截图、从 CI 环境里读取私密信息并注入构建、多个渠道包的差异化上传、内部测试分发到企业内部安装页等等。这些场景要的是自由度Fastlane 的脚本化能力就派上用场了。一句话总结EAS 是生产车间的“自动化机床”Fastlane 是机床后端的“自动物流分拣线”。两件事没有严格的重叠反而能形成完整的闭环。3. 实操从零搭一套 EAS Fastlane 的多端发布流水线这一章是全文的核心我会把整条流水线的搭建过程按步骤捋清楚。先交代一下实验背景项目是一个 Expo SDK 51 的 React Native 应用同时支持 iOS 和 Android托管工作流managed workflow目标是一套代码、两个平台、一个命令完成“构建 → 签名 → 上传 → 通知”的完整链路。3.1 第一步初始化项目并确认 EAS CLI如果你还没有装eas-cli先全局装一下这一步没什么好说的npm install -g eas-cli然后在项目根目录执行eas login登录之后用eas init在项目里生成一个eas.json。这个文件是整个 EAS 构建流程的配置中心后面的所有构建 profile 都写在这里。如果你的项目是从旧版 Expo 升级过来的可能项目里还没有这个文件跑一次eas init就会自动补上。这里要强调一个容易被忽略的点eas init会自动识别你的项目 SDK 版本并生成对应默认配置。但如果你是用npx create-expo-app新建的模板项目eas.json可能不是最新结构建议初始化之后打开看一眼确保字段完整。3.2 第二步配置 eas.json 的多环境构建 Profileeas.json里最核心的就是build字段下的几个 profile。我通常会配置三个development开发构建、preview内测分发、production正式发布。每个 profile 对应不同的环境和构建参数。下面是一个可以直接抄作业的配置{ cli: { version: 10.0.0, appVersionSource: remote }, build: { development: { developmentClient: true, distribution: internal, channel: development, ios: { buildConfiguration: Debug }, android: { buildType: apk } }, preview: { distribution: internal, channel: preview, android: { buildType: apk } }, production: { autoIncrement: true, channel: production, distribution: store, ios: { image: macos-ventura-13.4-xcode-15.0 } } }, submit: { production: { ios: { appleId: yourappleid.com, ascAppId: 1234567890, appleTeamId: TEAMID123 }, android: { serviceAccountKeyPath: ./google-service-account.json } } } }逐个字段解释一下cli.appVersionSource: 设为remote后版本号会从 EAS 云端读取配合autoIncrement使用可以自动递增 iOS 的 buildNumber 和 Android 的 versionCode。这是解决“每次发版忘记改版本号”的关键强烈推荐打开。developmentClient: 开发构建会打出一个带调试器的 App用于真机联调不是用于上架的包。distribution:internal表示内部测试store表示要上架的包。Store 包的 Android 产物会是.aabiOS 产物会是.ipa格式差异会自动处理。channel: 这个东西配合 EAS Update 使用决定 OTA 热更新推到哪个渠道。区分 development / preview / production 三个渠道等于区分了不同环境的更新投递路径。ios.image: 指定构建用的 macOS 镜像版本。如果要用 Xcode 15 的特性这里就得写明确不然默认镜像可能不是最新。3.3 第三步首次构建时把签名凭证托管给 EAS这一步是新手最容易卡住的。首次跑eas build -p ios --profile production的时候CLI 会检测项目还没有证书然后引导你选择自动管理证书推荐个人/小团队基本用这个使用已有证书如果你已经从 Apple Developer 后台手动下载过可以上传自动管理证书的逻辑是把你的 Apple Team ID 和证书私钥上传到 EAS 的密钥托管中心后续每次构建都自动复用。Android 侧同理第一次会让上传 Keystore如果还没有可以让 EAS 生成一份。生成后记得把 Keystore 文件和密码备份好因为 EAS 不保证永远帮你保留万一情况变化你还是得回到本地签名这套体系。Android 侧首次跑eas build -p android --profile production时也是同样流程CLI 会让你填 Keystore 路径或直接自动生成。我不会用一张表格整理这两个平台的凭证配置项因为 EAS CLI 的引导交互已经很清楚了只需要记住一个原则构建配置写进eas.json签名凭证托管给 EAS密钥口令不要写进代码仓库。3.4 第四步编写 Fastlane 的 lane 脚本EAS 构建完产物默认会下载到本地加--auto-submit可以在构建完立刻触发上传不过我先不用这个因为正式的发布流程里往往需要人工看一眼产物再上传。我的做法是单独写几个 Fastlane lane用来做构建后的分发和收尾。项目根目录新建fastlane文件夹里边的Fastfile长这样fastlane_version 2.220.0 default_platform :ios platform :ios do desc Upload iOS build to TestFlight lane :upload_testflight do api_key app_store_connect_api_key( key_id: ENV[APP_STORE_CONNECT_KEY_ID], issuer_id: ENV[APP_STORE_CONNECT_ISSUER_ID], key_filepath: ENV[APP_STORE_CONNECT_KEY_PATH], duration: 1200, in_house: false ) upload_to_testflight( api_key: api_key, app_identifier: com.yourcompany.yourapp, ipa: ENV[IPA_PATH] ) end desc Upload iOS build to App Store lane :upload_appstore do api_key app_store_connect_api_key( key_id: ENV[APP_STORE_CONNECT_KEY_ID], issuer_id: ENV[APP_STORE_CONNECT_ISSUER_ID], key_filepath: ENV[APP_STORE_CONNECT_KEY_PATH], duration: 1200, in_house: false ) upload_to_app_store( api_key: api_key, app_identifier: com.yourcompany.yourapp, ipa: ENV[IPA_PATH], skip_metadata: true, skip_screenshots: true ) end end platform :android do desc Upload Android AAB to Google Play internal track lane :upload_internal do upload_to_play_store( track: internal, json_key: ENV[PLAY_JSON_KEY_PATH], package_name: com.yourcompany.yourapp, aab: ENV[AAB_PATH] ) end desc Upload Android AAB to Google Play production lane :upload_production do upload_to_play_store( track: production, json_key: ENV[PLAY_JSON_KEY_PATH], package_name: com.yourcompany.yourapp, aab: ENV[AAB_PATH], release_status: draft ) end end解释一下几个关键点我用的是 App Store Connect API Key不是账号密码这是 Apple 现在推荐的认证方式。先在 Apple Developer 后台生成一个.p8密钥文件然后把 key id、issuer id 和文件路径通过环境变量传进来。不要把密钥硬编码到 Fastfile 里。Android 侧的json_key是 Google Play 的服务账号 JSON 文件路径。在 Google Cloud Console 给服务账号授权“发布应用”权限后下载 JSON把它放到 CI 的环境变量里或者本地安全目录。同样不要提交进 Git。upload_to_app_store里我加了skip_metadata和skip_screenshots意思是只上传二进制包商店的截图和文案我后续再用 EAS Metadata 或 App Store Connect API 单独同步。如果你希望一口气全做去掉这两个开关就行。3.5 第五步在 CI 里把 EAS 和 Fastlane 串成完整流水线本地能跑通之后要做的就是把这条链路放到 CI 里自动触发。我用的是 GitHub Actions配置思路跟其他 CI 大同小异核心就三步检出代码、安装依赖、执行打包命令。一个精简版的 workflow 文件如下name: Build and Release on: push: tags: - v* jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkoutv4 - name: Setup Node uses: actions/setup-nodev4 with: node-version: 20 - name: Setup EAS run: npm install -g eas-cli - name: Install dependencies run: npm ci - name: Build iOS app env: EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} run: eas build -p ios --profile production --non-interactive --no-wait - name: Build Android app env: EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} run: eas build -p android --profile production --non-interactive --no-wait注意这里我加了--no-wait意思是 CI 里发起构建后不用干等EAS 会在云端异步完成构建。构建完成后怎么拿到产物去触发 Fastlane 呢有两种常见做法方式一在本地执行eas build:run或者写一个脚本定期eas build:list查询最近的构建产物链接下载后传给 Fastlane。适合对流程控制要求高的场景。方式二直接给 EAS 构建命令加--auto-submit让 EAS 自己构建完上传。这种更省事但你就失去了 Fastlane 灵活编排的能力。我实际用的是方式一的变体用一个 shell 脚本在 CI 的后续 step 里轮询eas build:list --platform ios --limit 1 --non-interactive拿到最新的构建 ID然后eas build:download下载产物再把产物路径传给 Fastlane。脚本大概长这样VERSION$(jq -r .version package.json) eas build:list --platform ios --limit 1 --json /tmp/ios_build.json BUILD_ID$(jq -r .[0].id /tmp/ios_build.json) eas build:download --platform ios --id $BUILD_ID --output ./build/app.ipa --non-interactive IPA_PATH./build/app.ipa fastlane ios upload_testflight这种方式的核心思想是EAS 管“造包”Fastlane 管“上架”CI 管“串联”。每个环节的日志都能在对应工具里查到出了问题不会互相甩锅。3.6 第六步版本号同步和 OTA 更新渠道多端发布里最容易出问题的是版本号不同步。iOS 用 buildNumberAndroid 用 versionCode两者的递增逻辑还不完全一样。我的方案是让 app.json 里的version字段作为“语义版本号”比如 1.2.3然后 iOS 的 buildNumber 和 Android 的 versionCode 都交给 EAS 的autoIncrement字段自动递增。autoIncrement默认按最近一次构建的数字 1所以不用担心两个平台冲突。但有一点要注意如果你同时在两个平台跑构建它们各自维护自己的递增计数不需要手动对齐。OTA 更新这一块EAS Update 的 channel 机制是哪个 channel 的 App 收到更新就推哪个 channel 的 bundle。比如 production channel 的正式包只接收 production channel 发出的 OTA 更新而 preview channel 的内部测试包可以提前体验下一版 content bundle。这个设计很适合“内测用户先体验、正式用户稳定更新”的分级发布模式。实践中我用一个环境变量来区分渠道在构建时动态传入eas build -p android --profile preview --channel preview --non-interactive这里的--channel会覆盖 eas.json 里 profile 预设的 channel 值灵活度更高。4. 多端发布场景下的关键细节与问题排查自动化流程跑起来之后真正考验人的是“边缘情况”怎么处理。这一章我把这几年踩过的坑、经常被问的问题集中列一下做个速查表。4.1 证书和密钥最容易翻车的环节问题常见表现排查思路iOS 证书过期构建失败报 provisioning profile 无效在 EAS 后台刷新凭证或者删除后重新生成Android Keystore 丢失后续包无法用同一签名首次生成时一定下载备份放到密码管理工具里团队多人共用账号互相覆盖证书构建间歇性失败每人用独立 App Store Connect API KeyEAS 里按 team 隔离凭证本地证书与云端不同步本地能构建云端失败确保用eas credentials统一管理少手动导证书我个人的铁律是密钥只进密码管理器只通过环境变量或 CI secrets 注入绝不写进代码仓库。Fastlane 的match也能帮你把证书放进 Git 仓库备份但一定要给仓库加密我这里就不展开讲了。4.2 版本号冲突和商店校验问题版本号这块iOS 常见报错是“ITMS-90062: This bundle is invalid. The value for key CFBundleShortVersionString is not valid.” 这通常是 app.json 里 version 格式不对或者 buildNumber 用了非递增数字。Android 侧则常遇到“Version code X has already been used for another build”说明你往同一个轨道上传了重复 versionCode 的包。解决办法就一句话不要手动去改版本号让 EAS 自动递增或者写一个脚本统一在 CI 里修改 app.json。我在 CI 里会用 npm 包version自动打 tag再触发构建 workflow确保版本号来源只有一个。4.3 构建产物下载失败或超时EAS 构建完的下载链接有时效性如果你隔了很久才去下载链接可能已经失效。解决方法是及时在 CI 里抓取或者直接用eas build:download配合--output指定路径。另一个实践是构建完立刻归档到自己的对象存储或 GitHub Release避免依赖 EAS 的历史产物链接。如果发现 iOS 构建特别慢通常是因为 Xcode 版本没指定导致每次都用最新的编译时间更长。可以把 eas.json 里的ios.image固定到一个具体版本例如macos-ventura-13.4-xcode-15.0这样既能加速缓存也能保证构建环境稳定。4.4 Fastlane 上传失败认证和文件格式的坑Fastlane 上传 TestFlight 失败十有八九是认证问题。用 App Store Connect API Key 时注意三个东西必须配套key id、issuer id、.p8文件内容。这三者任何一个不对都会抛Authentication failed。Android 上传 Google Play 失败常见原因是服务账号没有授权或者上传的产物不是.aab。要注意upload_to_play_store默认接受.aab格式如果你传了.apk会遇到格式错误提示。测试轨道internal和生产轨道的包签名可以不同但同一轨道内的版本号必须严格递增。另外Fastlane 依赖 Ruby 环境国内网络下安装 fastlane 或更新 gem 经常遇到超时。建议用系统自带 Ruby然后通过 bundle 管理依赖同时配置镜像源。如果是在 GitHub Actions 里跑直接用ruby/setup-rubyv1一步到位不折腾本地环境。4.5 多端联调时如何区分内测包和正式包这里分享一个我比较满意的实践用 EAS 的 channel 和构建配置组合让内测包和正式包的 App 名、图标、接口地址都能区分开。做法是在 app.config.js 里读取环境变量动态生成不同的应用标识const channel process.env.EAS_BUILD_PROFILE || development; const appConfig { expo: { name: channel production ? 你的应用 : 你的应用-内测, ios: { bundleIdentifier: channel production ? com.yourcompany.yourapp : com.yourcompany.yourapp.beta, }, android: { package: channel production ? com.yourcompany.yourapp : com.yourcompany.yourapp.beta, }, extra: { apiUrl: channel production ? https://api.yourcompany.com : https://staging.api.yourcompany.com, }, }, }; module.exports appConfig;这个文件在构建时会被 EAS 读取自动生成对应环境的原生项目配置。这样内测包和正式包在设备上可以共存不用卸载来卸载去而且各自的 API 地址也不会串。5. 这套方案对团队协作的影响以及我的体会搭建这套流程最大的收益不是省掉了一个人的工作量而是把“发版”这个操作从“个人技能”变成了“团队能力”。以前发版依赖某个熟悉证书配置的人他一休假大家就只能干等现在只要代码能合并到主分支、打上 tag流水线就会自动完成剩下的事新同学也能在十分钟内学会触发一次发布。我个人在实践中有两个很深的体会。第一个是自动化不是“一次性搭建”的事而是“持续维护”的事。工具升级、包格式变更、商店政策更新都会让流水线偶尔“抽风”。所以不要把这套系统当成黑盒隔一段时间就要自己手动跑一遍确认每个环节都还正常。第二个体会是能花钱买时间的环节别吝啬。EAS 的收费档位我看了很久最后发现按量计费比固定套餐更划算因为并不是每天都发版。挑便宜的工具维护成本高不如在核心链路上用更成熟、更省心的服务把精力留到真正有业务价值的地方。最后再分享一个小技巧在 CI 里给 Fastlane 命令加上--verbose参数第一次跑通时把完整输出保存下来等以后出问题这份输出就是你最好的排查起点。流水线这东西跑通不算能耐能稳定地跑一年不出幺蛾子才算是真的“工程化终局”。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询