用 Node 脚本做 HarmonyOS 发布前静态自检:把项目约束写成代码

📅 2026/8/25 5:51:40
用 Node 脚本做 HarmonyOS 发布前静态自检:把项目约束写成代码
为什么已经有编译器还需要自检脚本ArkTS 编译器会检查语法和类型却不会知道你的产品约束Bundle ID 必须等于备案和市场后台中的值只允许一个设备认证权限中英日资源 Key 必须完全一致应用图标不能被小尺寸占位图替换本地优先 App 不应该突然加入网络权限统计页必须保留审核回归功能。这些规则如果只写在 README 中很容易在修改时被忽略。更可靠的方式是文档说明为什么 脚本检查有没有违反一、脚本不需要一开始就做成复杂工具项目使用普通 Node ESMimportfsfromnode:fs;importpathfromnode:path;constrootpath.resolve(newURL(..,import.meta.url).pathname);constreadJson(relative)JSON.parse(fs.readFileSync(path.join(root,relative),utf8));失败函数统一设置退出码constfail(message){console.error(FAIL:${message});process.exitCode1;};这样可以一次报告多个问题而不是遇到第一项就退出。二、检查 Bundle ID 和版本格式constappreadJson(AppScope/app.json5).app;constexpectedBundle你的稳定 Bundle ID;if(app.bundleName!expectedBundle){fail(bundleName must be${expectedBundle});}if(!Number.isInteger(app.versionCode)||app.versionCode0){fail(versionCode must be a positive integer);}if(!/^\d\.\d\.\d$/.test(app.versionName)){fail(versionName must use x.y.z format);}编译器允许很多合法字符串但团队可以定义更窄的版本规则。这里检查的是格式和固定身份。是否比上一个发布版本递增还需要读取发布记录、Git Tag 或 CI 参数。三、把允许权限做成白名单从模块配置读取constmoduleProfilereadJson(entry/src/main/module.json5).module;constpermissionsmoduleProfile.requestPermissions.map((item)item.name).sort();项目只允许设备认证constexpectedPermissions[ohos.permission.ACCESS_BIOMETRIC].sort();if(JSON.stringify(permissions)!JSON.stringify(expectedPermissions)){fail(unexpected permission set:${permissions.join(, )});}白名单比“禁止几个高风险权限”更适合小型本地应用任何新权限都会让检查失败开发者必须同步更新代码、用途文案、隐私政策和测试清单。四、比较三种语言的资源 KeyletcanonicalEntryKeys[];for(constlocaleof[base,zh_CN,ja_JP]){conststringsreadJson(entry/src/main/resources/${locale}/element/string.json).string;constnamesstrings.map((item)item.name);if(newSet(names).size!names.length){fail(${locale}has duplicate string keys);}constsortedNamesnames.slice().sort();if(localebase){canonicalEntryKeyssortedNames;}elseif(JSON.stringify(sortedNames)!JSON.stringify(canonicalEntryKeys)){// 输出 missing 和 extra}}这个检查能捕获新增中文文案但漏英文/日文拼写错误导致某语言多出 Key同一个文件内重复定义删除功能时只删了一种语言。它不能检查翻译质量但至少保证资源结构完整。五、应用名称资源也要单独检查桌面应用名称位于 AppScope不是页面模块资源。for(constlocaleof[base,zh_CN,ja_JP]){constappStringsreadJson(AppScope/resources/${locale}/element/string.json).string;constnamesappStrings.map((item)item.name);if(!names.includes(app_name)){fail(${locale}AppScope is missing app_name);}}否则应用内已经是日文桌面名称却可能回退基础语言。六、用文件特征防止资源被占位符替换项目检查图标文件大小for(consticonof[AppScope/resources/base/media/app_icon.png,entry/src/main/resources/base/media/icon.png]){conststatfs.statSync(path.join(root,icon));if(stat.size10000){fail(${icon}looks like a placeholder);}}文件大小不是严格的视觉验证但可以捕获常见事故真实图标被一个很小的临时 PNG 覆盖。可以继续加强读取图片宽高检查必须为 1024×1024检查颜色模式检查不含透明通道生成发布素材总览供人工确认。七、扫描本地优先架构的明显回退项目不使用网络因此验证脚本检查for(constforbiddenof[ohos.permission.INTERNET,http.request(,ohos/axios]){if(indexSource.includes(forbidden)){fail(unexpected network capability:${forbidden});}}这是低成本保护但必须认识局限只能扫描列出的文件字符串可能出现误报无法识别所有间接依赖第三方包内部能力不一定出现在页面源码不是安全审计或依赖分析的替代品。随着项目变大可以改成遍历所有.ets/.ts/.json5再结合依赖清单和权限配置检查。八、为审核回归功能保留“哨兵”某些功能曾因重构丢失可以用简单字符串做哨兵for(constrequiredof[uiRevision,refreshUi(),this.named(completed_checkins),this.named(reflection_cards),this.named(summary),eligibleDayIds(habit: Habit)]){if(!indexSource.includes(required)){fail(review regression coverage is missing:${required});}}这种检查比真正的 UI 自动化测试弱但能快速发现大段功能被误删。命名或架构重构时哨兵也需要更新所以它更适合作为短期回归护栏而不是永久规范。九、不要让静态脚本读取或打印真实签名秘密发布检查经常需要确认“签名已配置”但脚本不应该把密码、密钥路径或证书内容输出到日志。正确检查目标可以是公共仓库不包含已知敏感字段发布环境中必要文件存在构建产物已经签名CI Secret 名称存在但不打印值日志中对路径和凭据做脱敏。可以增加敏感模式扫描但只输出文件和字段名称不输出匹配到的秘密正文。一旦秘密进入 Git 历史仅从当前文件删除还不够通常还需要轮换。十、JSON5 解析是一个隐藏细节示例使用JSON.parse()读取.json5前提是当前文件内容实际上符合严格 JSON没有注释、尾随逗号或未加引号的 Key。如果以后开始使用完整 JSON5 语法脚本会解析失败。可选方案团队约定这些配置保持严格 JSON 子集引入 JSON5 解析库复用构建工具提供的配置解析能力。文件扩展名不等于实际解析器能力这一点应该在脚本说明中写清楚。十一、如何接入日常流程本地执行nodescripts/validate_project.mjs建议放在修改资源后 → 静态自检 → ArkTS 编译 → HAP/APP 构建 → 真机/模拟器回归如果使用 CI可以把脚本作为构建前置步骤。退出码非 0 时停止后续发布。但不要因为脚本通过就跳过人工审核。它适合检查确定性约束不适合判断截图是否好看、翻译是否自然、隐私文案是否准确。十二、适合继续增加的检查项版本号与上次发布记录比较三语言格式化占位符一致隐私政策 URL 使用 HTTPS 且可访问所有公开 URL 不指向测试域名设备类型与 QA 覆盖矩阵匹配HAP/APP 产物存在且时间为本次构建图标尺寸和 Alpha 通道Markdown 发布文档不含敏感信息签名配置未进入公共变更集导出结构版本与代码常量一致。总结一个几十行的自检脚本可以承担四类职责应用身份Bundle ID 和版本合规边界权限与网络能力资源完整性多语言和图标回归哨兵关键功能没有被误删。它的价值不在于替代编译器和测试而是把项目独有的约束变成每次都能重复执行的检查。本文案例来自“心晴手记MoodMemoir”HarmonyOS 项目的validate_project.mjs。参考资料HarmonyOS 应用开发知识地图