质量与交付篇(2/6):CI/CD 实战——自动构建、签名、分发

CI/CD 实战------自动构建、签名、分发

系列:质量与交付篇(2/6)

标签建议:Flutter CI/CD 自动化构建 签名 发布

很多 Flutter 团队都经历过同一种"发版焦虑":

本地打包成功了,但换台机器就失败;安卓签名文件在群里传来传去;iOS 证书过期到最后一天才发现;测试包分发靠手动上传,版本号还会写错。

这篇文章聚焦一个目标:让"发版"从人工操作变成可重复、可追溯、可回滚的流水线。


1. 问题背景:业务场景 + 现象

在业务项目中,通常要同时支持:

  • Android 多渠道/多环境(dev、staging、prod)
  • iOS TestFlight + 正式商店
  • 测试同学频繁要"最新可安装包"
  • 紧急修复需要"半小时内可交付"

常见现象:

  • 构建依赖人:只有某个同学机器能打包
  • 签名高风险:keystore、p12、provisioning profile 管理混乱
  • 版本不一致:Git tag、应用版本号、发版记录对不上
  • 分发链路断裂:包打出来后还要手动上传、手动通知
  • 失败不可定位:构建日志散落,本地复现成本高

2. 原因分析:核心原理 + 排查过程

核心原理

CI/CD 的本质不是"上一个平台",而是把发布过程拆成三段:

  1. CI(持续集成):代码合并即触发验证(lint/test/build)
  2. CD(持续交付):产物自动归档并分发到测试渠道
  3. Release(持续发布):满足条件后自动/半自动上架

为什么团队会卡住

  • 构建脚本写在个人本地,没进入仓库标准化
  • 环境变量、签名文件、密钥没有统一密管策略
  • "分支策略"和"发布策略"脱节(例如 main 能直接推生产)
  • 缺少"失败即阻断"的质量门禁(测试失败也能打包)

快速排查清单

  • 同一 commit 是否能在任意 Runner 复现产物?
  • 签名资产是否都在密钥系统里,且有权限审计?
  • 是否实现了"一键拿测试包链接"?
  • 是否有从 tag 到安装包的全链路追踪?

3. 解决方案:方案对比 + 最终选择

方案对比

  • 方案 A:纯手动脚本(本地执行)

    优点:上手快;缺点:不可控、不可审计、不可规模化

  • 方案 B:CI 只做测试,打包仍手动

    优点:比 A 稳一点;缺点:最容易在"最后一公里"出事故

  • 方案 C:全流程流水线(推荐)

    • PR 阶段:静态检查 + 单测 + Widget 测试
    • 合并阶段:自动构建 Android/iOS 候选包
    • Tag 阶段:自动签名、归档、分发(Firebase/App Center/TestFlight)
    • 生产阶段:审批后发布,失败可回滚

最终选择(中小团队可直接落地)

采用 "分层流水线 + 环境隔离 + 签名密管"

  • 分层流水线verify -> build -> sign -> distribute -> release
  • 环境隔离:dev/staging/prod 使用不同变量与密钥
  • 签名密管:签名文件不入库,统一走 CI Secret + 临时文件注入
  • 版本规范 :Git tag 驱动版本(如 v2.3.1+231

4. 关键代码:最小必要代码片段

以下示例为通用模板,平台可替换为 GitHub Actions / GitLab CI / Jenkins / Codemagic。

4.1 Flutter CI 基础流水线(校验 + 构建)

yaml 复制代码
name: flutter-ci

on:
  pull_request:
  push:
    branches: [main]
    tags: ['v*']

jobs:
  verify:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v4
      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.24.0'
      - run: flutter pub get
      - run: flutter analyze
      - run: flutter test --coverage

  build-android:
    if: startsWith(github.ref, 'refs/tags/v')
    needs: verify
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v4
      - uses: subosito/flutter-action@v2
      - run: flutter pub get
      - run: flutter build apk --release --dart-define=ENV=prod
      - uses: actions/upload-artifact@v4
        with:
          name: android-release-apk
          path: build/app/outputs/flutter-apk/app-release.apk

4.2 Android 签名注入(避免 keystore 入库)

bash 复制代码
# CI 中通过 Secret 注入
echo "$ANDROID_KEYSTORE_BASE64" | base64 --decode > android/app/release.keystore

cat > android/key.properties <<EOF
storePassword=$ANDROID_STORE_PASSWORD
keyPassword=$ANDROID_KEY_PASSWORD
keyAlias=$ANDROID_KEY_ALIAS
storeFile=release.keystore
EOF

4.3 iOS 签名与导出(Fastlane 思路)

ruby 复制代码
lane :beta do
  build_app(
    workspace: "ios/Runner.xcworkspace",
    scheme: "Runner",
    export_method: "app-store"
  )
  upload_to_testflight
end

4.4 自动分发到测试群(示例)

bash 复制代码
# 构建完成后上传 Firebase App Distribution
firebase appdistribution:distribute build/app/outputs/flutter-apk/app-release.apk \
  --app "$FIREBASE_APP_ID_ANDROID" \
  --groups "qa,product" \
  --release-notes "build from ${GIT_TAG}"

5. 效果验证:数据/截图/日志

上线后建议跟踪这 4 组指标:

  • 构建成功率:近 30 天主干构建成功率(目标 > 95%)
  • 平均交付时长:从 merge 到测试可安装包(目标 < 15 分钟)
  • 回归成本:每次发版人工步骤数(目标降到 3 步以内)
  • 发布事故率:签名错误/版本错误/错包分发次数(目标趋近 0)

日志侧重点:

  • 每个阶段有明确起止日志(verify/build/sign/distribute)
  • 产物命名统一(app-prod-v2.3.1+231.apk
  • 每次发布关联 commit、tag、构建号、发布人

6. 可复用结论:通用经验 + 避坑清单

通用经验

  • 先把"可重复"做对,再追求"全自动"
  • 签名资产永远不进仓库,只走受控 Secret 注入
  • Tag 驱动发布,不要靠手填版本号
  • 失败即阻断:测试/校验不过,禁止进入签名与分发环节
  • 发布结果可追溯:包名、日志、通知都能定位到 commit

避坑清单

  • --dart-define 环境变量和后端环境是否一一对应
  • Android/iOS 的 bundle id、包名、渠道名是否一致
  • 签名证书有效期是否有提前告警(至少提前 30 天)
  • PR 是否必须通过 CI 才能合并
  • 是否保留最近 N 个可回滚产物
  • 分发通知是否包含版本号、变更摘要、下载链接、回滚说明

结语

CI/CD 的价值不只是"省时间",而是把发布从"个人经验"升级为"团队系统能力"。

当你的团队做到:任何人、任何时间、任意机器都能稳定产出同质量包,交付就真正可控了。

相关推荐
吴声子夜歌1 小时前
JavaScript——call()、apply()和bind()
开发语言·前端·javascript
毕设源码-郭学长2 小时前
【开题答辩全过程】以 基于Web的网上问诊系统的设计与实现为例,包含答辩的问题和答案
前端
酉鬼女又兒2 小时前
零基础快速入门前端DOM 操作核心知识与实战解析(完整汇总版)(可用于备赛蓝桥杯Web应用开发)
开发语言·前端·javascript·职场和发展·蓝桥杯·js
喝拿铁写前端3 小时前
一套面向 Web、H5、小程序与 Flutter 的多端一致性技术方案
前端·架构
yaaakaaang3 小时前
(一)前端,如此简单!---下载Nginx
前端·nginx
牛奶3 小时前
为什么全国人民都能秒开同一个视频?
前端·http·cdn
KongHen024 小时前
uniapp-x实现自定义tabbar
前端·javascript·uni-app·unix
汪子熙4 小时前
TS2320 错误的本质、触发场景与在 Angular / RxJS 项目中的系统化应对
前端·javascript·angular.js
我命由我123454 小时前
React - BrowserRouter 与 HashRouter、push 模式与 replace 模式、编程式导航、withRouter
开发语言·前端·javascript·react.js·前端框架·html·ecmascript