Skip to content

CI/CD 流水线与 Fastlane 自动化分发 ​

回到总览:构建与发布工程相关模块:iOS 构建与依赖 · Gradle 构建生命周期 · Flutter 产物瘦身与符号化

一句话定义 ​

CI/CD 把「提交 → 检查 → 构建 → 测试 → 签名打包 → 分发/发布」固化为可重复流水线;Fastlane 用 lane 把签名、截图、上传、TestFlight/商店提交的胶水步骤脚本化,消除「本地能出包、CI 出不了」的环境差。

代码索引 ​

主题Lab 说明源码
Fastlane 边界收口样板flutter-host-fastlane-boundary-demoFastfile

为什么需要 ​

  • 一条 CI 流水线从提交到上线的关键环节有哪些?
    • 一句话答:触发 → 检查(lint/静态分析) → 构建 → 测试 → 签名打包 → 分发/发布;每环失败即阻断(详见 §底层机制.1)。
  • Fastlane 主要解决哪类问题?
    • 一句话答:把签名、打包、上传、TestFlight/商店提交等胶水步骤固化为可重复 lane,统一本地与 CI 环境(见 §底层机制.2)。
  • 为什么发布构建必须归档符号表(dSYM / mapping)?
    • 一句话答:线上崩溃堆栈是地址,需 dSYM(iOS) / mapping.txt(Android) / .symbols(Flutter) 才能符号化还原(呼应 07/10 瘦身页)。

底层机制 ​

1. 流水线阶段(以 GitHub Actions 为例) ​

yaml
# .github/workflows/release.yml(节选)
jobs:
  build:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v4
      - run: bundle exec fastlane beta   # 调用 Fastlane lane
  • 可能输出
    Run bundle exec fastlane beta
    [12:01:02]: fastlane finished with exit code 0
    [12:01:03]: Uploaded to TestFlight 🚀
  • 预期现象:PR 触发后依次跑检查/构建/测试,任一步非零退出即流水线标红阻断;fastlane beta 在末尾把包上传到 TestFlight。
  • 观察重点:runs-on 的镜像(如 macOS 版本)决定 Xcode 版本,是 iOS 构建“挑机器”的源头;用 actions/cache 缓存依赖可显著提速。

2. Fastlane lane 把胶水步骤串起来 ​

ruby
# Fastfile
lane :beta do
  build_app(scheme: "MyApp")          # 等价于 gym,内部调 xcodebuild archive
  upload_to_testflight(skip_waiting_for_build_processing: true)
end
  • 概念:build_app 封装 xcodebuild archive + 导出;upload_to_testflight 处理登录、上传、元数据。Android 侧对应 gradle(task: "assembleRelease") + upload_to_play_store。

3. 当前仓库为什么不应该为了 Fastlane 新造一套 iOS host ​

对应 Lab:flutter-host-fastlane-boundary-demo

这次任务里最容易“看起来很完整、实际上维护成本很高”的做法,就是为了证明 Fastlane 能用,临时再造一套独立 labs/ios/host。这不符合当前仓库的 host 结构,也会引入无法在非 macOS/Xcode 环境稳定维护的伪工程。

当前更诚实、也更可维护的收口是:

  1. 复用现有 labs/flutter/host 的 iOS Runner 与 Android app。
  2. 在 labs/ci/flutter-host-fastlane-boundary-demo/Fastfile 里只演示 lane 如何统一调 Flutter host。
  3. iOS lane 明确停在 flutter build ios --simulator --debug --no-codesign,只验证“Fastlane 能否统一调度构建命令”。
  4. 把签名、archive、export、TestFlight 上传明确声明为“需要真实 macOS + Xcode + 证书环境”的外部边界。
ruby
lane :host_ios_build_dry_run do
  sh("flutter --version")
  sh("flutter pub get", chdir: "../../flutter/host")
  sh("flutter build ios --simulator --debug --no-codesign", chdir: "../../flutter/host")
end
  • 可能输出
    text
    $ fastlane ios host_ios_build_dry_run
    $ flutter pub get
    $ flutter build ios --simulator --debug --no-codesign
  • 预期现象:在具备 Flutter+iOS 工具链的 macOS 机器上,可以成功走通“Fastlane → Flutter host iOS debug/simulator 构建”这一层。
  • 观察重点:这条 lane 证明的是 Fastlane 的胶水价值,不是证明当前仓库已经具备真实发布到 TestFlight 的完整环境。

4. 签名物料如何安全注入 ​

  • 证书/keystore/密钥绝不进仓库,存 CI Secret 或专用签名服务(如 fastlane match 走加密 Git 仓库)。
  • 构建机临时从 Secret 还原签名物料,构建完即清理,降低泄露面。

Android / Flutter / Web / Backend 对照 ​

环节移动端 CIWeb CI通用
触发push/PR/tag同webhook
构建Gradle / xcodebuild / flutter buildvite build / webpack语言构建
签名分发TestFlight / Play / 蒲公英CDN / OSS制品库
胶水自动化Fastlane各平台 CLI / 脚本脚本

常见场景 ​

  • PR 卡点:每次 PR 跑 lint + 单测,拦截低级错误。
  • Nightly / Release 分支发布:定时或打 tag 触发 beta/release lane。
  • 多环境:lane :staging 与 lane :production 共用打包、切不同签名与后端地址。

常见误配、事故后果与排障 ​

1. 事故:CI 缓存未命中,构建时间翻倍 ​

  • 误配原因:依赖缓存 key 设计不当或 actions/cache 路径错。
  • 后果:PR 排队变长,开发者等待。
  • 排障与修法:按 lock 文件内容做 cache key;确认缓存路径覆盖 Pods / node_modules / Gradle 缓存。

2. 误配:签名密钥硬编码进仓库或日志泄露 ​

  • 后果:攻击者可用你的身份发布恶意包。
  • 排障与修法:密钥只放 CI Secret / 签名服务;构建日志屏蔽敏感变量;定期轮换。

3. 事故:本地能出包、CI 不能(环境差) ​

  • 误配原因:本地 Xcode/Gradle 版本、签名物料与 CI runner 不一致。
  • 后果:发布阻塞在最后一刻。
  • 排障与修法:用 Fastlane 统一 lane,让本地与 CI 走同一脚本;CI 固定 runner 镜像版本。

4. 误配:把 Fastlane demo 写成“伪完整上线链路” ​

  • 后果:文档里看起来有 iOS 自动发布样板,但实际没有 Xcode、签名、App Store Connect 前置条件,后续维护者反而更难判断哪部分是真的可运行。
  • 排障与修法:像本仓库现在这样,把 dry-run / no-codesign / simulator 构建和真实发布链路分开写,边界明确。

相近概念对比 ​

  • CI vs CD:CI 聚焦自动构建/测试(质量门);CD 进一步自动部署/发布(交付)。
  • Fastlane vs 各平台 CLI:Fastlane 用统一 lane 抽象跨平台胶水;裸 CLI 更直白但重复劳动多。
  • lane vs job:Fastlane 的 lane 是单机内一组步骤;CI 的 job 是 runner 上的一次执行单元,二者常嵌套(job 里调 lane)。
  • artifact vs cache:artifact 是跨 job 传递的构建产物;cache 是加速复用的中间态。

对应实验 ​

Lab说明源码
flutter-host-fastlane-boundary-demo复用现有 Flutter host,演示 Fastlane lane 如何统一调度 Android / iOS 构建命令(与文首「代码索引」一致)Fastfile

复习检查题 ​

  1. CI 流水线「检查 → 构建 → 测试 → 打包 → 发布」中,哪一环失败最该“早阻断”?为什么? 答:越靠前越该早阻断——lint/静态分析在构建前就能拦下低级错误,成本最低;若等到打包/发布才失败,已耗费构建与签名资源。

  2. Fastlane 相比直接调 xcodebuild / gradlew 的价值? 答:把签名、打包、上传、TestFlight/商店提交等易出环境差的胶水步骤抽象成可重复 lane,本地与 CI 共用同一脚本,消除“本地能出包 CI 不能”。

  3. 为什么发布流水线要归档 dSYM / mapping / .symbols? 答:线上崩溃堆栈是裸地址,必须靠这些符号表才能还原成源码行号(呼应 07/10 瘦身页的符号化还原)。

速记 ​

  • 六环节:触发 → 检查 → 构建 → 测试 → 签名打包 → 分发。
  • Fastlane 价值:lane 统一本地/CI,专治“环境差”。
  • 仓库边界:当前只落地到 Flutter host + dry-run/no-codesign,不伪造真实 TestFlight 发布。
  • 签名物料:只进 Secret/签名服务,绝不进仓库。

站点构建时间:2026/8/24 23:43:17