diff --git a/01-规范-Git与CI-CD(Gitea版).md b/01-规范-Git与CI-CD(Gitea版).md index ba6dce9..7c9a528 100644 --- a/01-规范-Git与CI-CD(Gitea版).md +++ b/01-规范-Git与CI-CD(Gitea版).md @@ -147,7 +147,10 @@ EOF ### 3.1 一条命令,串行,失败即停 -每个仓库**必须**有一条"跑完本地门禁"的入口(`make check` / `npm run verify` / `scripts/verify.sh`)。 +每个仓库**必须**有一条"跑完本地门禁"的入口,而且**只许有一条**: +`make verify`(首选名字,本环境已统一)/ `npm run verify` / `scripts/verify.sh` 都行。 +⚠️ **名字可以不同,但 AGENTS、Runbook、CI 必须指向同一个**: +两边各写一套步骤的结局是"迟早只改一边",而症状是本地绿、CI 红(或反过来),谁都说不清哪边是对的。 - **必须串行**:构建会清空产物目录,而端到端/契约测试跑的是**产物** ⇒ 并行会随机报"某条路由 404"。这类假失败会把你带到完全错误的方向。 - **失败即停**:后面很多步骤依赖前面的产物,继续跑只会产生一堆同因假失败,把真正的线索埋掉。 @@ -160,14 +163,18 @@ EOF |---|---|---| | 1 格式 | `gofmt -l` / `prettier --check` / `ruff format --check` | CI 会因为这一条红 | | 2 静态检查 | `go vet` / `tsc --noEmit` / `ruff check` | 类型/静态错,后面全是噪声 | -| 3 构建 | `go build ./...` / `npm run build` | **会清空产物目录**,必须排在测试之前 | -| 4 单元测试 | `go test ./...` / `pytest -q` / `npm test` | 跑的是构建产物,不是源码 | +| 3 构建 | `go build ./...` / `npm run build` | 产物会被"跑产物"的套件消费 ⇒ 那些套件必须排在它之后;构建本身**不是**所有测试的前提 | +| 4 单元测试 | `go test ./...` / `pytest -q` / `npm test` | 大多数是**测源码**:Go 自己编译测试程序、pytest 直读源码、Vitest 默认也跑源码 | | 5 集成/端到端 | 真起服务、打真实端口 | 单测覆盖不到"TCP 上真的连得上" | | 6 部署脚本自测 | `bash deploy/deploy_test.sh` | 真跑一次 release;守的是**静默故障** | ### 3.2 八条通用铁律 -1. ⚠️ **单测跑的是构建产物,不是源码**:改了源码不重建,测的是**旧代码**(第一次一定会被这个骗一次)。 +1. ⚠️ **"跑产物"的套件(端到端 / 契约 / 产物自检)必须排在构建之后**:它们读的是构建输出, + 改了源码不重建,测的就是**旧代码**(第一次一定会被这个骗一次)。 + ⚠️ 但**别把这条推广成"所有单测都依赖预构建产物"** —— 那是错的:`go test` 自己编译测试程序、 + `pytest` 直读源码、`vitest` 默认也跑源码。写错这条的代价是给每个测试套件加上假的"先构建"依赖, + 白白拖慢门禁,还会制造"没构建所以红了"的假故障。(本规范早期版本就是这么写的,已改回。) 2. ⚠️ **只在"干净 clone"里出现的缺陷**:本地工作区总有"恰好能跑"的残留。要专门造一次干净树(`git clean -xdf` 后 clone / CI 的干净检出)验证。 3. ⚠️ **每个校验都要反向自检**: - 断言"它会红":故意弄坏一处,必须失败。 @@ -190,8 +197,13 @@ Gitea 没有 `workflow: rules`,触发写在 `on:`。本环境只允许三类 | 入口 | 写法 | 用途 | |---|---|---| | 推任意分支 | `on.push.branches: ["**"]` | 校验 + 编译(**不部署**) | -| 推默认分支 | `on.push.branches: [main]` | 校验 + 编译 + **自动部署测试环境** | -| 人工发起 | `on.workflow_dispatch` | 生产发布(**独立 workflow**,见 §5) | +| 人工发起 | `on.workflow_dispatch` | 手动重跑校验;生产发布是**独立 workflow**(见 §5) | + +⚠️ **CI 与 CD 分离(本环境口径)**:推送触发的流水线**只做 CI** —— 检查、测试、构建、证据。 +"推 main 就自动部署测试环境"**不是**通用规范:部署目标、产物形态、迁移与回滚由**每个项目自己设计** +(§8)。通用模板里默认**不含**任何部署作业。 +反例(本规范早期版本):模板里放一个 `deploy:test` 占位作业,它打印 TODO 然后返回成功 —— +那不是"还没做",那是**假报成功**:网页上看起来部署过了。 **硬约束**: @@ -207,7 +219,7 @@ Gitea 没有 `workflow: rules`,触发写在 `on:`。本环境只允许三类 | `gate` | 校验 | `.gitea/scripts/ci_gate.py` 提交信息闸门 | 失败就**不编译**,一次被拦的推送不会被误当编译错误 | | `build` | 构建 | 装依赖 → 静态检查 → 单测 → 构建 → **产物自检** → 打包/送产物 | 唯一产出交付物的作业 | | `validate` | 校验 | 环境参数不变量、配置语法、规范守门 | 只读仓库、几秒钟,没理由拖到部署阶段才发现 | -| `deploy:test` | 部署 | 推 `main` 自动部署测试环境 | 见 §4.1 | +| (无) | 部署 | **通用 CI 里没有部署作业** | CD 由项目独立设计(§8);占位作业不许假报成功 | 要点: - **校验类作业与 `gate` 归"校验/构建"**,用 `needs:` 显式等齐再进部署。 @@ -240,9 +252,13 @@ Gitea 没有 `workflow: rules`,触发写在 `on:`。本环境只允许三类 - 网络参数:`GOPROXY=https://goproxy.cn,direct`、`GOTOOLCHAIN=local`、npm 走国内源。 - ⚠️ **不缓存 `node_modules`**(或等价的语言级依赖目录):它是平台相关的产物,缓存里混进另一套架构/运行时版本的二进制时,症状是"某次构建突然报原生模块加载失败",而那次构建的 diff 里什么都没有。 -### 4.6 测试环境部署 +### 4.6 测试环境部署(**项目自己的 CD**,不属于通用 CI) -- 推 `main` → `gate` + `build` 通过 → 自动部署测试环境。 +> ⚠️ 本节描述的是**项目 CD 的目标形态**,不是通用 CI 的一部分。通用模板不会生成这些作业; +> 项目要做部署时**另写独立 workflow**(`workflow_dispatch` 触发、`cancel-in-progress: false`)。 + +- **触发方式由项目自己决定**(例如"CI 全绿之后由人工发起")。通用 CI **不**替你决定,也**不**自动部署。 + ⚠️ 本规范早期版本写的是"推 `main` → 自动部署测试环境"—— 那是把某个项目的选择当成了通用规则,已改。 - 部署后**必须读回**(不是"启动成功"就算): 1. 容器镜像 ID == 本次构建产物的镜像 ID(`docker inspect --format '{{.Id}}'`); 2. `GET /healthz` 返回 `{"status":"ok"}`; @@ -258,6 +274,35 @@ Gitea 没有 `workflow: rules`,触发写在 `on:`。本环境只允许三类 - ⚠️ 报告里给出的每个 URL(报告页、预览页)**都要探测到 200** 才算发出去了,否则会收到一条没有预览链接的通知。 - ⚠️ 静态产物打包**不要产生 `.` 根成员**:`tar -czf x.tgz -C dir .` 会写入 `.` 和 `./index.html`;严格解包器可能拒绝或报错,而流水线**仍显示成功** —— 通知到了、预览没有。bridge 侧应**规范化**(剥掉 `./`,丢弃归一化为 `.`/空的成员),而不是直接拒绝整个包。 +### 4.8 必需检查清单与结果口径(汇总不许自欺) + +每个项目声明自己的**必需检查清单**,并让清单能被打印出来(如 `make list`), +CI 与本地都按**清单**逐项核对: + +| ID | 必需检查 | 判据 | +|---|---|---| +| identity | 触发提交 / 检出身份 | 实际 `HEAD` 等于**事件完整 SHA**(不是短 sha,也不是"当时的分支 HEAD") | +| commits | 提交信息 | 本次推送范围内的提交全部通过规则 | +| deps-install | 依赖安装 | **按锁文件**装;缺锁即失败,不回落 `npm install` / `-mod=mod` | +| format | 格式 | 不合规即非零退出;CI **不自动修复** | +| static | 静态检查 / 类型 | 声明的语言模块全部通过 | +| build | 构建 | 声明的模块构建成功 | +| tests | 自动化测试 | 必需套件**实际执行**且通过;空套件 / 全跳过不算通过 | +| contracts | 项目契约 | 版本与产物/接口契约通过(被 SKIP 的断言**不算**通过) | +| security | 密钥 / 依赖漏洞 | 无泄漏;无未获授权的适用高危风险;**扫描器没跑成也不许算过** | +| manifests | 依赖清单 | 门禁过程中 `go.mod` / `go.sum` / 锁文件没有被任何一步改写 | + +结果口径(三条,都是踩出来的): + +1. **失败即停,但汇总照做**:第一个必需项失败后,剩下的记为 `not_run` 并**如实写进报告**, + 不许从报告里消失(消失了,下游就会当成"本来就没有这一项")。 +2. **`not_run` / `skipped` 都不算过**:汇总只认 `success`; + 不适用要**在清单里声明**(比如"本地没有事件负载,identity 只在 CI 必需"),不能在运行时悄悄跳过。 +3. **汇总本身故障 ⇒ 不能报绿**:读不到 / 解析不了报告时,结论是"质量无从判定",不是"没问题"。 + +预览与通知是**旁路**:预览只在全部必需项 `success` 之后发布(并且要断言产物包里真的有入口文件、 +报告里给出的每个地址都探测到 200);通知失败**不改质量结论**,只单独标注 `notification_status`。 + --- ## 5. 生产发布(人工确认 / 健康检查 / 互斥 / 回滚 / 同一产物) @@ -439,7 +484,7 @@ flock -n 9 || { echo "❌ 另一个部署正在进行,退出"; exit 1; } - [ ] **1. 仓库与默认分支**:默认分支定 `main`;分支收敛到 `main` + 短命分支(长期分支要么合要么删)。 - [ ] **2. `.gitignore`**:依赖 / 构建产物 / 运行期数据 / 密钥 / 发布产物各一条;再用 §1.4 的两条反查命令确认**没有已入库的漏网文件**。 -- [ ] **3. 门禁入口**:`make check` 或 `scripts/verify.sh` —— 一条命令跑完、**串行**、失败即停、末尾汇总;跳过项显式警告"不算全绿"。 +- [ ] **3. 门禁入口**:`make verify`(首选;`scripts/verify.sh` 亦可)—— 一条命令跑完、**串行**、失败即停、末尾汇总;跳过项显式警告"不算全绿"。**本地与 CI 必须是同一条命令**。 - [ ] **4. CI 骨架**:复制 `templates/gitea-build.yml` → `.gitea/workflows/build.yml`;随仓带 `.gitea/scripts/{ci_gate.py,ci_report.py}`(**逐字节复制**已验证的版本,不要重写)。 - [ ] **5. 仓库级 Runner**:`bash /srv/aiball/runner/attach_repo.sh `(幂等;注册令牌走文件挂载,不落容器 env)。 - [ ] **6. bridge 变量/密钥**:`CI_BRIDGE_URL`(variable)、`CI_BRIDGE_TOKEN`(secret),由上面的脚本写入;写完 `GET` 读回。 @@ -457,7 +502,7 @@ flock -n 9 || { echo "❌ 另一个部署正在进行,退出"; exit 1; } ```bash # ── 提交前 ──────────────────────────────────────────────── -make check # 或 npm run verify —— 本仓门禁,串行、失败即停 +make verify # 本仓门禁(本地与 CI 同一条),串行、失败即停 git status --short --ignored # 确认没有该入库却被忽略的文件 git commit -F - <<'EOF' # ⚠️ 不用 -m:正文里的反引号/$() 会被 shell 吃掉 feat(scope): 标题(中文) @@ -475,7 +520,7 @@ giteactl rerun / # 重跑(不推新提交即可验证整条 # 构建注入版本 → 写 release-.json → 部署测试 → 验收 → dispatch 生产 # ── 上线与回退 ─────────────────────────────────────────── -# 推 main → gate + build + deploy:test 自动跑 +# 推任意分支 → gate + build(**只做 CI,不部署**) # 生产 → Gitea「Actions → 生产 workflow → Run workflow」选 ref=main,填 sha 与 confirm # 回退(生产机上) → bash /opt/dobotai/container/ops/rollback_dobotai_prod.sh # ⚠️「取消流水线」不是回滚 @@ -494,7 +539,7 @@ giteactl rerun / # 重跑(不推新提交即可验证整条 | T5 | 断言"我这条守卫会红"之外,还要断言**不会假绿**:给它一条对照 | 通用 | | T6 | 测试里的**负向断言**会被"别的记录"撑成假绿(判据太宽) | 通用 | | T7 | 测试夹具只建不删 ⇒ `$TMPDIR` 攒到几万条,`mktemp -d` 开始卡住 | 通用 | -| T8 | 单测跑的是构建产物,不是源码 —— 改了源码不重建,测的是旧代码 | 通用 | +| T8 | "跑产物"的套件要排在构建之后;但**别**推广成"单测都依赖预构建产物" | 通用 | | **G1** | **act_runner 不自动检出代码**(Weknora 三仓)⇒ 工作流必须自 clone | 本环境 | | **G2** | **act 以 `bash -e -o pipefail` 执行每步** ⇒ `cmd \| head` 会假红 | 本环境 | | **G3** | **`uses:` 第三方 action 在部分 runner 上不可用**(实测 aiball runner 可用) | 本环境 | diff --git a/02-流程说明与Runbook.md b/02-流程说明与Runbook.md index 9491bf8..5550668 100644 --- a/02-流程说明与Runbook.md +++ b/02-流程说明与Runbook.md @@ -19,7 +19,8 @@ git log --oneline origin/main..HEAD # 本地领先的提交 = 本次要发的 ### A2. 改完、提交前 ```bash -make check # 或 npm run verify / scripts/verify.sh —— 本仓门禁,串行、失败即停 +make verify # 本仓唯一门禁入口(本地与 CI 同一条),串行、失败即停 +# 名字不同也行(npm run verify / scripts/verify.sh),但 AGENTS / Runbook / CI 必须指同一个 ``` **判据**:末尾汇总表全绿;有跳过项会显式警告"不算全绿"且非 0 退出 —— 那就不是绿。 @@ -116,15 +117,19 @@ python3 ~/.hermes/scripts/gitea_api.py GET "/api/v1/repos///actions/se --- -## C. 构建与测试环境部署 +## C. 构建与测试环境部署(**项目自己的 CD**;推送流水线不含部署) ### C1. 自动(推 main) ``` -git push origin main -→ gate(提交信息闸门)→ build(依赖/静态/单测/构建/产物自检)→ deploy:test +git push origin <分支> +→ gate(提交信息闸门)→ build(依赖/静态/格式/单测/构建/安全/契约) ``` +⚠️ **推送只做 CI,不做 CD**:上面这条流水线里**没有**部署作业(正本 §4.1 / §8)。 +部署(测试环境/生产)由各项目自己设计,见 §D。本模板里不再有 `deploy:test` 占位作业 —— +占位作业会打印 TODO 然后返回成功,那是假报成功。 + ### C2. 测试环境读回(每次部署都做全) ```bash diff --git a/03-待配置事项.md b/03-待配置事项.md index 6bca87a..f177d7f 100644 --- a/03-待配置事项.md +++ b/03-待配置事项.md @@ -161,7 +161,8 @@ ## 不建议做的事(写下来防止被"顺手加上") - ❌ 不要为了能 push 去改 Gitea 凭据/token scope —— 权限没配好要**问人**,自己换 remote/换账号会把"权限没配好"变成"动作做了一半"。 -- ❌ 不要把 `deploy:test` 也挂到 `workflow_dispatch` 上"顺便重部署测试环境"(生产 workflow 就是生产 workflow)。 +- ❌ 不要把测试环境部署塞回 `build.yml`(CI 流水线只管校验与编译);生产 workflow 就是生产 workflow。 + 占位作业在填完真实逻辑之前**必须红**,不许"打印 TODO 然后返回成功"。 - ❌ 不要给生产 workflow 设 `concurrency.cancel-in-progress: true`(会取消正在进行的部署,而取消不是回滚)。 - ❌ 不要在生产与测试**同时**跑压测(两服共用同一套 RDS,会扰动生产)。 - ❌ 不要在源码路径里用 `dist` / `data` / `secrets` 这些名字(`.gitignore` 会静默吞掉)。 \ No newline at end of file diff --git a/04-模板评估与修正.md b/04-模板评估与修正.md index 044ecd7..2ed9a0a 100644 --- a/04-模板评估与修正.md +++ b/04-模板评估与修正.md @@ -125,7 +125,7 @@ ## F. 保留的(原稿最有价值的部分,不要因为"平台换了"就丢掉) 1. **§0 四条地基**:结论只来自可复现命令 / 每个校验都要反向自检 / 密钥不进命令行 / 提交前跑门禁。 -2. **§4.2 六条铁律**:单测跑产物不跑源码、干净 clone、反向自检三问(会红吗 / 会假绿吗 / 真的作用在目标上吗)、断言要有唯一命中点、夹具要打扫、先确认无遗留进程。 +2. **§3.2 八条通用铁律**:只有"跑产物"的套件要排在构建之后(**不是**"所有单测都依赖预构建产物")、干净 clone、反向自检三问(会红吗 / 会假绿吗 / 真的作用在目标上吗)、断言要有唯一命中点、夹具要打扫、先确认无遗留进程。 3. **§2.2 正文写"为什么"** 的四种要素(现象/根因/取舍/证据)与示例形状。 4. **§2.3 heredoc 禁忌**(反引号 `$()` 会被 shell 先执行,且退出码仍是 0)。 5. **§1.3 `.gitignore` 是契约** 与两条反查命令。 diff --git a/05-应用机制-让规范在以后所有工程生效.md b/05-应用机制-让规范在以后所有工程生效.md index 237e2a8..08d0059 100644 --- a/05-应用机制-让规范在以后所有工程生效.md +++ b/05-应用机制-让规范在以后所有工程生效.md @@ -92,7 +92,8 @@ bash /srv/aiball/runner/attach_repo.sh / | 空目录 → 生成 | ✅ exit 0,9 个文件齐全 | | 非空目录 → 拒绝 | ✅ exit 1,`❌ 目标目录非空:…(不覆盖任何东西)` | | `` 替换 | ✅ 生成物首行 `# demo-svc —— 提交触发的 CI(Gitea Actions)` | -| 生成物 YAML 合法性 | ✅ `build.yml` jobs=`gate,build,deploy:test`;`deploy-production.yml` jobs=`guard,publish` | +| 生成物 YAML 合法性 | ✅ 生成后解析:`build.yml` jobs=`gate,build`(**无 deploy 作业**,CI-only);`deploy-production.yml` jobs=`guard,publish` | +| 生成物不含假成功占位 | ✅ `grep -c '^ deploy' build.yml` = 0;prod-deploy 的未实现步骤是 `exit 1` | | AGENTS.md 指针 | ✅ 含 `eng-standards-main` | | 闸门脚本逐字节一致 | ✅ `ci_gate.py` 3280 B / `ci_report.py` 2700 B | | 无构建入口时门禁行为 | ✅ exit 1("入口待补,不算全绿"),**不是静默通过** | diff --git a/06-接入既有工程.md b/06-接入既有工程.md index 5687659..f02e5bb 100644 --- a/06-接入既有工程.md +++ b/06-接入既有工程.md @@ -2,6 +2,10 @@ > 一句话:接入 = **前置体检 → 加指针 → 开保护 → 反向自检**,一条命令走完。 > 为什么要有"前置体检"这一关,见 §2 —— 跳过它会把 PR **永久锁死**。 +> +> ⚠️ **工具不替人合并、也不替人审批**(2026-10 改):它建完 PR 就把状态如实打给你,然后停下。 +> 批准之后才跑 `--merge-pr `,而它会**先核对检查状态**:失败 / 取消 / 从未运行 / +> 状态拿不到 / 等超时 / 头提交变了 —— 任一情形都拒绝请求合并(`--self-test` 逐条证明)。 ## 1. 一条命令 @@ -24,7 +28,7 @@ python3 ~/eng-standards/scripts/adopt-existing.py --apply --with-pr | 步 | 动作 | 怎么落地 | |---|---|---| | 1 | 体检 | 读默认分支、工作流触发方式、runner(仓库级/组织级)、最新提交的 status context、已有保护 | -| 2 | 加指针 | 新起分支 → 写 `AGENTS.md` → 提交(过本机闸门)→ **PR** → 等 CI → 合并 → 删分支 | +| 2 | 加指针 | 新起分支 → 写 `AGENTS.md` → 提交(过本机闸门)→ **PR** → 有界等 CI → **停下**(工具不替你合并) | | 3 | 开保护(**仅 `--with-protection` 时**) | `POST/PATCH /branch_protections`:禁直推 + 必须 PR;**状态检查按第 1 步判定**。**默认不做** —— 原稿口径是直推 `main`(正本 §1.3) | | 4 | 给自检命令 | 打印"直推必须被拒"的验证命令(**这步工具不代做,必须你跑**) | @@ -72,7 +76,7 @@ python3 ~/eng-standards/scripts/adopt-existing.py --apply --with-pr | 分支 + 提交 | `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` | +| 合并 | 当时由工具**自动**合并: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`** | @@ -112,7 +116,10 @@ cd /tmp/t && git commit --allow-empty -m 'chore(probe): 不该进 main' && git p 1. `AGENTS.md`(或 `.hermes.md`)在默认分支上,且含指向 `zheromiku/eng-standards-main` 的路径。 2. **(只在开了保护时)** `GET /repos/{r}/branch_protections` 非空、`enable_push=false`,且直推默认分支**被服务端拒绝**。 ⚠️ 默认口径是**直推 `main`**,所以正常情况下这里应当是"没有保护" —— **别把"没保护"当成缺陷**。 -3. 推一个故意违规的提交 → 本机 hook 红、CI 的 `gate` 红。**直推也拦得住**,因为 gate 在 CI 里(正本 §4.1)。 +3. 推一个故意违规的提交 → 本机 hook 红、CI 的 `gate` 红。 + ⚠️ 注意口径:CI 的 `gate` 是**推完再跑**的,它拦不住那次推送本身(正本 §2 / §1.3)。 + "直推也拦得住"是**错的**说法:要拦推送只有两条路 —— 本机 hook,或开分支保护(禁直推)。 + gate 的价值是"把违规提交标红并挡住编译/部署",不是"阻止它进 main"。 4. **(只在开了保护时)** PR 能正常合并 —— 证明强制状态检查没有把 PR 锁死。 5. 会话里问"这个仓提交信息什么规范",代理能**不靠你重复**答出来(说明指针真的被注入了)。 diff --git a/README.md b/README.md index c4df94d..027b731 100644 --- a/README.md +++ b/README.md @@ -18,17 +18,20 @@ | `04-模板评估与修正.md` | 附件 15 处问题的证据与改法(含实测反证) | | `05-应用机制-让规范在以后所有工程生效.md` | 三渠道(技能 / 仓内指针 / 生成器+守门)、已配 vs 待配、验证记录 | | `06-接入既有工程.md` | **既有仓怎么接进来**:前置体检(含 PR 锁死陷阱)、一条命令、试点记录、验收判据 | -| `templates/` | 可直接落仓的骨架:`gitea-build.yml`、`gitea-prod-deploy.yml`、`gitignore`、`commit-msg`、`ci_gate.py`、`ci_report.py`、`README.md`(内含 AGENTS.md 骨架) | -| `scripts/new-project.sh` | 新工程生成器:一条命令产出 9 个默认合规文件(已端到端测过) | -| `scripts/adopt-existing.py` | 既有工程接入器:体检 → 加指针 → 开保护(幂等,默认 DRY-RUN) | +| `templates/` | 可直接落仓的骨架:`gitea-build.yml`(**只做 CI**)、`gitea-prod-deploy.yml`(人工发起;未实现的步骤故意红)、`gitignore`、`commit-msg`、`ci_gate.py`、`ci_report.py`、`README.md`(内含 AGENTS.md 骨架) | +| `scripts/new-project.sh` | 新工程生成器:一条命令产出默认合规的骨架(CI + 门禁 + 提交规范;已端到端测过) | +| `scripts/adopt-existing.py` | 既有工程接入器:体检 → 加指针(建 PR 后**停下等你批准**)→ 开保护(幂等,默认 DRY-RUN);`--merge-pr ` 才是显式批准的合并 | +| `docs/architecture/` | 两份设计依据草案:`universal-ci-standard.md`(通用 CI 规范)、`knowledge-governance-ci-pilot.md`(试点交接)。**已生效的部分在 `01-规范` 里**;它们是过程记录,不是配置 | -## 0. 三十秒版(只记五句) +## 0. 三十秒版(只记六句) 1. **结论只来自可复现的命令**。数字、清单、成败一律由脚本打印或断言,禁止手抄进文档(原稿 §0-1,保留)。 2. **每个校验都要有反向自检**:故意弄坏一处,它必须红。永远返回 0 的校验器比没有校验更糟。 3. **密钥只从 Gitea Secrets / 目标机文件进**,不入库、不回显、不进命令行参数(会进 `ps`)。 -4. **提交前跑本仓门禁命令**;CI 不是第一次验证,CI 只是"再来一遍 + 部署"。 +4. **提交前跑本仓门禁命令**(唯一入口,如 `make verify`);CI 不是第一次验证,CI 只是"再来一遍"。 + **CI 与 CD 分开**:推送流水线只做检查/测试/构建/证据,**不含部署**;部署由每个项目自己设计。 5. **生产部署只部署"测试验收过的那一份产物"**(同 sha、同镜像 ID)。这条是本环境新增的硬约束,见 §6.1。 +6. **占位不许假报成功**:没实现的步骤要 `exit 1`(红的占位是设计),不要"打印 TODO 然后返回成功"。 ## 1. 本环境事实(**唯一取值表**;以命令输出为准,不要手抄到别处)