Commercial GA 是发布批次门禁,不是日常 CI 的最后一个普通 Job。它只验证受控 release workflow 已经产生的不可变 artifact,不从产品源码现场重新打包。
触发前要准备什么
| 输入 | 由谁提供 | 作用 |
|---|---|---|
| release run id、artifact name | 发布 workflow | 定位不可变候选产物 |
| 40 位 release commit | 发布负责人/自动化 | 绑定源码与 provenance |
| 认证 HTTPS Feed URL | 包平台 | 真实客户还原入口 |
| 短期只读 credential | 身份与 Secret 平台 | 最小权限访问 Feed |
| signer fingerprints | 安全/发布团队 | 限定受信签名者 |
| vulnerability threshold | 安全治理 | 定义漏洞阻断线 |
| third-party license policy | 法务/开源治理 | 定义许可证准入 |
| external Consumer artifact | release workflow | 仓库外消费对象 |
Artifact 约定
候选 artifact 至少包含:
packages/ 已签名的不可变 nupkgconsumer/ 一个外部 .slnx 及其项目provenance.json 包、hash、commit、signer 与证据索引SBOM / policy evidence包版本发布后不可覆盖。修复必须产生新版本和新 provenance。
验证顺序
检查外部前置变量 → 校验 artifact 布局 → 校验 provenance schema 与 release commit → 校验包集合、版本和 SHA-256 → dotnet nuget verify --all → 校验 signer 集合、SBOM、漏洞和许可证策略 → 外部 Consumer 空缓存 restore/build/test/publish顺序很重要。包 hash 或 commit 已不一致时,没有必要继续消费测试,因为候选批次的身份已经不可信。
为什么必须 fail-closed
以下做法都会制造“看起来通过”的假证据,因此被禁止:
- Feed 不可用时改用本地文件夹;
- 下载失败后从当前源码重新 pack;
- 没有 signer 配置时跳过签名验证;
- SBOM 或策略文件缺失时只打印 warning;
- 用产品仓库里的解决方案代替外部 Consumer。
运行与诊断
先检查前置变量:
scripts/build/verify-commercial-ga.sh --check-prerequisites缺输入时会列出变量并返回失败。补齐后,由 GitHub Actions Commercial GA workflow 注入 artifact 路径并运行完整验证。
| 输出 | 含义 | 负责人 |
|---|---|---|
external prerequisite unavailable | 外部发布输入未准备好 | 发布/平台团队 |
| provenance schema/hash mismatch | artifact 内容或索引不一致 | release workflow 维护者 |
| signer mismatch | 签名者不在受信集合 | 安全与发布团队 |
| policy evidence failed | 漏洞或许可证策略未通过 | 安全/开源治理 |
| Consumer restore/build failure | Feed、包闭包或模板消费回归 | 框架与包维护者 |
脚本要求的精确环境变量
--check-prerequisites 只检查变量非空,不验证它们指向的内容;完整运行才执行 schema、hash、签名、策略和 Consumer 验证。
| 变量 | 内容 |
|---|---|
BITZORCAS_COMMERCIAL_FEED_URL | 无内嵌凭据、query 或 fragment 的 HTTPS Feed |
BITZORCAS_RELEASE_PACKAGES_DIR | release artifact 的 packages/ |
BITZORCAS_RELEASE_PROVENANCE_PATH | provenance.json 路径 |
BITZORCAS_RELEASE_GIT_COMMIT | 40 位受控 release commit |
BITZORCAS_TRUSTED_SIGNER_FINGERPRINTS | 非空 SHA-256 signer 指纹集合 |
BITZORCAS_VULNERABILITY_THRESHOLD | 必须与 provenance/扫描证据一致的阈值 |
BITZORCAS_THIRD_PARTY_LICENSE_POLICY | 必须与许可证策略证据一致的策略名/阈值 |
BITZORCAS_APPROVED_LICENSE_EXPRESSIONS | 第三方许可证 SPDX 准入表达式白名单 |
BITZORCAS_RELEASE_CONSUMER_SOLUTION | vendor 仓库之外的唯一 .slnx |
BITZORCAS_RELEASE_PUBLISH_PROJECTS | 以平台路径分隔符连接的 publish 项目 |
NuGetPackageSourceCredentials_BitzOrcasCommercial | NuGet 认可的短期 Feed Credential |
# 只显示变量是否存在,不输出任何 Secret 值。for name in \ BITZORCAS_COMMERCIAL_FEED_URL \ BITZORCAS_RELEASE_PACKAGES_DIR \ BITZORCAS_RELEASE_PROVENANCE_PATH \ BITZORCAS_RELEASE_GIT_COMMIT \ BITZORCAS_TRUSTED_SIGNER_FINGERPRINTS \ BITZORCAS_VULNERABILITY_THRESHOLD \ BITZORCAS_THIRD_PARTY_LICENSE_POLICY \ BITZORCAS_RELEASE_CONSUMER_SOLUTION \ BITZORCAS_RELEASE_PUBLISH_PROJECTS \ NuGetPackageSourceCredentials_BitzOrcasCommercialdo # 间接展开变量名;只输出 present/missing 状态。 test -n "${!name:-}" && printf '%s=present\n' "$name" || printf '%s=missing\n' "$name"done不要把这组变量写入仓库脚本或共享 shell history。正式 workflow 从 GitHub Variables、Secrets、下载后的 artifact 和受控 inputs 注入。
provenance 与包身份检查
验证器会:
- 用
0012-commercial-release-provenance.schema.json校验结构; - 确认 provenance commit 等于 workflow 的受控 commit;
- 确认 signer 集合与受控指纹集合完全相等;
- 确认物理 nupkg、provenance 声明和 commercial catalog 的 packageId 集合完全一致;
- 校验每个文件的 SHA-256(nupkg 另携带 SHA-512,与 NuGet global-packages 表示对齐)、nuspec id/version 和依赖;
- 拒绝源码、PDB、Source Link、本机路径和 Credential 标记;
- 校验 SBOM 覆盖所有发布包身份和包依赖;
- 拒绝未解决策略 finding 与已过期 exemption。
同一个 packageId/version 如果出现不同 hash,会被视为不可变性破坏。缺包和多包都失败,不采用“至少包含目录里的包”这种宽松集合判断。
外部 Consumer 验证
GA 在临时目录写入带 <clear /> 与 Package Source Mapping 的 NuGet.Config,把所有 BitzOrcas.* 强制指向正式 Feed,并隔离 NuGet packages、HTTP、plugin 与 CLI home。
对每个包先运行带受信指纹的 dotnet nuget verify --all,然后对外部 Solution 依次 Restore、Release Build、Release Test,并 Publish 清单中的每个项目。命令 15 分钟超时后会终止整个进程组,失败输出在回传前脱敏。
当前 GA publish 没有强制传入 PublishTrimmed=true。是否裁剪取决于外部项目自身设置;若商业 Profile 宣称裁剪安全,应把该属性固化到 release Consumer 并新增明确断言。
workflow_dispatch 操作
正式 GitHub Actions workflow 只接受三个人工输入:release run id、artifact name 和完整 release commit。它以该 commit checkout 验证器代码,再从指定 run 下载候选 artifact。
下载后必须恰好找到一个顶层 Consumer .slnx 和至少一个 .csproj;否则以 external prerequisite unavailable: release Consumer artifact layout 失败。不要手工改 $GITHUB_ENV 指向仓库内 Solution。
通过后的切换
GA 通过只是允许进入切换窗口。随后仍要按 runbook 验证配置、readiness、License 状态、可观测性、数据库兼容和回滚条件,并在观察期内保留上一版本。
治理清单
GA 门禁与运行时强制执行依赖一组可评审的治理清单,维护在源码仓库 docs/architecture/00-governance/manifests/。它们是生产就绪的机器可读真相源,不是文档散文。
| 清单 | 范围 |
|---|---|
0001-module-governance-legacy-ledger.json | 遗留模块根 ratchet |
0002-production-adapter-readiness.json | 适配器就绪守卫 |
0003-operations-runtime-surface.json | 运维运行时可见面 |
0004-template-upgrade-map.json | 模板版本升级 map |
0005-test-drift-ledger.json | 测试漂移分类 |
0006-persistence-surface-classification.json | 实体/持久化面分类 |
0008-error-catalog.json | 强类型错误目录(1,968 码,`jq ‘.entries |
0009-error-catalog-legacy-baseline.json | 遗留双段错误码基线 |
0010-commercial-package-catalog.json | 商业包 / profile / 许可目录 |
0011-profile-package-closure.json | profile 包闭包 + 可运行组合 |
0012-commercial-release-provenance.schema.json | 发布来源 schema(上文引用) |
0013-runtime-license-policy-catalog.json | 共享运行时许可策略 + 静态组合边界 |
(编号从 0006 跳到 0008;无 0007。)这些清单支撑的完整治理叙事与门禁见治理区。
GA 完成清单
- workflow inputs 绑定到受信 release run 和 40 位 commit;
- 下载 artifact 与 provenance/package 物理集合完全一致;
- 所有 nupkg 的 hash、身份、内容和 signer 验证通过;
- SBOM 同时覆盖发布身份和传递依赖;
- 漏洞与第三方许可证策略通过且豁免未过期;
- 外部 Consumer 从正式 HTTPS Feed 空缓存 Restore;
- Consumer 的 Release Build、Test 与所有指定 Publish 通过;
- Runtime License、迁移、readiness、监控和回滚 runbook 已在目标环境验收;
- 发布记录保存 workflow run、artifact、provenance、策略证据和批准人;
- 观察窗口结束前未删除上一版本或破坏回滚路径。
任何一项缺失时,状态是“GA 未完成”或“外部前置条件未就绪”,不是“部分 GA 通过”。