先说工具全貌 :hmharness 是一个开源的 HarmonyOS/OpenHarmony 开发智能体框架,把工程创建、检查、构建、签名、安装、启动、日志读取和结果验证接成本地工具链,让 AI 不只是生成代码,还要用本机环境证明结果;自进化行为受审批、预算、canary 和回滚约束。它不是 DevEco Studio 的替代品,而是开发智能体和鸿蒙工具链之间的验证层。本文只展开其中一环:
harmony_sign与harmony_device_test组成的签名排障链。
命令行构建鸿蒙应用时,签名这一步最容易把人拖回"玄学排障":命令参数看起来都对,证书文件也都在 SDK 里,最后却停在 sign-app、verify-profile 或 hdc install。更麻烦的是,网上教程常常只给一组能跑通的参数,不讲每个参数为什么存在;SDK 或工具版本一变,那组参数就成了新的坑。
这篇文章把一次真实排障拆成五个证据点:模式词、证书有效期、key alias、证书链、Windows 路径。它们不是五个孤立技巧,而是一条从"读工具帮助"到"设备日志回读"的排查链。结论来自 hmharness 2026-09-05 的公开 DEVLOG,并被后续源码和测试固化下来。

1. 先分清你要签的是 debug 还是 release
hmharness 当前封装的是本地 debug 签名流,目标是让构建出的 HAP 能安装到模拟器或开发者模式设备上验证;release/AGC 签名仍应回到 DevEco Studio 或 AppGallery Connect 流程。这不是能力保守,而是边界清楚:自动化工具不应该悄悄替代你的发布身份。
harmony_sign 的职责很具体:找到最新构建出的 unsigned HAP,定位 DevEco/OpenHarmony 工具链里的 hap-sign-tool.jar、OpenHarmony.p12、OpenHarmonyProfileDebug.pem、调试 profile 模板和 Java,然后生成 -signed.hap。也可以显式传入 profile、p12、cer、key_alias 等身份参数覆盖 SDK 默认值。

如果只记一个原则:先读当前安装版本的 hapsigntool 帮助和错误输出,再确定参数。工具的第一手报错比二手教程可靠。
2. 第一关:-mode 不是 debug/release
直觉上,签名模式似乎应该写 debug 或 release。但这次实测里,hapsigntool 的 sign-app 与 sign-profile 需要的是 localSign。
这类错误的价值不在于"记住一个字符串",而在于它提醒我们:命令行工具的模式词描述的是签名路径,不一定是应用构建类型。把 debug/release 的心智模型直接搬过来,第一步就可能偏航。
当前 hmharness 源码中的调用会固定传入:
text
-mode localSign
这也是为什么排查时应该先看当前版本帮助输出:参数词汇以本机工具为准,历史教程只能当线索,不能当裁判。
3. 第二关:SDK 调试模板可能已经过期
SDK 自带的 UnsgnedDebugProfileTemplate.json 看起来是"官方模板",但那次实测发现它的 validity 区间停在 2021-2023;对后来的新工程来说,它就是一份过期 profile。
hmharness 的处理不是忽略错误,而是先用 verify-profile 语义看 not-before/not-after 数字,把"玄学失败"变成可判断的时间边界。生成新调试 profile 时,它会克隆模板、刷新 validity 窗口并替换 UUID,再用 profile 链签名生成新的 .p7b。

如果你在自己的环境里遇到签名失败,先别反复重跑签名命令。把证书起止时间拿出来,确认当前时间是否落在区间内;这一步能直接排除一类高频问题。
4. 第三关:keyAlias 不是看起来最像答案的那个
OpenHarmony.p12 里可能有多个 alias。实测时,sign-app 可用的 keyAlias 是:
text
openharmony application profile debug
而更符合直觉的 application release 别名反而不能通过链校验。这个结果不是猜出来的,而是列出 store 中的别名、记录组合、用证书链错误逐步排除后得到的。
这里的关键不是让大家死记这个 alias,而是把排查动作固定下来:
- 列出 keystore 里的 alias;
- 每次只改一个组合;
- 保存原始错误;
- 用链校验结果排除,而不是靠名字猜。
签名材料的命名和语义经常不完全一致,组合穷举比直觉更可靠。
5. 第四关:appCertFile 要配对证书链
第四关是文件配对:OpenHarmonyProfileDebug.pem 内含三级证书链,OpenHarmony.p12 与 Profile pem 属于 profile 链。把它们误当成任意可互换的"证书文件",会在链校验处失败。
当前默认 SDK 流程里,sign-profile 和 sign-app 都使用 openharmony application profile debug 别名,appCertFile 使用 OpenHarmonyProfileDebug.pem 这条证书链,再配合新生成的 .p7b profile。显式身份参数则保留给已有发布/团队材料,由调用者自己保证配对关系。
换句话说,证书不是"有文件就行",而是 keystore、alias、证书链、profile 四者要能对上。
6. 第五关:Windows 下 hdc install 对路径更挑剔
签名成功不代表验证结束。那次实测里,同一份 HAP 在 Windows 上安装时,正斜杠路径可能被拼接成错误位置,反斜杠 Windows 路径可以正常安装。
这属于自动化工具很容易踩的坑:Node 或脚本层面认为路径语义正确,设备端命令却按另一套规则解析。hmharness 的 harmony_device_test 在安装前把 HAP 解析成当前平台的原生绝对路径,避免只检查"文件存在"就交给设备。

7. 签完以后,还要证明应用真的启动了
hmharness 把设备验证固化为四步:安装、启动 Ability、轮询 hilog 查找生命周期标记、清理卸载。那次实测的判定标记是:
text
EntryAbility onCreate
这几步的意义在于区分三种结果:
- 签名命令返回成功,但还没有证明能安装;
- HAP 安装成功,但还没有证明应用能启动;
- 应用启动后,hilog 里能回读到它自己的生命周期标记,才算本机环境给出证据。
harmony_device_test 会逐步输出 PASS/FAIL;安装失败会短路,日志标记缺失也会诚实失败。它验证的是最小设备链路,不等价于完整 UI 测试或业务测试。
8. 你可以按这个顺序排查自己的环境
如果今天遇到类似问题,我建议按这个顺序走:
- 确认
hapsigntool当前版本的帮助输出和模式词; - 检查 profile 的
not-before/not-after,先排除过期; - 列出 p12 alias,一次只测一个组合并保存原始错误;
- 区分 app 证书链与 profile 证书链,不要把不同链混配;
- 到安装阶段再处理 Windows 路径,交给设备前转成原生绝对路径;
- 最后用
hdc安装、启动并回读 hilog 生命周期标记。
这个顺序的原则是:每一关都产生一个可记录证据,下一关只处理上一关排除后仍然存在的问题。
9. 怎么复现证据
历史实测记录公开在 DEVLOG 2026-09-05「签名封装 + 设备测试」。当前实现可以核对 public main 提交 c6fdd73422ef8cb05d41412ed448e7d4e8f5a226:
- signing.ts:
harmony_sign与默认 SDK 身份 - ondevice.ts:安装、启动、日志标记、卸载四步判定
- signing.test.ts:身份解析与设备测试失败路径
- 官网 FAQ:命令行打包签名
- 一页速查表:signing-five-gates.pdf
当前安装与发布边界也要分开写:npm 在线包是 @hmharness/cli@0.23.50,registry 读回的 gitHead 是 c6fdd734...;GitHub Latest Release 仍是 v0.23.26。public main、npm 包和 GitHub Release 是三个不同发布面,不能合并成一句"最新版全部已发布"。

10. 为什么这条链值得自动化
签名排障最贵的地方不是敲命令,而是把五个不同层面的证据连起来:参数语义、时间窗口、别名、证书链、路径与设备日志。人工排一次能靠经验,自动化要在下次环境变化时仍然知道每一关怎么失败、下一步看什么。
hmharness 做的就是把这链路产品化:harmony_build 产出 HAP,harmony_sign 处理本地 debug 签名,harmony_device_test 用真实设备链路验证结果。它不隐藏底层工具,也不把失败包装成成功;相反,每个失败输出都是下一轮排查的输入。
项目是独立社区开源项目,与 Huawei/OpenAtom 没有隶属或背书关系。如果你在不同 SDK、Windows 版本、设备或 Node 环境下遇到签名或安装差异,可以把 OS、SDK/DevEco 版本、终端和 Node 版本反馈到 GitHub Discussions #4。