From 49141fe6f061765b72eb3345f6052efe31bbad6d Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Sat, 24 Oct 2026 11:55:28 +0800 Subject: [PATCH] =?UTF-8?q?ci:=20=E6=8A=8A=20check-ci-gates.py=20=E6=8E=A5?= =?UTF-8?q?=E8=BF=9B=20go=20job=EF=BC=8CCI=20=E8=87=AA=E5=B7=B1=E6=A3=80?= =?UTF-8?q?=E6=9F=A5=E8=87=AA=E5=B7=B1=E7=9A=84=E9=85=8D=E7=BD=AE=E4=B8=8D?= =?UTF-8?q?=E8=AE=B8=E9=9D=99=E9=BB=98=E9=80=80=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 背景:.github/scripts/check-ci-gates.py 上一轮已写好并入仓(ed8f28e), 但它**没被任何 workflow 调用** —— `grep check-ci-gates .github/workflows/ci.yml` 零命中。等于判据只活在评审口径里,CI 实际上不跑它。本次把它接上。 ## 一、ci.yml:新增 2 步(go job,插在 go vet 之后、go test 之前) - `actions/setup-python@v7`(3.12) - `CI 配置门禁自检(动态枚举 / 排除表 / 启动图判据不许静默退化)` 位置为什么在 go test ./... **之前**而不是之后: · 该步骤是纯静态检查 —— 只读 ci.yml,秒级,无网络、无 cgo、 不建 SQLite、不占端口;而 go test ./... 要跑 20 分钟。 · 「门禁本身坏掉」应该第一时间红。放后面会让一次坏配置白白消耗 20 分钟机器时间才发现,也让失败信号埋在 93 个包的输出后面。 runner 的 pyyaml **不假设自带**:先 `python3 -c 'import yaml'` 探测, 缺了才 `pip install pyyaml`(幂等,几秒)。本机实测 pyyaml 6.0.3 已有, 但本机的 Python 环境不等于 runner —— 所以做探测而不是赌。 ## 二、顺手纳管 internal/kernelboot/modules/registry.go 的注释改动 该文件 +6/−3 的改动是**纯注释**,内容为 11:08 那轮已拍板要记进文件头的说明: 真相源为什么刻意不扫 `kernelmod.Register(...)` 调用点 —— 删掉某模块的 init() 后调用点消失,源码侧与登记表侧会同时少一个, 比对照样相等 ⇒ 假绿。手写模块名清单同理。故真相源取 「目录+类型名复合键 + 四方法齐全」的类型,而不是要检测对象的入口。 ★ 这点在 commit message 里点明,是为了不让它变成夹带:它不是本次新写的代码, 是上一轮已定但未提交的说明文字,与本次「门禁入 CI」同一主题(判据不许假绿)。 ## 三、验证(先证能红,再证能绿,都不靠嘴说) 从 ci.yml 里**抽出该 step 的 run 块原样执行**(不是手敲等价命令): · 变色前(动态枚举改回写死 ./internal/agent/core/ ./cmd/waiter/) → EXIT=1,报 `race job 应恰有一步用 go list ./... 动态枚举包(实际 0 步)` · 还原后 → EXIT=0,报「CI 门禁自检通过:动态枚举 / 排除表校验 / 启动图判据步骤 / 交叉枚举 / 全部 run 步骤语法」 另在临时副本里抽查 5 个变异,4 个如期变红(写死枚举 / 去掉 -count=1 / -race 不吃枚举 / cross 写死 cmd)。第 5 个「删掉排除表的原因校验」**没红** —— 已定位为脚本自身缺口,见下条,不在本次授权范围故未改。 ## 四、已知遗留(未修,留给后续) check-ci-gates.py 第 100 行用的是 `any("没有写原因" in r for ...)` —— 它只检查**提示文案还在**,不检查那段 `if [ -z "${reason// /}" ]` 校验逻辑本身。 于是把条件改成 `if false;` 而保留文案,断言仍然绿(实测 EXIT=0)。 判据被注释/文案顶包,与它自己在 docstring 里批判的「注释骗过 grep」是同一类错误。 属于该脚本的缺陷,本次任务授权只到「接进 CI」,故只记录不修。 --- .github/workflows/ci.yml | 33 +++++++++++++++++++++++++ internal/kernelboot/modules/registry.go | 9 ++++--- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 29121703..626a3abe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,6 +62,39 @@ jobs: - name: go vet ./... run: go vet ./... + # ── CI 配置门禁自检(.github/scripts/check-ci-gates.py)── + # + # ## 为什么 CI 要检查自己的配置文件 + # + # ci.yml 是「静默退化」的高发区:把 race 门的动态枚举改回写死包列表, + # 流水线照样绿(只是罩得少了),没人会发现。该脚本用 yaml.safe_load + # 解析(丢掉注释 ⇒ 「注释里写一句同名机制」顶不了包),断言这些仍在: + # · race 门动态枚举 + 真剔除 cmd/gui + go test -race 真吃了枚举结果 + # · -skip 排除表每项都写了原因(缺原因那一步自己会失败) + # · go job 的「整机启动图判据」步骤还在、且带 -count=1 + # · cross job 的 cmd 清单是实测枚举、构建真吃枚举结果 + # · 每个 run 步骤的 bash 语法 + # + # ★ 位置刻意放在 go test 之前:它是纯静态检查(只读 ci.yml,秒级, + # 无网络、无 cgo、不建库不起端口),而 go test ./... 要跑 20 分钟。 + # 门禁本身坏掉应该第一时间红,不该等全量测试跑完才发现。 + # + # ★ 不假设 runner 自带 pyyaml:先探测、缺了才装(幂等,几秒)。 + # 「我以为 runner 有」正是假红/假绿的来源,代价比显式装一行高得多。 + - uses: actions/setup-python@v7 + with: + python-version: '3.12' + + - name: CI 配置门禁自检(动态枚举 / 排除表 / 启动图判据不许静默退化) + run: | + set -euo pipefail + if ! python3 -c 'import yaml' 2>/dev/null; then + echo " pyyaml 缺失,安装" + python3 -m pip install --quiet pyyaml + fi + python3 -c 'import yaml; print(" pyyaml", yaml.__version__)' + python3 .github/scripts/check-ci-gates.py + # ./... 不点名 cmd/gui(该目录是纯 Electron,无 .go 文件): # 显式 `go test ./cmd/gui` 会报 "no Go files",那是误报,不是缺陷。 - name: go test ./... diff --git a/internal/kernelboot/modules/registry.go b/internal/kernelboot/modules/registry.go index 0cf121c8..f288e599 100644 --- a/internal/kernelboot/modules/registry.go +++ b/internal/kernelboot/modules/registry.go @@ -21,9 +21,12 @@ // // 判据有两条(缺一不可): // -// · `TestEveryRegisteredModuleIsCompiledIn` —— 用 AST 扫源码树里的 -// `kernelmod.Register(X{})`,与登记表做集合比对(独立真相源, -// 手写清单会跟着漏,所以不用清单) +// · `TestEveryRegisteredModuleIsCompiledIn` —— 用 AST 扫源码树里 +// 「目录+类型名 复合键 + Name/Requires/Provides/Init 四方法齐全」的类型, +// 取其 Name() 与登记表做集合比对(独立真相源)。 +// ★ 刻意**不**扫 `kernelmod.Register(...)` 调用点:删掉某模块的 init() 后 +// 调用点消失,源码侧与登记表侧会同时少一个 ⇒ 比对照样相等 ⇒ 假绿。 +// 手写模块名清单同理(清单会跟着漏),两者都不用。 // · `TestRealRegistryAssemblesWholeMachine` 等 —— 用真实登记表装配整机、 // 断言每个模块都进图且设施就绪、并严格逆序关闭(见同目录测试文件) package modules