HarmonyOS ohpm × Node.js:HAR许可证声明与NOTICE文件缺项门禁【鸿蒙心迹】
一、先把“包已经安装”与“材料已经齐全”分开
一个第三方 HAR 已经能够通过依赖解析,不代表交付清单就完整。团队准备向客户移交工程或在内部发布组件时,常要同时保存版本、许可证标识、许可证文本和引用说明。过去有人只看 ohpm install 没报错,就把材料打成“就绪”;等交付人员追问 LICENSE 或 NOTICE 文件时,问题才从研发侧转回。两件事的责任完全不同:安装器解决依赖,材料门禁解决项目自己约定的归档要求。
本篇选取一个较窄的工程问题:读取规范化后的三方包元数据,对照我们自己的交付合同,检查 license 字段、LICENSE 文件存在性,以及约定必须附带的 NOTICE 文件。Demo 叫 LicenseReceipt,任务编号 LC-408。

二、为何不能直接从包管理日志推断合规
华为 ohpm 文档中的 oh-package.json5 示例包含 license 字段,官方的 HAR 共享包发布指导也列出了 LICENSE 等文件。这里能确定的是元数据与文档材料在工程中有具体位置。实际授权条款应由具体许可证文本、依赖关系和法务流程共同判断。
为了避免混淆,我把输入收敛成一个 snapshot.json。每条记录包含 name、version、license、hasLicenseFile 与 hasNoticeFile。这些字段是示例用的规范化快照合同,其中后两个布尔值需要上游采集程序在真实项目中从归档目录核对。示例仅针对四个示例包,不会自动扫描用户机器上的任意目录。
门禁状态只分四种:PASS 表示三项企业交付要求齐全;HOLD_LICENSE 表示许可证标识为空;HOLD_LICENSE_FILE 表示许可证文件缺失;HOLD_NOTICE 表示企业要求的说明文件缺失。HOLD 意味着交付材料等待补全,不表示依赖恶意、许可证违法或平台审核失败。项目可选择将 NOTICE 从强制项改成提示项,但需要先改合同与测试,不应该悄悄改结论。
三、第一段代码只负责确定缺什么
下方是纯函数:不打开网络、不修改依赖目录,也不执行任何包安装。它按优先顺序返回第一个问题。先检查包名是为了保证每条结果可追溯;其次检查声明的许可证,最后检查两个本地材料标志。多种材料同时缺失时,单状态模型只展示第一项,生产版报告可改为 issues 数组。
// tools/license-audit.mjs:面向规范化离线快照的项目门禁
export function inspectPackage(pkg) {
if (!pkg || typeof pkg.name !== 'string') {
throw new TypeError('缺少包名,无法追溯');
}
if (typeof pkg.license !== 'string' || !pkg.license.trim()) {
return { name: pkg.name, status: 'HOLD_LICENSE' };
}
if (pkg.hasLicenseFile !== true) {
return { name: pkg.name, status: 'HOLD_LICENSE_FILE' };
}
if (pkg.hasNoticeFile !== true) {
return { name: pkg.name, status: 'HOLD_NOTICE' };
}
return { name: pkg.name, status: 'PASS' };
}
纯函数的输入和输出都容易通过 Node.js 测试,后续换成图形页面、构建脚本或者 CI 平台,都不需要改判断核心。
typeof 防御用于识别元数据缺失或非字符串情况。通过 trim() 防止空格构成一个看似有效的许可证值。hasLicenseFile 和 hasNoticeFile 必须是明确的布尔真值,不能把 'yes'、1 或空字符串视为“已确认”。这不是矫情:材料清单经常由表格导出,模糊的真值判断会让报告误判通过。
四、四个样本刻意覆盖不同缺陷
本轮使用的都是示例包名。@demo/chart 与 @demo/router 具备完整字段和文档,应该通过。@demo/map 故意留空许可证标识,即使文件存在也要进入待补全。@demo/codec 有许可证字段和 LICENSE 文件,却没有项目合同要求归档的 NOTICE,因此同样等待补全。这四个样本不涉及真实许可证合法性判断。
// 所有字段来自教学用fixtures,非实时ohpm仓库查询
const packages = [
{ name: '@demo/chart', version: '1.4.1', license: 'Apache-2.0',
hasLicenseFile: true, hasNoticeFile: true },
{ name: '@demo/router', version: '2.0.0', license: 'MIT',
hasLicenseFile: true, hasNoticeFile: true },
{ name: '@demo/map', version: '0.8.0', license: '',
hasLicenseFile: true, hasNoticeFile: true },
{ name: '@demo/codec', version: '3.1.0', license: 'Apache-2.0',
hasLicenseFile: true, hasNoticeFile: false }
];
如果真实包同时存在多个版本,归档记录必须以“包名 + 版本”为键,而不是只有包名。两个版本的 LICENSE 内容可能不同,依赖关系也可能不同。本文为了突出材料缺项,保留了 version 字段。应用内的依赖清单通常还有直接依赖、传递依赖和开发依赖等层级,实际交付合同应先说明哪些层级需要进入核对范围。
需要特别说明 JSON5 的处理:oh-package.json5 允许的语法并不等价于标准 JSON。示例使用普通 JavaScript 对象或严格 JSON 快照作为输入,并不是建议用 JSON.parse() 直接解析任意 JSON5 文件。若需自动化采集,应选用经过版本验证的 JSON5 解析方式,或由已有构建工具导出标准 JSON,再在这里执行检查。
五、把结果做成可以拒绝发布的报告
第三段代码调用纯函数,先对每条示例样本做严格断言,再打印带任务号的明细。预期的顺序固定为 PASS、PASS、HOLD_LICENSE、HOLD_NOTICE。最后得到 PASS 2 / HOLD 2。CI 模式下如有 HOLD,把退出码设置为 2,方便构建流水线识别;普通讲解模式仍以 0 退出,便于查看全部报告。
import assert from 'node:assert/strict';
const outcomes = packages.map(inspectPackage);
assert.deepEqual(outcomes.map(x => x.status),
['PASS', 'PASS', 'HOLD_LICENSE', 'HOLD_NOTICE']);
const pass = outcomes.filter(x => x.status === 'PASS').length;
const hold = outcomes.length - pass;
for (const result of outcomes) {
console.log(`LC-408 ${result.name} ${result.status}`);
}
console.log(`LC-408 summary PASS=${pass} HOLD=${hold}`);
if (process.argv.includes('--ci') && hold > 0) process.exitCode = 2;
脚本在当前环境执行了构造样本,检查结果与断言一致。这个“通过”指测试函数逻辑完成。为了让读者能复核,本轮素材包附了单独的 Node.js 入口和构造 JSON,执行日志也写入文件;复现时只需具备对应 Node.js 环境,不必下载示例依赖包。

六、归档路径和扫描风险比状态码更重要
在真实工程落地,危险点往往不是判断条件,而是路径来源。不能把第三方元数据直接拼到任意磁盘路径上:名称中可能包含 ../、混合路径分隔符或符号链接,扫描程序如果不限制根目录,就可能错误读取别处文件。上游采集器应以受控的缓存目录为根,做规范化、真实路径校验和符号链接策略,再只输出 hasLicenseFile 等布尔结论给本脚本。
包内同名文件大小写也应注意。某些文件系统可能把 LICENSE、License 或 license 当作同一路径,另一些则区分;不能在不同平台上凭感觉认为文件一定存在。最好把真正命中的文件相对路径写入扩展报告,并保存摘要,以便交付人员追溯。更严格的场景还需要核对许可证文本与字段是否相匹配,但这是比“文件存在性”更高一级的内容审核,不在本文纯函数范围。
日志也不要直接打印整个许可证文件或内部私仓令牌。当前脚本只输出示例包名和状态,避免泄露路径与认证信息。未来若添加私仓下载,要明确超时、重试上限、凭据存储和离线失败策略;无法访问私仓 应是独立的采集错误,而不是把所有包都判成 HOLD_LICENSE。
七、为什么页面上仍然显示“通过两项”
配套的 ArkUI 风格演示页面分四行展示包名、状态和一个“导出核对报告”按钮。按钮代表业务页面可触发本地报告导出;结果区固定写 PASS 2 / HOLD 2,明细中 @demo/map 显示 HOLD_LICENSE,@demo/codec 显示 HOLD_NOTICE,与脚本断言保持一致。

实际使用时,按钮应先确认扫描已经完成,再导出当前有效版本的报告,避免一边刷新依赖缓存、一边把旧结果写入文件。建议对报告增加批次 ID 和输入快照哈希;同一个输入哈希对应同一份检查结果,后续追溯才能说明究竟核对了哪套依赖。扫码式面板的颜色仅便于识别,不应成为唯一的信息载体:状态文字、包名和缺项原因都要在无颜色条件下仍可读。
后续如果要接真正的 ohpm 工程,可以先运行官方的依赖查看命令,确认项目真实安装了哪些包及其层级,再为目标交付范围建立规范化清单;不要反过来把本例的四个示例包当作扫描结果。另一个实际难题是间接依赖随版本升级而变化,因此构建与交付之间应重新生成快照,避免用上周的清单证明今天的产物。
八、结论与真实能力边界
这一轮和前几轮写过的依赖覆盖版本漂移不同:版本漂移关注声明和解析结果是否相同,本篇关注已经选定的交付包是否具有完整的许可证归档材料。它也不同于资源大小写冲突检查,后者处理文件名映射风险,本篇处理包级元信息和说明材料的完整性。虽然都采用 Node.js 离线预检,但输入数据、判定规则、失败原因与工程责任完全不同。
对正式 HAR 包发布、LICENSE 文件和包管理字段的了解应以 华为 oh-package.json5 配置文档、HAR 发布指南 及 ohpm list 指南 为准。任何企业 NOTICE 归档要求都应根据自己的合规流程单独确认。
更多推荐

所有评论(0)