欢迎光临
我们一直在努力

鸿蒙APP CI/CD流水线搭建:从代码提交到上架全自动

鸿蒙APP CI/CD流水线搭建:从代码提交到上架全自动

在鸿蒙应用开发领域,持续集成与持续部署(CI/CD)已成为推动项目高效进展、保障应用质量的核心动力。随着鸿蒙生态的蓬勃发展,应用功能日益复杂,用户对应用的更新速度与稳定性要求不断攀升——但现实中,很多团队仍然停留在“开发人员本地打包→压缩包传群→测试人员手动安装”的原始阶段。

这种模式的痛点显而易见:

  • 重复性操作:每次迭代需打开DevEco Studio手动打包,效率低下
  • 环境不一致:不同开发者的本地环境差异导致“我这边能跑,你那不行”
  • 签名混乱:测试签名和正式签名混用,甚至出现过用测试证书上线的惨案
  • 发布延迟:无法实时响应代码提交,需人工触发打包,影响测试与发布节奏

而CI/CD流水线正是解决这些问题的核心方案。本文将手把手教你搭建从代码提交到应用上架的全自动化流水线,涵盖Jenkins/GitLab CI集成、自动化签名与版本管理、灰度发布与A/B测试配置三大核心模块。

一、鸿蒙CI/CD整体架构

1.1 什么是CI/CD?

  • 持续集成(CI):开发人员频繁地将代码合并到主分支,通过自动化工具运行构建和测试,验证新代码是否破坏现有功能
  • 持续交付(CD):将通过测试的应用版本自动化部署到测试环境或生产环境

1.2 鸿蒙应用构建的核心工具

鸿蒙应用构建离不开两个核心工具:

工具作用说明
hvigorw 鸿蒙官方构建工具 替代Gradle,用于编译、打包HAP/APP,支持命令行调用
ohpm 鸿蒙包管理器 管理项目依赖,类似npm

hvigorw是搭建流水线时的核心——少了它,流水线就无法正常运行。它让我们可以使用命令行工具来调用Hvigor任务进行构建,不再依赖DevEco Studio开发工具。

1.3 流水线整体流程

[代码提交] → [触发CI] → [环境准备] → [依赖安装] → [代码检查] → [单元测试]
↓
[构建打包] → [自动化签名] → [产物归档] → [灰度发布] → [全量上架]
↓
[通知反馈](邮件/钉钉/企业微信)

二、Jenkins/GitLab CI集成方案

2.1 环境准备:搭建自动化构建基础

在开始配置CI/CD工具前,需先准备好基础环境。推荐使用Linux(Ubuntu 20.04+)或macOS(鸿蒙打包对Windows兼容性稍弱)。

2.1.1 必备工具清单
工具名称作用说明推荐版本
JDK 鸿蒙构建的基础Java环境 11(LTS)
HarmonyOS SDK 鸿蒙应用开发与打包核心工具集 API 10+
Node.js 用于运行构建脚本 18+
hvigor 鸿蒙官方构建工具 3.1.0+
ohpm 鸿蒙包管理器 随SDK安装
Git 代码版本控制 2.30+
Jenkins/GitLab CI/CD核心工具 Jenkins 2.400+ / GitLab 15.0+
2.1.2 环境变量配置

所有工具安装完成后,需配置全局环境变量,确保CI系统能调用相关命令:

# 配置 JDK
export JAVA_HOME=/usr/lib/jvm/jdk-11-oracle
export PATH=$JAVA_HOME/bin:$PATH

# 配置鸿蒙 SDK(关键:指向DevEco Studio中的SDK路径)
export HARMONYOS_SDK_HOME=~/DevEcoStudio/sdk
export PATH=$HARMONYOS_SDK_HOME/toolchains/hvigor/bin:$PATH

# 配置 hvigor
export HVIGOR_HOME=~/DevEcoStudio/sdk/toolchains/hvigor
export PATH=$HVIGOR_HOME/bin:$PATH

# 配置 ohpm
export OHPM_HOME=~/DevEcoStudio/sdk/toolchains/ohpm
export PATH=$OHPM_HOME/bin:$PATH

配置完成后,执行source /etc/profile生效,并通过以下命令验证环境是否正常:

java -version # 应输出JDK 11版本信息
hvigor -v # 应输出hvigor版本信息
ohpm -v # 应输出ohpm版本信息

2.2 Jenkins集成方案

Jenkins是目前业界主流的CI/CD工具,通过插件扩展实现丰富的功能。

2.2.1 Jenkins插件安装

登录Jenkins(默认地址:http://服务器IP:8080),进入「系统管理 → 插件管理 → 可选插件」,安装以下插件:

  • Git Plugin:用于拉取Git仓库代码
  • Pipeline Plugin:支持通过Jenkinsfile定义自动化流程(推荐)
  • Huawei Cloud Pipeline Plugin:可选,简化与华为云服务的集成
2.2.2 配置全局工具

进入「系统管理 → 全局工具配置」,将本地安装的JDK、hvigor关联到Jenkins:

配置JDK:

  • 取消「自动安装」,选择「手动安装」
  • 「名称」填写JDK_11
  • 「JAVA_HOME」填写本地JDK路径(如/usr/lib/jvm/jdk-11-oracle)

配置hvigor:

  • 取消「自动安装」,选择「手动安装」
  • 「名称」填写hvigor_3.1
  • 「HVIGOR_HOME」填写本地hvigor路径(如~/DevEcoStudio/sdk/toolchains/hvigor)
2.2.3 Jenkins Pipeline脚本

在项目根目录创建Jenkinsfile,定义完整的CI/CD流程:

pipeline {
agent any

// 环境变量
environment {
// 从Jenkins凭据中获取签名密码
RELEASE_STORE_PWD = credentials('harmony-release-store-pwd')
RELEASE_KEY_PWD = credentials('harmony-release-key-pwd')

// 版本号相关
VERSION_CODE = "${env.BUILD_NUMBER}"
VERSION_NAME = "1.0.${env.BUILD_NUMBER}"
}

stages {
stage('代码检出') {
steps {
checkout scm
}
}

stage('安装依赖') {
steps {
sh 'ohpm install'
}
}

stage('代码静态检查') {
steps {
sh 'hvigor lint'
}
}

stage('单元测试') {
steps {
sh 'hvigor test'
}
post {
always {
junit '**/build/reports/tests/*.xml'
}
}
}

stage('版本号更新') {
steps {
script {
// 更新app.json5中的版本号
sh """
sed -i 's/"code": [0-9]*/"code":
${VERSION_CODE}/' entry/src/main/module.json5
sed -i 's/"name": "[0-9.]*"/"name": "
${VERSION_NAME}"/' entry/src/main/module.json5
"""

}
}
}

stage('构建Release包') {
steps {
sh 'hvigor clean'
sh 'hvigor assembleRelease'
}
}

stage('自动化签名') {
steps {
script {
// 调用签名脚本
sh """
./scripts/sign_hap.sh \\
–input=build/outputs/hap/release/entry-default-signed.hap \\
–output=build/outputs/signed/entry-release-signed.hap
"""

}
}
}

stage('产物归档') {
steps {
archiveArtifacts artifacts: 'build/outputs/signed/*.hap', fingerprint: true
}
}

stage('上传到分发平台') {
when {
branch 'main'
}
steps {
script {
// 调用API上传到测试分发平台
sh """
curl -X POST https://distribution.example.com/upload \\
-F "file=@build/outputs/signed/entry-release-signed.hap" \\
-F "version=
${VERSION_NAME}" \\
-F "branch=
${env.BRANCH_NAME}"
"""

}
}
}

stage('通知') {
steps {
script {
// 发送钉钉/企业微信通知
def message = "构建成功:${VERSION_NAME}\\n下载地址:https://distribution.example.com/download/${VERSION_NAME}"
sh """
curl -X POST https://qyapi.weixin.qq.com/cgi-bin/webhook/send \\
-H 'Content-Type: application/json' \\
-d '{"msgtype":"text","text":{"content":"
${message}"}}'
"""

}
}
}
}

post {
failure {
// 构建失败通知
emailext (
to: 'team@example.com',
subject: "构建失败:${env.JOB_NAME} – ${env.BUILD_NUMBER}",
body: "请查看日志:${env.BUILD_URL}"
)
}
}
}

2.3 GitLab CI集成方案

对于使用GitLab进行代码管理的团队,GitLab CI是更轻量级的选择。

2.3.1 .gitlab-ci.yml配置

在项目根目录创建.gitlab-ci.yml文件:

stages:
– build
– test
– package
– deploy

variables:
HARMONY_SDK_HOME: "/opt/DevEcoStudio/sdk"
JAVA_HOME: "/usr/lib/jvm/java-11-openjdk"

# 缓存ohpm依赖
cache:
paths:
– oh–packages/

before_script:
– export PATH=$HARMONY_SDK_HOME/toolchains/hvigor/bin:$PATH
– export PATH=$HARMONY_SDK_HOME/toolchains/ohpm/bin:$PATH
– ohpm config set registry https://repo.harmonyos.com/ohpm/

# 安装依赖
install_deps:
stage: build
script:
– ohpm install
artifacts:
paths:
– oh–packages/
expire_in: 1 hour

# 代码检查
lint:
stage: test
script:
– hvigor lint
dependencies:
– install_deps

# 单元测试
unit_test:
stage: test
script:
– hvigor test
artifacts:
reports:
junit: '**/build/reports/tests/*.xml'
dependencies:
– install_deps

# 构建Release包
build_release:
stage: package
script:
– hvigor clean
– hvigor assembleRelease
artifacts:
paths:
– build/outputs/hap/release/
expire_in: 1 week
only:
– tags
– main
dependencies:
– install_deps

# 签名
sign:
stage: package
script:
– |
# 从GitLab CI变量中获取密码
hvigor sign \\
-PstorePassword=$RELEASE_STORE_PWD \\
-PkeyPassword=$RELEASE_KEY_PWD \\
-PbuildMode=release

artifacts:
paths:
– build/outputs/signed/
dependencies:
– build_release
only:
– tags
– main

# 部署到测试环境
deploy_test:
stage: deploy
script:
– |
curl -F "file=@build/outputs/signed/entry-release-signed.hap" \\
-F "version=$CI_COMMIT_TAG" \\
https://test-server.example.com/upload

only:
– tags
dependencies:
– sign

# 创建GitHub Release(如果是公开项目)
release:
stage: deploy
image: registry.gitlab.com/gitlab–org/release–cli:latest
script:
– echo "Creating release $CI_COMMIT_TAG"
release:
tag_name: $CI_COMMIT_TAG
description: 'Release $CI_COMMIT_TAG'
assets:
links:
– name: 'HarmonyOS App HAP'
url: 'https://gitlab.com/yourproject/-/jobs/artifacts/$CI_COMMIT_TAG/raw/build/outputs/signed/entry-release-signed.hap?job=sign'
only:
– tags

2.4 两种方案的对比与选择

维度JenkinsGitLab CI
适用场景 复杂流程、多项目集中管理 与GitLab仓库紧密集成
维护成本 需单独维护Jenkins服务器 无需额外维护(SaaS版)
插件生态 丰富,2000+插件 相对有限,但常用功能都有
学习曲线 较陡峭 较平缓,YAML配置直观
权限管理 基于项目和角色的RBAC 与GitLab仓库权限一致

选择建议:

  • 已有Jenkins基础设施的团队 → 继续使用Jenkins
  • 团队规模小、希望轻量级方案 → GitLab CI
  • 需要复杂工作流编排(如并行构建、动态参数)→ Jenkins Pipeline更强大

三、自动化签名与版本管理

3.1 鸿蒙签名机制详解

鸿蒙应用的签名体系通常包含两层:

  • 应用级签名(App-Level Cert):用于标识开发者身份
  • 模块级签名(HAP Cert):用于验证每个功能模块的完整性

签名不仅仅是“盖章”,而是整个应用信任链的起点。缺少签名或签名错误的应用无法安装到真机,更无法上架应用市场。

3.1.1 签名所需的文件
文件类型说明示例
.p12文件 包含私钥的证书文件 release.p12
.cer文件 数字证书(公钥) release.cer
.profile文件 应用权限配置文件 release.profile

调试与发布用的Profile与证书通常分开。调试包能装在测试设备,上架必须用发布证书+发布Profile。

3.1.2 签名证书生成

# 生成密钥对和证书请求
keytool -genkeypair -alias release_key \\
-keyalg RSA -keysize 2048 -validity 3650 \\
-keystore release.p12 \\
-storetype pkcs12

# 导出证书请求文件(.csr)
keytool -certreq -alias release_key \\
-keystore release.p12 \\
-file release.csr

然后登录鸿蒙开发者平台,上传.csr文件生成profile文件。

3.2 工程中配置签名

3.2.1 签名配置分离

在项目hvigor/ohos/signingConfigs.js中配置签名信息:

// signingConfigs.js
module.exports = {
debug: {
storeFile: 'signing/debug/debug.p12',
storePassword: process.env.DEBUG_STORE_PWD,
keyAlias: 'debug_key',
keyPassword: process.env.DEBUG_KEY_PWD,
profile: 'signing/debug/debug-profile.p7b'
},
release: {
storeFile: 'signing/release/release.p12',
storePassword: process.env.RELEASE_STORE_PWD,
keyPassword: process.env.RELEASE_KEY_PWD,
keyAlias: 'release_key',
profile: 'signing/release/release-profile.p7b'
}
};

在hvigorfile.js中引用:

// hvigorfile.js
const signing = require('./ohos/signingConfigs');

export default {
system: {
// …其他配置
},
app: {
signingConfigs: signing,
// 构建时指定使用release签名
buildTypes: {
release: {
signingConfig: 'release'
}
}
}
}

关键原则:密码等敏感信息绝对不能硬编码在代码仓库中。通过环境变量注入,CI系统使用“机密变量”管理。

3.2.2 多版本签名管理

不同版本可使用不同的keystore:

  • demoApp_dev.jks → 测试
  • demoApp_pre.jks → 预发布
  • demoApp_prod.jks → 正式版

3.3 CI环境下的签名实践

3.3.1 密码管理方案

在Jenkins中,使用“Credentials”插件管理密码:

// Jenkinsfile中引用凭据
environment {
RELEASE_STORE_PWD = credentials('harmony-release-store-pwd')
RELEASE_KEY_PWD = credentials('harmony-release-key-pwd')
}

stage('签名') {
steps {
sh """
hvigor sign \\
-PstorePassword=
${RELEASE_STORE_PWD} \\
-PkeyPassword=
${RELEASE_KEY_PWD} \\
-PbuildMode=release
"""

}
}

在GitLab CI中,使用“CI/CD Variables”:

# 在GitLab UI中设置变量
# Settings → CI/CD → Variables
# 添加 RELEASE_STORE_PWD 和 RELEASE_KEY_PWD(均设置为Masked)

sign:
script:
– |
hvigor sign \\
-PstorePassword=$RELEASE_STORE_PWD \\
-PkeyPassword=$RELEASE_KEY_PWD \\
-PbuildMode=release

3.3.2 离线签名流程

另一种常见做法是“构建未签名包 + 单独签名”的离线签名流程:

  • 构建未签名HAP:通过构建参数输出unsigned包

    hvigor assembleRelease -Psigned=false

  • 签名Job拉取证书和密码:从CI安全库获取私钥和profile

  • 调用签名工具签名:

    hap-sign-tool sign-app \\
    -keyAlias release_key \\
    -keyPwd $RELEASE_KEY_PWD \\
    -keystore release.p12 \\
    -keystorePwd $RELEASE_STORE_PWD \\
    -appCertFile release.cer \\
    -profileFile release.profile \\
    -inFile unsigned.hap \\
    -outFile signed.hap

  • 验签:校验签名块与证书链

    hap-sign-tool verify -inFile signed.hap

  • 优点:开发机无需接触发布证书,证书权限集中、可审计。

    3.4 版本号自动化管理

    版本号管理是CI/CD中容易出错但又至关重要的环节。常见策略:

    3.4.1 语义化版本 + 构建号

    // Jenkins Pipeline中
    environment {
    // 使用构建号作为版本code
    VERSION_CODE = "${env.BUILD_NUMBER}"
    // 版本名:主版本.次版本.构建号
    VERSION_NAME = "1.0.${env.BUILD_NUMBER}"
    }

    stage('更新版本号') {
    steps {
    // 更新module.json5中的版本号
    sh """
    sed -i 's/"versionCode": [0-9]*/"versionCode":
    ${VERSION_CODE}/' entry/src/main/module.json5
    sed -i 's/"versionName": "[0-9.]*"/"versionName": "
    ${VERSION_NAME}"/' entry/src/main/module.json5
    """

    }
    }

    3.4.2 Git Tag驱动版本

    # GitLab CI中,使用tag作为版本名
    variables:
    VERSION_NAME: $CI_COMMIT_TAG
    VERSION_CODE: $CI_PIPELINE_IID

    before_script:
    – |
    if [ -n "$CI_COMMIT_TAG" ]; then
    echo "Tag构建,版本名:$CI_COMMIT_TAG"
    # 提取版本code(假设tag格式为 v1.2.3)
    VERSION_CODE=$(echo $CI_COMMIT_TAG | sed 's/v//' | awk -F. '{print $1*10000 + $2*100 + $3}')
    fi

    3.4.3 版本自动递增脚本

    #!/bin/bash
    # scripts/update_version.sh

    MODULE_JSON="entry/src/main/module.json5"

    # 读取当前版本code
    CURRENT_CODE=$(grep -o '"versionCode": [0-9]*' $MODULE_JSON | grep -o '[0-9]*')
    NEW_CODE=$((CURRENT_CODE + 1))

    # 读取当前版本name
    CURRENT_NAME=$(grep -o '"versionName": "[0-9.]*"' $MODULE_JSON | grep -o '[0-9.]*')
    # 递增最后一位
    NEW_NAME=$(echo $CURRENT_NAME | awk -F. '{$NF = $NF + 1;} 1' OFS=.)

    # 更新文件
    sed -i "s/\\"versionCode\\": $CURRENT_CODE/\\"versionCode\\": $NEW_CODE/" $MODULE_JSON
    sed -i "s/\\"versionName\\": \\"$CURRENT_NAME\\"/\\"versionName\\": \\"$NEW_NAME\\"/" $MODULE_JSON

    echo "版本已更新:$CURRENT_NAME($CURRENT_CODE) → $NEW_NAME($NEW_CODE)"

    3.5 技术难点与解决方案

    难点描述解决思路
    签名钥匙安全 私钥泄露=全盘崩塌 使用CI机密变量管理,禁止进代码库
    版本冲突 多分支打包易错 Git Tag + Build ID严格管理
    多模块依赖 模块间版本需兼容 统一版本管理文件,CI自动检查依赖
    渠道包一致性 资源差异难排查 构建后生成哈希校验文件

    四、灰度发布与A/B测试配置

    4.1 灰度发布概念与价值

    灰度发布是指按特定规则(如比例、设备类型、用户分组)逐步放量新版本的过程。其核心价值在于:

    • 降低风险:小范围验证,发现问题及时回滚
    • 收集反馈:在影响全量用户前获取真实数据
    • 平滑升级:避免突发流量或兼容性问题

    真正的“稳上线”,不是永不出错,而是出错能优雅退场。

    4.2 鸿蒙元服务灰度发布机制

    鸿蒙的“元服务”(原子化服务)支持灰度发布,核心能力包括:

    • 精准控制:支持按比例(10%、30%、100%)或标签分发
    • 动态调整:实时监控指标,支持手动/自动扩量
    • 跨设备协同:在手机、平板等多端同步状态
    4.2.1 AppGallery Connect灰度配置

    在AGC控制台配置灰度发布:

  • 进入“我的应用”→选择应用→“版本管理”
  • 点击“创建灰度版本”
  • 设置灰度规则:
    • 按比例:如10%用户获取新版本
    • 按设备:指定机型(如Mate60、Pura70)
    • 按地域:指定国家/地区
    • 按用户标签:如“活跃用户”“新用户”
  • 设置监控指标和扩量策略:{
    "rollout": {
    "percent": 10,
    "regions": ["CN", "EU"],
    "devices": ["Mate60", "Pura70"],
    "metrics": {
    "crash_rate_threshold": 0.05,
    "launch_success_rate": 0.99
    }
    }
    }

  • 4.2.2 自动回滚机制

    当检测到异常指标超过阈值时,自动触发回滚:

    // 伪代码:灰度监控服务
    class GrayMonitor {
    checkMetrics(version, metrics) {
    if (metrics.crashRate > 0.05 || metrics.signatureFail > 100) {
    this.rollbackTo(version, 'v2.3.1');
    }
    }

    rollbackTo(failedVersion, targetVersion) {
    // 通知AGC停止灰度
    agc.grayControl.stop(failedVersion);
    // 将流量切回旧版本
    agc.distribution.setDefault(targetVersion);
    // 发送告警
    alert(`灰度版本 ${failedVersion} 已回滚`);
    }
    }

    4.3 A/B测试配置

    A/B测试是通过对比两个或多个版本的用户行为数据,验证功能/设计假设的科学方法。

    4.3.1 实验配置管理

    通过远程配置服务(如AGC Remote Config)管理实验参数:

    {
    "experiment_id": "home_banner_v2",
    "version": "A",
    "target_metric": "click_through_rate",
    "goal": 0.15,
    "variants": {
    "A": {
    "ui_component": "BannerV1",
    "background_color": "#FFFFFF"
    },
    "B": {
    "ui_component": "BannerV2",
    "background_color": "#F0F0F0"
    }
    },
    "distribution": {
    "strategy": "random",
    "ratio": { "A": 0.5, "B": 0.5 }
    }
    }

    4.3.2 端侧实验分组判定

    // ExperimentManager.ts
    import remoteConfig from '@ohos.remoteConfig';

    export class ExperimentManager {
    static async getVariant(screenName: string): Promise<string> {
    // 拉取远程配置
    const config = await remoteConfig.getValue('experiment_config');
    const experiment = config.experiments.find(exp => exp.screen === screenName);

    if (!experiment) return 'control'; // 默认对照组

    // 根据用户ID哈希分配分组(确保同一用户始终看到同一版本)
    const userId = await this.getUserId();
    const hash = this.hashString(userId + experiment.experimentId);
    const ratio = experiment.distribution.ratio;

    return hash < ratio.A ? 'A' : 'B';
    }

    private static hashString(str: string): number {
    let hash = 0;
    for (let i = 0; i < str.length; i++) {
    hash = (hash << 5) – hash + str.charCodeAt(i);
    hash |= 0;
    }
    return Math.abs(hash) % 1000000;
    }

    private static async getUserId(): Promise<string> {
    // 从本地存储获取用户ID
    return await AppStorage.get('userId') || 'anonymous';
    }
    }

    4.3.3 动态组件渲染

    // HomePage.ets
    import { ExperimentManager } from './ExperimentManager';

    @Entry
    @Component
    struct HomePage {
    @State bannerVariant: string = 'A';

    aboutToAppear() {
    ExperimentManager.getVariant('home_screen').then(variant => {
    this.bannerVariant = variant;
    });
    }

    build() {
    Column() {
    if (this.bannerVariant === 'A') {
    BannerV1()
    } else {
    BannerV2()
    }

    // 其他页面内容
    List() { /* … */ }
    }
    .width('100%')
    .height('100%')
    }
    }

    4.3.4 数据埋点与采集

    // analytics.ts
    import analytics from '@ohos.analytics';

    export class Analytics {
    static trackExperiment(experimentId: string, variant: string, event: string) {
    analytics.logEvent({
    name: 'experiment_click',
    params: {
    experiment_id: experimentId,
    variant: variant,
    event: event,
    timestamp: Date.now()
    }
    });
    }

    static trackScreenView(screenName: string, variant: string) {
    analytics.logEvent({
    name: 'screen_view',
    params: {
    screen: screenName,
    variant: variant,
    timestamp: Date.now()
    }
    });
    }
    }

    // 在组件中使用
    BannerV1({
    onClick: () => {
    Analytics.trackExperiment('home_banner_v2', 'A', 'banner_click');
    // 跳转逻辑
    }
    })

    4.4 灰度与A/B测试的最佳实践

    4.4.1 实验流程闭环

    [实验设计] → [代码实现] → [远程配置] → [灰度发布] → [数据采集]
    ↓
    [效果分析] → [决策] → [全量发布或终止]
    ↑ ↓
    [反馈优化] ← [存档]

    4.4.2 关键指标监控
    指标说明阈值
    崩溃率 实验版本崩溃次数/总用户数 <0.05
    点击率(CTR) 点击次数/曝光次数 需显著提升
    转化率(CVR) 目标行为次数/点击次数 需显著提升
    页面停留时长 用户在实验页面的平均时长 需提升或持平
    启动成功率 应用成功启动的比例 >0.99
    4.4.3 默认降级策略

    在代码中务必为每个特性开关提供硬编码的default逻辑,防止在极端脱网场景下出现UI空白。

    function getFeatureEnabled(featureName: string): boolean {
    try {
    // 尝试从远程配置获取
    return remoteConfig.getBoolean(featureName);
    } catch (error) {
    // 网络异常或配置缺失时使用默认值
    console.warn('使用默认配置', error);
    return DEFAULT_FEATURE_FLAGS[featureName] || false;
    }
    }

    五、实战:全流程流水线配置示例

    5.1 项目结构

    harmony-app/
    ├── .gitlab-ci.yml # GitLab CI配置
    ├── Jenkinsfile # Jenkins Pipeline配置
    ├── hvigorfile.js # hvigor配置文件
    ├── oh-packages/ # ohpm依赖缓存
    ├── scripts/
    │ ├── sign.sh # 签名脚本
    │ ├── update_version.sh # 版本更新脚本
    │ └── upload_to_pgy.sh # 上传到分发平台
    ├── signing/ # 签名文件(不提交到Git)
    │ ├── debug/
    │ │ ├── debug.p12
    │ │ └── debug-profile.p7b
    │ └── release/
    │ ├── release.p12
    │ └── release-profile.p7b
    └── entry/ # 主模块
    └── src/main/
    └── module.json5 # 模块配置(含版本号)

    5.2 完整GitLab CI配置

    # .gitlab-ci.yml
    stages:
    – prepare
    – test
    – build
    – sign
    – deploy
    – notify

    variables:
    HARMONY_SDK_HOME: "/opt/DevEcoStudio/sdk"
    JAVA_HOME: "/usr/lib/jvm/java-11-openjdk"
    CACHE_KEY: "ohpm-${CI_COMMIT_REF_SLUG}"

    cache:
    key: ${CACHE_KEY}
    paths:
    – oh–packages/

    before_script:
    – export PATH=$HARMONY_SDK_HOME/toolchains/hvigor/bin:$PATH
    – export PATH=$HARMONY_SDK_HOME/toolchains/ohpm/bin:$PATH
    – ohpm config set registry https://repo.harmonyos.com/ohpm/

    install:
    stage: prepare
    script:
    – ohpm install
    artifacts:
    paths:
    – oh–packages/
    expire_in: 1 day

    lint:
    stage: test
    script:
    – hvigor lint
    dependencies:
    – install

    unit_test:
    stage: test
    script:
    – hvigor test
    artifacts:
    reports:
    junit: '**/build/reports/tests/*.xml'
    dependencies:
    – install

    build_debug:
    stage: build
    script:
    – hvigor clean
    – hvigor assembleDebug
    artifacts:
    paths:
    – build/outputs/hap/debug/
    expire_in: 1 week
    except:
    – tags

    build_release:
    stage: build
    script:
    – hvigor clean
    – hvigor assembleRelease
    artifacts:
    paths:
    – build/outputs/hap/release/entry–default–unsigned.hap
    expire_in: 1 week
    only:
    – tags
    dependencies:
    – install

    sign_release:
    stage: sign
    script:
    – |
    hap-sign-tool sign-app \\
    -keyAlias release_key \\
    -keyPwd $RELEASE_KEY_PWD \\
    -keystore signing/release/release.p12 \\
    -keystorePwd $RELEASE_STORE_PWD \\
    -appCertFile signing/release/release.cer \\
    -profileFile signing/release/release-profile.p7b \\
    -inFile build/outputs/hap/release/entry-default-unsigned.hap \\
    -outFile build/outputs/signed/entry-release-signed.hap

    – hap–sign–tool verify –inFile build/outputs/signed/entry–release–signed.hap
    artifacts:
    paths:
    – build/outputs/signed/entry–release–signed.hap
    expire_in: 1 month
    only:
    – tags
    dependencies:
    – build_release

    deploy_test:
    stage: deploy
    script:
    – |
    curl -F "file=@build/outputs/signed/entry-release-signed.hap" \\
    -F "version=$CI_COMMIT_TAG" \\
    -F "branch=$CI_COMMIT_REF_NAME" \\
    $TEST_DEPLOY_URL

    only:
    – tags
    dependencies:
    – sign_release

    create_release:
    stage: deploy
    image: registry.gitlab.com/gitlab–org/release–cli:latest
    script:
    – echo "Creating release $CI_COMMIT_TAG"
    release:
    tag_name: $CI_COMMIT_TAG
    description: 'Release $CI_COMMIT_TAG'
    assets:
    links:
    – name: 'HarmonyOS App HAP'
    url: 'https://yourdomain.com/download/${CI_COMMIT_TAG}/entry-release-signed.hap'
    only:
    – tags
    dependencies:
    – sign_release

    notify:
    stage: notify
    script:
    – |
    curl -X POST $DINGTALK_WEBHOOK \\
    -H 'Content-Type: application/json' \\
    -d "{
    \\"msgtype\\": \\"markdown\\",
    \\"markdown\\": {
    \\"title\\": \\"构建通知\\",
    \\"text\\": \\"### HarmonyOS应用构建完成\\n- 版本:$CI_COMMIT_TAG\\n- 分支:$CI_COMMIT_REF_NAME\\n- 状态:成功\\n- 下载:[HAP包]($CI_JOB_URL/artifacts/file/build/outputs/signed/entry-release-signed.hap)\\"
    }
    }"

    only:
    – tags
    dependencies:
    – sign_release

    5.3 Jenkins Pipeline完整配置

    // Jenkinsfile
    pipeline {
    agent {
    label 'harmonyos-builder'
    }

    parameters {
    choice(
    name: 'BUILD_TYPE',
    choices: ['debug', 'release'],
    description: '构建类型'
    )
    string(
    name: 'VERSION_NAME',
    defaultValue: '',
    description: '版本名称(留空自动生成)'
    )
    }

    environment {
    // 从凭据获取
    RELEASE_STORE_PWD = credentials('harmony-release-store-pwd')
    RELEASE_KEY_PWD = credentials('harmony-release-key-pwd')
    DEBUG_STORE_PWD = credentials('harmony-debug-store-pwd')
    DEBUG_KEY_PWD = credentials('harmony-debug-key-pwd')

    // 版本信息
    VERSION_CODE = "${env.BUILD_NUMBER}"
    VERSION_NAME = params.VERSION_NAME ?: "1.0.${env.BUILD_NUMBER}"

    // 产物路径
    UNSIGNED_HAP = "build/outputs/hap/${params.BUILD_TYPE}/entry-default-unsigned.hap"
    SIGNED_HAP = "build/outputs/signed/entry-${params.BUILD_TYPE}-signed.hap"
    }

    stages {
    stage('检出代码') {
    steps {
    checkout scm
    }
    }

    stage('安装依赖') {
    steps {
    sh 'ohpm install'
    }
    }

    stage('代码质量检查') {
    when {
    expression { params.BUILD_TYPE == 'release' }
    }
    steps {
    sh 'hvigor lint'
    sh 'hvigor test'
    }
    post {
    always {
    junit '**/build/reports/tests/*.xml'
    }
    }
    }

    stage('更新版本号') {
    steps {
    script {
    sh """
    sed -i 's/"versionCode": [0-9]*/"versionCode":
    ${VERSION_CODE}/' entry/src/main/module.json5
    sed -i 's/"versionName": "[0-9.]*"/"versionName": "
    ${VERSION_NAME}"/' entry/src/main/module.json5
    """

    }
    }
    }

    stage('构建应用') {
    steps {
    sh "hvigor clean"
    sh "hvigor assemble${params.BUILD_TYPE.capitalize()} -Psigned=false"
    }
    }

    stage('签名') {
    steps {
    script {
    if (params.BUILD_TYPE == 'release') {
    sh """
    hap-sign-tool sign-app \\
    -keyAlias release_key \\
    -keyPwd
    ${RELEASE_KEY_PWD} \\
    -keystore signing/release/release.p12 \\
    -keystorePwd
    ${RELEASE_STORE_PWD} \\
    -appCertFile signing/release/release.cer \\
    -profileFile signing/release/release-profile.p7b \\
    -inFile
    ${UNSIGNED_HAP} \\
    -outFile
    ${SIGNED_HAP}
    """

    } else {
    sh """
    hap-sign-tool sign-app \\
    -keyAlias debug_key \\
    -keyPwd
    ${DEBUG_KEY_PWD} \\
    -keystore signing/debug/debug.p12 \\
    -keystorePwd
    ${DEBUG_STORE_PWD} \\
    -appCertFile signing/debug/debug.cer \\
    -profileFile signing/debug/debug-profile.p7b \\
    -inFile
    ${UNSIGNED_HAP} \\
    -outFile
    ${SIGNED_HAP}
    """

    }
    }
    }
    }

    stage('验证签名') {
    steps {
    sh "hap-sign-tool verify -inFile ${SIGNED_HAP}"
    }
    }

    stage('上传产物') {
    steps {
    archiveArtifacts artifacts: SIGNED_HAP, fingerprint: true

    script {
    // 上传到蒲公英(示例)
    if (params.BUILD_TYPE == 'debug') {
    sh """
    curl -F "file=@
    ${SIGNED_HAP}" \\
    -F "_api_key=
    ${PGYER_API_KEY}" \\
    https://www.pgyer.com/apiv2/app/upload
    """

    }
    }
    }
    }

    stage('灰度发布') {
    when {
    expression { params.BUILD_TYPE == 'release' && env.BRANCH_NAME == 'main' }
    }
    steps {
    script {
    // 调用AGC API创建灰度版本
    sh """
    curl -X POST https://connect-api.cloud.huawei.com/api/publish/gray \\
    -H "Authorization: Bearer
    ${AGC_TOKEN}" \\
    -H "Content-Type: application/json" \\
    -d '{
    "packagePath": "
    ${SIGNED_HAP}",
    "versionCode":
    ${VERSION_CODE},
    "versionName": "
    ${VERSION_NAME}",
    "grayRules": {
    "percent": 10,
    "regions": ["CN"]
    }
    }'
    """

    }
    }
    }
    }

    post {
    success {
    emailext (
    to: 'team@example.com',
    subject: "构建成功:${env.JOB_NAME} – ${env.BUILD_NUMBER}",
    body: """
    构建信息:
    – 版本:
    ${VERSION_NAME} (${VERSION_CODE})
    – 类型:
    ${params.BUILD_TYPE}
    – 产物:
    ${env.BUILD_URL}artifact/${SIGNED_HAP}
    """

    )
    }
    failure {
    emailext (
    to: 'team@example.com',
    subject: "构建失败:${env.JOB_NAME} – ${env.BUILD_NUMBER}",
    body: "请查看日志:${env.BUILD_URL}console"
    )
    }
    }
    }

    六、总结

    本文从鸿蒙应用CI/CD的实际需求出发,系统讲解了从代码提交到应用上架的全自动化流程:

    6.1 核心要点回顾

  • CI/CD工具选择:

    • Jenkins适合复杂流程、多项目管理,Pipeline功能强大
    • GitLab CI与代码仓库紧密集成,配置简单,适合中小团队
  • 自动化签名:

    • 证书私钥绝对禁止进入代码仓库
    • 通过CI机密变量注入密码,实现安全自动化签名
    • 离线签名流程进一步隔离证书权限
  • 版本管理:

    • 版本code与构建号关联,确保单调递增
    • Git Tag驱动版本发布,实现可追溯
  • 灰度发布:

    • 支持按比例、设备、地域、用户标签分发
    • 设置监控指标,异常时自动回滚
    • 真正的稳定上线,不是永不出错,而是出错能优雅退场
  • A/B测试:

    • 远程配置动态调整实验参数
    • 端侧根据用户哈希稳定分流
    • 数据埋点驱动产品决策
  • 6.2 最佳实践清单

    类别检查项说明
    代码管理 Git-flow分支模型 主分支保护,仅允许Merge Request
    构建 使用hvigor命令行 不依赖IDE,确保可重复构建
    签名 证书与密码分离 密码使用CI机密变量
    版本 版本号自动递增 与构建号或Git Tag关联
    测试 单元测试自动化 失败则终止流水线
    产物 归档所有构建产物 保留至少30天
    通知 构建结果多渠道通知 钉钉/企业微信/邮件
    灰度 设置回滚阈值 崩溃率>5%自动回滚

    6.3 未来展望

    随着鸿蒙生态的持续发展,CI/CD领域也在不断演进:

    • 云原生构建:基于容器的构建环境,实现完全一致的构建结果
    • AI辅助测试:智能生成测试用例,自动发现兼容性问题
    • 全链路观测:从代码提交到用户使用,全程可观测、可追溯
    • 声明式发布:通过YAML描述发布策略,实现GitOps式应用交付
    赞(0)
    未经允许不得转载:171主机测评 » 鸿蒙APP CI/CD流水线搭建:从代码提交到上架全自动
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址