Continuous Integration (CI) — 是一种开发实践,团队中的每个成员每天至少将他们的更改集成到公共仓库中一次,每次集成都通过自动构建和测试进行验证。CI在早期阶段发现代码冲突和回归错误,降低修复成本。根据Puppet State of DevOps Report, 2025,使用CI的团队修复bug的速度比没有自动化的团队快4倍。
要点
Continuous Integration (CI) — 是一种开发方法论,它将来自多个参与者的代码集成到单一代码库的过程自动化。该术语由Martin Fowler在2000年代初期引入,作为一组防止“集成地狱”的实践——即开发者隔离工作数周,在合并更改时出现大量需要数天手动解决的冲突的情况。
没有CI,开发者完成一个功能,尝试将更改合并到main分支,发现同事修改了相同文件。解决冲突需要数小时,且经常破坏正常工作的代码。CI通过每天强制集成多次来解决这个问题:集成越频繁,冲突越少,解决越容易。实践表明,每日集成时冲突解决只需几分钟,而每周集成则需要数小时。
根据IBM Systems Sciences Institute的数据,在编码阶段修复bug的成本为25美元,在测试阶段为100美元,在生产阶段为2500美元。CI将缺陷检测尽可能左移(shift left),在提交阶段发现错误,此时修复成本几乎为零。使用CI的团队平均花费15%的时间进行调试,而没有CI的团队则为35%。
Martin Fowler定义了CI的关键实践,这些实践无论技术栈如何都保持有效。遵循这些原则可确保CI带来好处,而不是成为官僚负担。移动开发提出了额外要求,但核心保持不变。
项目的所有代码存储在一个拥有单一版本控制系统(Git)的仓库中。单一事实来源排除了功能在fork中开发且数周不与主要代码库同步的情况。在移动项目中,这意味着Android、iOS和backend部分可以位于一个仓库(单仓库)或具有公共版本控制方案的独立仓库中。
项目构建必须通过一个命令执行。对于Android是./gradlew assembleDebug,对于iOS是xcodebuild或fastlane build。构建脚本检查可重现性:CI服务器上的构建必须与开发者机器上的结果相同。任何环境差异都通过容器化或IaC(Infrastructure as Code)消除。
构建后执行所有级别的测试:模块、集成和UI。如果测试失败——提交被视为无效。保持绿色状态是团队的共同责任。在移动项目中,通常将快速测试(每次提交最多执行5分钟)和慢速测试(在真实设备上运行的UI测试,较少执行)分开。
// 带有CI友好报告的单元测试示例
class LoginViewModelTest {
private val repository = mock<AuthRepository>()
private val viewModel = LoginViewModel(repository)
@Test
fun loginWithValidCredentials_success() {
val email = "test@example.com"
val password = "ValidPass123"
whenever(repository.login(email, password))
.thenReturn(Result.success(User("token-xyz")))
val result = viewModel.login(email, password)
assertEquals(LoginState.Success, result)
verify(repository).login(email, password)
}
}
CI结果对全团队公开:每个人都能看到谁的提交破坏了构建。透明度创造了责任文化:开发者在push前检查自己的更改,并插队修复被破坏的构建。CI服务器在构建状态变化时通过Slack或Telegram发送通知。
一个完整的CI系统由多个相互交互的组件组成。每个组件负责管道的自身部分:从启动到报告。理解CI架构有助于诊断问题和优化性能。
管理构建队列、资源分配和结果发布的中央组件。CI服务器可以是基于云的(GitHub Actions, GitLab CI, CircleCI)或自托管的(Jenkins, TeamCity)。服务器通过webhook或polling跟踪仓库变化,并在每次push或pull request时启动管道。
Runner是执行构建任务的虚拟或物理机器。在云CI中,runner由提供商提供并按使用时间付费。自托管runner安装在自己的基础设施上并需要维护。iOS构建需要macOS runner,Android需要Linux或Windows。
构建后,CI系统将制品(APK、IPA、测试报告)保存在存储中——可供下载和部署。缓存依赖项(Gradle缓存、CocoaPods缓存)在运行之间使后续构建加速3–5倍。
| 组件 | 用途 | 示例 |
|---|---|---|
| CI服务器 | 构建编排 | Jenkins, GitHub Actions |
| Runner | 执行任务 | 用于iOS的macOS runner |
| 仓库 | 存储代码 | GitHub, GitLab |
| Artifact storage | 存储制品 | AWS S3, Artifactory |
| Notification | 通知团队 | Slack, Telegram, 邮件 |
移动开发对CI提出了特殊要求,不同于Web或后端项目。构建时间长(Android 3–15分钟,iOS 5–20分钟),多种制品类型(APK、AAB、IPA),需要签名和混淆——所有这些都需要单独配置CI管道。
Android的典型CI包括:linting(ktlint, detekt)和静态分析,使用JUnit和MockK进行单元测试,构建debug和release APK/AAB,在CI内的模拟器上进行仪器测试以及发布制品。Gradle缓存加速重复构建——没有它,每次构建都会重新下载依赖项,损失3–5分钟。
iOS CI需要macOS runner来编译Swift/Objective-C代码。管道包括:安装CocoaPods或SPM依赖项,SwiftLint用于样式检查,使用XCTest进行单元测试,构建IPA,通过Fastlane match签署证书并上传到TestFlight。数据中心中Mac mini或Mac上的自托管runner——云macOS runner的替代方案。
Flutter和React Native编译为两个平台的原生构建。CI必须支持两个runner:Linux用于Android构建,macOS用于iOS构建。最优策略——分离的管道:Linux runner上的Android构建,macOS runner上的iOS构建,之后两个制品合并为一个发布版本。
选择CI工具取决于团队规模、所需性能、预算和技术栈。以下是针对移动开发的流行解决方案比较。自托管解决方案提供控制但需要管理,基于云的解决方案提供便利但限制配置。
对公共仓库免费(2000分钟/月)。GitHub Actions提供适用于Android(gradle/actions)和iOS(apple-actions)的现成actions生态系统。缺点——macOS runner仅在付费方案中可用。适用于开源和已在使用GitHub的小团队。
开源的自托管CI服务器。Jenkins通过Groovy Pipeline配置,支持数百个插件,可在任何硬件上运行。需要DevOps工程师进行安装和维护。在企业领域流行,其中对基础设施的控制至关重要。
GitLab中内置的CI/CD,具有开放的runner架构。GitLab CI允许在免费方案中使用自己的runner(包括macOS)。YAML配置比GitHub Actions更强大,但更难学习。适合将GitLab作为单一DevOps平台使用的团队。
注重速度的云CI。CircleCI支持Docker、macOS和Android镜像,自动缓存依赖项。定价基于积分——对于小团队比GitHub Actions贵,但由于优化后的runner速度更快。推荐用于有速度要求的生产项目。
让我们看看使用GitHub Actions为Android项目配置CI。管道在每次push和pull request到main分支时执行静态分析、构建和测试。最小配置需要15分钟,不需要外部服务。
name: Android CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: 17
distribution: temurin
- run: ./gradlew ktlintCheck detekt
unit-tests:
needs: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: 17
distribution: temurin
- uses: gradle/actions/setup-gradle@v4
- run: ./gradlew testDebugUnitTest
- uses: actions/upload-artifact@v4
with:
name: test-results
path: app/build/reports/tests/
管道由两个并行job组成:lint(执行静态分析)和unit-tests(依赖于lint——如果linting失败,则不运行测试)。unit-tests job将测试报告作为制品上传——团队可以在GitHub Actions界面中查看,无需在本地下载文件。
为避免因琐碎错误导致CI失败,请在Git中配置pre-push hook或执行相同本地检查的Gradle任务。例如:./gradlew ktlintCheck detekt testDebugUnitTest。如果本地检查超过3分钟——将它们分为快速(linter)和慢速(测试),在每次提交前运行快速检查,仅在push前运行慢速检查。
常见问题
CI专注于代码的集成和验证(构建+测试),而CD增加了部署自动化。CI检查代码是否正确;CD确保正确的代码可以交付给用户。CI是CD的前提条件,但CD没有CI就无法工作。
最低频率——每个开发者每天一次。理想实践——在每个完成的逻辑工作单元(每1–4小时)推送到仓库。集成越频繁,冲突越少,解决越容易。如果集成间隔超过2天——你没有在使用CI。
对于Android,GitHub Actions(免费,易于配置)或GitLab CI(自己的runner)是最优选择。对于iOS——CircleCI(最好的macOS支持)或Bitrise(专门用于移动项目的CI)。对于跨平台——带有两个runner(Linux + macOS)的GitLab CI。
是的,但有保留。UI测试很慢(10–30分钟)且不稳定(flaky)。最优策略:在每次push时运行快速测试(单元+集成),在pull request、夜间或发布前运行UI测试。使用Device Farm或CI中的模拟器进行UI测试。
有效CI的指标:构建时间少于15分钟,绿色构建百分比高于85%,故障后平均恢复时间少于30分钟。如果构建经常失败——CI不是在帮助,而是在妨碍。审查测试:删除flaky测试,优化依赖项,缩短构建时间。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。