# 接入既有工程:让现在的工程启用这套规范 > 一句话:接入 = **前置体检 → 加指针 → 开保护 → 反向自检**,一条命令走完。 > 为什么要有"前置体检"这一关,见 §2 —— 跳过它会把 PR **永久锁死**。 > > ⚠️ **工具不替人合并、也不替人审批**(2026-10 改):它建完 PR 就把状态如实打给你,然后停下。 > 批准之后才跑 `--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. 一条命令 ```bash # ① 只体检 + 打印计划(**不动任何东西**)—— 先看,再决定 python3 ~/eng-standards/scripts/adopt-existing.py # ② 计划无误再执行 python3 ~/eng-standards/scripts/adopt-existing.py --apply # 需要"谁都不能直推"时才开分支保护 —— **默认不开**(正本 §1.3:原稿口径是直推 main) python3 ~/eng-standards/scripts/adopt-existing.py --apply --with-protection ``` 工具只依赖 Python 标准库 + git;凭据运行时从 `~/.git-credentials` 读,**永不打印**。 **幂等**:重复跑不会重复建 PR、不会改 `main`。 它做的事: | 步 | 动作 | 怎么落地 | |---|---|---| | 1 | 体检 | 读默认分支、工作流触发方式、runner(仓库级/组织级)、最新提交的 status context、已有保护 | | 2 | 加指针 | 新起分支 → 写 `AGENTS.md` → 提交(过本机闸门)→ **PR** → 有界等 CI → **停下**(工具不替你合并) | | 3 | 开保护(**仅 `--with-protection` 时**) | `POST/PATCH /branch_protections`:禁直推 + 必须 PR;**状态检查按第 1 步判定**。**默认不做** —— 原稿口径是直推 `main`(正本 §1.3) | | 4 | 给自检命令 | 打印"直推必须被拒"的验证命令(**这步工具不代做,必须你跑**) | ⚠️ `main` 一旦被保护,**所有改动都得走 PR** —— 包括工具自己加 `AGENTS.md` 这一步。 ## 2. 前置体检为什么是必须的(本环境最容易踩的坑) "必须状态检查通过"开错了,症状是:**PR 打开后永远等一个不会出现的检查,永远合不了**。 判据只有一条:**该仓的工作流,在 PR 的头提交上会不会产生 status?** | 工作流触发 | PR 头提交有没有状态 | 能否开强制状态检查 | |---|---|---| | `on: push: branches: "**"`(推**任意分支**都跑) | 有 —— 分支推送产生 `xxx (push)` | ✅ **可以** | | `on: push: branches: [main]`,无 `pull_request` | **没有** —— PR 完全不触发 | ⛔ 开了就锁死 | | `on: push: branches: [main]` + `pull_request` | 有,但 context 是 `xxx (pull_request)` | ⛔ 与 `(push)` 不匹配;要开得按 `(pull_request)` 人工确认 | 工具按这张表自动判定,**判定不通过就只开"禁直推 + 必须 PR",不碰状态检查**。 实测证据(本机 7 个仓): | 仓 | 工作流触发 | 判定 | |---|---|---| | `Weknora/knowledge-governance` | `push: "**"` | ✅ 可开 —— context `build / build (push)` | | `Weknora/dobotai-platform` | `push: "**"` | ✅ 可开 —— 同上 | | `aiball/software-pc` | `push: [main]`,无 `pull_request` | ⛔ 不可开 | | `aiball/protocol-docs` | `push: [main]` + `pull_request` | ⛔ 上下文不匹配 | | `Weknora/knowledge-adapter-service` | 默认分支上**没有**工作流 | ⛔ 无 CI 可强制 | | `Weknora/Enterprise-Knowledge-Base` | 本仓无工作流(现有 status 来自仓外) | ⛔ 不可开 | | `zheromiku/eng-standards-main` | 无工作流 | ⛔ 不可开 | **要做的是先补 CI,再开检查** —— 不是先开检查(那会锁死)。所以接入顺序是: **工作流能覆盖任意分支 → 再开强制状态检查**。工作流只覆盖 `main` 的仓(aiball 两个), 第一步是把它改成 `on: push: branches: "**"` 或补一个 `pull_request` 触发。 ## 3. 试点记录:`Weknora/knowledge-governance`(已跑通) > ⚠️ **口径已更新**:下表里的"分支保护"作法**后来按原稿口径关掉了**(正本 §1.3:默认不开、直推 `main`)。 > 记录保留有两个用处:① 万一将来真要开保护,它证明"PR + 强制状态检查"是**能跑通**的; > ② §2 那条体检判据是真的有用。其余部分(加 `AGENTS.md` 指针)不受影响。 执行前该仓状态:无 `AGENTS.md`、无分支保护;`build.yml` 是 `push: "**"`;仓库级 runner `runner-weknora-kg-01`。 | 步 | 实测结果 | |---|---| | 分支 + 提交 | `docs/agents-pointer`,提交通过本机提交闸门(`~/.githooks/commit-msg`) | | 推送触发 CI | 头提交 `d025fc9adfe2` → context `build / build (push)` → `combined=success` | | PR | **PR #1**,`mergeable=True`(**关键结论:强制状态检查认 PR** —— 因为推分支就产生了 `(push)` 状态) | | 合并 | 当时由工具**自动**合并:HTTP 200(**空 body,属正常**,不重试)→ 读回 `state=closed` `merged=true` `merge_commit_sha=32b0ec58a612`。**该行为已改**:现在工具不自动合并(见上) | | 结果 | `AGENTS.md` 在 `main`;分支已自动删除 | | 反向自检 | 直推 main 被服务端拒绝:`Not allowed to push to protected branch main` / `pre-receive hook declined` | | 幂等 | 再跑一次 `--apply`:`AGENTS.md` 跳过、保护值不变、**main 仍是 `32b0ec58a612`** | ## 4. 不想用工具?手工四步 ```bash # ① 体检(判据同 §2) python3 ~/.hermes/scripts/gitea_api.py GET "/api/v1/repos///actions/tasks?limit=1" git clone /tmp/t && cd /tmp/t python3 -c "import re;print([l for l in open('.gitea/workflows/build.yml') if re.match(r'^\s*(on|push|pull_request|branches):',l)])" docker ps --format '{{.Names}}' | grep runner # 本仓有没有 runner # ② 加指针:走分支 + PR,不要直推 main git switch -c docs/agents-pointer # AGENTS.md 内容取自 ~/eng-standards/templates/README.md 里的 ```markdown 骨架块, # 把 / / 替换掉,并如实写下该仓的 CI / runner / 保护现状 git commit -F - <<'EOF' docs(agents): 加 AGENTS.md 操作手册与规范指针 指向系统级规范(zheromiku/eng-standards-main)。 EOF git push -u origin HEAD # 然后网页开 PR → 等 CI → 合并 # ③ 开保护:网页 Settings → Branches;或 API(字段见 swagger CreateBranchProtectionOption) # enable_push=false、enable_force_push=false、required_approvals=0、 # block_on_outdated_branch=true、dismiss_stale_approvals=true、 # enable_status_check=<按 §2 判定>、status_check_contexts=<匹配的 context> # ④ 反向自检(必须) cd /tmp/t && git commit --allow-empty -m 'chore(probe): 不该进 main' && git push origin main # 判据:Not allowed to push to protected branch ``` ## 5. 接入后的验收判据(缺一即不算接入) 1. `AGENTS.md`(或 `.hermes.md`)在默认分支上,且含指向 `zheromiku/eng-standards-main` 的路径。 2. **(只在开了保护时)** `GET /repos/{r}/branch_protections` 非空、`enable_push=false`,且直推默认分支**被服务端拒绝**。 ⚠️ 默认口径是**直推 `main`**,所以正常情况下这里应当是"没有保护" —— **别把"没保护"当成缺陷**。 3. 推一个故意违规的提交 → 本机 hook 红、CI 的 `gate` 红。 ⚠️ 注意口径:CI 的 `gate` 是**推完再跑**的,它拦不住那次推送本身(正本 §2 / §1.3)。 "直推也拦得住"是**错的**说法:要拦推送只有两条路 —— 本机 hook,或开分支保护(禁直推)。 gate 的价值是"把违规提交标红并挡住编译/部署",不是"阻止它进 main"。 4. **(只在开了保护时)** PR 能正常合并 —— 证明强制状态检查没有把 PR 锁死。 5. 会话里问"这个仓提交信息什么规范",代理能**不靠你重复**答出来(说明指针真的被注入了)。 ## 6. 会遇到的拒绝与处理 | 现象 | 原因 | 处理 | |---|---|---| | 推 `main` 被拒 | 已开保护 —— 这是**预期行为** | 走分支 + PR | | PR 一直显示等待状态检查 | 该仓工作流没覆盖 PR(§2 表后两行) | 先改工作流触发,或临时关掉 `enable_status_check` | | PR 报 `mergeable=false` 但没有冲突 | 强制状态检查未满足 / 分支过期 | 看 `GET /pulls/{n}` 的 `mergeable` 与 head 上的 status | | 合并接口返回空 body | **Gitea 合并成功的正常返回** | **永不重试**,改 `GET /pulls/{n}` 读回 `merged=true` | | run 一直 `queued` | 该仓没有归属自己的 runner | `bash /srv/aiball/runner/deploy_repo_runner.sh ` | | 工具说"没有凭据" | `~/.git-credentials` 里主机是 URL 编码的 | 工具已按解码后比对;手工查时别按字面比 | ## 7. 扩到其余仓的顺序建议 先补 CI 再开检查,别反过来: 1. `Weknora/knowledge-adapter-service` —— 先把 `build.yml` 从 `ci/setup` 合到 `main`(现在 main 上零校验) 2. `aiball/software-pc`、`aiball/protocol-docs` —— 先把工作流触发改成覆盖 PR,再开强制检查 3. `Weknora/dobotai-platform` —— 工作流已覆盖任意分支,可直接 `--apply` 4. `Weknora/Enterprise-Knowledge-Base` —— 需先接 CI + runner(分支也待收敛) 5. `zheromiku/eng-standards-main` —— 等守门脚本做完,再给自己开检查