HarmonyOS应用《民族图鉴》开发第94篇:CI/CD流水线——自动化构建测试与发布

📖 引言
不知道你有没有过这样的经历:
- 发版日跟打仗一样,好几个人围着一台电脑,手动打包、手动测试、手动上传
- 改完代码合并到主分支,发现编译不过,所有人都等着修编译问题
- 上线前一晚加班到深夜,第二天还得盯着线上,生怕出问题
- 好不容易发完版,发现忘了跑测试,带着bug就上线了
- 每次发版都要找专人负责,这人请假了就没人会发版
这些问题的根源是什么?太多手动操作,人干了太多机器该干的活。
「民族图鉴」项目早期也是这样:
- 打包靠手动,打包一次要半小时
- 测试靠手点,发一次版要点30多个用例
- 发版靠人记,经常忘这忘那
- 出问题靠滚,手忙脚乱
后来我们引入了 CI/CD,把这些重复性的工作全都自动化了:
- 代码一提交,自动跑Lint、跑测试、自动打包
- 测试不通过,代码合并不了
- 发版一键搞定,不用守着电脑
- 出问题一键回滚,秒级恢复
这篇文章,我们就来聊聊「民族图鉴」的CI/CD实践。你会学到:
- 什么是CI/CD?它能解决什么问题?
- CI/CD流水线的核心环节有哪些?
- 「民族图鉴」的CI/CD方案怎么设计?
- 构建优化、质量门禁、发布策略怎么搞?
- 常见的坑和最佳实践
🎯 学习目标
完成本文后,你将能够:
- ✅ 理解CI/CD的核心概念与价值
- ✅ 掌握CI/CD流水线的核心环节
- ✅ 学会设计一套完整的CI/CD流水线
- ✅ 了解常用CI/CD工具选型(Huawei Cloud DevCloud / Jenkins / GitLab CI)
- ✅ 掌握构建优化技巧(增量编译、缓存、并行)
- ✅ 理解质量门禁的设计与落地
- ✅ 了解灰度发布、分阶段发布、回滚机制
- ✅ 避开CI/CD的常见坑
📖 引言
3.1 CI:持续集成(Continuous Integration)
持续集成是一种软件开发实践:团队成员频繁地将代码集成到主干,每次集成都通过自动化构建(编译+测试)来验证,从而尽早发现集成错误。
说人话就是:代码一提交,就自动构建、自动测,有问题立刻反馈。
为什么叫"持续集成"?
- 持续:不是一天一次,而是一天N次,每次提交都集成
- 集成:不是各写各的,而是频繁合并到一起
- 验证:不是合并完了才发现有问题,而是每次合并都自动验证
CI的好处:
- 🐛 早发现bug:刚写完就发现,改起来成本低
- 🧩 减少集成问题:不会到最后才发现"你的代码和我的合不到一起
- 🚀 加快迭代速度:不用等"集成日",随时可以发布
- ✅ 信心更足:知道改完有自动测试兜底
3.2 CD:持续交付 & 持续部署
CD有两层意思:
持续交付(Continuous Delivery):
持续交付是一种软件工程方法,软件的构建、测试和发布过程被设计成可以随时安全地部署到生产环境,发布由人工触发。
简单说:自动化到"随时可以发布,但发布需要人点一下。
持续部署(Continuous Deployment):
持续部署是持续交付的进阶,代码变更后自动部署到生产环境,不需要人工干预。
简单说:全自动化,代码一提交,自动上线。
三者的关系:
代码提交 → 构建 → 测试 → 预发布 → 生产
├───────────────────────────┤
CI(持续集成)
├───────────────────────────────┤
持续交付(人工审批后发布)
├───────────────────────────────────────┤
持续部署(全自动)
「民族图鉴」目前做到了持续交付——流水线一直到打包完成,发布需要人点一下。还没做到持续部署,因为App发布涉及应用市场审核,没法全自动。
3.3 鸿蒙应用CI/CD的特殊挑战
鸿蒙应用的CI/CD跟传统Android/iOS有什么不一样?为什么说鸿蒙的CI/CD有自己的特点?
我们来梳理一下鸿蒙应用CI/CD面临的特殊挑战:
挑战1:多HAP/多FA的构建复杂性
鸿蒙应用不是一个单一的安装包,而是由多个HAP(Harmony Ability Package)组成的:
.app 包
├── entry.hap(主入口)
├── ethnic.hap(民族模块)
├── music.hap(音乐模块)
├── game.hap(游戏模块)
└── settings.hap(设置模块)
每个HAP可以独立编译、独立运行、独立分发,这就带来了几个问题:
- 构建依赖更复杂:HAP之间有依赖关系,要按顺序构建
- 测试更复杂:每个HAP都有自己的测试,还要测集成
- 版本管理更复杂:各HAP版本可以独立演进,也可以统一发布
挑战2:签名机制更严格
鸿蒙应用的签名比Android更严格:
- 不仅App要签名,每个HAP也要签名
- 有Profile文件的限制(设备数量、有效期等)
- 调试签名和发布签名是两套体系
- 原子化服务还有特殊的签名要求
这意味着CI/CD流水线要管理更多的签名材料,签名环节更复杂。
挑战3:设备多样性
鸿蒙不是只有手机,而是"1+8+N"全场景设备:
| 设备类型 | 屏幕尺寸 | 系统能力 | 特殊适配 |
|---|---|---|---|
| 手机 | 6-7英寸 | 完整能力 | 正常适配 |
| 平板 | 10-13英寸 | 完整能力 | 大屏适配 |
| 折叠屏 | 可大可小 | 完整能力 | 折叠态适配 |
| 车机 | 8-15英寸 | 受限能力 | 驾驶场景适配 |
| 智能手表 | 1-2英寸 | 受限能力 | 小屏适配 |
| 智慧屏 | 55-85英寸 | 受限能力 | 遥控器交互 |
| IoT设备 | 无屏/小屏 | 极简能力 | 特殊适配 |
这意味着CI/CD流水线要:
- 针对不同设备类型分别构建
- 跑多设备的兼容性测试
- 验证各设备的功能完整性
挑战4:分布式能力验证难
鸿蒙的核心竞争力是分布式能力,但分布式能力的测试特别难:
- 多端协同需要多台设备
- 分布式数据管理需要验证数据同步
- 任务流转需要验证跨端迁移
- 超级终端需要设备组网
这些在CI环境里很难模拟,需要专门的分布式测试环境。
挑战5:原子化服务的特殊性
原子化服务是鸿蒙的特色,它跟传统App不一样:
- 免安装,直接使用
- 可以从服务中心、负一屏、扫码等多个入口进入
- 有独立的上架审核流程
- 卡片(Widget)是重要的展示形式
原子化服务的CI/CD需要额外考虑:
- 服务卡片的构建与测试
- 快速实例(Quick App)的构建
- 免安装场景的兼容性验证
- 服务发现与跳转的验证
「民族图鉴」的应对策略:
面对这些挑战,我们的做法是:
1. 模块化构建
按HAP拆分构建任务,增量构建只编改动的HAP
2. 签名材料统一管理
用密钥管理服务统一管理签名证书,流水线按需拉取
3. 分层测试策略
单元测试全跑,集成测试核心模块,UI测试抽关键路径
4. 真机测试农场
接入华为云测试服务,远程真机测试覆盖主流设备
5. 分布式能力人工验证
核心分布式场景走人工测试,确保质量
虽然鸿蒙CI/CD有这些特殊挑战,但整体思路跟传统App是一致的,只是有些环节需要针对鸿蒙特性做适配。
3.4 CI/CD的价值
为什么要花功夫搞CI/CD?因为ROI(投入产出比)太高了:
| 指标 | 没有CI/CD | 有CI/CD |
|---|---|---|
| 打包时间 | 30分钟/次 | 5分钟/次 |
| 测试时间 | 1小时/次 | 自动跑 |
| 发版频率 | 每月1次 | 每周1次 |
| 线上bug率 | 高(经常漏测) | 低(有自动化测试兜底) |
| 发版压力 | 大(加班到深夜) | 小(一键搞定) |
| 谁能发版 | 只有1个人会 | 所有人都会 |
「民族图鉴」引入CI/CD后,发版从"大动干戈"变成了"日常操作"。
💡 需求分析
一套完整的CI/CD流水线,通常包含这些环节:
代码提交
↓
触发构建
↓
代码检查(Lint + 类型检查)
↓
单元测试
↓
集成测试
↓
构建打包(Debug/Release)
↓
静态扫描(安全、性能、质量)
↓
产物归档
↓
发布(内测 → 公测 → 正式)
我们一个个来讲。
4.1 代码提交 → 触发构建
流水线的起点是触发器,什么情况下触发流水线?
**常见的触发方式:
| 触发方式 | 说明 | 适用场景 |
|---|---|---|
| 代码提交触发 | 每次 push 代码都触发 | 开发分支、CI验证 |
| PR/MR 触发 | 提交 Pull Request 时触发 | 代码合并前验证 |
| 定时触发 | 每天固定时间触发 | 每日构建、夜间构建 |
| 手动触发 | 人点一下才触发 | 发布、紧急修复 |
| 标签触发 | 打 tag 时触发 | 版本发布 |
「民族图鉴」的触发策略:
- feature分支:push 触发,跑Lint+单元测试
- develop分支:push 触发,跑全量+打包
- main分支:打 tag 触发,发布流水线
- 每日构建:每天凌晨2点,全量构建+测试
4.2 代码检查(Lint + 类型检查)
第一道关:代码质量检查。
**检查内容:
- Lint检查:代码风格、规范、最佳实践
- 类型检查:TypeScript/ArkTS类型错误
- 安全扫描:常见安全漏洞
- 性能检查:常见性能问题
「民族图鉴」用的是 DevEco 自带的 Lint 工具,配置在 code-linter.json5:
{
"files": ["**/*.ets"],
"ruleSet": [
"plugin:@performance/recommended",
"plugin:@typescript-eslint/recommended"
],
"rules": {
"@security/no-unsafe-aes": "error",
"@security/no-unsafe-hash": "error"
}
}
为什么把Lint放在最前面?
- 跑得快,几秒就出结果
- 代码风格问题越早改越好
- 不用等编译完才发现代码写得烂
4.3 单元测试
第二道关:单元测试。
单元测试是针对单个函数、单个类的测试,跑起来很快。
「民族图鉴」的单元测试:
- 工具函数测试:日期格式化、防抖节流、类型判断
- Service层测试:StorageService、ThemeService、MusicService
- 模型测试:数据模型的方法
// StorageService.test.ets 示例
import { describe, it, expect } from '@ohos/hypium';
import { StorageService } from '../services/StorageService';
describe('StorageService', () => {
it('should save and get string value', () => {
const storage = StorageService.getInstance();
storage.set('test_key', 'hello');
expect(storage.get('test_key')).assertEqual('hello');
});
});
单元测试的指标:
- 测试覆盖率:核心模块覆盖率要达到70%以上
- 执行时间:全部单测要在5分钟内跑完
4.4 集成测试
第三道关:集成测试。
集成测试是把多个模块放在一起测,验证模块间的交互是否正常。
比如:
- 页面跳转是否正常
- 路由是否正常
- 数据从Service到页面是否正常
- 组件组合使用是否正常
「民族图鉴」的集成测试:
- 页面路由测试
- 核心流程测试(启动→列表→详情→收藏→返回)
- 状态同步测试(收藏后各页面同步)
4.5 构建打包
测试都过了,就可以打包了。
构建类型:
| 构建类型 | 说明 | 用途 |
|---|---|---|
| Debug | 调试版,没混淆,有日志 | 开发、测试 |
| Release | 发布版,有混淆,无日志 | 正式发布 |
「民族图鉴」的构建配置在 build-profile.json5:
{
"app": {
"buildModeSet": [
{ "name": "debug" },
{ "name": "release" }
]
}
}
构建命令:
# Debug 构建
hvigorw assembleHap --mode debug
# Release 构建
hvigorw assembleHap --mode release
4.6 静态扫描
打包完了,还要做静态扫描,从各个维度检查包的质量。
扫描维度:
| 扫描类型 | 检查内容 |
|---|---|
| 安全扫描 | 敏感信息泄露、加密算法、权限问题 |
| 性能扫描 | 包体积、启动时间、内存占用 |
| 质量扫描 | 代码复杂度、重复率、坏味道 |
| 合规扫描 | 隐私合规、权限声明 |
HarmonyOS 官方有 HUAWEI DevEco Testing 工具,可以做静态扫描。
4.7 产物归档
构建产物(HAP包、APP包)要归档保存起来,方便追溯和回滚。
归档内容:
- 安装包(.hap / .app)
- 构建日志
- 测试报告
- 符号表(用于崩溃分析)
归档位置:
- 按版本号存放
- 保留最近10个版本
- 重要版本永久保存
4.8 发布
最后一步:发布。
发布不是一上来就全量,而是分阶段:
内测版 → 公测版 → 正式版
↓ ↓ ↓
内部员工 少量用户 全部用户
每一步都观察数据,没问题再往下走。
💡 需求分析
5.1 工具选型
常见的CI/CD工具有很多,怎么选?
| 工具 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| **Huawei Cloud DevCloud | 鸿蒙生态好,和DevEco集成好 | 功能相对少 | 鸿蒙项目、小团队 |
| Jenkins | 灵活、插件多、生态成熟 | 配置复杂、需要自己搭 | 大团队、定制化需求多 |
| GitLab CI | 和GitLab集成好、配置简单 | 需要GitLab | 用GitLab的团队 |
| GitHub Actions | 免费、和GitHub集成好 | 国内访问可能慢 | 开源项目、小项目 |
「民族图鉴」选的是 Huawei Cloud DevCloud,原因:
- 鸿蒙生态最好,官方支持
- 和 DevEco Studio 集成好,IDE里就能看
- 小团队够用了,不用自己搭
- 有免费额度
当然,如果你团队用的是其他工具,思路是一样的,只是配置语法不同而已。
5.2 分支策略与流水线设计
CI/CD跟分支策略是紧密相关的。
「民族图鉴」用的是 Git Flow 的简化版:
main分支(生产环境)
↑ 合并(打tag发布)
develop分支(开发环境)
↑ 合并(功能开发完了合并
feature/xxx分支(功能分支)
↑ 开发
对应三条流水线:
流水线1:Feature 分支流水线
- 触发:feature 分支 push 代码
- 环节:Lint → 单元测试
- 目的:快速验证代码质量
流水线2:Develop 分支流水线
- 触发:develop 分支 push 代码 / PR合并
- 环节:Lint → 单元测试 → 集成测试 → Debug构建
- 目的:保证开发分支的质量
流水线3:Main 分支发布流水线
- 触发:main 分支打 tag(v1.x.x
- 环节:Lint → 单元测试 → 集成测试 → Release构建 → 静态扫描 → 产物归档 → 发布
- 目的:正式发布
5.3 流水线配置示例
以 DevCloud 为例,流水线配置大概长这样:
# devcloud-pipeline.yml
name: 民族图鉴CI流水线
triggers:
- type: push
branches:
- feature/*
- develop
- main
stages:
- stage: 代码检查
jobs:
- job: lint
steps:
- name: 代码检出
action: checkout
- name: 安装依赖
action: shell
command: hvigorw clean
- name: Lint检查
action: shell
command: hvigorw lint
- name: 类型检查
action: shell
command: hvigorw typeCheck
- stage: 测试
jobs:
- job: unit_test
steps:
- name: 单元测试
action: shell
command: hvigorw test --mode unit
- name: 测试报告
action: publish_test_report
report_path: build/reports/test/
- name: 覆盖率检查
action: check_coverage
threshold: 70%
- stage: 构建
jobs:
- job: build_debug
when: branch == 'develop'
steps:
- name: Debug构建
action: shell
command: hvigorw assembleHap --mode debug
- name: 产物归档
action: archive
path: build/outputs/default/outputs/default/
- job: build_release
when: tag starts with 'v'
steps:
- name: Release构建
action: shell
command: hvigorw assembleApp --mode release
- name: 产物归档
action: archive
path: build/outputs/default/outputs/default/
- stage: 扫描
jobs:
- job: static_scan
when: tag starts with 'v'
steps:
- name: 静态扫描
action: static_scan
type: ['security', 'performance', 'quality']
- name: 扫描报告
action: publish_scan_report
- stage: 发布
jobs:
- job: publish_internal
when: tag starts with 'v'
steps:
- name: 上传到应用市场(内测)
action: publish_appgallery
type: internal
- job: publish_beta
needs: publish_internal
steps:
- name: 手动审批
action: manual_approve
- name: 上传到应用市场(公测)
action: publish_appgallery
type: beta
- job: publish_release
needs: publish_beta
steps:
- name: 手动审批
action: manual_approve
- name: 上传到应用市场(正式)
action: publish_appgallery
type: release
💡 不同工具的配置语法不一样,但核心环节是一样的:检出代码→安装依赖→跑检查→跑测试→构建→扫描→发布。
5.4 触发规则详解
1. PR触发(代码合并前)
提交PR的时候自动跑流水线,测试不通过不能合并。
开发提PR → 自动跑CI → 通过才能合并
好处:
- 不会把烂代码合进去
- Code Review的时候已经过了CI,Review只看逻辑
- 主分支一直是好的
2. 定时构建
每天凌晨自动跑一遍全量构建+测试。
好处:
- 发现一些偶现的问题
- 保证代码库一直是可构建的
- 每天早上来就能看到结果
3. 手动触发
随时可以手动点一下跑流水线。
适用场景:
- 发布版本
- 紧急修复
- 验证某些特殊情况
🛠️ 核心实现
CI/CD的核心价值之一就是自动化测试。没有自动化测试的CI/CD,只能叫"自动打包",不能叫"持续集成"。
6.1 测试金字塔
测试不是越多越好,而是要分层,形成一个测试金字塔:
/\
/ \
/ E2E \ 端到端测试:少量、核心场景
/--------\
/ 集成测试 \ 集成测试:适量、模块间交互
/------------\
/ 单元测试 \ 单元测试:大量、快速、稳定
/________________\
各层测试的特点:
| 测试类型 | 数量 | 速度 | 稳定性 | 成本 | 覆盖粒度 |
|---|---|---|---|---|---|
| 单元测试 | 多 | 快 | 高 | 低 | 函数/类级别 |
| 集成测试 | 中 | 中 | 中 | 中 | 模块级别 |
| UI/E2E测试 | 少 | 慢 | 低 | 高 | 用户场景级别 |
「民族图鉴」的测试配比:
- 单元测试:70%(工具函数、Service、模型)
- 集成测试:20%(页面路由、状态同步)
- UI/E2E测试:10%(核心用户流程)
6.2 单元测试集成
单元测试是金字塔的底座,也是最容易集成到CI/CD的。
单元测试的要求:
- 每个工具函数都要有测试
- Service层的核心方法要有测试
- 复杂的业务逻辑要有测试
- 边界条件要有测试
「民族图鉴」的单元测试框架:
- 测试框架:
@ohos/hypium(鸿蒙官方测试框架) - 断言库:hypium 内置
- 覆盖率工具:DevEco Testing
// 测试用例示例
import { describe, it, expect, beforeAll, afterAll } from '@ohos/hypium';
import { DateUtils } from '../utils/DateUtils';
describe('DateUtils', () => {
// 测试用例1:格式化日期
it('should format date correctly', () => {
const date = new Date(2024, 0, 1); // 2024-01-01
const result = DateUtils.format(date, 'YYYY-MM-DD');
expect(result).assertEqual('2024-01-01');
});
// 测试用例2:边界条件 - 空值
it('should return empty string when date is null', () => {
const result = DateUtils.format(null, 'YYYY-MM-DD');
expect(result).assertEqual('');
});
// 测试用例3:边界条件 - 非法日期
it('should return empty string when date is invalid', () => {
const result = DateUtils.format(new Date('invalid'), 'YYYY-MM-DD');
expect(result).assertEqual('');
});
});
CI中的单元测试:
- 每次代码提交都跑单元测试
- 测试不通过,不让合并
- 覆盖率不达标,不让发布
- 测试报告归档,方便追溯
6.3 UI测试集成
UI测试(端到端测试)是模拟用户真实操作的测试,成本高、速度慢,但能发现很多单元测试发现不了的问题。
「民族图鉴」的UI测试策略:
- 只测核心流程,不测边角料
- 用自动化测试框架 + 人工测试结合
- 关键路径:启动 → 列表 → 详情 → 收藏 → 返回
- 每个大版本前跑一遍全量UI测试
鸿蒙UI测试工具:
- DevEco Testing:官方测试工具
- UiAutomator:UI自动化测试框架
- 华为云测试服务:远程真机测试
6.4 性能测试集成
性能测试不能等到上线前才测,要集成到CI/CD里,每次构建都测。
性能测试的指标:
- 启动时间:冷启动、热启动
- 页面加载速度:列表页、详情页
- 内存占用:峰值内存、内存泄漏
- 包体积:HAP大小、APP大小
- 帧率:滑动流畅度
「民族图鉴」的性能门禁:
| 指标 | 阈值 | 说明 |
|---|---|---|
| 冷启动时间 | ≤ 2s | 从点击图标到首页可交互 |
| 热启动时间 | ≤ 500ms | 从后台切到前台 |
| 列表页加载 | ≤ 500ms | 进入列表页到数据展示 |
| 详情页加载 | ≤ 300ms | 进入详情页到内容展示 |
| 包体积(手机) | ≤ 50MB | 主HAP + 核心HAP |
| 内存峰值 | ≤ 200MB | 正常使用场景 |
性能测试集成到CI里的好处:
- 每次构建都知道性能有没有退化
- 发现性能问题立刻定位是哪次提交导致的
- 防止性能越变越差
6.5 测试覆盖率门禁
测试覆盖率不是越高越好,但太低肯定不行。
覆盖率的几个指标:
- 行覆盖率:有多少行代码被执行过
- 分支覆盖率:有多少if/else分支被走过
- 函数覆盖率:有多少函数被调用过
- 语句覆盖率:有多少语句被执行过
「民族图鉴」的覆盖率要求:
- 工具函数:≥ 90%
- Service层:≥ 80%
- 整体:≥ 70%
- UI组件:不做硬性要求(难测)
💡 覆盖率是手段不是目的。为了凑覆盖率写的测试没有意义。重点是核心逻辑要有测试覆盖。
🛠️ 核心实现
流水线跑太慢?构建优化很重要。等流水线的时间,就是浪费生命。
「民族图鉴」的构建优化前后对比:
| 指标 | 优化前 | 优化后 | 提升 |
|---|---|---|---|
| 全量构建 | 8分30秒 | 3分20秒 | 快了60% |
| 增量构建 | 5分10秒 | 1分30秒 | 快了71% |
| 单元测试 | 3分20秒 | 40秒 | 快了80% |
怎么做到的?我们用了这些手段:
6.1 增量编译
最基础也是最有效的优化:增量编译。
- 全量编译:所有文件都重新编一遍
- 增量编译:只编译改动的文件和它影响的文件
HarmonyOS 的 hvigor 构建工具本身就支持增量编译,只要你别每次都 clean 就行。
# ❌ 不要每次都clean,全量编译慢
hvigorw clean
hvigorw assembleHap
# ✅ 不clean,增量编译快
hvigorw assembleHap
6.2 构建缓存
把不变的东西缓存起来,下次直接用。
**可以缓存的东西:
- 依赖包(oh_modules)
- 编译中间产物
- 测试结果(没改的代码不用重测)
**DevCloud/Jenkins 都支持缓存配置:
# 缓存配置示例
cache:
paths:
- oh_modules/
- .hvigor/
- build/intermediates/
key: "${CI_COMMIT_REF_SLUG}-${hash('oh-package.json5')}
6.3 并行构建
把能并行的任务并行跑,不要排队等。
**可以并行的:
- 不同模块的构建并行
- 不同类型的测试并行
- Lint和测试并行
传统方式:串行
Lint → 单测 → 集成测试 → 构建
总耗时 = 2分 + 3分 + 5分 + 5分 = 15分
优化方式:并行
┌────────┐
│ Lint │ → 2分
├──────┤
│ 单测 │ → 3分
├──────┤
└──────┘
↓
集成测试 → 5分
↓
构建 → 5分
总耗时 = max(2,3) + 5 + 5 = 13分
6.4 按需构建
不是每次都跑全量,根据改动的内容决定跑哪些。
比如:
- 只改了文档,不用跑测试和构建
- 只改了民族模块,不用测音乐模块
- 只改了工具函数,只跑相关测试
# 按需触发规则:
- 改动 .md 文件 → 只跑文档检查
- 改动 common 模块 → 跑全量测试
- 改动 ethnic 模块 → 只跑民族模块的测试
- 改动 music 模块 → 只跑音乐模块的测试
6.5 优化依赖管理
依赖不要乱加,每个依赖都会增加构建时间。
- 去掉不用的依赖
- 用轻量的依赖
- 统一依赖版本,避免版本冲突
🛠️ 核心实现
流水线不能只是"跑一下就行",要设质量门禁——达不到标准就不让过。
7.1 什么是质量门禁
质量门禁(Quality Gate)是一套自动化的质量检查标准,代码必须达到这些标准才能进入下一个环节。
说人话就是:达不到标准,不让过。
比如:
- Lint 错误数 > 0 → 不让合并
- 单元测试通过率 < 100% → 不让合并
- 测试覆盖率 < 70% → 不让发布
- 安全扫描有高危漏洞 → 不让发布
7.2 「民族图鉴」的质量门禁
| 门禁 | 检查项 | 阈值 | 失败后果 |
|---|---|---|---|
| 代码风格 | Lint错误数 | = 0 | 不让合并 |
| 单元测试 | 测试通过率 | = 100% | 不让合并 |
| 单元测试 | 覆盖率 | ≥ 70% | 不让发布 |
| 安全扫描 | 高危漏洞数 | = 0 | 不让发布 |
| 性能扫描 | 启动时间 | ≤ 2s | 警告 |
| 包体积 | APK/HAP大小 | ≤ 50MB | 警告 |
7.3 门禁设置的原则
1. 严格但合理
不要太严,太严了大家都受不了;也不要太松,太松了没用。
- 错误级别的:必须0容忍(比如Lint错误、高危漏洞)
- 警告级别的:可以适当放宽(比如代码风格建议)
- 覆盖率:核心模块高要求,非核心可以低一点
2. 逐步收紧
一开始别一下子把标准定太高,大家都过不了,就没人用了。
- 第一阶段:先有,保证能跑通
- 第二阶段:加Lint门禁,错误0容忍
- 第三阶段:加测试门禁,通过率100%
- 第四阶段:加覆盖率门禁,从50%→60%→70%
一步步来,让大家有个适应过程。
3. 质量不是目的,是手段
门禁是为了保证质量,不是为了卡人。
- 真有特殊情况,可以申请豁免
- 定期复盘门禁规则,不合理的就改
- 帮助大家达到标准,而不是只说"你不行"
🛠️ 核心实现
发布上线是最后一步,也是最关键的一步。出了问题影响的是用户。
8.1 灰度发布
灰度发布(金丝雀发布)是指让一部分用户先用新版本,没问题再逐步扩大范围。
为什么叫金丝雀?
- 以前矿工下井,会带一只金丝雀
- 金丝雀对瓦斯敏感,有问题先死
- 看到金丝雀死了,就知道有危险,赶紧撤
灰度发布就是这个思路:
- 先给少量用户用新版本
- 没问题,再扩大范围
- 有问题,立刻停止,影响面小
「民族图鉴」的灰度策略:
阶段1:内部测试(0% → 内部员工)
↓ 1-2天,没问题
阶段2:小流量灰度(1% → 5% → 10%)
↓ 2-3天,没问题
阶段3:逐步放量(20% → 50% → 100%)
↓ 全量发布
每个阶段观察的指标:
- 崩溃率:有没有升高
- 错误率:有没有报错
- 启动时间:有没有变慢
- 用户反馈:有没有投诉
**灰度的好处:
- ✅ 风险小:出问题只影响少量用户
- ✅ 发现早:有问题早发现,不至于全量了才发现
- ✅ 有缓冲:可以慢慢放量,心里踏实
8.2 分阶段发布
除了按用户比例灰度,还可以按渠道、按地区分阶段发布。
比如:
- 先在应用市场的"内测版"发布
- 没问题再上"公测版"
- 没问题再上"正式版"
或者:
- 先在国内发布
- 没问题再发海外
8.2 语义化版本管理
版本号怎么定?随便拍脑袋可不行。推荐用语义化版本(Semantic Versioning)。
语义化版本的格式:MAJOR.MINOR.PATCH
v1.2.3
│ │ │
│ │ └─ PATCH:bug修复,不影响功能(向后兼容)
│ └──── MINOR:新增功能,向后兼容
└─────── MAJOR:不兼容的API改动( Breaking Change)
举例说明:
| 版本变化 | 原因 | 示例 |
|---|---|---|
| PATCH 升级 | 修复bug、优化性能 | v1.2.3 → v1.2.4 |
| MINOR 升级 | 新增民族、新增功能模块 | v1.2.3 → v1.3.0 |
| MAJOR 升级 | 架构重构、不兼容升级 | v1.2.3 → v2.0.0 |
「民族图鉴」的版本管理规范:
版本号格式:v{MAJOR}.{MINOR}.{PATCH}
发布周期:
- PATCH版本:随时可以发,修复bug
- MINOR版本:每2周一个迭代,新增功能
- MAJOR版本:每季度/半年一个大版本,重大升级
版本标签:
- alpha:内测版,功能可能不完整
- beta:公测版,基本稳定
- rc:候选版,准备发布
- release:正式版
版本号的作用:
- 用户知道这次更新有多大
- 开发知道能不能直接升级
- 运营知道怎么写更新日志
- 回滚知道回退到哪个版本
8.3 蓝绿部署 & 金丝雀发布
除了灰度发布,还有两种常见的发布策略:
1. 蓝绿部署(Blue-Green Deployment)
蓝绿部署的思路是:准备两套完全一样的环境,一套在跑(蓝),一套准备新版本(绿),准备好了直接切流量。
发布前:
用户 → 蓝环境(v1.0)
发布中:
绿环境部署v2.0,测试验证
发布后(切换流量):
用户 → 绿环境(v2.0)
蓝环境保留,随时可以回滚
回滚:
切回蓝环境,秒级回滚
蓝绿部署的优点:
- ✅ 回滚快,秒级切换
- ✅ 发布过程用户无感知
- ✅ 新版本可以充分测试再切流量
缺点:
- ❌ 需要两套环境,成本高
- ❌ 数据库兼容是难题(新旧版本数据结构可能不一样)
适用场景:
- 服务端应用(后端服务)
- 有冗余资源的团队
- 对可用性要求极高的场景
💡 对于App来说,蓝绿部署不太适用,因为App是安装在用户手机上的,不能像服务端那样切流量。App更常用的是灰度发布。
2. 金丝雀发布(Canary Release)
金丝雀发布其实就是灰度发布的另一种说法,核心思想一样:先让一小部分用户用新版本,没问题再逐步扩大。
阶段1:1%用户用v2.0,99%用户用v1.0
↓ 观察数据,没问题
阶段2:10%用户用v2.0,90%用户用v1.0
↓ 观察数据,没问题
阶段3:50%用户用v2.0,50%用户用v1.0
↓ 观察数据,没问题
阶段4:100%用户用v2.0
金丝雀发布的关键指标:
- 崩溃率:新版本崩溃率不能高于旧版本
- 错误率:接口错误率、异常率
- 性能指标:启动时间、页面加载速度
- 业务指标:留存率、转化率、使用时长
- 用户反馈:评论、投诉、客服反馈
「民族图鉴」的发布策略选型:
| 发布策略 | 适用场景 | 我们怎么用 |
|---|---|---|
| 灰度发布 | App发布 | 主要方式,按用户比例逐步放量 |
| 蓝绿部署 | 服务端 | 后端API用蓝绿部署 |
| 热修复 | 紧急bug修复 | 小问题热修复,不用发版 |
| 强制更新 | 严重问题/大版本升级 | 关键bug修复时强制更新 |
不同场景用不同策略,组合起来用效果最好。
8.4 回滚机制
万一发布后发现大问题,要能快速回滚。
回滚方式:
| 回滚方式 | 速度 | 适用场景 |
|---|---|---|
| 应用市场撤回 | 几分钟 | 大问题、必须马上撤 |
| 版本降级 | 几小时 | 问题不大,慢慢降回去 |
| 热修复 | 几分钟 | 小问题,热修复就行 |
回滚触发条件:
- 崩溃率 > 1%
- 核心功能不可用
- 严重安全漏洞
- 大量用户投诉
回滚流程:
- 发现问题
- 评估严重程度
- 决定回滚
- 执行回滚
- 通知用户
- 复盘原因
⚠️ 常见问题与解决方案
Q1:构建太慢了,等得人都凉了怎么办?
A:优化!从这几个方面入手:
- 增量编译:不要每次都clean
- 构建缓存:依赖、中间产物缓存起来
- 并行构建:能并行的都并行
- 按需构建:只跑改动相关的
- 优化依赖:去掉没用的依赖
- 升级机器:配置高点,CPU内存大点
如果还是慢,那就……多喝热水,等一等。
Q2:测试不稳定,时好时坏怎么办?
A:不稳定的测试比没有测试还糟糕。
不稳定的测试(Flaky Test)——有时候过有时候不过,不知道是真有问题还是测试本身有问题。
怎么办?
- 找出不稳定的测试,标记出来
- 修复不稳定的原因(依赖时间、依赖网络、依赖顺序)
- 给不稳定的测试单独跑,不影响主流水线
- 定期清理长期不稳定的测试
Q3:发布事故了怎么办?
A:别慌,按流程来:
- 先止血:能回滚先回滚,把影响降到最低
- 查原因:定位问题,找到根因
- 修复:改bug,测试验证
- 复盘:为什么会发生?怎么防止下次再发生?
- 改进:加测试、加监控、加门禁
重要的是:发布事故不可怕,可怕的是同样的事故反复发生。
Q4:团队抵触CI/CD,觉得麻烦怎么办?
A:正常,改变习惯总是不舒服。
- 从小处着手:先搞最简单的(自动打包),让大家尝到甜头
- 用数据说话:以前打包30分钟,现在5分钟,省下来的时间干嘛不好?
- 减少痛苦:流水线尽量少打断开发,跑的快一点,别让大家等太久
- 共建:让大家一起参与配置,不是强加的是大家一起搞的
Q5:CI/CD配置太复杂,搞不定怎么办?
A:从简单开始,一步步来。
不要一开始就搞个大而全的,先搞最简单的版本:
- 第一步:能自动打包就行
- 第二步:加个Lint检查
- 第三步:加单元测试
- 第四步:加发布…
先跑起来最重要,然后慢慢完善。能用的简单流水线,好过完美但用不起来的流水线。
📝 本章小结
这篇文章讲了「民族图鉴」的CI/CD实践,核心要点:
-
什么是CI/CD:
- CI(持续集成):代码一提交就自动构建测试
- CD(持续交付/持续部署):自动发布到生产环境
-
流水线核心环节:
代码提交 → 触发 → Lint → 测试 → 构建 → 扫描 → 归档 → 发布 -
「民族图鉴」方案:
- 工具:Huawei Cloud DevCloud
- 三条流水线:feature分支(快速验证)、develop分支(开发验证)、main分支(正式发布)
- 触发方式:push触发、PR触发、定时触发、手动触发
-
构建优化:增量编译、构建缓存、并行构建、按需构建、依赖管理
-
质量门禁:Lint零错误、测试100%通过、覆盖率达标、安全零漏洞
-
发布策略:灰度发布、分阶段发布、回滚机制
CI/CD不是目的,是手段。目的是让开发更高效、发布更安全、质量更有保障。
更多推荐

所有评论(0)