feat(saas): enforce signed release evidence and verified deployments

This commit is contained in:
2026-09-11 12:11:28 +08:00
parent 7293a9c0fb
commit f8f063e946
9 changed files with 921 additions and 104 deletions

View File

@@ -1,3 +1,7 @@
本项目固定使用私服发行的 Core 8.1.0、Module Runtime 9.0.4 和 RPC 9.0.7;精确依赖、锁文件与逐文件校验基线一并提交。
V2 发布与受控开发监听以 [RELEASE-V2.md](RELEASE-V2.md) 为准;历史 V1 操作说明不适用于新 V2 候选。
# COOL SaaS 模块微服务起始项目
这是一个可复制到 `cool-midway-biz/microservices/<service-name>` 的干净起始项目。
@@ -54,36 +58,42 @@ pnpm build
生成后可随时执行 `pnpm exec cool saas-model verify` 检查当前分支、候选锁、生成文件与
控制面的精确 Candidate 是否一致。`pnpm dev` 会先执行同一校验,再调用
`package.json` 中 `coolSaas.lifecycle.dev` 的非递归命令构建并启动原有开发宿主;因此
配置里不要写回 `pnpm dev`。项目接入自己的 watch/reload 工具后,可将该命令替换为对应
的开发启动命令。
配置里不要写回 `pnpm dev`。持续开发使用 `pnpm exec cool saas-model dev --watch`,由外层
CLI 捕获源码身份、构建独立快照、排空旧实例并发布新代次。不要用仅重启内层宿主的 watch
替代这个入口;依赖或候选锁变化需要结束当前 watch 后重新启动。
CLI 生成的 `models.ts`、`contracts.ts`、`saas-actions.ts`、`saas-events.ts`、
`module.ts`、`index.ts` 和锁文件属于同一个不可变 Build,请整体保留且不要手工修改。
## 固化 Candidate
开发联调完成后,先提交并将当前 release 分支推送到 `origin`,然后执行:
开发联调完成后,停止开发 watch,提交并推送当前候选分支,然后使用外部 builder 配置:
```powershell
pnpm run publish
pnpm run publish --builder-config <外部绝对配置路径>
```
该脚本只调用 `cool saas-model finalize`。CLI 从 `package.json` 的 `coolSaas.lifecycle`
读取并依次执行 `test` 与 `build`(本模板分别为 typecheck 和原有 build),确认工作区仍然
干净且当前提交已推送后,创建并推送 `v<candidate-version>` Tag,最后向控制面幂等提交
freeze 证据。生命周期子命令不得调用 `pnpm dev`、`pnpm publish` 或再次调用
`cool saas-model dev/finalize`,以免递归。
`publish` 脚本执行 `cool saas-model finalize`,不会发布 npm 包或直接晋升 Stable。
Core 8.1.0 先领取平台签名构建任务,从已推送的 Git 提交提取独立源码目录,复制并核对实际
依赖,再按 `coolSaas.lifecycle` 执行测试和构建。源码、启动脚本、依赖在构建后发生变化
都会拒绝固化。CLI 签名保存完整 bundle、推送版本 Tag,并幂等提交平台 freeze。
生命周期子命令不能再次调用 `pnpm dev`、`pnpm publish` 或 `cool saas-model dev/finalize`。
freeze 成功后,原来的 development 实例会因身份不再匹配而停止参与路由。再次执行
`pnpm dev`,CLI 会读取候选的 Frozen Artifact,只允许从完全一致且干净的 Git commit
以 `runtimeMode=frozen` 启动。请用这个固化实例完成最终验收;验收通过后才能在管理端
晋升 Stable。`accepted` 状态也可以用同一命令重启精确固化实例,但已晋升、拒绝、替代
或放弃的候选会被拒绝。
保存 CLI 输出的 frozen deployment directory 及其外部构建回执。目录内包含已经构建的
`dist`、启动器和独立依赖;不要重新安装、构建或修改其中内容。freeze 后原 development
身份不能继续参与路由,V2 候选也不能再用 `pnpm dev` 重建。配置精确候选 Release ID、
BUILD_SHA、独立 Runtime 票据/状态、租户凭证、外部 builder 公钥信任文件和短期制品读取凭证,
从该目录执行 `pnpm run start:frozen`,再由平台的专用 runner 执行自动验收。
控制面地址与凭证继续使用 `COOL_SAAS_CONTROL_PLANE_URL`、`COOL_SAAS_TOKEN`,或 CLI 的
`--endpoint`、`--token` 参数。CLI token 只用于校验/freeze 请求,不会写入候选锁,也
不会传给测试、构建或开发宿主子进程。freeze 调用
`/internal/saas/module_release/freeze`,只发送 Bearer 凭证,不依赖浏览器 Cookie。
管理员查看不可变报告,选择有效的 run 单独批准,随后晋升 Stable。Stable 使用同一份
deployment directory 与独立运行身份执行 `pnpm run start:stable`。已晋升来源 RC 如需继续
运行,也必须保留原制品和有效关联;拒绝、放弃、撤销或关联失效的候选不能重新启动。
完整配置、回执恢复和验收顺序见 [RELEASE-V2.md](RELEASE-V2.md)。
控制面地址与凭证使用 `COOL_SAAS_CONTROL_PLANE_URL`、`COOL_SAAS_TOKEN`,或 CLI 的
`--endpoint`、`--token` 参数。CLI token 不写入候选锁,也不传给测试、构建或开发宿主
子进程。freeze 调用 `/internal/saas/module_release/freeze`,使用 Bearer 凭证和受限的二进制
请求体传输完整 bundle,不依赖浏览器 Cookie。
## 依赖与发布
@@ -116,7 +126,7 @@ SAAS_CONTROL_PLANE_URL=https://control-plane.example.com
生产环境由受信部署编排器在应用配置之外完成以下工作:
- 签发绑定精确 Build/Release 的 bootstrap ticket,并注入 `BUILD_SHA`、Redis 和
RPC HMAC 材料;Release 身份及摘要由控制面权威响应回填,不要求手工填写。
每个进程独立的 Ed25519 私钥及受限公钥信任文件;Release 身份及摘要由控制面权威响应回填。
- 为每个副本分别挂载短期、一次性且绑定模块/Build/Release/制品的
`SAAS_RUNTIME_BOOTSTRAP_TOKEN_FILE`,不能让两个副本共用一个票据。票据签发结果会
同时返回并绑定该副本的 `instanceId`。