610 lines
13 KiB
Markdown
610 lines
13 KiB
Markdown
# domainCheck Release Hub Contract
|
||
|
||
## 1. 目标
|
||
|
||
这份文档用于冻结 Release / Rollout / Launchpad / Gate 的正式 contract。
|
||
|
||
核心原则:
|
||
|
||
- 先有 Release,再有 Rollout
|
||
- Release 是不可变版本对象
|
||
- Rollout 是分批推进对象
|
||
- Gate 是是否可发的统一门禁对象
|
||
- Launchpad 是给页面与 Codex 消费的发布驾驶舱对象
|
||
|
||
建议与 [ops_job_contract.md](/www/wwwroot/getDomain/docs/schemas/ops_job_contract.md:1) 配套阅读,因为 Rollout 的真正执行颗粒度最终仍然是 `ops job`。
|
||
|
||
---
|
||
|
||
## 2. Release Object
|
||
|
||
来源:
|
||
|
||
- `POST /api/v1/ops/releases`
|
||
- `GET /api/v1/ops/releases`
|
||
- `GET /api/v1/ops/releases/latest`
|
||
- `GET /api/v1/ops/releases/{id}`
|
||
- `POST /api/v1/ops/releases/{id}/activate`
|
||
|
||
最小字段:
|
||
|
||
```json
|
||
{
|
||
"id": 1,
|
||
"release_version": "2026.04.18+bd6bcb2",
|
||
"channel": "stable",
|
||
"commit_sha": "bd6bcb2",
|
||
"status": "ready",
|
||
"status_label": "已就绪",
|
||
"artifact_url": "https://release.example.com/domaincheck-2026.04.18.tgz",
|
||
"checksum": "sha256:...",
|
||
"notes": "",
|
||
"metadata": {},
|
||
"created_by": "www",
|
||
"created_at": "2026-04-18 05:30:00",
|
||
"activated_at": "2026-04-18 05:35:00",
|
||
"summary": "2026.04.18+bd6bcb2 已就绪,制品与校验信息齐备,可进入 Rollout。",
|
||
"summary_text": "2026.04.18+bd6bcb2 已就绪,制品与校验信息齐备,可进入 Rollout。",
|
||
"focus_ref": {
|
||
"kind": "release_hub",
|
||
"release_id": 1,
|
||
"release_version": "2026.04.18+bd6bcb2",
|
||
"channel": "stable",
|
||
"rollout_id": 0,
|
||
"rollout_code": "",
|
||
"section": "release_detail"
|
||
}
|
||
}
|
||
```
|
||
|
||
约束:
|
||
|
||
- 同一 `release_version` 只对应一份发布物
|
||
- Release 创建后不应重新指向另一份 artifact
|
||
- `artifact_url + checksum` 是发布可验证性的最小基线
|
||
|
||
推荐状态:
|
||
|
||
- `draft`
|
||
- `ready`
|
||
- `active`
|
||
- `archived`
|
||
|
||
---
|
||
|
||
## 3. Rollout Object
|
||
|
||
来源:
|
||
|
||
- `POST /api/v1/ops/releases/{id}/rollouts`
|
||
- `GET /api/v1/ops/rollouts/{id}`
|
||
- `GET /api/v1/ops/rollouts/{id}/jobs`
|
||
- `POST /api/v1/ops/rollouts/{id}/advance`
|
||
|
||
最小字段:
|
||
|
||
```json
|
||
{
|
||
"id": 11,
|
||
"release_id": 1,
|
||
"rollout_code": "rollout-20260418-001",
|
||
"target_selector": {
|
||
"region": "mainland",
|
||
"role": "worker"
|
||
},
|
||
"target_nodes": [
|
||
"mainland-worker-01",
|
||
"mainland-controller-01"
|
||
],
|
||
"policy": {
|
||
"batch_size": 1,
|
||
"require_approval_between_batches": true,
|
||
"rollback_on_failure": true
|
||
},
|
||
"status": "running",
|
||
"status_label": "执行中",
|
||
"batch_cursor": 1,
|
||
"batches_total": 2,
|
||
"jobs_total": 2,
|
||
"jobs_created": 1,
|
||
"result_summary": {},
|
||
"target_node_codes": [
|
||
"mainland-worker-01",
|
||
"mainland-controller-01"
|
||
],
|
||
"summary": "当前已覆盖 1/2 台节点,仍有 1 条任务执行中。",
|
||
"summary_text": "当前已覆盖 1/2 台节点,仍有 1 条任务执行中。",
|
||
"focus_ref": {
|
||
"kind": "release_rollout",
|
||
"rollout_id": 11,
|
||
"rollout_code": "rollout-20260418-001",
|
||
"release_id": 1,
|
||
"section": "rollout_jobs"
|
||
}
|
||
}
|
||
```
|
||
|
||
正式定义:
|
||
|
||
> 某个 Release 在某批目标节点上的一次分批推进计划
|
||
|
||
推荐状态:
|
||
|
||
- `planned`
|
||
- `running`
|
||
- `awaiting_approval`
|
||
- `ready_for_next_batch`
|
||
- `halted`
|
||
- `completed`
|
||
- `completed_with_issues`
|
||
|
||
---
|
||
|
||
## 4. Default Rollout Gate
|
||
|
||
来源:
|
||
|
||
- `GET /api/v1/ops/overview`
|
||
- `overview.release_hub.default_rollout_gate`
|
||
|
||
这层是 Release Hub 最关键的后端 gate contract。
|
||
|
||
最小字段:
|
||
|
||
```json
|
||
{
|
||
"status": "attention",
|
||
"status_label": "待处理",
|
||
"status_type": "warning",
|
||
"summary": "当前已有 Release,但默认目标节点中仍有未接管或未通过巡检的节点。",
|
||
"summary_text": "当前已有 Release,但默认目标节点中仍有未接管或未通过巡检的节点。",
|
||
"release": {
|
||
"id": 1,
|
||
"release_version": "2026.04.18+bd6bcb2",
|
||
"status": "ready",
|
||
"status_label": "已就绪"
|
||
},
|
||
"execution_mode": "remote-agent",
|
||
"execution_mode_label": "Node Agent",
|
||
"target_node_codes": [
|
||
"mainland-worker-01"
|
||
],
|
||
"default_target_node_codes": [
|
||
"mainland-worker-01"
|
||
],
|
||
"blocking_reasons": [],
|
||
"warning_reasons": [
|
||
"存在未通过标准巡检的节点"
|
||
],
|
||
"recommendations": [
|
||
"先执行标准巡检,再进入 Rollout"
|
||
],
|
||
"operational_readiness": {
|
||
"summary": "remote-agent 就绪 1/2,标准巡检通过 1/2",
|
||
"rows": []
|
||
},
|
||
"focus_ref": {
|
||
"kind": "release_hub",
|
||
"release_id": 1,
|
||
"release_version": "2026.04.18+bd6bcb2",
|
||
"channel": "stable",
|
||
"rollout_id": 0,
|
||
"rollout_code": "",
|
||
"section": "default_rollout_gate"
|
||
}
|
||
}
|
||
```
|
||
|
||
推荐状态:
|
||
|
||
- `missing_release`
|
||
- `release_not_ready`
|
||
- `artifact_missing`
|
||
- `no_targets`
|
||
- `blocked`
|
||
- `attention`
|
||
- `ready`
|
||
|
||
要求:
|
||
|
||
- 首页驾驶建议
|
||
- Release 页面门禁
|
||
- Rollout 预检
|
||
- Codex 自动驾驶
|
||
|
||
必须共用这同一份 Gate。
|
||
|
||
---
|
||
|
||
## 5. Default Rollout Execution
|
||
|
||
来源:
|
||
|
||
- `overview.release_hub.default_rollout_execution`
|
||
- `overview.release_hub.default_rollout_gate_options`
|
||
|
||
作用:
|
||
|
||
- 告诉页面 / Codex 这轮默认应使用什么执行方式
|
||
- 给出其他可选 gate / mode
|
||
|
||
最小字段:
|
||
|
||
```json
|
||
{
|
||
"recommended_mode": "remote-agent",
|
||
"recommended_mode_label": "Node Agent",
|
||
"recommended_gate": {},
|
||
"gates": {
|
||
"remote-agent": {},
|
||
"ssh": {}
|
||
}
|
||
}
|
||
```
|
||
|
||
推荐执行模式:
|
||
|
||
- `remote-agent`
|
||
- `ssh`
|
||
- `control-plane`
|
||
|
||
说明:
|
||
|
||
- 正式发布优先 `remote-agent`
|
||
- `ssh` 只作为首发接管与紧急救援兜底
|
||
- `control-plane` 不应用来承载大规模 Worker 发布
|
||
|
||
---
|
||
|
||
## 6. Release Launchpad
|
||
|
||
来源:
|
||
|
||
- `overview.release_hub.launchpad`
|
||
|
||
作用:
|
||
|
||
- 给页面与 Codex 提供更偏“驾驶舱”的发布聚合视角
|
||
|
||
最小字段:
|
||
|
||
```json
|
||
{
|
||
"latest_package": {},
|
||
"latest_release": {},
|
||
"worker_rollout_preview": {},
|
||
"control_rollout_preview": {},
|
||
"launchpad_status": {
|
||
"status": "attention",
|
||
"status_label": "待处理",
|
||
"summary": "建议先完成 Worker 点状灰度,再决定是否推进控制面。",
|
||
"summary_text": "建议先完成 Worker 点状灰度,再决定是否推进控制面。",
|
||
"recommended_action_code": "open_release_deploy_worker",
|
||
"recommended_target_node_code": "mainland-worker-01",
|
||
"recommended_recovery_label": "签发接入工单",
|
||
"recommended_recovery_summary": "当前节点仍处于接入阶段,建议先签发 onboarding.bootstrap。",
|
||
"onboarding_bootstrap_pending_nodes": 1,
|
||
"onboarding_acceptance_ready_nodes": 0,
|
||
"recommended_mode": "remote-agent",
|
||
"recommended_mode_label": "Node Agent",
|
||
"focus_ref": {
|
||
"kind": "release_hub",
|
||
"release_id": 1,
|
||
"release_version": "2026.04.18+bd6bcb2",
|
||
"channel": "stable",
|
||
"rollout_id": 0,
|
||
"rollout_code": "",
|
||
"section": "release_launchpad",
|
||
"mode": "worker"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
`launchpad_status` 至少应回答:
|
||
|
||
- 当前先补什么
|
||
- 推荐动作码是什么
|
||
- 当前首个缺口节点是谁
|
||
- 当前恢复动作的人类标签是什么
|
||
- 当前缺口更偏“待接入”还是“待验收”
|
||
- 推荐执行方式是什么
|
||
- 先看 Worker 预案还是先看 Control 预案
|
||
|
||
正式约束:
|
||
|
||
- `recommended_target_node_code`
|
||
- 一旦 launchpad 已经收敛出首个缺口节点,就应直接返回
|
||
- 页面、CLI、Codex 不应再分别从 gap rows 中自行挑第一台
|
||
- `recommended_recovery_label`
|
||
- 是给人看的动作标题,例如:
|
||
- `签发接入工单`
|
||
- `补执行面接管`
|
||
- `执行接管验收`
|
||
- `recommended_recovery_summary`
|
||
- 是解释“为什么当前默认不是发版,而是先补接管”的正式摘要
|
||
- `onboarding_bootstrap_pending_nodes / onboarding_acceptance_ready_nodes`
|
||
- 是 launchpad 汇总层面的正式计数
|
||
- 允许被 `go-live-summary`、CLI 摘要、Codex brief 直接复用
|
||
|
||
---
|
||
|
||
## 7. 发布动作 contract
|
||
|
||
当前发布相关动作不应再退回“通用发任务”语义,而应正式区分:
|
||
|
||
- `deploy.release.control`
|
||
- `deploy.release.worker`
|
||
- `deploy.release.custom`
|
||
|
||
这三个动作必须共用:
|
||
|
||
- 同一份 Gate 判断
|
||
- 同一份 Release 版本对象
|
||
- 同一份 Rollout 编排结果
|
||
|
||
---
|
||
|
||
## 8. Artifact 不可变要求
|
||
|
||
上线前建议把 Release 供应链固定为:
|
||
|
||
1. 构建产物
|
||
2. 生成 `checksum`
|
||
3. 生成 `manifest`
|
||
4. 上传 artifact
|
||
5. 创建 Release
|
||
6. 激活 channel
|
||
7. 创建 Rollout
|
||
|
||
Node Agent 侧部署链路要求:
|
||
|
||
1. 下载 artifact
|
||
2. 校验 checksum
|
||
3. 解压到 `releases/<version>`
|
||
4. 切换 `current`
|
||
5. 重启服务
|
||
6. 健康检查
|
||
7. 失败回滚
|
||
|
||
---
|
||
|
||
## 9. Rollout 与 Ops Job 的关系
|
||
|
||
约束:
|
||
|
||
- Rollout 是分批推进对象
|
||
- Ops Job 是单次动作对象
|
||
- Rollout 通过一批或多批 `deploy.release.*` Job 落地
|
||
|
||
控制面责任:
|
||
|
||
- 决定下一批是否生成 Job
|
||
- 汇总 Job 结果
|
||
- 反推 Rollout 状态
|
||
- 决定是否暂停、继续或回滚
|
||
|
||
Node Agent 不负责:
|
||
|
||
- 推进下一批
|
||
- 修改 Rollout 策略
|
||
- 修改 Release 状态
|
||
|
||
---
|
||
|
||
## 10. 页面 / Codex 共同消费约束
|
||
|
||
以下对象必须作为稳定后端 contract 存在:
|
||
|
||
- `default_rollout_gate`
|
||
- `default_rollout_execution`
|
||
- `launchpad_status`
|
||
- `worker_rollout_preview`
|
||
- `control_rollout_preview`
|
||
|
||
页面只负责展示和少量交互兜底。
|
||
|
||
Codex 只负责消费后端给出的:
|
||
|
||
- 当前是否可发
|
||
- 建议先发哪一侧
|
||
- 应该使用什么 mode
|
||
- 下一步推荐动作是什么
|
||
|
||
---
|
||
|
||
## 11. Artifact Manifest Contract
|
||
|
||
Release Hub 不能只停在:
|
||
|
||
- `artifact_url`
|
||
- `checksum`
|
||
|
||
这只能证明“有个包”,还不能证明“这个包会把哪些服务改成什么样”。
|
||
|
||
建议把 `release.metadata.manifest` 固定为正式对象。
|
||
|
||
最小结构:
|
||
|
||
```json
|
||
{
|
||
"artifact_kind": "tarball",
|
||
"package_name": "domaincheck-2026.04.18+bd6bcb2.tgz",
|
||
"package_size_bytes": 186542331,
|
||
"checksum": "sha256:...",
|
||
"build_id": "build-20260418-001",
|
||
"commit_sha": "bd6bcb2",
|
||
"built_at": "2026-04-18 05:20:00",
|
||
"included_services": [
|
||
"domaincheck-api",
|
||
"domaincheck-worker",
|
||
"domaincheck-sync-agent",
|
||
"domaincheck-node-agent"
|
||
],
|
||
"required_env_keys": [
|
||
"NODE_CODE",
|
||
"NODE_REGION",
|
||
"NODE_ROLE"
|
||
],
|
||
"health_checks": [
|
||
{
|
||
"name": "api_health",
|
||
"type": "http",
|
||
"url": "http://127.0.0.1:8100/health"
|
||
}
|
||
],
|
||
"rollback_hint": "切回上一个 current 并重启对应服务"
|
||
}
|
||
```
|
||
|
||
正式要求:
|
||
|
||
- Release 创建后,`manifest` 不应再被覆盖成另一份语义不同的发布物
|
||
- Node Agent、页面、Codex 必须共用这同一份 manifest
|
||
- 是否可发、怎么回滚、要验哪些服务,不应再散落在:
|
||
- shell 脚本
|
||
- 页面表单
|
||
- 人工脑补
|
||
|
||
---
|
||
|
||
## 12. Operational Readiness Row Contract
|
||
|
||
`default_rollout_gate.operational_readiness.rows[]` 不应只是“几行提醒文案”,而应成为逐节点门禁对象。
|
||
|
||
最小结构:
|
||
|
||
```json
|
||
{
|
||
"node_code": "mainland-worker-01",
|
||
"region": "mainland",
|
||
"role": "worker",
|
||
"agent_managed": true,
|
||
"execution_mode_ready": true,
|
||
"execution_mode": "remote-agent",
|
||
"execution_mode_label": "Node Agent",
|
||
"inspection_status": "healthy",
|
||
"inspection_status_label": "已通过",
|
||
"current_release_version": "2026.04.11+1921318",
|
||
"desired_release_version": "2026.04.18+bd6bcb2",
|
||
"risk_level": "low",
|
||
"blocking_reasons": [],
|
||
"warning_reasons": [],
|
||
"recommended_action_code": "",
|
||
"focus_ref": {
|
||
"kind": "release_hub",
|
||
"release_id": 1,
|
||
"release_version": "2026.04.18+bd6bcb2",
|
||
"channel": "stable",
|
||
"rollout_id": 0,
|
||
"rollout_code": "",
|
||
"section": "default_rollout_gate"
|
||
}
|
||
}
|
||
```
|
||
|
||
正式要求:
|
||
|
||
- `rows[]` 应能直接回答:
|
||
- 这台节点能不能进下一轮 rollout
|
||
- 不能的话卡在哪
|
||
- 只是 warning 还是 hard block
|
||
- 页面、CLI、Codex 不应再从:
|
||
- inspection
|
||
- runtime
|
||
- nodes
|
||
三处数据手工拼门禁结论
|
||
|
||
推荐风险分级:
|
||
|
||
- `low`
|
||
- `medium`
|
||
- `high`
|
||
- `blocked`
|
||
|
||
---
|
||
|
||
## 13. Rollout Preview Contract
|
||
|
||
真正创建 Rollout 之前,还应保留一层正式 preview,而不是由页面自己预估批次和风险。
|
||
|
||
建议入口:
|
||
|
||
- `POST /api/v1/ops/releases/{id}/rollouts/preview`
|
||
|
||
最小请求体:
|
||
|
||
```json
|
||
{
|
||
"release_id": 1,
|
||
"target_node_codes": [
|
||
"mainland-worker-01",
|
||
"mainland-controller-01"
|
||
],
|
||
"policy": {
|
||
"batch_size": 1,
|
||
"require_approval_between_batches": true,
|
||
"rollback_on_failure": true
|
||
},
|
||
"execution_mode": "remote-agent"
|
||
}
|
||
```
|
||
|
||
最小返回体:
|
||
|
||
```json
|
||
{
|
||
"release": {},
|
||
"execution_mode": "remote-agent",
|
||
"execution_mode_label": "Node Agent",
|
||
"target_nodes_total": 2,
|
||
"target_node_codes": [
|
||
"mainland-worker-01",
|
||
"mainland-controller-01"
|
||
],
|
||
"expected_batches_total": 2,
|
||
"expected_jobs_total": 2,
|
||
"gate": {},
|
||
"batch_plan": [
|
||
{
|
||
"batch_no": 1,
|
||
"node_codes": [
|
||
"mainland-worker-01"
|
||
],
|
||
"risk_level": "low"
|
||
},
|
||
{
|
||
"batch_no": 2,
|
||
"node_codes": [
|
||
"mainland-controller-01"
|
||
],
|
||
"risk_level": "medium"
|
||
}
|
||
],
|
||
"summary": "建议先灰度 1 台 Worker,再推进 Controller。",
|
||
"summary_text": "建议先灰度 1 台 Worker,再推进 Controller。",
|
||
"focus_ref": {
|
||
"kind": "release_hub",
|
||
"release_id": 1,
|
||
"release_version": "2026.04.18+bd6bcb2",
|
||
"channel": "stable",
|
||
"rollout_id": 0,
|
||
"rollout_code": "",
|
||
"section": "rollout_preview"
|
||
}
|
||
}
|
||
```
|
||
|
||
正式要求:
|
||
|
||
- preview 是:
|
||
- 页面发 rollout 前的统一预检
|
||
- Codex 自动驾驶发 rollout 前的统一预检
|
||
- CLI 批量发布前的统一预检
|
||
- 不允许前端重新自己算:
|
||
- 该分几批
|
||
- 哪台应该先发
|
||
- 哪台属于高风险
|
||
|
||
只有这样,Release Hub 才是真正的发布控制面,而不是“能看版本的页面”。
|