diff --git a/02-流程说明与Runbook.md b/02-流程说明与Runbook.md index 5550668..0143a17 100644 --- a/02-流程说明与Runbook.md +++ b/02-流程说明与Runbook.md @@ -268,4 +268,23 @@ tail -n 50 /srv/aiball/ci/logs/bridge.log | 通知到了但预览 404 | 产物包的 `.` 根成员被拒绝 | 规范化成员名(§4.7) | | 构建随机报原生模块加载失败 | 缓存里混了平台相关依赖 | 不缓存 `node_modules` | | "登录成功却又要重新登录" | 两个环境共用「父域 + Cookie 名」 | 改其中一个 DOMAIN 或换 COOKIE_NAME | -| 境外请求全 000 | mihomo 节点被钉死 | `PUT /proxies/三毛机场 {"name":"<活节点>"}` | \ No newline at end of file +| 境外请求全 000 | mihomo 节点被钉死 | `PUT /proxies/三毛机场 {"name":"<活节点>"}` | + +--- + +## 附:第二轮修正后的入口与自检命令(都可直接跑) + +| 想确认什么 | 命令 | 期望 | +|---|---|---| +| 门禁清单与识别到的语言 | `python3 scripts/ci_checks.py --list` | 打印必需清单;识别不到语言 ⇒ 明说"会判红" | +| 汇总/前端判据/pytest 退出码语义 | `python3 scripts/ci_checks.py --self-test` | 全过;含「进程非零必红」「不写死 vitest」 | +| 报告能不能采信(发布前这一道) | `python3 .gitea/scripts/ci_report.py --verify-only` | 0=可采信 / 1=不可采信 / 2=核不了 | +| 上报核对逻辑 | `python3 .gitea/scripts/ci_report.py --self-test` | 11 分支全拦下 | +| **预览在坏报告下会不会乱发** | `python3 scripts/ci_preview_negative_test.py` | 10 分支:坏报告 publish 0 次、好报告 1 次 | +| 提交范围取法 | `python3 .gitea/scripts/ci_gate.py --self-test` | 11 分支(截断/空范围/新分支/手工运行…) | +| 检出协议保留 | `bash .gitea/scripts/ci_checkout.sh --print-remote` | https 保持 https、未知协议拒绝 | +| 合并调用链 | `python3 ~/eng-standards/scripts/adopt-existing.py --self-test-chain` | 13 分支只有全条件成立才发 merge | +| 合并判定纯函数 | `python3 ~/eng-standards/scripts/adopt-existing.py --self-test` | 11 分支全拒绝 | + +⚠️ 这些都是**本地**证据:本地绿不等于远端 CI 已跑通。远端证据只有一条 —— 真实 run 的链接 + 完整 SHA。 + diff --git a/04-模板评估与修正.md b/04-模板评估与修正.md index 2ed9a0a..2e98bf6 100644 --- a/04-模板评估与修正.md +++ b/04-模板评估与修正.md @@ -133,4 +133,29 @@ 7. **§5.7 目标机三层布局** 与"停容器/换目录必须在同一个脚本里、顺序不许挪"。 8. **§5.6 不缓存 `node_modules`**。 9. **§2.4 陷阱库拆两份**(指令文件放索引、旁边放全文,因为指令有字节预算)。 -10. **§11 规范自己怎么被管理**(单一来源、投影、可检验、有版本)—— 见本目录的 `README.md` 与 `03-待配置事项.md` 第 13 项。 \ No newline at end of file +10. **§11 规范自己怎么被管理**(单一来源、投影、可检验、有版本)—— 见本目录的 `README.md` 与 `03-待配置事项.md` 第 13 项。 + +--- + +## G. 第二轮审查修正(模板/生成器/接入器,2026-10-12) + +审查(GPT,第二轮)用**模拟项目**逐个打穿了模板的假检查。逐条修法与判据: + +| 编号 | 问题(实际证据) | 修法 | 负例在哪 | +|---|---|---|---| +| T-1 | **通用 Python 检查空跑**:模拟 Python 项目,依赖/格式/构建/测试全返回成功,实际没执行 | `templates/ci-gate.py` 的 Python 分支真跑 `uv sync --frozen` / `uv run ruff format --check` / `compileall` / `uv run pytest`;工具缺失 ⇒ FAILURE(`need()` 抛错) | 夹具 `gate-py-neg`:抹掉 `uv` 后五项全红;补上工具后五项真绿;把一个用例改失败 ⇒ `pytest 退出码 1` 红 | +| T-2 | **前端测试忽略进程失败**:测试进程退出 1、JSON 写 pass,门禁仍绿 | `judge_frontend()` 把退出码当判据;空套件 / skip / todo / 没报告 ⇒ 红;框架读 `test-scope.json` 声明,不写死 vitest | 夹具 `gate-node-neg`:A 例(rc=1 + 报告写 pass)红、B 例(rc=0 + 报告全过)绿 | +| T-3 | **通用清单没有安全项** | 清单新增 `security`(密钥扫描 + 依赖漏洞,工具缺失/输出解析不了 ⇒ 红);模板带 `templates/check-secrets.py`,生成器落到 `scripts/check_secrets.py` | `--self-test` 含清单比对 | +| T-4 | **test-scope 可用空对象逃逸**:条目缺 package/test 时会通配任意跳过 | 严格 schema:必须给 `package` 或 `test` 至少一个 + `reason` + `owner`;package 级还要 `expected_skips`(跳过数涨了要重新声明);`review_by` 过期 ⇒ 整份失效 | `--self-test` 分支「范围声明有问题 ⇒ 红」 | +| T-5 | **预览早于报告核对**(模板里还 `cat /tmp/ci-sha`、跨作业 `/tmp` 假契约) | 模板 workflow 收成**单作业**;预览改调 `.gitea/scripts/ci_preview.py`(先 `verify()`,不过则 0 请求) | 项目侧 `scripts/ci_preview_negative_test.py`(假 bridge 数请求,10 分支) | +| T-6 | **协议被拼成 http** | 检出抽成 `templates/ci_checkout.sh`:remote 用 `GITHUB_SERVER_URL` 原样拼 + 断言不降级 + URL 不带凭据 + 顺带取事件 `before` | `ci_checkout.sh --print-remote`:https/http 保留、`ftp://` 拒绝;https 真跑一次检出验证 | +| T-7 | **合并必需 context 可为空** | `adopt-existing.py`:必需 context 只认显式策略(`--require-context` / `.gitea/required-contexts.txt`),空/缺失 ⇒ 拒绝;并核对 base/state/head 仓库 | `--self-test-chain`(假 Gitea + 真实 `merge_pr()`,13 分支) | +| T-8 | **提交范围可能漏查** | `.gitea/scripts/ci_gate.py` 以 Git 范围为准(`before..after` / 新分支 `after --not --remotes`);声明与范围不一致、空范围、浅克隆缺 before、列表提交不在检出 ⇒ 拒绝;手工运行只核 HEAD 并写明 | `--self-test` 11 分支(含"声明 6 条 / 列表 1 条 / Git 2 条 ⇒ 报错") | + +**生成器同步**:`new-project.sh` 现在会落 `ci_preview.py`、`ci_checkout.sh`、`check_secrets.py`、 +`test-scope.json`(合法但空的骨架)、`.gitea/required-contexts.txt`(**故意留空**并要求你填 —— 不预填猜出来的 context, +否则会把没跑过必需检查的 PR 放过去)。 + +**口径修正(文档)**:模板不再声称"CI 能拦住直推";"所有单测都依赖预构建产物"改成"只有跑产物的套件才排在构建之后"; +通用模板默认不携带任何部署步骤,占位一律 `exit 1`(不许假报成功)。 + diff --git a/06-接入既有工程.md b/06-接入既有工程.md index f02e5bb..bea8c1b 100644 --- a/06-接入既有工程.md +++ b/06-接入既有工程.md @@ -4,8 +4,16 @@ > 为什么要有"前置体检"这一关,见 §2 —— 跳过它会把 PR **永久锁死**。 > > ⚠️ **工具不替人合并、也不替人审批**(2026-10 改):它建完 PR 就把状态如实打给你,然后停下。 -> 批准之后才跑 `--merge-pr `,而它会**先核对检查状态**:失败 / 取消 / 从未运行 / -> 状态拿不到 / 等超时 / 头提交变了 —— 任一情形都拒绝请求合并(`--self-test` 逐条证明)。 +> 批准之后才跑 `--merge-pr --approve-sha <完整40位SHA>`,而它会**先核对**: +> ① 获批的就是头提交那个**完整 SHA**(短 sha 不算批准依据);② **明确策略要求的**每一个 status context +> 都是 `success`(失败 / 取消 / 从未运行 / 状态拿不到 / 等超时 / 头提交变了 —— 任一情形都拒绝); +> ③ PR 的目标分支就是默认分支、状态是 `open`、head 就是本仓(跨仓/合错分支都不做)。 +> `--self-test`(11 分支)+ `--self-test-chain`(假 Gitea 跑真实 `merge_pr()`,13 分支)逐条证明。 +> +> **"要求哪些检查"只能来自明确策略**:`--require-context "build / build (push)"`, +> 或仓库里的 `.gitea/required-contexts.txt`(一行一个)。两个都没有 / 文件是空的 ⇒ **拒绝合并** —— +> **不接受"体检时观察到哪些状态"来代替**:`combined state` 只报"报过状态的都成功", +> 一个无关检查成功就能骗过它,于是没跑过必需检查的 PR 会被合进去。 ## 1. 一条命令 diff --git a/README.md b/README.md index 027b731..26fe28c 100644 --- a/README.md +++ b/README.md @@ -67,4 +67,37 @@ curl -s http://127.0.0.1:8899/healthz | 构建受限 | 目标机 **不可达 Docker Hub** → 用 daocloud 镜像源;Go 用 `GOPROXY=https://goproxy.cn,direct` | | 本机网络 | 出网走 mihomo;境外全 000 时先查 `~/.clashctl` 节点(`PUT /proxies/三毛机场 {"name":"<活节点>"}`) | -> ⚠️ 上表里**只有一个数字是"规范"**(如"生产路径 /opt/dobotai"),其余都是"现状快照"。现状会漂;所以每项都给了取数命令。 \ No newline at end of file +> ⚠️ 上表里**只有一个数字是"规范"**(如"生产路径 /opt/dobotai"),其余都是"现状快照"。现状会漂;所以每项都给了取数命令。 + +--- + +## 本地自检(不需要连 CI,可直接跑) + +```bash +# 通用门禁模板:汇总 / 前端判据 / pytest 退出码语义 / 完整 SHA / 清单比对 +python3 templates/ci-gate.py --self-test + +# 上报核对(缺项/重复/未知项/短 SHA/档案/运行模式) +python3 templates/ci_report.py --self-test + +# 预览脚本的打包判定(空产物 / 缺 index.html 必须被拒) +python3 templates/ci_preview.py --self-test + +# 提交范围取法(截断/空范围/新分支/手工运行/shallow) +python3 templates/ci_gate.py --self-test + +# 检出脚本的协议保留(https 不许被降级;未知协议拒绝;URL 不带凭据) +GITHUB_SERVER_URL=https://gitea.example.com GITHUB_REPOSITORY=owner/repo \ + bash templates/ci_checkout.sh --print-remote + +# 合并:判定纯函数 + 真实调用链(假 Gitea,断言 merge 请求发没发) +python3 scripts/adopt-existing.py --self-test +python3 scripts/adopt-existing.py --self-test-chain + +# 生成器端到端 +bash scripts/new-project.sh /tmp/probe Weknora/probe r1 && ls /tmp/probe/.gitea/scripts +``` + +⚠️ `templates/ci_report.py --self-test` 在**规范仓里**会因为找不到 `scripts/ci_checks.py` +(实现随项目走)而明确报"自检跑不了"—— 那是**故意**的:不许拿一份空清单去跑出莫名其妙的结论。 +