5b65ed9579
按草案逐条落到正本,并把三处已知错误的口径改回: ① 测试语义:删掉"单测跑的是构建产物,不是源码"这条被过度推广的说法。 只有"跑产物"的套件(端到端 / 契约 / 产物自检)才必须排在构建之后; go test 自己编译测试程序、pytest 直读源码、vitest 默认也跑源码。 写错这条的代价是给每个套件加假依赖、拖慢门禁并制造假故障。T8 同步改。 ② CI 与 CD 分离:触发表删掉"推默认分支 → 自动部署测试环境",作业划分表删掉 deploy:test 行, §4.6 标注为"项目自己的 CD、不属于通用 CI",速查表同步。 ③ 门禁入口:统一为 make verify(名字可不同,但 AGENTS/Runbook/CI 必须指同一个), §3.1、§8 清单、§9 速查表一致。 ④ 新增 §4.8「必需检查清单与结果口径」:10 项必需检查的判据表 + 三条结果口径 (失败即停但汇总照做、not_run/skipped 不算过、汇总故障不能报绿), 以及预览/通知作为旁路的规则。 ⑤ 接入文档:修掉"直推也拦得住"这句错口径,并写明接入器不再自动合并、 --merge-pr 会先核对状态;试点记录里那一步标注为"当时的自动合并行为已改"。 ⑥ 03/04/05 里的同源表述一并同步(生成物 jobs 与假成功占位的判据)。 验证:本批 18 处精确替换在脚本里逐处断言"恰好命中 1 次"(DRY-RUN 先过一遍再应用); 替换后全仓 grep 复查,旧口径只在"说明它已被改掉"的上下文里出现。
11 KiB
11 KiB
模板评估与修正(附件 eng-standards-main.zip)
结论先说:原稿的"地基"(四条铁律、反向自检、"为什么"写在正文、门禁串行失败即停)全部保留; 但它整体是 GitLab / glab 口径,而本环境是 Gitea 1.26.2 + act_runner + 自建 ci-bridge + 阿里云双机。 逐条评估结果如下。每条的"证据"都是本次在本机/实例上实测到的,不是推断。
一、结论
| 分类 | 数量 | 处理 |
|---|---|---|
| A. 平台错配(GitLab 语义在本环境不存在) | 6 | 全部改写为 Gitea 口径 |
| B. 与实测事实矛盾(原稿断言不成立) | 3 | 修正断言,写清成立条件 |
| C. 模板内部自相矛盾 / 数值不可信 | 3 | 改掉矛盾点 |
| D. 具体取值是他人环境的 | 3 | 全表换成"本环境事实"(README.md §1) |
| E. 覆盖缺口(原稿根本没约束) | 1 | 新增铁律(同一份构建产物,规范 §5.1) |
A. 平台错配(6 处)
A1. 触发体系:workflow: rules / $CI_* 全部不存在
- 原稿:
.gitlab-ci.yml的workflow.rules用$CI_PIPELINE_SOURCE == "web"与$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH两条件。 - 实测:本实例是 Gitea Actions,
.gitea/workflows/*.yml,触发在on:;可用变量是GITHUB_*(GITHUB_REF_NAME、GITHUB_REPOSITORY、GITHUB_SERVER_URL)。 - 修正:见规范 §4.1,三类入口写成
on.push.branches与on.workflow_dispatch。原稿那条"刻意不接 tag"的判断保留,理由改写(见 A2)。
A2. 手动作业语义:when: manual 的"阻塞型"论证是 GitLab 专有
- 原稿:手动作业没有
allow_failure: true时流水线永远到不了success(停在blocked),取消会把已成功作业一起标canceled。 - 实测:Gitea 里
if:不成立的作业是 skipped,不会把运行挂死;生产发布的正确形态是独立的workflow_dispatch运行。 - 修正:保留原稿的真问题("取消 ≠ 回滚"、"不要重写会抹掉证据"),但改挂到 Gitea 的真实风险上:
concurrency.cancel-in-progress会取消正在进行的部署。生产 workflow 必须cancel-in-progress: false。
A3. resource_group 在 Gitea 1.26.2 不存在
- 原稿:同一环境的部署用
resource_group: <env>防并发。 - 实测:Gitea 没有
resource_group;concurrency:只对同 workflow 同 group 生效,跨 workflow / 人手手动执行挡不住。 - 修正:互斥下移到目标机(
flock -n,规范 §5.4)。这是"可靠性只在这一层"的取舍,必须写明。
A4. 变量作用域:Gitea 没有 environment scope,也没有 File 类型 secret,勾选坑不同
- 原稿:GitLab CI/CD Variables 用
environment scope区分环境;有 "Expand variable reference" / "Masked" / "Protected" 三个勾选坑;私钥用 File 类型(因为多行勾不了 Masked)。 - 实测:Gitea 仓库变量/密钥没有环境作用域;私钥只能整体存 secret(无 File 类型);三个勾选坑不存在。
- 修正:环境区分改用命名后缀(
DEPLOY_TEST_*/DEPLOY_PROD_*);私钥由运行时脚本落盘 0600(规范 §7.1 / §7.3)。 另有接口形状要写对(实测):变量POST/PUT …/actions/variables/{name}body{"value":…};密钥只有PUT …/actions/secrets/{name}body{"data":…};集合级 POST 返回 405 空 body(像权限问题,实际不是)。
A5. CLI:glab → giteactl / gitea_api.py
- 原稿:§6 整章讲
glab,含projects/<ns>%2F<repo>显式路径坑(细粒度只读 token 下:id占位符会 403)。 - 实测:本机没有
glab;有~/.local/bin/giteactl(runs/show/logs/watch/rerun)、~/.hermes/scripts/gitea_api.py、giteacli(本机 token 缺read:user,whoami/login会失败但仓库功能可用)。 - 修正:§9 速查表与 §02 Runbook 换成 giteactl/gitea_api.py 的真实命令。原稿"读日志用显式路径、不用交互式 TUI、trace 要清洗"的经验保留,但换成 Gitea 的日志接口地址。
A6. GIT_DEPTH: 1 / after_script + $CI_JOB_STATUS 通知 / tags: [docker]
- 原稿:CI 变量
GIT_DEPTH: 1;通知用after_script拿$CI_JOB_STATUS;Runner 标签docker。 - 实测:本环境是自 clone 模型(
git fetch --depth 50);通知走.gitea/scripts/ci_report.py+if: always()步骤 + 主机侧 bridge;Runner 标签是ubuntu-latest/24.04/22.04。 - 修正:全部替换。原稿"通知失败不能让作业失败"、"可能的话两条通知链路分开"的判断保留。
B. 与实测事实矛盾(3 处)
B1. "不用任何第三方 action" —— 本环境不成立
- 原稿:文件头写"不用任何第三方 action(作业容器不联网拉 action)",并把它当铁律。
- 实测:
aiball/software-pc的.gitea/workflows/validate.yml用了actions/checkout@v4+actions/setup-node@v4,run#1 = success(GET /repos/aiball/software-pc/actions/runs显示completed success)。而 Weknora 三仓的 runner 上工作流刻意自 clone。 - 修正:规则改成按 Runner 配置区分(规范 §4.3):默认不用
uses:;例外要在文件头写明该 runner 支持取 action。并给出一条判据:"时红时绿且报拉取失败"先怀疑uses:。
B2. .gitignore 模板与正本条款自相矛盾
- 原稿:正本 §1.3 明说"处理办法不是加
!例外(例外越加越多,规则本身就没人看得懂了)";但templates/gitignore里就给了!src/secrets/和!src/secrets/**。 - 实测:原稿自己的
spec-check.mjs的ARTIFACT_PREFIX正则包含secrets/—— 于是"按模板加了例外、又真把src/secrets/入库"的工程会被tracked-artifacts判红。 - 修正:模板去掉
!例外,改为一句话:"别在源码路径里用dist/data/secrets这些名字"(见templates/gitignore)。
B3. spec-check.mjs 的 AGENTS_MAX_BYTES = 32768 与正本数字不一致
- 原稿:正本 §2.4 写"实测 65983 字节时尾部 739 字节被静默截断";而守门判据硬编码
AGENTS_MAX_BYTES = 32768。 - 矛盾点:正本自己立了规矩"文档里不许出现会漂移的数字,要么由脚本打印要么写成断言"(§4.3),却在这里放了一个来源不明的常量。
- 修正:本次不引入该脚本(任务边界是"只制定规范"),但在
03-待配置事项.md第 16 项写明:改写 Gitea 版守门时,上限值必须由一次真实测量得出并写成断言,不许手抄。
C. 模板内部问题(3 处)
C1. templates/verify.mjs 写死跨机路径
- 原稿:门禁第 5 步
cmd: [process.execPath, '__STANDARDS_DIR__/scripts/spec-check.mjs', ...]—— 一个绝对路径,在 CI 容器里根本不存在(原稿自己的validate:repo注释也承认了这一点)。 - 修正:门禁入口只跑本仓的东西;规范守门要么随仓携带(
.gitea/scripts/),要么在 CI 里先git clone规范仓库到固定路径。见templates/gitea-build.yml的validate作业注释。
C2. spec-check.mjs 两条判据过窄/无对象
ci-cache-node-modules:只匹配- node_modules/这一种写法;本环境的缓存是主机挂载目录(/opt/citools-cache),判据完全够不着。glab-placeholder:在 Gitea 环境里没有glab,判据无对象(永远是 ok)。- 修正:说明见
03-待配置事项.md第 16 项。同时保留原稿两条好经验:判据要认"这件事"而不是"那种写法"(原稿自己在 v1.0.4 里踩过)、并且"报错文案里也不能出现针的字面量"(会被下一次扫描命中)。
C3. check_env.sh 的必需键是按"网关 + Cookie"项目定的
- 原稿:
REQUIRED="ENV_NAME APP_DIR DOMAIN COOKIE_NAME",并断言"两个环境的父域 + Cookie 名不能同时相同"。 - 实测:该不变量属于原稿样板项目(带 nginx + Cookie 的网关)。本环境的项目是 Go 服务(DobotAI / knowledge-governance / adapter),没有这一对不变量;DobotAI 的环境参数在生产机
/opt/dobotai/config/dobotai.env(不入库),也不是deploy/env/*.env。 - 修正:本规范不照搬该脚本。保留它的形状("仓库层面的不变量放在 CI 校验,在传任何字节之前失败"),但要按项目定键(规范 §7.2)。
D. 具体取值是他人环境的(3 处 → 全表替换)
| 原稿取值 | 本环境实际 |
|---|---|
https://gitlab.artificialheart.cn/liuzhufu/...、默认组 liuzhufu |
http://192.168.50.223:3000(https://gitea.zheromiku.top);组 Weknora / aiball |
Runner 标签 docker;镜像 node:24-alpine |
标签 ubuntu-latest/24.04/22.04 → docker://docker.gitea.com/runner-images:* |
APP_DIR=/volume1/docker/...(群晖 NAS)、域名 *.artificialheart.cn / *.dobotoa.cn |
阿里云 ECS:测试 120.76.217.51、生产 120.76.218.137,路径 /opt/dobotai/... |
以上全部换成了 README.md §1 的唯一取值表,并给了取数命令(避免手抄漂移)。
E. 覆盖缺口(1 处 → 新增铁律)
E1. 「生产使用测试验收过的同一份构建产物」原稿没有约束
- 原稿:§5.5 讲产物按生命周期拆包、§5.7 讲目标机三层布局与回滚,但没有要求"测试与生产部署同一份产物"。
- 实测:本环境现状恰恰相反 —— DobotAI 的既有流程是在目标机上各自 build(生产机 clone 源码 +
docker build),测试与生产不是同一份二进制。 - 修正:作为本次规范的第一优先级新增(规范 §5.1):构建只做一次 → 产出
image_id+tar_sha256写进发布清单 → 测试与生产都docker load同一份 tar 并各自断言image_id相同。并给出完整的证据链表(§5.1)与"迁移不属于镜像、先迁移后切镜像"的例外。
F. 保留的(原稿最有价值的部分,不要因为"平台换了"就丢掉)
- §0 四条地基:结论只来自可复现命令 / 每个校验都要反向自检 / 密钥不进命令行 / 提交前跑门禁。
- §3.2 八条通用铁律:只有"跑产物"的套件要排在构建之后(不是"所有单测都依赖预构建产物")、干净 clone、反向自检三问(会红吗 / 会假绿吗 / 真的作用在目标上吗)、断言要有唯一命中点、夹具要打扫、先确认无遗留进程。
- §2.2 正文写"为什么" 的四种要素(现象/根因/取舍/证据)与示例形状。
- §2.3 heredoc 禁忌(反引号
$()会被 shell 先执行,且退出码仍是 0)。 - §1.3
.gitignore是契约 与两条反查命令。 - §5.4 密钥只走 stdin 写文件、
chmod 600、去 CRLF。 - §5.7 目标机三层布局 与"停容器/换目录必须在同一个脚本里、顺序不许挪"。
- §5.6 不缓存
node_modules。 - §2.4 陷阱库拆两份(指令文件放索引、旁边放全文,因为指令有字节预算)。
- §11 规范自己怎么被管理(单一来源、投影、可检验、有版本)—— 见本目录的
README.md与03-待配置事项.md第 13 项。