在这里插入图片描述

📖 引言

不知道你有没有过这样的经历:

  • 发版日跟打仗一样,好几个人围着一台电脑,手动打包、手动测试、手动上传
  • 改完代码合并到主分支,发现编译不过,所有人都等着修编译问题
  • 上线前一晚加班到深夜,第二天还得盯着线上,生怕出问题
  • 好不容易发完版,发现忘了跑测试,带着bug就上线了
  • 每次发版都要找专人负责,这人请假了就没人会发版

这些问题的根源是什么?太多手动操作,人干了太多机器该干的活。

「民族图鉴」项目早期也是这样:

  • 打包靠手动,打包一次要半小时
  • 测试靠手点,发一次版要点30多个用例
  • 发版靠人记,经常忘这忘那
  • 出问题靠滚,手忙脚乱

后来我们引入了 CI/CD,把这些重复性的工作全都自动化了:

  • 代码一提交,自动跑Lint、跑测试、自动打包
  • 测试不通过,代码合并不了
  • 发版一键搞定,不用守着电脑
  • 出问题一键回滚,秒级恢复

这篇文章,我们就来聊聊「民族图鉴」的CI/CD实践。你会学到:

  1. 什么是CI/CD?它能解决什么问题?
  2. CI/CD流水线的核心环节有哪些?
  3. 「民族图鉴」的CI/CD方案怎么设计?
  4. 构建优化、质量门禁、发布策略怎么搞?
  5. 常见的坑和最佳实践

🎯 学习目标

完成本文后,你将能够:

  • ✅ 理解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,原因:

  1. 鸿蒙生态最好,官方支持
  2. 和 DevEco Studio 集成好,IDE里就能看
  3. 小团队够用了,不用自己搭
  4. 有免费额度

当然,如果你团队用的是其他工具,思路是一样的,只是配置语法不同而已。

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%
  • 核心功能不可用
  • 严重安全漏洞
  • 大量用户投诉

回滚流程:

  1. 发现问题
  2. 评估严重程度
  3. 决定回滚
  4. 执行回滚
  5. 通知用户
  6. 复盘原因

⚠️ 常见问题与解决方案

Q1:构建太慢了,等得人都凉了怎么办?

A:优化!从这几个方面入手:

  1. 增量编译:不要每次都clean
  2. 构建缓存:依赖、中间产物缓存起来
  3. 并行构建:能并行的都并行
  4. 按需构建:只跑改动相关的
  5. 优化依赖:去掉没用的依赖
  6. 升级机器:配置高点,CPU内存大点

如果还是慢,那就……多喝热水,等一等。

Q2:测试不稳定,时好时坏怎么办?

A:不稳定的测试比没有测试还糟糕。

不稳定的测试(Flaky Test)——有时候过有时候不过,不知道是真有问题还是测试本身有问题。

怎么办?

  1. 找出不稳定的测试,标记出来
  2. 修复不稳定的原因(依赖时间、依赖网络、依赖顺序)
  3. 给不稳定的测试单独跑,不影响主流水线
  4. 定期清理长期不稳定的测试

Q3:发布事故了怎么办?

A:别慌,按流程来:

  1. 先止血:能回滚先回滚,把影响降到最低
  2. 查原因:定位问题,找到根因
  3. 修复:改bug,测试验证
  4. 复盘:为什么会发生?怎么防止下次再发生?
  5. 改进:加测试、加监控、加门禁

重要的是:发布事故不可怕,可怕的是同样的事故反复发生。

Q4:团队抵触CI/CD,觉得麻烦怎么办?

A:正常,改变习惯总是不舒服。

  • 从小处着手:先搞最简单的(自动打包),让大家尝到甜头
  • 用数据说话:以前打包30分钟,现在5分钟,省下来的时间干嘛不好?
  • 减少痛苦:流水线尽量少打断开发,跑的快一点,别让大家等太久
  • 共建:让大家一起参与配置,不是强加的是大家一起搞的

Q5:CI/CD配置太复杂,搞不定怎么办?

A:从简单开始,一步步来。

不要一开始就搞个大而全的,先搞最简单的版本:

  1. 第一步:能自动打包就行
  2. 第二步:加个Lint检查
  3. 第三步:加单元测试
  4. 第四步:加发布…

先跑起来最重要,然后慢慢完善。能用的简单流水线,好过完美但用不起来的流水线。


📝 本章小结

这篇文章讲了「民族图鉴」的CI/CD实践,核心要点:

  1. 什么是CI/CD

    • CI(持续集成):代码一提交就自动构建测试
    • CD(持续交付/持续部署):自动发布到生产环境
  2. 流水线核心环节
    代码提交 → 触发 → Lint → 测试 → 构建 → 扫描 → 归档 → 发布

  3. 「民族图鉴」方案

    • 工具:Huawei Cloud DevCloud
    • 三条流水线:feature分支(快速验证)、develop分支(开发验证)、main分支(正式发布)
    • 触发方式:push触发、PR触发、定时触发、手动触发
  4. 构建优化:增量编译、构建缓存、并行构建、按需构建、依赖管理

  5. 质量门禁:Lint零错误、测试100%通过、覆盖率达标、安全零漏洞

  6. 发布策略:灰度发布、分阶段发布、回滚机制

CI/CD不是目的,是手段。目的是让开发更高效、发布更安全、质量更有保障。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐