HarmonyOS上架自动化实战:云证书签名、HAR合并与自检2.0
2026/9/19 17:40:51 网站建设 项目流程

这几个月我们团队一直在跟应用上架较劲。不是功能开发拖了后腿,而是卡在上架前那一堆繁琐的工程操作上:证书手动导出上传、十几个 HAR 共享包依赖理不清、提交前反复人工核对各种配置。后来我把整套上架流程按标题里说的三个方向重构了一遍——云管理证书自动签名、多 HAR 合并、提交自检 2.0,整体提效非常明显。今天这篇就是把这三块方案的完整实现拆开讲清楚,顺便把我在 HarmonyOS 7 环境下踩过的坑也一并列出,适合正在维护中大型 HarmonyOS 应用、或者准备把上架流程做成自动化的开发者和交付负责人参考。

1. 一场上架提速的起因:签名、依赖和审核的三大堵点

1.1 传统证书签名的"人肉流程"为什么最先被优化

HarmonyOS 应用上架的第一步就是签名。以前我们用的是本地证书模式:在 AppGallery Connect 上生成证书请求 CSR,下载到本地,配置 Profile 文件,再在打包工具里手动勾选。这套流程单看没啥问题,但一旦团队超过三个人、或者应用数量超过两个,就开始乱了。

最常见的场景:证书文件存在同事 A 的电脑里,同事 A 请假了,同事 B 要出 release 包,只能干等;或者换了一台新电脑,p12 密钥库文件拷过来拷过去,密码还容易记混;更头疼的是一个应用对应一个签名证书,应用多了之后,哪个包用哪个证书、哪个 Profile 对应哪个版本,全靠脑子记。我见过最离谱的一次,是团队里有人把 debug 签名的包误当成 release 包传到了审核后台,被驳回之后才发现签名类型不对,整条上架链路推倒重来。

所以第一个要优化的就是签名环节。HarmonyOS 7 的云管理证书方案,本质上把"证书和私钥的保管"这件事从本地挪到了云端,开发者只负责在后台创建证书并关联应用,打包时工具自动拉取证书配置完成签名。对个人开发者来说可能差别不大,但对团队协作和 CI 流水线来说,省掉的是一大堆交接成本和环境同步问题。

1.2 HAR 满天飞:业务扩展带来的依赖治理负债

HAR 是 HarmonyOS 的静态共享包(Harmony Archive),可以简单理解成我们平时说的通用模块或组件库。业务早期只有一个 HAR,大家没觉得有什么问题;但随着业务线扩张,按功能拆模块、按 UI 拆组件、按团队拆工程,HAR 数量迅速膨胀,我们项目最多的时候同时存在 18 个 HAR。

HAR 多到一定程度,问题就暴露了:构建时间越来越长,因为要逐个模块编译;依赖关系越来越复杂,经常出现两个 HAR 同时依赖同一个底层库的不同版本;还有一个隐蔽的问题——多个 HAR 各自携带资源文件,上架打包后同一个资源被重复打进 HAP,包体越来越大。最典型的一次,我们发现一个网络库的 so 文件被 6 个 HAR 各带了一份,最终包里出现了 6 份完全相同的二进制文件。

这个阶段不改,后面只会越来越痛。所以多 HAR 合并不是"代码洁癖",而是实打实的构建效率、包体规模、依赖安全三重诉求。

1.3 从"人工核对"到"提交自检":上架审核的最后一公里

签名搞定了、HAR 合并了,以为就万事大吉了?还差最后一步——提交审核。HarmonyOS 应用上架审核对版本号、权限声明、图标资源、隐私政策这些字段有严格要求,而这些信息分散在 module.json5、build-profile.json5、工程资源目录等多个地方。人工核对一次要 20 到 40 分钟,还容易漏项。

我们做提交自检 2.0 的初衷很简单:把上架提交前的人工核对项,用脚本在 CI 打包后自动跑一遍,发现问题直接让构建失败,把"审核驳回后返工"变成"提交前自动拦截"。这个思路在移动端上架流程里不算新鲜,但真正在 HarmonyOS 工程里完整落地、并且能和签名、打包、归档串成一条流水线的实践,确实值得展开讲讲。

2. 云管理证书自动签名:把发布证书放进流水线

2.1 云管理证书与传统本地证书的本质区别

先说结论:云管理证书和传统本地证书最大的区别在于私钥的存放位置和管理方式。

对比项传统本地证书云管理证书
私钥存放本地 p12 密钥库文件AGC 云端托管
证书交接依赖团队间拷贝文件后台权限控制
打包方式手动选择证书文件和 Profile打包工具自动拉取关联配置
适用团队单人或小团队多人协作、CI 自动化
泄露风险本地文件易丢失或泄露私钥不出云,风险更低

用生活化类比解释:本地证书就像一把自己保管的实体钥匙,钥匙丢了就得换锁;云管理证书像是小区物业统一管理的门禁卡,你在物业登记一下手机号,进出小区时自动验证,不用随身带钥匙。

HarmonyOS 7 的云管理证书接入了 AppGallery Connect 的证书服务,创建之后会生成对应的发布 Profile。你在工程里只需要配置"用哪个云证书 ID 签名",剩下的签名材料获取、证书匹配、摘要算法选择,都是由构建工具在云端完成的。

2.2 在 DevEco Studio 里配置云证书签名的完整步骤

我按照当前我使用的 HarmonyOS 7 配套开发工具版本为例,完整走一遍:

  1. 登录 AppGallery Connect 控制台,进入目标应用的"用户与访问 > 证书管理"页面。
  2. 创建"发布证书"类型的云证书,填写证书名称。创建成功后,会生成一个证书指纹(SHA256),这个指纹必须和应用签名信息一致。
  3. 在同一页面创建发布 Profile,选择刚才创建的云证书,并关联应用包名(bundleName)。Profile 下载到本地一份,后面配置工程要用。
  4. 回到 DevEco Studio,打开工程,进入 File > Project Structure > Signing Configs,勾选"使用云证书"(不同版本菜单名称可能有差异,核心是选 Cloud 模式)。
  5. 在弹出的配置里填入云证书 ID、Profile 文件路径,签名算法保持默认的 SHA256withECDSA 即可。
  6. 保存配置后,构建工具会自动把签名配置写入工程的 build-profile.json5。

配置完的效果是:你不再需要把 p12 文件放进工程目录,也不需要记住 storePassword 和 keyPassword。本地只留 Profile 文件,它的权限控制由 AGC 后台管理。

对应的工程配置片段长这样,供你们对照:

{ "app": { "signingConfigs": [ { "name": "cloud-release", "type": "HarmonyOS", "material": { "certpath": "", "storePassword": "", "keyAlias": "", "keyPassword": "", "profile": "release_profile.p7b", "signAlg": "SHA256withECDSA", "storeFile": "" }, "cloudCertificateId": "C1234567890" } ] } }

注意:certpath、storePassword 这些字段在云证书模式下可以为空,核心的关联信息是 cloudCertificateId 和 profile 指向的 p7b 文件路径。

2.3 命令行与 CI 环境下的自动签名姿势

DevEco Studio 里点出来的配置适合本地手动打包,真正要提效,必须让命令行和 CI 也能调用同一套签名。

HarmonyOS 工程的构建命令基于 hvigor,签名配置读取的是上面那份 build-profile.json5。你只需要在命令行执行:

hvigorw assembleHap --mode module -p product=default

构建工具就会根据模块里的签名配置自动完成签名。如果项目里有多个模块,可以用 -p module 指定目标模块,或者不指定直接构建默认模块。

CI 环境下的做法稍微讲究一点。我的建议是把云证书 ID 和 Profile 文件路径做成 CI 变量,不要在仓库里硬编码。GitLab CI 的流水线里可以这样做:

build-release: stage: build script: - echo "CLOUD_CERT_ID=$CLOUD_CERT_ID" > local.properties - hvigorw clean assembleHap --mode module -p product=default -p buildMode=release artifacts: paths: - entry/build/default/outputs/default/*.hap

构建完成后,只需要把产物目录里的 HAP 包传到 AGC 后台做上架提交或用脚本上传分发。整个过程中没有任何人手动触碰证书文件。

2.4 自动签名最常见的三个失败现场

第一,云证书指纹和应用签名不匹配。这种情况多数发生在应用包名变更或证书重建之后,报错信息会提示签名校验失败。解决办法是在 AGC 后台核对应用的签名 SHA256 指纹是否与云证书生成时的一致。

第二,Profile 文件过期。发布 Profile 通常有有效期,过期后构建工具会报"Profile not valid"。这个最坑,因为它不是每次都在构建时报,有时候是构建成功但安装到真机运行时系统提示签名异常。处理方式是在 AGC 后台重新生成 Profile,替换本地路径。

第三,多模块工程的签名配置不一致。一个 HAP 工程如果有多个 HAR 依赖,每个 HAR 可能都有自己的签名配置或 debug 配置。如果某个模块的签名配置没走上云证书,最终 HAP 的签名串就会异常。排查方式是对每个模块逐一执行签名校验,或者统一在工程根目录的 build-profile.json5 里覆盖签名配置,避免子模块各自为政。

3. 多 HAR 合并:从 18 个共享包收敛到 5 个的工程化实践

3.1 合并前先算清依赖账:依赖树就是合并边界

合并 HAR 之前,我强烈建议先搞清楚两个问题:每个 HAR 被谁依赖?每个 HAR 依赖了谁?这两个问题的答案就是合并的边界。

HarmonyOS 工程里可以用 hvigor 的构建任务输出模块依赖关系。我用的方式是在工程根目录执行:

hvigorw --mode module -p product=default --info

构建日志里会打印模块依赖图,也可以直接查看每个 HAR 的 oh-package.json5 里的 dependencies 字段,把关系理一遍。我们当时的依赖关系大致是这样的:

  • base-utils:被所有业务模块依赖
  • base-network:被大部分业务模块依赖,依赖 base-utils
  • common-ui:被多个业务模块依赖,依赖 base-utils
  • feature-login:依赖 base-network、common-ui
  • feature-pay:依赖 base-network、common-ui
  • ...

我画了一张简化的依赖矩阵,用来决定哪些 HAR 可以合并:

HAR 名称被谁依赖依赖谁合并建议
base-utils全部保持独立
base-network大部分业务base-utils保持独立
common-ui大部分业务base-utils保持独立
feature-login仅入口模块common-ui, base-network可与相关业务模块合并
feature-pay仅入口模块common-ui, base-network可与相关业务模块合并
feature-share仅入口模块common-ui可与 feature-login 合并

关键判断标准:被多个业务模块复用的基础能力(工具、网络、UI 组件)继续保持独立;只有单一入口、业务强相关的模块,才适合合并。

3.2 按"业务域"合并 HAR 的落地步骤

确定了合并边界之后,我按"业务域"把 HAR 分成了几组:登录域(feature-login、feature-share)、支付域(feature-pay、feature-order)、公共基础域(base-utils、base-network、common-ui 保持原样)。

具体合并操作是在目标 HAR 的 oh-package.json5 里添加依赖,同时把被合并模块的源码目录迁移过来。举个例子,我要把 feature-share 合并到 feature-login,就在 feature-login 的 oh-package.json5 里把 feature-share 的源码包依赖进来:

{ "name": "feature-login", "version": "1.0.0", "description": "登录与分享业务合集", "main": "Index.ets", "dependencies": { "base-utils": "file:../base-utils", "base-network": "file:../base-network", "common-ui": "file:../common-ui" } }

然后把 feature-share 的 ets 源码和 resource 目录按目录结构拷贝到 feature-login 的 src/main 下,再更新导出入口 Index.ets,把原来 feature-share 对外暴露的接口重新导出:

export { default as ShareService } from './share/ShareService'; export { default as ShareSheet } from './share/ShareSheet';

这一步完成后,原来依赖 feature-share 的地方不需要改动,因为它们 import 的还是同名 API,只是物理文件位置变了。

三组分别合并后,HAR 数量从 18 个降到了 7 个,后续构建日志明显清爽很多。

3.3 合并后的四大验证项:路由、资源、so、权限

合并 HAR 不是搬完文件就结束,我在实践中验证了四个方面,缺一个都容易出线上问题。

第一个是路由表冲突。如果被合并的 HAR 里有基于模块名的路由注册,合并后可能出现两个模块注册同一个路由的情况。我在应用启动后的模块初始化逻辑里加了一个路由表 dump,检查有没有重复 key。没有冲突才能继续。

第二个是资源文件命名冲突。HAR 之间资源命名空间虽然理论上隔离,但不同 HAR 里的 media 资源如果同名,合并后可能会相互覆盖。我在执行合并后跑了一次资源检查脚本,扫描 src/main/resources 下所有同名资源文件,逐个确认是否需要重命名。

第三个是 so 文件重复打包。这是包体最大的隐患。检查方法是解包最终生成的 HAP,查看 libs 目录下的 so 文件列表,凡是出现名字相同、MD5 相同的文件,就说明重复打包了。解决方式是合并后统一清理各模块 libs 下的重复 so,只保留一份,并在构建脚本里做一次"so 唯一性检查"。

第四个是权限声明带出。HAR 的 module.json5 里可能有自己的 requestPermissions 声明,合并后会带进 HAP。我在提交自检脚本里加了一个权限白名单校验,避免合并后意外把不必要的权限声明带入上架包。

3.4 为什么我不建议在产物层强行合并 HAR

市面上有人直接把多个 HAR 的产物解包、再把里面的资源文件塞进一个新的 HAR 里,这种做法我不推荐。

原因很简单:HAR 不仅是文件集合,它还携带了自己的配置文件、能力声明和依赖元数据。在产物层强行合并,等于把多个模块的 metadata 揉在一起,很容易出现依赖引用找不到、资源 ID 冲突、构建工具无法识别等问题。更麻烦的是,后续一旦某个内部模块要升级,你必须重新做一次产物合并,自动化成本和维护成本都非常高。

我推荐的方式是在工程源码层做依赖聚合,让 hvigor 在构建时自然地把多个模块编译到同一个 HAR 或直接打进 HAP。这样合并的边界是清晰的、可追溯的,后续改动也有版本管理兜底。如果你们的工程已经是用 HAR 产物包来分发的(比如提供给其他团队使用),那就更应该保持在源码级配置依赖,而不是手动合并产物。

4. 提交自检 2.0:用脚本把上架审核环节前置

4.1 上架被拒的高频原因对照表

做自检之前,我先把 HarmonyOS 应用上架审核常见的驳回原因列了一遍,然后挨个转成可检查的规则。

审核常见驳回原因对应的工程检查点检查类型
版本号与后台不一致module.json5 的 versionCode/versionName配置比对
权限声明超范围requestPermissions 白名单配置比对
应用图标尺寸不全resources/base/media 下图标文件资源检查
隐私政策链接缺失应用配置里的隐私声明字段配置比对
备案信息不完整上架附件材料人工复核
包名与 AGC 后台不一致bundleName 比对配置比对

这些检查点全部可以用脚本自动完成。人工只需要在脚本无法判断的环节(比如隐私政策文案本身是否合规)做一次复核。

4.2 核心自检脚本的数据来源和检查逻辑

自检脚本的数据来源主要有三个:module.json5(模块配置)、build-profile.json5(构建配置)、资源目录文件列表。我用 Python 写了一个检查脚本,核心逻辑大致如下:

import json import os import hashlib APP_ROOT = "./entry/src/main" MODULE_CONFIG = os.path.join(APP_ROOT, "module.json5") BUILD_CONFIG = "./build-profile.json5" def load_json5(path): # 这里用 json5 库解析,因为 HarmonyOS 配置文件是 json5 格式 import json5 with open(path, "r", encoding="utf-8") as f: return json5.load(f) def check_version(): config = load_json5(MODULE_CONFIG) app = config["app"] assert app["bundleName"] == "com.example.app", "bundleName 不匹配" assert app["versionCode"] >= 1001001, "versionCode 低于预期" assert app["versionName"] == "1.0.1", "versionName 不一致" print("[PASS] version check") def check_permissions(): config = load_json5(MODULE_CONFIG) allowed = {"ohos.permission.INTERNET", "ohos.permission.GET_NETWORK_INFO"} request_permissions = config.get("module", {}).get("requestPermissions", []) for perm in request_permissions: name = perm["name"] if name not in allowed: raise ValueError(f"发现白名单外权限: {name}") print("[PASS] permission check") def check_icon(): media_dir = os.path.join(APP_ROOT, "resources", "base", "media") required = {"foreground.png", "background.png", "icon.png"} for name in required: assert os.path.exists(os.path.join(media_dir, name)), f"缺少图标: {name}" print("[PASS] icon check") def check_hap_hash(hap_path): sha256 = hashlib.sha256() with open(hap_path, "rb") as f: for block in iter(lambda: f.read(4096), b""): sha256.update(block) print(f"[INFO] HAP SHA256: {sha256.hexdigest()}") if __name__ == "__main__": check_version() check_permissions() check_icon() check_hap_hash("./entry/build/default/outputs/default/entry-default-signed.hap") print("All checks passed.")

这个脚本的精髓不在于检查项多,而在于每一条检查都直接映射到上架审核的真实规则。比如 versionCode 的检查,背后原因是应用程序市场要求版本号依次递增,如果提交的版本号小于线上版本,审核后台会自动驳回。

4.3 从自检 1.0 到 2.0:新增的检查项与判稳机制

自检 1.0 是我们最早做的一版,只检查了版本号和 bundleName,属于最基础的兜底。

自检 2.0 在 1.0 的基础上增加了四类检查项:

第一类是签名校验增强。自动签名跑完之后,脚本会用签名工具主动读取 HAP 的签名信息,验证签名证书的指纹是否与 AGC 后台的云证书指纹一致。这一项直接解决了"打包成功但签错证书"的隐蔽问题。

第二类是 HAR 合并后的完整性检查。合并完 HAR 后,脚本会检查最终 HAP 里是否存在重复的 so 文件、重复的资源名,以及依赖模块是否能正常解析。这个检查结合了我们在合并阶段总结的验证项,做成自动化。

第三类是权限白名单校验。我们把每个应用允许声明的权限维护成一份白名单列表,脚本逐个比对。新增权限需要先在白名单列表里登记,并注明使用场景,否则脚本直接报错。这一条逼着团队在开发阶段想清楚"到底需不需要这个权限"。

第四类是上架材料清单检查。上架时经常要填版本说明、隐私政策链接、应用截图等材料,脚本会生成一份"上架材料清单",把需要人工确认的项目逐条列出,避免提交时发现缺文件。这属于半自动检查,但很实用。

另外,2.0 版加了一个"判稳机制":所有检查项必须连续通过 3 次,才允许进入正式提交流程。这个逻辑是为了防止 CI 偶发的网络超时或缓存问题导致误报通过。实践下来,判稳机制确实拦住了一次因为 Profile 刚替换、本地缓存未刷新导致的假通过。

4.4 和 CI 流水线串起来的完整提交流程

自检脚本单独跑没意义,要和打包、签名串起来才算完整。我现在的 CI 流水线是这样设计的:

  1. 代码合并到 release 分支后,触发构建任务。
  2. 构建任务执行 hvigor 打包,自动使用云证书签名。
  3. 打包完成后,立即运行提交自检脚本。自检脚本读取签名后的 HAP,执行上面说的四类检查。
  4. 如果自检失败,流水线中断,构建产物不归档,开发者收到失败通知。
  5. 如果自检通过,产物归档,同时生成一份 Markdown 格式的"上架自检报告",包含 HAP 哈希、签名指纹、版本信息、权限清单、图标检查结果,直接作为上架材料附件。

整套流程跑下来,从代码合并到拿到一份可提交的 HAP 和自检报告,耗时不到 10 分钟,而且全程不需要人工干预。

5. 实际效果数据与避坑备忘

5.1 一个真实版本从构建到可提交的耗时对比

拿我们最近一次上版来说,在重构前,整个流程大致是:下午 3 点开始准备 release 包,先找证书、确认 Profile,再手动编译多个 HAR 模块,最后人工核对配置和材料,折腾到 6 点多才把包传上去,中间还因为 icon 尺寸漏了一张被打回一次。总耗时约 3 小时,其中纯人工操作超过 1.5 小时。

重构后,同样的版本,我下午 4 点把 release 分支代码推送,4 点 10 分收到自检通过的通知,4 点 15 分已经在上架后台填完了材料(材料清单是脚本生成的,照着复制就行)。总耗时约 15 分钟,人工操作不到 5 分钟。

环节优化前耗时优化后耗时
证书签名准备30~60 分钟0(自动)
多 HAR 编译与打包40~90 分钟5~8 分钟
上架前人工核对20~40 分钟3~5 分钟
提交材料整理15~30 分钟0(脚本生成)
总计2~3 小时10~15 分钟

数字本身不夸张,真正可贵的是这套流程把"人的不确定性"剔除了。换人、换电脑、换分支都不影响出包质量,这对持续迭代的团队是实打实的解放。

5.2 自动化上架链路中最值得记住的 5 个教训

第一,云证书模式也不是完全不用管证书。Profile 文件虽然不用手动上传了,但它的有效期还是要盯着的。我在 CI 里加了一个"Profile 剩余有效期"的检查项,低于 15 天就直接在流水线中告警,防止发版时才发现过期。

第二,har 合并前一定要确定所有引用方都适配了新的导入路径。我们曾遇到过一个业务模块仍然通过旧路径 import 被合并掉的 HAR 的 API,导致运行时模块找不到。合并后最好全仓库搜一遍旧模块名,确认没有残留引用。

第三,自检脚本的权限白名单要定期更新。HarmonyOS 新版本有新权限能力,但如果那个权限不是应用核心功能需要的,建议默认不加进白名单。每加一个权限都要有审核场景支撑,这个把关尺度直接影响上架通过率。

第四,CI 里使用云证书时,需要给构建机配置 AGC 的访问凭证。不同团队的网络策略不一样,有些构建环境访问 AGC 接口会被防火墙拦截,导致签名步骤超时。提前和基础设施团队确认好构建机到 AGC 的网络连通性,比什么都重要。

第五,自检脚本的报错信息要写得足够清晰。刚开始我们脚本失败时会报一堆 traceback,开发者要花时间拆解。后来我把每条检查项包了一层 try-except,失败时直接输出"检查项名称 + 期望值 + 实际值"三段式信息,排查效率提升非常明显。

5.3 后续还可以怎么扩展这套流程

这套自动化上架流程跑稳之后,我准备做两件事来进一步提效。

一件是把自检报告和 AGC 后台的上架信息打通,尝试用开放接口直接填入版本说明和材料信息,省掉最后的复制粘贴环节。不过这块要结合团队的 AGC 权限模型来做,不能做成通用方案。

另一件是为多个应用做统一的证书和签名管理。现在我们是单应用流水线,后续如果接手更多应用,计划做一个小的配置中心,把每个应用的证书 ID、Profile 路径、权限白名单、图标要求放到一个地方统一维护,流水线启动时按应用拉取配置。这样一来,新应用接入自动化上架的成本会从几天压缩到半天以内。

最后分享一个真实的个人体会:自动化这件事,最怕的不是技术难点,而是做了一半就停。我们 1.0 版本只做版本号检查,跑了半年,期间还是出现过一次 icon 缺失被驳回。后来下定决心把 2.0 补齐,一次性把权限和签名校验全做进去,才真正感受到"提交前心里有底"是什么体验。如果你也正在搭建这套流程,建议先跑通最小闭环,再逐步补检查项,不要一开始就追求大而全。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询