“提测还在手点打包?”——一套HarmonyOS应用的CI与自动化构建流水线该长啥样?
你是不是也在想——“鸿蒙这么火,我能不能学会?”
答案是:当然可以!
这个专栏专为零基础小白设计,不需要编程基础,也不需要懂原理、背术语。我们会用最通俗易懂的语言、最贴近生活的案例,手把手带你从安装开发工具开始,一步步学会开发自己的鸿蒙应用。
不管你是学生、上班族、打算转行,还是单纯对技术感兴趣,只要你愿意花一点时间,就能在这里搞懂鸿蒙开发,并做出属于自己的App!
📌 关注本专栏《零基础学鸿蒙开发》,一起变强!
每一节内容我都会持续更新,配图+代码+解释全都有,欢迎点个关注,不走丢,我是小白酷爱学习,我们一起上路 🚀
全文目录:
前言
先问个扎心的:每次提测前,你是不是还在 DevEco Studio 里点来点去,“Clean→Build→Sign→装到测试机→截包发群”?这活儿别说优雅了,连可靠都谈不上。今天我把鸿蒙(HarmonyOS/OpenHarmony)应用从“代码提交”到“可分发产物”的持续集成(CI)与自动化构建完整方案摊开讲:规范→触发→拉依赖→编译→测试→签名→制品→发布→回滚,配上GitHub Actions / GitLab CI / Jenkins 三版脚本示例,顺手把缓存、并行、版本号、环境隔离这些坑一并填了。看完这篇,你可以把“点点点”打包方式永远按下葫芦。😎
目录快览
- 前言:为啥CI必须现在上
- 架构蓝图:从提交到产物的一条龙
- 工程规范:分支、版本、包型、签名物料
- 关键环节:依赖、构建、测试、签名、制品、质量门禁
- 三套CI脚本:Actions / GitLab / Jenkins
- 环境与缓存优化:分钟级出包不是梦
- 发布与回滚:灰度开关与渠道打通
- 清单与常见坑
一、前言:没有自动化,交付就是玄学
移动端构建链条长、变量多:ohpm 依赖偶尔抽风、hvigor 任务一多就“谁先谁后”难以复现、签名Profile/证书 一换就“今天又打不出来了”。CI的意义是把“个人经验”固化为“可重复流程”:同一提交,任何机器、任何时间都能稳定产出同样的HAP/APP包,并附上测试与质量证明。
二、流水线蓝图(心中要有图)
Push/MR/Tag
│
├─ ① 代码规范检查(lint / format / commit-msg)
├─ ② 依赖安装(ohpm)+ 缓存
├─ ③ 编译(hvigor)→ 生成 HAP/HAR/APP
├─ ④ 测试(单测:Hypium;UI:UI测试/集成)
├─ ⑤ 质量门禁(阈值:覆盖率/包体积/敏感API)
├─ ⑥ 签名(hap-sign)→ 产出可安装包
├─ ⑦ 产物归档(Artifacts / 制品库)
├─ ⑧ 渠道分发(内测/灰度/商店API)
└─ ⑨ 回滚与基线(版本锁/Release Notes/镜像)
关键原则:可声明、可缓存、可并行、可追溯、可回滚。
三、工程规范先立起来(不立规矩,后面都白搭)
3.1 分支与触发策略(推荐)
main:受保护分支,仅允许通过PR合并;develop:日常集成,触发快照构建;release/x.y:预发分支,触发签名+灰度;hotfix/x.y.z:紧急修复;feat/*,fix/*:功能/缺陷分支,触发快速构建+单测。
3.2 版本号与制品命名
- 语义化版本:
MAJOR.MINOR.PATCH; - 自动打标签:
v1.4.0+build.2025.1111.shaabcdef; - 制品名:
app-${flavor}-${arch}-${buildType}-${version}.hap。
3.3 包型与构建任务
-
HAP:应用包(常用);HAR:库;APP:多HAP打包的发布产物;
-
常见 hvigor 目标(示意):
hvigor assembleHap(编译HAP)hvigor bundleApp(打APP包)hvigor --profile release(切换构建Profile)
具体任务名以你的工程脚手架/hvigor版本为准,下面脚本用占位任务名,你替换成项目里真实的。
3.4 签名物料与机密管理
- 证书/密钥/发行Profile 放入CI的Secret或Jenkins Credentials;
- 运行时下发到工作目录,用后立即清理;
- 永远不要把.p12/.cer/.profile 放仓库。
四、关键环节详解与代码片段
4.1 依赖与缓存(ohpm)
# 安装依赖(根目录与各module都可能有oh-package.json)
ohpm install
# 若有子模块:
for d in */ ; do [ -f "$d/oh-package.json" ] && (cd "$d" && ohpm install); done
缓存要点
- 缓存
~/.ohpm、~/.hvigor、node_modules(若使用ESLint/工具链); - 缓存Key包含:
os + lockfile hash + hvigor version,防止脏缓存。
4.2 编译(hvigor)
# Debug/Release 多矩阵
hvigor assembleHap --mode debug
hvigor assembleHap --mode release
# 如需多产品形态:
hvigor assembleHap -Pproduct=phone
hvigor assembleHap -Pproduct=car
# 打APP(多hap合并分发)
hvigor bundleApp --mode release
4.3 单元测试(Hypium/ArkTS 示例)
// test/Example.test.ets —— Hypium风格极简示例
import { describe, it, expect } from '@ohos/hypium';
describe('calc', () => {
it('add', () => {
const sum = 1 + 2;
expect(sum).assertEqual(3);
});
});
CI中执行(示意,按工程实际命令替换):
hvigor test --module app
# 或指定test profile/报告输出目录
hvigor test --report-dir build/reports/tests
4.4 代码质量(ESLint+API敏感扫描)
// .eslintrc.json
{
"env": { "es2022": true },
"extends": ["eslint:recommended"],
"parserOptions": { "ecmaVersion": "latest", "sourceType": "module" },
"rules": {
"no-unused-vars": "warn",
"complexity": ["warn", 15]
}
}
CI片段:
npx eslint --max-warnings=0 ./entry/src ./feature-*/src
敏感API/权限审计(思路)
- 解析
module.json5/oh-package.json与签名Profile,校对权限白名单; - 探测
@ohos.net.http、文件系统等调用集中度,超阈值报警。
4.5 签名与制品
你需要在CI里解密证书与profile,然后调用签名工具(示例命令名仅作参考,按你的签名工具链替换):
# 假设CI中把证书与profile写入工作目录
export SIGN_CERT=cert.p12
export SIGN_CERT_PASS=$SIGN_PASS
export PROFILE=release.profile
# 构建产物目录(示意)
ART=./build/outputs/hap/release/app-release.hap
# 签名(示意)
hap-sign-tool sign \
--in $ART \
--out ./dist/app-release-signed.hap \
--cert $SIGN_CERT --cert-pass $SIGN_CERT_PASS \
--profile $PROFILE
4.6 质量门禁与阈值
- 单测覆盖率
< 60%→ 失败; - 包体积 > 目标上限(比如 50MB)→ 失败;
- 关键 lint 规则违反 → 失败;
- 可选:启动时长、包体敏感文件检查 作为门禁。
五、三套CI脚本给你(拿走即用,按需替换任务名)
5.1 GitHub Actions
name: Harmony CI
on:
push:
branches: [ develop, release/**, hotfix/** ]
pull_request:
branches: [ develop, main ]
workflow_dispatch:
jobs:
build-test:
runs-on: ubuntu-latest
strategy:
matrix:
mode: [debug, release]
product: [phone]
steps:
- uses: actions/checkout@v4
- name: Setup Node (for tooling)
uses: actions/setup-node@v4
with: { node-version: '20' }
- name: Cache ohpm
uses: actions/cache@v4
with:
path: |
~/.ohpm
~/.hvigor
**/node_modules
key: ${{ runner.os }}-ohpm-${{ hashFiles('**/oh-package-lock.json','**/package-lock.json') }}-hv${{ env.HVIGOR_VER }}
restore-keys: |
${{ runner.os }}-ohpm-
- name: Install tool deps
run: |
npm ci || npm i
- name: Install ohpm deps
run: |
ohpm install || echo "ohpm fallback"
for d in */ ; do [ -f "$d/oh-package.json" ] && (cd "$d" && ohpm install || true); done
- name: Lint
run: npx eslint --max-warnings=0 ./entry/src
- name: Build
run: |
hvigor assembleHap --mode ${{ matrix.mode }} -Pproduct=${{ matrix.product }}
- name: Unit Test
run: |
hvigor test --module app || true
[ -d build/reports ] && tar -czf test-reports.tgz build/reports || true
- name: Upload Test Reports
uses: actions/upload-artifact@v4
with:
name: test-reports-${{ matrix.mode }}
path: test-reports.tgz
if-no-files-found: ignore
- name: Sign (release only)
if: matrix.mode == 'release'
env:
SIGN_PASS: ${{ secrets.SIGN_PASS }}
run: |
echo "${{ secrets.SIGN_CERT_BASE64 }}" | base64 -d > cert.p12
echo "${{ secrets.PROFILE_BASE64 }}" | base64 -d > release.profile
ART=$(ls build/outputs/hap/release/*.hap | head -n1)
hap-sign-tool sign --in "$ART" --out "dist/app-${{ matrix.product }}-${{ matrix.mode }}-signed.hap" \
--cert cert.p12 --cert-pass "$SIGN_PASS" --profile release.profile
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
name: hap-${{ matrix.product }}-${{ matrix.mode }}
path: |
build/outputs/hap/${{ matrix.mode }}/*.hap
dist/*.hap
5.2 GitLab CI
stages: [lint, build, test, sign, deploy]
variables:
NODE_ENV: production
cache:
key: "${CI_COMMIT_REF_SLUG}"
paths:
- .ohpm/
- .hvigor/
- node_modules/
lint:
stage: lint
image: node:20
script:
- npm ci || npm i
- npx eslint --max-warnings=0 ./entry/src
only: [merge_requests, branches]
build:
stage: build
image: node:20
script:
- ohpm install || true
- hvigor assembleHap --mode release
artifacts:
paths:
- build/outputs/hap/**/*
expire_in: 7 days
only: [branches, tags]
test:
stage: test
image: node:20
script:
- hvigor test --module app || true
artifacts:
when: always
paths:
- build/reports/**/*
expire_in: 7 days
sign:
stage: sign
image: alpine:3.20
dependencies: [build]
script:
- echo "$SIGN_CERT_BASE64" | base64 -d > cert.p12
- echo "$PROFILE_BASE64" | base64 -d > release.profile
- ART=$(ls build/outputs/hap/release/*.hap | head -n1)
- hap-sign-tool sign --in "$ART" --out "dist/app-release-signed.hap" \
--cert cert.p12 --cert-pass "$SIGN_PASS" --profile release.profile
artifacts:
paths:
- dist/app-release-signed.hap
expire_in: 14 days
only: [tags, /^release\/.*$/]
deploy:
stage: deploy
script:
- echo "Upload to internal store or test hub..."
when: manual
only: [tags]
5.3 Jenkins(Declarative Pipeline)
pipeline {
agent any
options { timestamps() }
environment {
SIGN_PASS = credentials('sign-pass')
// 证书、profile建议用 "Secret file" 方式注入
}
stages {
stage('Checkout') { steps { checkout scm } }
stage('Install') {
steps {
sh 'npm ci || npm i'
sh 'ohpm install || true'
}
}
stage('Lint') { steps { sh 'npx eslint --max-warnings=0 ./entry/src' } }
stage('Build Debug') { steps { sh 'hvigor assembleHap --mode debug' } }
stage('Build Release') { steps { sh 'hvigor assembleHap --mode release' } }
stage('Test') {
steps {
sh 'hvigor test --module app || true'
junit 'build/reports/tests/**/*.xml'
}
}
stage('Sign') {
when { branch 'release/*' }
steps {
withCredentials([file(credentialsId: 'sign-cert', variable: 'CERT'),
file(credentialsId: 'release-profile', variable: 'PROFILE')]) {
sh '''
ART=$(ls build/outputs/hap/release/*.hap | head -n1)
hap-sign-tool sign --in "$ART" --out "dist/app-release-signed.hap" \
--cert "$CERT" --cert-pass "$SIGN_PASS" --profile "$PROFILE"
'''
}
}
}
}
post {
always {
archiveArtifacts artifacts: 'build/outputs/hap/**,dist/*.hap', fingerprint: true
}
}
}
六、环境与速度优化(把“十几分钟”打到“几分钟”)
- Runner镜像预装 hvigor/ohpm/Node:避免每次下载;
- 缓存分层:
ohpm与node_modules分开缓存; - 并行矩阵:
mode × product × arch并行跑; - 跳过无关任务:仅前端变更不跑Native构建,仅文档变更跳过全流程;
- 制品复用:库(HAR)项目单独CI出产物,上层应用引用固定版本,减少重复编译。
七、发布与回滚(稳住阵脚的关键)
-
分发层:
- 内部:上传到制品库(如Nexus/OSS)或企业分发平台;
- 外部:接入商店API(若有),走草稿→审核→灰度流程;
-
灰度开关:版本开关与远程配置绑定(开关一关,某功能即刻软回滚);
-
回滚:
release/x.y出问题→从上一个标签vX.Y.Z-1自动重构并分发; -
Release Notes 自动生成:基于 Conventional Commits,
feat/fix自动聚合到变更日志。
八、质量门禁模板(开箱即用的阈值)
- Lint 0 Error;
- 单测覆盖率 ≥ 60%(核心模块 ≥ 80%);
- 包体积 ≤ 目标阈值;
- 敏感权限变更需人工审批;
- 关键启动用例(仪表测试)持续通过。
九、落地清单(Checklist)
- 规范化分支与触发策略(main/develop/release/hotfix)
- 缓存 ohpm/hvigor/node_modules(含Key策略)
- hvigor 任务与产品形态矩阵构建
- Hypium 单测与测试报告上传
- ESLint + 权限/敏感API审计
- 签名证书与Profile放入CI机密,运行态注入
- 产物归档(Artifacts/制品库)与指纹追踪
- 自动版本与Release Notes
- 灰度/手动门禁/回滚通道
- 失败可复现(日志齐全、环境可重跑)
十、常见大坑(都是血与泪)
- 证书泄露:切记只在 Runner 临时目录解密,完事删;PR 不要跑签名步骤。
- 脏缓存:ohpm锁文件变了没更新缓存Key,构建“成功但不完整”。
- 任务串行:明明可并行非要串着来,时间都浪费在排队上。
- 无门禁:测试红了还继续往下签名发包,事故都是这么来的。
- 环境漂移:Runner系统更新导致 hvigor 版本不兼容;固定镜像版本最稳妥。
结语:自动化不是花活,是团队的“交付契约” 📦
把这套CI流水线落下去,你会发现两件好事同时发生:出包更快、质量更稳。更重要的是,“人肉经验”被固化为“团队契约”——任何人、任何时候、在任何机器上,都能生成同样可验收的包。
❤️ 如果本文帮到了你…
- 请点个赞,让我知道你还在坚持阅读技术长文!
- 请收藏本文,因为你以后一定还会用上!
- 如果你在学习过程中遇到bug,请留言,我帮你踩坑!
更多推荐




所有评论(0)