HarmonyOS 7 ReleaseGuard 上架审核工程实录 05:Version History × Signing:版本升级、签名连续性与分阶段发布前检查【鸿蒙心迹】
前四篇做的事情都围绕“当前这个 1.3.0 包能不能进入发布流程”。
01 看包名、版本号和 release 签名;02 看权限、隐私与三方 SDK;03 看商店文案和素材;04 再回到构建后包体,检查 deviceTypes、Profile 权限子集、HAP 签名和 packageType。
做到这里,单个候选包已经很干净了。
第五篇开始换一个视角:发布不是只判断“现在这个包对不对”,还要判断“它相对上一正式版本发生了什么变化”。
AppGallery Connect 当前提供 Version History,可以查看历史版本的包信息、新功能和审核详情;分阶段发布则面向版本更新场景,可以先按一定比例向用户分发,再逐步扩大范围,并支持暂停、恢复和调整。ReleaseGuard 05 就围绕这两条能力,把版本差异、签名身份、权限变化和灰度计划放在一张“升级预检”里。
本轮统一数据:
taskId:
release_20261002_05
上一正式版本:
1.2.9
10290
候选版本:
1.3.0
10300
版本码增量:
+10
上一版包体:
16.9MB
当前包体:
18.4MB
包体增量:
+1.5MB
+8.9%
previousSigningIdentity:
release_cert_sha256_6a8f3c1d
currentSigningIdentity:
release_cert_sha256_6a8f3c1d
signingContinuity:
true
previousDeviceTypes:
phone / tablet
currentDeviceTypes:
phone / tablet
addedPermission:
ohos.permission.READ_IMAGEVIDEO
permissionDelta:
+1
privacyReviewState:
REVIEWED
listingRevision:
listing_130_r3
phasedReleaseEligible:
true
phasePlan:
5% → 20% → 50% → 100%
checks:
11 / 11
blockers:
0
status:
VERSION_READY

一、第五篇不再扫描“当前值”,而是生成版本差异
如果只看当前包:
versionCode=10300
很难判断它是否真的比上一版新。
所以 05 的输入变成两个 Package Report:
PackageReport_1.2.9.json
PackageReport_1.3.0.json
Version History 也会被同步成 ReleaseGuard 的历史快照,用来和当前候选版本对照。
版本差异模型:
export interface VersionSnapshot {
versionName: string
versionCode: number
artifactSizeMb: number
signingIdentity: string
deviceTypes: string[]
permissions: string[]
listingRevision: string
}
export interface VersionDiff {
versionCodeDelta: number
artifactSizeDeltaMb: number
artifactSizeDeltaPercent: number
signingContinuity: boolean
addedPermissions: string[]
removedPermissions: string[]
addedDevices: string[]
removedDevices: string[]
}
这一篇不是重新解析 .app。
04 已经完成包体报告,05 只消费稳定输出。
二、版本号递增是第一条硬门槛
当前:
1.2.9 / 10290
→
1.3.0 / 10300
ReleaseGuard 只把 versionCode 当作机器比较依据:
export class VersionHistoryAudit {
compare(
previous: VersionSnapshot,
current: VersionSnapshot
): CheckItem {
const passed =
current.versionCode >
previous.versionCode
return {
key:
'VERSION_CODE_INCREMENT',
passed,
message:
`${current.versionCode} > ${previous.versionCode}`
}
}
}
versionName 仍然保留给人阅读。
这样可以避免:
1.10
1.9
这类字符串比较误判。
三、签名连续性先做“身份一致”检查,不替代官方证书校验
05 最重要的一条变化是:
上一版签名身份
=
当前候选签名身份
当前:
release_cert_sha256_6a8f3c1d
两边一致。
ReleaseGuard 的 signingIdentity 来自构建阶段证书指纹摘要,不在 ArkTS 里重新生成私钥或签名。
export class SigningContinuityAudit {
check(
previous: VersionSnapshot,
current: VersionSnapshot
): CheckItem {
const passed =
previous.signingIdentity ===
current.signingIdentity
return {
key:
'SIGNING_CONTINUITY',
passed,
message:
passed
? current.signingIdentity
: 'signing identity changed'
}
}
}
这里要把边界说清楚。
这个检查是 ReleaseGuard 的发布连续性门禁,不等于“本地工具已经替 AppGallery Connect 完成全部签名校验”。真正 release certificate、Profile 和包签名有效性仍然以当前官方发布与包解析规则为准。
四、签名身份变化要直接升级为 BLOCKER
包体大小变化可以是正常变化,签名身份变化不能轻描淡写。
如果:
previous:
release_cert_sha256_6a8f3c1d
current:
release_cert_sha256_9b22aa90
ReleaseGuard 不会给:
WARNING
而是:
SIGNING_IDENTITY_CHANGED
BLOCKER
这条规则对版本更新尤其重要。
当前 AppGallery Connect 的证书与签名配置文档也明确区分 debug 与 release 证书;发布阶段应该使用正确的 release 身份。
五、包体从 16.9MB 到 18.4MB,合法不代表不用解释
本轮:
+1.5MB
+8.9%
没有触发 Blocker。
但 ReleaseGuard 会把包体变化放进 VersionDiff。
export class ArtifactDeltaAudit {
calculate(
previousMb: number,
currentMb: number
): {
deltaMb: number
percent: number
} {
const delta =
currentMb -
previousMb
return {
deltaMb:
Number(
delta.toFixed(1)
),
percent:
Number(
(
delta /
previousMb *
100
).toFixed(1)
)
}
}
}
8.9% 只作为趋势信息。
如果下一版突然:
18.4 → 31.7MB
即使仍在平台允许范围内,也应该先确认:
是不是新增三方库
资源重复
调试文件误打包
多语言资源扩张
六、权限变化不能只显示“当前有 3 个”
上一版:
2 permissions
当前:
3 permissions
新增:
ohos.permission.READ_IMAGEVIDEO
这条变化必须关联到 02 的隐私审核结果。
当前:
privacyReviewState=REVIEWED
所以版本升级可以继续。
如果新增权限,但 02 的 Privacy Report 还没重新确认:
PERMISSION_CHANGE_NOT_REVIEWED
ReleaseGuard 直接阻断。
这就把 02 和 05 连起来了。
七、设备支持删减比新增更值得警惕
本轮:
previous:
phone / tablet
current:
phone / tablet
没有变化。
如果候选版本突然变成:
phone
ReleaseGuard 会标记:
REMOVED_DEVICE_TYPE=tablet
这种变化不一定绝对不能发布,但一定要人工确认。
因为 04 已经证明当前 .app 支持范围是发布计划的一部分,05 再比较历史后,就能发现“版本升级误删设备支持”。
八、Listing Revision 也要进入版本对比
当前:
listing_130_r3
意味着 1.3.0 的商店素材已经走到第 3 版批准快照。
05 不关心截图具体长什么样,只确认:
候选版本
已经绑定一个最新 Listing Report
如果:
version=1.3.0
listing revision 仍属于 1.2.9
就不允许 VERSION_READY。
这是 03 和 05 的连续关系。
九、分阶段发布只在版本更新阶段启用
当前官方分阶段发布说明明确:
用于版本更新
不适用于应用首次发布
并支持设置分发比例、逐步扩大用户范围,以及暂停、恢复和调整发布过程。
ReleaseGuard 因此先判断:
phasedReleaseEligible=true
因为当前是:
1.2.9 → 1.3.0
版本更新,不是首次上架。
十、5% → 20% → 50% → 100% 是项目策略,不是官方固定比例
本轮团队计划:
5%
→
20%
→
50%
→
100%
这组比例属于 ReleaseGuard 的发布策略。
AppGallery Connect 的能力是:
支持按比例分阶段发布
并允许逐步扩大。
平台并没有规定必须使用上面这四个百分比。
项目配置:
export interface PhasedReleasePlan {
eligible: boolean
phases: number[]
observationHours:
number[]
}
export const plan:
PhasedReleasePlan = {
eligible: true,
phases:
[5, 20, 50, 100],
observationHours:
[24, 24, 24, 0]
}
每一阶段至少观察 24 小时是 ReleaseGuard 当前团队策略,不是平台规则。
十一、灰度计划还要绑定“暂停条件”
如果只配置比例,没有暂停阈值,灰度就只是慢一点的全量发布。
当前计划还保存:
crash rate threshold
ANR / freeze threshold
review feedback threshold
business metric guard
真正数据可以来自 APMS、运营分析和团队自己的业务监控。
05 不把这些阈值写死成系统标准,只要求:
每个阶段有可执行的 pause condition
否则:
PHASE_PLAN_INCOMPLETE
十二、DevEco 图里最重要的是“版本差异不是当前值”
开发图:

HiLog:
taskId=
release_20261002_05
version:
1.2.9(10290)
→
1.3.0(10300)
signing:
release_cert_sha256_6a8f3c1d
→
release_cert_sha256_6a8f3c1d
continuity=
true
artifact:
16.9MB
→
18.4MB
delta:
+1.5MB
+8.9%
permissionAdded:
READ_IMAGEVIDEO
privacyReview:
REVIEWED
phasePlan:
5,20,50,100
checks:
11/11
blockers:
0
status:
VERSION_READY
这种“前后版本同时可见”的报告,才真正适合升级前检查。
十三、运行图把风险变化集中到一屏
最终运行图:

页面不再重复 01~04 的每一项明细。
只展示版本更新最关键的变化:
版本号增长
签名身份连续
包体增量
权限新增
隐私已复核
分阶段发布计划
最终:
VERSION_READY
它表达的是:
当前候选版本相对上一正式版本的变化已经被解释,没有发现阻断升级的问题。
十四、Version History 不是简单“上一版版本号查询”
AppGallery Connect 当前 Version History 可以查看历史版本的包信息、新功能和审核详情。
ReleaseGuard 最终会把历史版本快照缓存到内部报告里:
version
package
release review result
publishedAt
下一次升级时可以直接作为上一基线。
如果历史版本还在审核中,也不能把它随意当成“上一正式版本”。
版本基线要明确区分:
已发布
审核中
灰度中
已回滚
十五、签名身份检查还要防“配置名一样,实际证书变了”
不能只比较:
signingConfig:
release_prod_202610
因为配置名可能没改,底层证书文件已经换了。
所以 05 真正比较的是:
certificate fingerprint
配置名只是辅助信息。
这和 01 里“release_ 开头只是团队命名规则”保持一致。
十六、灰度发布也必须有退出路径
当前 AppGallery Connect 分阶段发布支持暂停、恢复和更新。
团队侧因此要求:
每个阶段都能暂停
异常版本有明确处置人
继续扩量必须有人确认
全量前再跑一次 release gate
这几条属于流程治理。
ReleaseGuard 不直接替人做最终发布决策。
十七、11 项版本检查怎么组成
本轮:
1 versionCode 递增
2 versionName 更新
3 signing identity 连续
4 deviceTypes 未被意外删除
5 permission delta 已识别
6 新增权限已通过隐私复核
7 包体增量在团队预警范围
8 Listing Revision 属于候选版本
9 Package Report 属于候选 artifact
10 分阶段发布资格成立
11 分阶段发布计划完整
最终:
11 / 11
blockers=0
十八、05 最后固定八组故障注入
正常链以外,我会故意制造:
1 versionCode 没递增
2 签名 fingerprint 改变
3 tablet 支持被删除
4 新增权限但 Privacy Report 未复核
5 Listing Revision 指向旧版本
6 Package Report hash 与 candidate artifact 不一致
7 首次发布却启用 phased release
8 灰度计划没有 pause condition
每一种必须得到不同风险码。
工具只有能抓住这些“升级特有问题”,才算真正从单包预检升级成版本治理工具。
十九、下一篇会把前五篇收成一个 CI Release Gate
01~05 已经分别有:
Package Preflight
Privacy Audit
Listing Audit
Package Report
Version Audit
06 不再新增业务检查。
最后只做:
把五份报告绑定同一个 releaseContextId
生成 Evidence Pack
让 CI 在进入上传步骤前自动判断
到那一步,ReleaseGuard 才真正从“几个检查页面”变成发布门禁。
二十、版本历史对比还要区分“上一正式版”和“上一候选版”
真正跑发布流水线时,历史里可能同时出现:
1.2.9
已全量发布
1.3.0-rc1
内部候选
1.3.0
当前候选
如果 ReleaseGuard 只是取“时间上最近的一条”,很容易把 rc1 当成上一正式版本。
所以历史基线必须带发布状态:
FULL_RELEASED
PHASED
UNDER_REVIEW
ROLLED_BACK
INTERNAL_ONLY
05 只把:
FULL_RELEASED
作为版本升级的主比较基线。
分阶段发布中的版本可以作为风险参考,但不替代“上一正式版”身份。
这也是为什么 Version History 快照不能只保存:
versionName
versionCode
还要保存:
releaseState
publishedAt
reviewState
否则后续 rollback、open testing、phased release 一多,比较基线就会越来越混乱。
二十一、版本回滚以后,签名与版本比较也不能回到旧逻辑
假设:
1.2.9
→ 1.3.0
→ 回滚到 1.2.9
下一次再发 1.3.1,团队很容易误以为“当前线上是 1.2.9,所以只和 1.2.9 比”。
ReleaseGuard 会保留:
lastFullRelease
latestAcceptedSigningIdentity
latestVersionCodeEverReleased
三个维度。
即使线上展示版本发生回滚,也不会把已经使用过的签名身份、版本历史和证据记录一起“倒退”。
这样版本治理不会因为一次回滚失去历史连续性。
二十二、权限新增不仅要检查隐私,也要看 Profile 是否同步
05 当前新增:
READ_IMAGEVIDEO
02 已经确认隐私披露。
04 还确认:
package permissions
⊆
release Profile permissions
所以版本对比里,新增权限需要同时满足两条证据:
Privacy Report reviewed
Package Report permissionSubset=true
缺一不可。
如果隐私已写,但 release Profile 还没更新:
PERMISSION_PROFILE_NOT_READY
仍然阻断。
这样 02、04、05 三篇的结果真正连在一起,不会出现“每篇都绿,组合起来却不能发”。
二十三、签名连续性报告最好保存“来源证据”
最终只展示:
release_cert_sha256_6a8f3c1d
对 CI 足够,但人工排查还需要知道它从哪里来。
所以 Evidence 里会同时保存:
certificate file fingerprint
profile identity
signingConfig
build task id
artifact hash
如果某次签名身份变化,可以快速判断:
是证书换了
Profile 换了
配置指错了
还是产物根本不是同一流水线
这种“来源证据”比单纯的 true/false 更有发布价值。
二十四、灰度发布计划也需要和版本风险等级联动
当前 1.3.0 的变化包括:
新增 1 个权限
包体 +8.9%
设备范围不变
签名不变
ReleaseGuard 可以给出一个团队内部风险级别:
LOW
MEDIUM
HIGH
本轮因为新增权限,所以不会直接判 LOW,而是:
MEDIUM
于是默认采用:
5 → 20 → 50 → 100
如果只是文案修复、无权限变化、无包体大幅增加,团队可能选择更快节奏。
这仍然属于项目发布策略,不是 AppGallery Connect 的平台规则。
工具只负责把变化翻译成可执行的发布建议。
二十五、每个灰度阶段都要绑定观察证据
“观察 24 小时”如果没有指标,只是等时间。
所以每一阶段还绑定:
APMS 稳定性
崩溃 / 冻屏趋势
关键接口错误率
用户反馈
核心业务完成率
真正扩量前必须有一个:
phaseEvidenceId
记录为什么从 5% 升到 20%。
这样分阶段发布不是“凭感觉点下一步”,而是有依据地扩大用户范围。
二十六、05 的最终版本报告应该回答五个问题
发布负责人看到 Version Audit,应该在一分钟内回答:
这个版本真的比上一版新吗
签名身份连续吗
权限与设备发生了什么变化
商店素材和隐私是否已经同步
分阶段发布是否有明确计划
如果报告只能告诉你:
11/11
却回答不了这五个问题,仍然不够。
所以手机图里才刻意把:
版本
签名
权限
隐私
分阶段计划
放在一起。
VERSION_READY 的真正价值不是“更多绿勾”,而是让版本变化已经被完整解释。
二十七、VERSION_READY 还要和候选包状态绑定
最后再补一条容易被忽视的规则:VERSION_READY 不是永久状态。
只要候选包重新构建、签名身份改变、权限集合变化,或者 Listing Revision 被更新,这个状态就必须失效并重新跑 05。ReleaseGuard 会把版本报告和当前 artifactHash、releaseContextId 绑定,任何输入发生变化都转成 VERSION_AUDIT_STALE。
这样发布负责人不会拿着半小时前已经过期的 11/11 报告继续灰度。版本治理真正可靠的前提,是“检查结果只对当时那份候选版本有效”,而不是一次通过以后长期绿灯。
参考资料
- AppGallery Connect:Viewing the Version History
https://developer.huawei.com/consumer/fr/doc/app/agc-help-maintain-version-history-0000002236334582 - AppGallery Connect:Phased Release
https://developer.huawei.com/consumer/cn/agconnect/phased-release - AppGallery Connect:Configuring the App Signing Certificate Fingerprint
https://developer.huawei.com/consumer/fr/doc/app/agc-help-signature-info-0000001628566748
更多推荐

所有评论(0)