灯光模拟HarmonyOS应用实战-95-versionCode、versionName与buildVersion都存在为何仍会发错包:用ReleaseVersionLedger绑定升级证据
app.json5里已经写了versionCode、versionName和buildVersion,并不等于版本治理已经完成。只要团队允许同一个versionCode对应两份不同字节的 APP,或构建产品覆盖了配置却没有在制品侧回读,应用市场、测试人员和开发者看到的“1.0.0”就可能不是同一个包。
The_kemusan/AppScope/app.json5当前给出versionCode=1000000、versionName=1.0.0、buildVersion=1;已有pack.info也记录了同一组三元值。静态一致只能说明这份配置与这份历史描述文件表面相符,不能证明它们对应今天要交付的源码,更不能证明1000000尚未在其他发布中使用。
本文把三个字段放进ReleaseVersionLedger:分配、构建、包内回读、制品摘要与发布结论必须落到同一条不可变记录。第 94 篇关注“产物从哪里来”,本篇只解决“新旧版本如何排序、展示与追踪”。
一、先把当前三元组当成事实,不当成发布结论
当前应用级配置可以缩成下面这段:
{ "app": { "bundleName": "com.example.the_kemusan", "versionCode": 1000000, "versionName": "1.0.0", "buildVersion": "1" } }根build-profile.json5的products/default没有为这三个字段提供覆盖值,所以按当前文件可以预期应用级值进入构建。但“预期”仍需在包内回读。构建工具、产品变体、流水线注入或之后的配置修改,都可能让最终制品与工作区文件不同。
本机历史pack.info记录的公开版本结构是:
{"version":{"code":1000000,"name":"1.0.0","build":"1"}}它说明那份描述文件中的值一致,没有证明这组三元组可以再次发布。是否已在渠道占用,只能从发布账本和渠道记录确认。
二、三个字段分别回答三个问题
华为app.json5文档说明,versionCode是用于判断版本新旧的数值,新版本必须使用更大的值;versionName是面向用户展示的版本名称。buildVersion是构建版本标识,近期工具链还允许在工程级产品配置中覆盖版本字段。华为 app.json5 配置 华为工程级 build-profile.json5
| 字段 | 主要问题 | 合法但危险的做法 | 账本约束 |
|---|---|---|---|
versionCode | 哪个版本更新 | 同一数值构建多份不同 APP | 已发布后不可复用 |
versionName | 用户看到什么 | 修复包仍显示旧名称 | 与发布说明绑定 |
buildVersion | 哪一次构建 | 多模块值不一致或无法追踪 | 同一发布单元保持一致 |
三者不应相互替代。把versionName从1.0.0改为1.0.1,却不增加versionCode,不能建立新的升级顺序;只增加versionCode而不登记构建摘要,也不能区分两次内部重打包。
三、先解析“有效版本”,再讨论是否可发布
版本值可能来自应用级配置,也可能被产品配置替换。建议把解析结果连同来源一起返回:
exportinterfaceVersionField<T>{value:T;source:'APP_JSON5'|'PRODUCT_OVERRIDE';}exportinterfaceEffectiveReleaseVersion{versionCode:VersionField<number>;versionName:VersionField<string>;buildVersion:VersionField<string>;}exportfunctionresolveField<T>(baseValue:T,overrideValue:T|undefined):VersionField<T>{if(overrideValue!==undefined){return{value:overrideValue,source:'PRODUCT_OVERRIDE'};}return{value:baseValue,source:'APP_JSON5'};}解析器只负责说明值来自哪里,不负责批准发布。若字段缺失、类型错误或产品名未知,应返回结构化失败,不能偷偷使用上一轮流水线环境变量。
四、ReleaseVersionLedger 记录一次分配的完整身份
账本条目至少包含应用身份、三元组、源码身份、构建身份和制品摘要:
exportinterfaceReleaseVersionEntry{ledgerVersion:number;bundleName:string;channel:string;product:string;versionCode:number;versionName:string;buildVersion:string;sourceRevision:string;buildId:string;appSha256:string;status:'ALLOCATED'|'BUILT'|'VERIFIED'|'PUBLISHED'|'RETIRED';allocatedAtUtc:string;}ALLOCATED表示编号已占用但还没有制品;BUILT才允许写入 APP 摘要;VERIFIED需要包内版本与计划完全一致;PUBLISHED需要渠道回执。状态只能单向推进,失败构建也不应把已经分配的versionCode还给另一份源码。
五、同一 versionCode 不得对应不同制品
发布门禁的关键不是“版本号有没有增加”,而是历史中是否已经出现同一个应用、渠道和versionCode。若存在,就比较摘要与状态:
exportfunctionguardVersionReuse(history:ReleaseVersionEntry[],candidate:ReleaseVersionEntry):string[]{constissues:string[]=[];for(constitemofhistory){if(item.bundleName!==candidate.bundleName||item.channel!==candidate.channel||item.versionCode!==candidate.versionCode){continue;}if(item.appSha256!==candidate.appSha256){issues.push('VERSION_CODE_REUSED_WITH_DIFFERENT_ARTIFACT');}else{issues.push('VERSION_CODE_ALREADY_REGISTERED');}}returnissues;}即使摘要相同,也不应重新创建一条“新发布”记录;可以引用原条目继续补充缺失阶段。摘要不同则必须阻止,因为测试报告和线上问题将无法唯一指向某个二进制。
六、buildVersion 要在多模块发布单元中一致
当前pack.info列出 entry HAP 和libraryHSP,目标 API 为 23。华为打包工具文档说明,从 API version 23 起,同一 APP 中所有 HAP/HSP 的buildVersion需要保持一致。这个规则说明buildVersion不是只写在应用级文件里供人阅读;多模块打包时还必须在包内逐个核对。华为打包工具
exportinterfaceModuleVersionReceipt{moduleName:string;moduleType:string;buildVersion:string;}exportfunctionfindBuildVersionMismatch(modules:ModuleVersionReceipt[]):string[]{if(modules.length===0){return['NO_MODULE_VERSION_RECEIPT'];}constexpected:string=modules[0].buildVersion;returnmodules.filter((item)=>item.buildVersion!==expected).map((item)=>`BUILD_VERSION_MISMATCH:${item.moduleName}`);}这段函数只比较解析后的受控字段。真正的包内信息应由拆包工具读取;不能遍历任意 JSON 后把未知字段原样打印,因为构建目录可能含有不适合公开的材料。
七、回滚也要形成新的版本记录
线上回滚常被误解成“把旧 APP 再发一次”。如果渠道要求versionCode单调增加,正确做法是以新versionCode构建一份恢复旧业务行为的新产物,同时记录它基于哪个历史版本和哪些补丁。
exportinterfaceRollbackPlan{newVersionCode:number;displayVersionName:string;buildVersion:string;behaviorBaselineVersionCode:number;reasonCode:string;}exportfunctionvalidateRollback(plan:RollbackPlan,latestPublishedCode:number):string[]{constissues:string[]=[];if(plan.newVersionCode<=latestPublishedCode){issues.push('ROLLBACK_VERSION_NOT_NEWER');}if(plan.reasonCode.length===0){issues.push('ROLLBACK_REASON_MISSING');}returnissues;}behaviorBaselineVersionCode表示恢复哪一版业务行为,不表示复用那一版二进制。新制品仍需重新构建、签名、回归和发布,并拥有自己的摘要与阶段回执。
八、配置值与包内值必须双向对账
构建前先冻结计划版本;构建后从候选 APP 读取包内pack.info。两者任一字段不同,都应停止交付:
exportfunctioncompareVersion(plan:ReleaseVersionEntry,packed:EffectiveReleaseVersion):string[]{constissues:string[]=[];if(plan.versionCode!==packed.versionCode.value){issues.push('PACKED_VERSION_CODE_MISMATCH');}if(plan.versionName!==packed.versionName.value){issues.push('PACKED_VERSION_NAME_MISMATCH');}if(plan.buildVersion!==packed.buildVersion.value){issues.push('PACKED_BUILD_VERSION_MISMATCH');}returnissues;}对账方向是“账本计划 → 包内事实”。不能在发现包内值不同后自动修改账本去迁就制品;那会掩盖产品覆盖、缓存输出或流水线注入造成的偏差。
九、发布前验证矩阵
| 编号 | 条件 | 期望结果 | 证据 |
|---|---|---|---|
| V95-01 | 首次分配未使用的versionCode | 创建 ALLOCATED 条目 | 账本唯一键 |
| V95-02 | 同 code、不同 APP 摘要 | 阻止 | 固定问题码 |
| V95-03 | versionName改了、code 未增加 | 阻止新发布 | 最新渠道记录 |
| V95-04 | 产品覆盖版本 | 记录来源为 PRODUCT_OVERRIDE | 有效配置快照 |
| V95-05 | entry 与 HSP buildVersion 不同 | 阻止 APP | 包内模块回执 |
| V95-06 | 计划与 pack.info 完全一致 | 推进 VERIFIED | 三字段比较结果 |
| V95-07 | 需要回滚旧行为 | 新 code、新摘要 | RollbackPlan |
| V95-08 | 构建失败 | 保留已分配编号 | FAILED 构建记录 |
测试应同时准备正向与反向样本。只验证当前1000000 / 1.0.0 / 1能被解析,不能证明重复编号、覆盖值和多模块不一致会被门禁拦下。
十、常见版本错配与排查顺序
| 现象 | 先核对 | 常见原因 | 修复 |
|---|---|---|---|
| 市场认为不是新版本 | versionCode与渠道最新值 | 只改了 versionName | 分配更大的 code |
| 设置页显示名称正确,测试包行为旧 | APP 摘要与源码修订 | 取到历史制品 | 重新生成来源回执 |
| 本地配置与 pack.info 不同 | 产品覆盖来源 | build-profile 覆盖或缓存 | 清理构建并回读有效值 |
| APP 打包时提示模块版本不一致 | 各 HAP/HSP buildVersion | 模块来自不同构建 | 同一 buildId 重新构建 |
| 回滚包无法覆盖安装 | 新旧 versionCode | 复用了旧二进制 | 用新编号重建回滚版本 |
| 同 code 有两份测试报告 | APP SHA-256 | 重打包未换编号 | 废弃冲突产物并登记原因 |
排查时先从渠道最新versionCode、账本唯一键和 APP 摘要开始,再回到配置来源。仅比较展示名称,很容易把“用户看到的版本”错当成“系统判断的新旧顺序”。
十一、让每个版本只能指向一份可解释制品
三个版本字段不是三个相近的文案。versionCode建立升级顺序,versionName面向用户表达,buildVersion追踪构建并约束多模块一致性;ReleaseVersionLedger再把它们与源码、构建 ID、APP 摘要和渠道阶段绑定。这样一次发布失败、重试或回滚,都不会把同一编号解释成不同二进制。
本文只核对当前app.json5、工程级产品配置与历史pack.info的公开版本字段,没有修改版本号,没有运行构建,没有生成或验签 APP/HAP,没有安装、升级、回滚或上传应用市场。账本模型、门禁与测试示例属于建议方案,接入时还需按目标 DevEco Studio、API 版本和渠道规则复核字段支持与发布流程。