移动端 CI/CD:自动化构建、测试与分发
移动端 CI/CD:自动化构建、测试与分发
什么是移动端 CI/CD
CI/CD 是一套软件开发实践,包含持续集成(Continuous Integration)和持续交付/部署(Continuous Delivery/Deployment)。在移动应用开发中,它的目标是让代码提交后自动完成构建、测试、签名、打包,并推送到测试分发平台或应用商店,从而减少人工操作、尽早发现错误、加快交付速度。
- 持续集成(CI):开发者频繁地将代码合并到主干分支,每次合并都会触发自动化构建和测试,以便快速发现集成问题。
- 持续交付(CD):在 CI 通过后,自动将应用打包成可安装的产物,并上传到内部测试分发平台(如 TestFlight、Firebase App Distribution),供测试人员或产品经理随时获取最新版本。
- 持续部署(CD):更高阶的阶段,自动将通过所有测试的版本直接发布到 Google Play 或 App Store,通常用于灰度或内部发布。
本教程将引导你从零搭建一套适用于 Android 和 iOS 项目的 CI/CD 流程,即使没有 DevOps 经验也能跟随操作。
为什么移动端需要 CI/CD
移动应用构建环境复杂,依赖特定的操作系统、SDK 版本、签名证书、配置文件等。手动构建和分发常会遇到:
- 开发机器环境不一致,导致“在我电脑上能跑”的问题。
- 打包、测试、上传重复性劳动耗时且容易出错。
- 证书和配置文件管理混乱,经常过期或错误签署。
- 测试反馈缓慢,难以频繁发布 Beta 版本。
引入 CI/CD 可以:
- 统一构建环境:所有构建在隔离的、可复现的容器或虚拟机中运行。
- 自动化门禁:每次提交必然经过编译检查、静态分析、单元测试,不合格的代码无法进入测试分发。
- 解放生产力:开发者专注代码,构建与分发由机器自动完成。
- 快速反馈:几分钟内就能知道改动是否编译成功、测试是否通过,测试人员也能第一时间获得安装包。
核心概念与流程概览
一个典型的移动端 CI/CD 流水线包含如下阶段:
- 触发器:代码推送到仓库、PR 创建、标签推送或定时计划。
- 环境准备:拉取依赖镜像(如 Android SDK、Xcode 环境),安装必要工具。
- 代码检出:克隆仓库,切换到对应分支。
- 安装依赖与预处理:运行
npm install(React Native/Flutter)、pod install(iOS)或 Gradle 依赖解析。 - 静态检查与测试:Lint 检查、代码风格、单元测试、UI 测试。
- 编译与打包:调用 Gradle 或 xcodebuild 生成 APK/AAB/IPA。
- 签名:使用预先配置的密钥库或 Apple 证书对应用签名。
- 上传分发:将包上传到测试平台(Firebase App Distribution、TestFlight、App Center 等)或应用商店。
- 通知:通过 Slack、邮件等告知团队新构建已就绪。
下面将以工具选择和具体实现为主线展开。
工具选择
构建执行环境
- GitHub Actions:免费额度友好,与 GitHub 深度集成,社区 Action 丰富。
- GitLab CI/CD:自托管或 SaaS,配置灵活,Runner 可自定义。
- Jenkins:历史悠久,高度可定制,适合复杂场景;需自行维护服务器。
- Bitrise:专为移动端设计,可视化配置,自带 macOS 和 Android 环境。
- CircleCI / Travis CI:提供 macOS 构建机,适合需要 iOS 构建的团队。
本教程选用 GitHub Actions 作为示例,同时使用 fastlane 来简化构建、签名和分发任务。
自动化工具
- fastlane:移动端 CI/CD 的事实标准,提供
gym(构建 iOS)、gradle(构建 Android)、match(管理证书)、pilot(TestFlight)、supply(Google Play)等一整套工具链。 - Gradle + Shell:Android 构建可直接通过 Gradle 命令行完成,配合 Shell 脚本上传。
- xcodebuild + xcrun:iOS 纯命令行方案,适合不依赖 fastlane 的场景。
环境准备与签名配置
Android 签名配置
Android 应用发布需要签名证书(.jks 或 .keystore 文件)。安全起见,不应将证书文件提交到代码仓库。在 CI 环境中通常采用以下方式:
- 将证书文件用 base64 编码后保存在 CI 的 Secrets 中。
- 在 CI 作业中解码恢复,写入临时目录。
- 通过 Gradle 属性或环境变量传递密码和别名。
Gradle 配置示例(app/build.gradle 或 gradle.properties):
android {
signingConfigs {
release {
storeFile file(System.getenv("KEYSTORE_PATH") ?: "debug.keystore")
storePassword System.getenv("KEYSTORE_PASSWORD")
keyAlias System.getenv("KEY_ALIAS")
keyPassword System.getenv("KEY_PASSWORD")
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}
iOS 签名与证书管理
iOS 构建必须使用 Apple Developer 账号生成的 Provisioning Profile 和证书。手动管理极易出错,推荐使用 fastlane 的 match 或 sigh 工具。
match 会将证书和配置文件加密存储在 Git 仓库中,CI 机器第一次配置后即可自动拉取和解密,保证团队使用相同的签名材料。
初始化 match 并配置仓库:
fastlane match init
随后在 CI 中使用 match 获取证书:
fastlane match appstore --readonly
(首次运行可能需要交互,需在 CI 环境提供 MATCH_PASSWORD 等环境变量)
实施步骤:从零搭建 GitHub Actions + fastlane 流水线
以下演示将用 GitHub Actions 触发构建、运行测试、打包并上传到 Firebase App Distribution(Android)和 TestFlight(iOS)。请确保项目根目录已初始化 fastlane。
步骤 1:配置 fastlane
在项目根目录创建 fastlane/Fastfile,内容如下:
Android 的 Fastfile(片段):
default_platform(:android)
platform :android do
desc "Build and upload to Firebase"
lane :firebase_distribution do
gradle(task: "clean assembleRelease")
firebase_app_distribution(
app: "1:123456789:android:abcde12345",
service_credentials_file: "firebase_credentials.json",
groups: "qa-team",
release_notes: "Automated build"
)
end
end
iOS 的 Fastfile(片段):
default_platform(:ios)
platform :ios do
lane :testflight do
match(type: "appstore", readonly: true)
gym(scheme: "YourApp", export_method: "app-store")
pilot
end
end
步骤 2:存储敏感信息到 GitHub Secrets
在仓库 Settings → Secrets and variables → Actions 中添加:
KEYSTORE_BASE64:用 base64 编码后的 Android keystore 文件内容KEYSTORE_PASSWORD、KEY_ALIAS、KEY_PASSWORDMATCH_PASSWORD:用于解密 match 仓库APP_STORE_CONNECT_API_KEY_JSON:App Store Connect API 密钥(用于上传),或使用APP_STORE_CONNECT_ISSUER_ID、KEY_ID和私钥FIREBASE_CREDENTIALS_JSON:Firebase 服务帐号凭证内容- 其他必要的环境变量
步骤 3:编写 GitHub Actions 工作流文件
在 .github/workflows/ 下创建 mobile-ci.yml。
Android CI 示例:
name: Android CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
build-and-upload:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '17'
- name: Decode Keystore
run: |
echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode > android/app/release.keystore
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Run unit tests
run: ./gradlew testDebugUnitTest
- name: Build Release APK
env:
KEYSTORE_PATH: "android/app/release.keystore"
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
run: ./gradlew assembleRelease
- name: Upload to Firebase App Distribution
# 假设 fastlane 已配置,需要设置 firebase_credentials 文件
env:
FIREBASE_CREDENTIALS_CONTENT: ${{ secrets.FIREBASE_CREDENTIALS_JSON }}
run: |
echo "$FIREBASE_CREDENTIALS_CONTENT" > firebase_credentials.json
bundle exec fastlane android firebase_distribution
iOS CI 示例(需要在 macOS runner 上运行):
name: iOS CI
on:
push:
branches: [ main, develop ]
jobs:
build-and-upload:
runs-on: macos-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
- name: Install Fastlane & Cocoapods
run: |
bundle install
- name: Run tests
run: xcodebuild test -scheme YourApp -destination 'platform=iOS Simulator,name=iPhone 15'
- name: Build and upload to TestFlight
env:
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
APP_STORE_CONNECT_API_KEY_JSON: ${{ secrets.APP_STORE_CONNECT_API_KEY_JSON }}
run: bundle exec fastlane ios testflight
步骤 4:触发并验证
将上述文件推送到 GitHub,查看 Actions 选项卡中的运行情况。若出现错误,根据日志调整。
自动化测试集成
CI 流水线应强制运行测试,避免携带破坏性修改的构建进入分发。
Android 测试
- 单元测试:使用
./gradlew testDebugUnitTest,报告可生成 HTML 或 JUnit XML。 - 仪器测试(UI 测试):需启动模拟器或连接到真实设备。可在 CI 中使用 Android Emulator Action 或借助 Firebase Test Lab。
GitHub Actions 中启动模拟器的示例:
- name: Run instrumentation tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 34
target: google_apis
arch: x86_64
script: ./gradlew connectedCheck
iOS 测试
默认的 xcodebuild 测试命令可运行单元测试和 UI 测试。UI 测试需要模拟器,macOS runner 默认已包含 Xcode 和模拟器。使用 xcodebuild test 指定 -destination 参数即可。若使用 fastlane,可调用 scan:
scan(scheme: "YourApp")
代码质量检查
在构建之前集成静态分析,可以及早发现潜在 bug 和风格问题。
- Android:可在 Gradle 中配置 Lint 检查,并运行
./gradlew lint。也可集成 Detekt、Ktlint 等。 - iOS:使用 SwiftLint,通过脚本或构建阶段执行。
- 通用:SonarQube、CodeClimate 等平台可接入 CI 流程,提供可视化质量报告。
分发到测试平台与应用商店
Android
- 内部/封闭测试:通过 fastlane 的
supply或 Gradle Play Publisher 插件上传到 Google Play Console 的 Alpha/Beta 轨道。 - Firebase App Distribution:适合快速分发给测试人员,无需等待商店审核。使用 fastlane 插件
firebase_app_distribution或直接调用 Firebase CLI。 - App Center:微软提供的替代方案,功能类似,支持崩溃分析。
iOS
- TestFlight:苹果官方分发渠道,外部测试邀请需要经过 Beta 审核。通过 fastlane
pilot上传 ipa,自动填写测试信息。 - Firebase App Distribution:同样支持 iOS,需提供 Ad Hoc 或 Development Provisioning Profile 签名的 IPA。
- App Center:同样可用。
最佳实践与常见问题
- 密钥管理:绝不将任何明文密码或证书文件提交到仓库。所有敏感数据通过 CI Secrets 注入,证书可通过 match 加密存储。
- 构建缓存:利用 CI 的缓存机制,缓存 Gradle、CocoaPods、npm 等依赖,加速后续构建。
- GitHub Actions 示例:
actions/cache@v3缓存~/.gradle/caches和Pods目录。
- GitHub Actions 示例:
- 分支策略:推荐采用 Git Flow 或 Trunk-Based 开发,将 CI 与 PR 检查结合,确保合并到主分支的代码总是可构建、可测试。
- 矩阵构建:如需同时构建多个 SDK 版本或架构,使用 CI 提供的矩阵策略,例如多个 API level 测试。
- 环境一致性:在 CI 中明确指定 Java 版本、Ruby 版本、Xcode 版本,避免因环境差异导致构建突然失败。
- 通知与反馈:构建结束后通过 Slack、钉钉、邮件等发送结果。fastlane 也有内置的通知机制。
- 监控失败:尽快修复 CI 失败,避免坏构建堆积。鼓励团队将 CI 失败视为最高优先级的事项。
小结
移动端 CI/CD 的落地并不复杂,关键在于选择合适的工具链并逐步推进。通过 GitHub Actions 结合 fastlane,你可以在几小时内为 Android 和 iOS 项目搭建起从代码提交到测试分发的一站式自动化管道。随着团队成熟,还可扩展为自动截图、翻译更新、多版本发布等高级能力。现在就从添加一个简单的工作流文件开始,享受自动化带来的开发效率提升吧。