文件
hyb-oa/.trellis/tasks/archive/2026-08/08-02-deployment-preflight-backup-contract-refresh/implement.md
T
wangsli 6d89fdd9da
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s
chore(task): 归档问题盘点、制单与部署体检任务
2026-08-03 22:49:16 +08:00

68 行
6.6 KiB
Markdown
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
# 部署前聚合体检与备份合同自动刷新:实施计划
## 0. 边界与回滚点
- [x] 用户批准本规划后才运行 `task.py start`;批准规划不等于批准生产安装。
- [x] 实施阶段只修改仓库代码、测试、spec/runbook;不连接生产、不改 root `authorized_keys`、不安装 forced-command、不部署。
- [x] 保留 `deploy.sh` 首次初始化/应急兼容;`deploy-existing.sh` 的自动强制启用与生产 forced-command 安装安排在另行批准的同一窗口。
- [x] 不删除旧源码目录、旧环境文件、旧 unit 或备份制品。
## 1. 共享合同读取与聚合体检
- [x] 提取六键 systemd Environment allowlist 的共享只读解析逻辑,供 `deploy-existing.sh` 和新体检入口使用;任何输出不包含值。
- [x] 新增聚合器与固定检查表,主流程不用 `set -e`,每项子 shell 隔离、显式收集状态,最后统一返回。
- [x] 实现普通部署身份的仓库根、可写性、env 可读性、PGDATA/附件和运行容器 mount/build 核对。
- [x] 实现独立 `DEPLOY_REFRESH_SERVER` 的 transport/probe 核对,并把身份不可用与程序不可用分开分类。
- [x] 让 `deploy-existing.sh` 在任何远端写入前要求体检全绿;保留既有 remote preflight。
## 2. root-owned 自包含刷新程序
- [x] 新增无仓库运行时依赖的 forced-command target:固定 PATH/umask、清理危险环境、精确解析 `SSH_ORIGINAL_COMMAND`。
- [x] `probe` 验证 root 身份、固定工具、root-owned unit 与固定目标前提,只输出固定标识。
- [x] `refresh` 取得固定锁,从 root-owned unit 与唯一 running server 解析活动 build,拒绝调用方路径/SHA/unit 输入。
- [x] 以原子 root-owned systemd drop-in 覆盖四个 service 的两项 build 环境,daemon-reload 后验证最终 SHA。
- [x] 验证四个 timer enabled/active;用稳定退出码区分 unit 未刷新和 timer 未运行。
- [x] 不调用 repository installer,不使用 `eval`、动态 shell、动态 unit 名或部署用户可写可执行文件。
## 3. forced-command 安装器
- [x] 将 target 安装到固定 `/usr/local/sbin`,验证 root ownership、mode 和非 symlink。
- [x] 读取显式专用公钥,拒绝同 key blob 的不受限 root 授权;只原子维护自身 managed authorized_keys 行并保留其他 key。
- [x] forced-command 选项禁用 agent/port/X11/PTY/user-rc,安装完成后执行本地静态合同检查;不把 installer 本身加入免密或 forced-command。
- [x] 增加卸载/回滚说明,但不在无独立批准时自动删除授权行或 drop-in。
## 4. 部署后接线与状态表达
- [x] `deploy-existing.sh` 只在 `deploy.sh` 完整成功后调用 `refresh`。
- [x] 成功时才打印完整部署完成;失败时固定输出 `DEPLOYED_BUT_CONTRACT_REFRESH_FAILED`、四类之一并退出非零,不转发敏感 stderr。
- [x] 不增加 optional/跳过刷新开关;新版已有部署入口缺少 `DEPLOY_REFRESH_SERVER` 即在写前阻塞。生产窗口先安装并 probe forced-command,再运行新版 wrapper。
- [x] 更新 runbook/checklist,明确两个 SSH 身份、forced-command 专用 key、部署前聚合报告、后刷新非零语义和生产安装单独批准。
## 5. 测试与门禁
- [x] 新增聚合器 fixtures:多个同时失败、首项失败仍执行末项、依赖失败、固定输出顺序和最终退出码。
- [x] 新增脱敏 fixtures:底层输出含伪造路径/主机/env/容器 ID 时,外层报告不泄露。
- [x] 新增 forced-command fixtures:空命令、未知命令、附加参数、probe、refresh、锁、原子 drop-in、四 service SHA 与四 timer。
- [x] 覆盖四类后刷新失败:root transport、程序 126/127、unit SHA、timer 状态;断言 wrapper 非零且不打印完整成功。
- [x] 扩展 deployment safety contract,锁定体检在 mkdir/rsync 前,refresh 在 `/ready`、smoke 和公网检查后,且 helper 不执行仓库路径。
- [x] 运行:
- `bash -n`(所有新增/修改 shell)
- `node deploy/trialrun/validate-env-contract.js`
- `cd server && npm test -- tests/unit/scripts/deployment-safety-contract.test.js`
- 新增聚合/forced-command fixture runner
- `bash scripts/qa/run-automatic-backup-contract.sh`
- `python3 ./.trellis/scripts/task.py validate 08-02-deployment-preflight-backup-contract-refresh`
- Trellis governance、process contract、docs registry、sensitive scan、`git diff --check`
## 6. 审查、提交与生产门
- [x] 实施后执行只读安全/部署 reviewer,重点核查 forced-command key 是否真正受限、root target 是否执行部署用户可写内容、聚合器是否短路、状态分类是否准确;reviewer 不改代码。
- [x] 用 `trellis-update-spec` 更新 deployment-trialrun 的稳定合同,避免只把知识留在 task。
- [x] 中文提交并引用 `08-02-deployment-preflight-backup-contract-refresh`;不自动提交、不自动部署。
- [x] 获得独立生产批准后,安装专用 forced-command/key,完成真实 `probe`、安装完整性复核和 13 项只读聚合体检;未部署应用、未调用 `refresh`、未清理旧目录。
- [x] 下一次真实发布使用新版 wrapper 验证自动 `refresh` 与完整成功语义;2026-08-03 发布完整通过,应用与公网检查成功后 forced-command `refresh` 返回成功,部署后 13 项体检仍为 `PASS=13 FAIL=0`。
本地验证(2026-08-02):聚合/forced-command fixture 通过,覆盖 13 项汇总、多个并发失败不短路、脱敏输出、四类收尾映射、精确命令白名单、四个 build drop-in、四个 timer、managed key 更新及不受限重复 key 拒绝;deployment safety Vitest 9 passed;环境合同通过;automatic-backup 合同 73 checks passed;全部 shell/Node 语法通过。只读复查发现 forced-command target 缺少运行时自身 ownership/mode/path 校验,已补 `validate_self()`;最终复核又将 systemd build 校验从子串匹配收紧为精确键与最后覆盖值,并用 `OLD_BUILD_SHA` 回归证明不会静默误判。相关聚合、环境合同和 Vitest 均复跑通过;复查未发现新的 root 提权、动态执行或部署顺序 blocker。该本地验证阶段未连接、安装或部署生产。
生产安装证据(2026-08-02,脱敏):现有 root 管理身份与普通部署身份均唯一定位;专用 key/alias 已建立,root-owned target、managed authorization、临时 staging 清理及已提交源码摘要全部 PASS;forced-command `probe` PASS;生产只读聚合体检 `PASS=13 FAIL=0`。未运行 `refresh`,未部署应用,未删除历史源码目录或环境文件。