Files
eng-standards-main/templates/README.md
T
姚章浩 79c154f9af docs: 版本条目改固定四类 + 破坏性变化固定写法 + 构建身份(口径变更)
对着试点仓落地时暴露的问题做的口径与模板修正(本轮不推、不打 tag、不部署):

- 条目形状:四个自定义必填节 → **固定四类** Added/Changed/Fixed/Security
  (分类可省略、不许空栏目);过程记录进 docs/archive/,未合并事项进 docs/BACKLOG.md。
- 破坏性变化/迁移:固定写在 Changed 里、以 `**破坏性**` 开头并**必须链接发布文档**,
  仓内 `docs/….md` 链接**对账**(不存在即红)。
- 构建身份:release_version / build_kind / commit_sha / display_version / built_at;
  开发构建显示 X.Y.Z-dev+g<短SHA>(无基线 dev+g<短SHA>),**不许只显示上一正式版本号、
  不许自称正式版**;release 只认显式发布上下文。
- **还没有正式版本一律放行**:不判红;首个正式版本号**由人指定**(预检把它列成阻塞项)。
- **没核对远端就不许说校验成功**:CI/预检用 `--require-remote`(本地模式明确写「未核对远端」)。
- 修正"CI 取不到 tag 所以不用 tag"的过时口径:正式版本**就是**用注释 tag,
  只是**构建时不要用 git describe 取版本号**;**测试 tag 只在临时测试仓库里创建**。
- 模板同步:templates/check-changelog.py、templates/release-preflight.py、
  新增 templates/version.py;ci-gate.py 的 version 项在 CI 下强制核对远端;
  new-project.sh 落 scripts/version.py 并按四类重写 CHANGELOG 骨架;
  README/07/模板 README 同步;04 增加 §I(V-1~V-4 四条,含负例在哪)。

验证:templates 两个检查器 --self-test 全过;new-project.sh bash -n 通过;
生成的新工程里 check_changelog --tags 绿、--identity 给出 dev+g<短SHA>、
CI=true 下 version 项因"未核对远端"判红(预期);试点仓反向夹具 80/0。
2026-10-13 14:59:48 +08:00

9.1 KiB
Raw Blame History

templates/ —— 可直接落仓的骨架

每个文件都替换掉附件原稿的 GitLab 口径;改法见 ../04-模板评估与修正.md。 ⚠️ 复制到仓库后不要就地重写:ci_gate.py / ci_report.py 请从已验证的仓库逐字节复制 (Weknora/knowledge-governance/.gitea/scripts/ 或 /srv/aiball/ci/attach/)。

文件 落仓位置 说明
gitea-build.yml .gitea/workflows/build.yml 提交触发的校验+编译+预览+上报。只做 CI:里面没有任何部署作业(CD 由项目另写独立 workflow,见正本 §8)
gitea-prod-deploy.yml .gitea/workflows/deploy-production.yml 生产发布唯一入口,只 workflow_dispatch,不构建。⚠️ 未实现的步骤故意 exit 1:占位不许返回成功
gitignore .gitignore 去掉原稿自相矛盾的 !src/secrets/ 例外
commit-msg ~/.githooks/commit-msg 本机提交闸门;规则必须与 ci_gate.py 一致
AGENTS.md 骨架 AGENTS.md 见下(agent 指令文件有写入保护,故内联在此)
—— 随仓携带 .gitea/scripts/ci_gate.py
.gitea/scripts/ci_report.py
逐字节复制已验证版本,不要重写

落仓后的自检(反向自检,规范 §3.2-3)

# 1) 故意违规的提交必须被闸门拦住,且 build 不跑
git commit -F - <<'EOF'
update stuff
EOF
#   判据:本地 hook 报 ❌;push 后 CI 的 gate 红、build 未执行
#   ⚠️ 口径别说过头:CI 的 gate 是**推完再跑**的,它拦不住那次推送本身。
#      要拦推送只有两条路:本机 hook,或开分支保护(禁直推)。gate 的价值是
#      "把违规提交标红、挡住编译与预览",不是"没进 main"。

# 2) 合规提交必须过(正例对照:证明不是"永远红")
git commit -F - <<'EOF'
ci(deploy): 接入 Gitea Actions 生产发布 workflow

生产发布改成独立 workflow(只 workflow_dispatch),避免塞进 push 流水线;
concurrency 明确 cancel-in-progress: false —— 取消不是回滚。
验证:手动发起一次,confirm 不匹配时 guard 作业必须红。
EOF

# 3) 产物地址每个都要 200
curl -m 5 -o /dev/null -w '%{http_code}\n' http://192.168.50.134:8899/healthz
curl -m 5 -o /dev/null -w '%{http_code}\n' http://100.92.49.78:8899/healthz

上报脚本的非秘密配置(写在 workflow 里,便于审查)

变量 作用 缺失时
CI_PUBLIC_BASE_URL 通知里 CI 运行 / 提交 / 变更 三类链接的基址(对外域名,如 https://gitea.example.com) 上报脚本明确回退 GITHUB_SERVER_URL(内网地址),并把来源写进上报的 link_base_source
  • 不覆盖 GITHUB_SERVER_URL:检出、gate、内部调用继续用它;只影响通知里的三类链接。
  • 非法值(非 http(s)、带 userinfo、带空白)⇒ 明确回退并在 CI 日志里写明原因,不生成垃圾链接。
  • 消息里"查看"的报告入口就是 Gitea 运行页(不附 bridge 自己的 /r/<id> 内网页); 只有拿不到运行链接时才退回 bridge 报告页。
  • bridge 侧 ci_base_url 是链接的可信来源(字符串或数组): 公网域名与(回退时的)内网地址都应登记,否则会被当成"非可信链接"丢掉。

AGENTS.md 骨架(复制到仓库根,按本工程改写 TODO)

# AGENTS.md — 给在 <PROJECT> 里干活的 AI 代理

> 这是**操作手册**,不是给用户看的介绍。改代码前先读「硬约束」与「已知陷阱」。
>
> **系统级规范(本机所有 Gitea 仓库都适用)**:仓库 `zheromiku/eng-standards-main`(本机工作副本 `~/eng-standards/`)
> —— `01-规范-Git与CI-CD(Gitea版).md`(正本)+ `02-流程说明与Runbook.md`。
> 做 git / 提交 / CI / 发布 / 回滚 / 密钥 这些事之前先读它;本文件只写**本工程特有**的东西。

## 0. 这个工程是什么

<PROJECT>(一句话说清做什么、给谁用、部署在哪)。

## 1. 硬约束(违反其中任何一条,这次改动就不合格)

1. TODO:不许改变对外可观测行为(接口、报文、退出码、配置语义)
2. TODO:本工程最硬的那条证据是什么(对拍 / 契约 / 字节一致 / 快照)
3. **同一含义的代码只准有一份**(禁止同义复制)
4. **注释写"为什么"**,不写"是什么"
5. 提交按"一件事一个提交",conventional commits,正文用中文,标题 ≤72 字符
6. **生产使用测试验收过的同一份构建产物**(同 sha / 同镜像 ID)—— 不许在生产机上另行 build

## 2. 本地验证闭环(提交前的最低要求)

```bash
make verify       # 本仓唯一门禁入口(本地与 CI 同一条命令);跳过项会显式警告「不算全绿」
```

- 门禁**串行**、失败即停;**汇总按必需清单逐项核对**:失败即停后剩下的记 `not_run`,
  而 `not_run` / `skipped` 都不算通过(别把「没跑到」当成「没问题」)。
- 只有「跑产物」的套件(端到端 / 契约 / 产物自检)才必须排在构建之后;
  单测(`go test` / `pytest` / `vitest`)各自跑源码,不需要先构建。
- 改到部署脚本或产物形状时,必须另外跑一次真实的装配/启动自检。

## 3. CI 闭环

```bash
giteactl runs <ns>/<repo>                 # 最近运行
giteactl logs <ns>/<repo> <runId>         # 作业日志
python3 ~/.hermes/scripts/gitea_api.py GET "/api/v1/repos/<ns>/<repo>/actions/tasks?limit=5"
```

- 失败时**先读日志再改代码**;追到第一处 `❌`。
- `status=pending` 且 `status_count=0` **不是**失败(那是"没有配置任何状态检查")。
- run 一直 `queued` ⇒ 先查**本仓库有没有自己的 Runner**,别怀疑工作流。
- 合并读回:`GET /pulls/{n}` → `state=closed` + `merged=true` + `merge_commit_sha` 非空。
  ⚠️ 合并接口成功时可能返回**空 body** → **永不重试**,改读回。

## 4. 已知陷阱(索引)

全文在 `docs/agents/traps.md`。**只追加、不重排、不复用旧号**;编号会被代码注释与测试引用。
⚠️ 新增陷阱要改两处(traps.md 加全文 + 这张表加一行)。

| # | 一句话 | 全文 |
| --- | ------ | ---- |
| 1 | TODO | [读](docs/agents/traps.md#trap-1) |

## 5. 提交与推送

- **默认直推 `main`**(原稿口径,正本 §1.1);**需要评审时**才开短命分支 + PR(正本 §1.2,那是**建议**、不是门禁)。
- conventional commits,正文中文,**说清"为什么"**;一件事一个提交。
- 提交信息不要用反引号 / `$()`(会触发命令替换):用 `git commit -F -` + heredoc。
- 推之前跑完第 2 节;推送后用第 3 节确认流水线,失败就**先读 trace 再改代码**。
- 生产发布**不走 push**:它是独立的 `deploy-production` workflow,只由人手动发起(见规范 §5.2)。

## 6. 版本与发版(**CI 不代劳,必须人批准**)

- **开工前先读系统级规范的正本 §6**(`~/eng-standards/01-规范-Git与CI-CD(Gitea版).md`)
  与 `07-发版操作说明(tag与Release).md`。
- 版本号的**唯一权威来源**:本仓 `CHANGELOG.md` 顶部那条已发布版本(正本 §6.1)。
  禁止再放 `VERSION` 文件、禁止写进源码常量、禁止在 CI 里用 `git describe`(检出取不到 tag)。
- **完成一个功能**时,把**使用者可见**的变化写进 `CHANGELOG.md` 的 `## [Unreleased]`
  (四类小节 `Added` / `Changed` / `Fixed` / `Security`,分类可省略、别留空栏目;
  **不要求每个提交升版本**)。过程记录放 `docs/archive/`,未合并事项放 `docs/BACKLOG.md`。
- **构建身份**:`python3 scripts/version.py --identity` —— 开发构建显示 `X.Y.Z-dev+g<短SHA>`
  (没有正式基线时 `dev+g<短SHA>`),**不许只显示上一正式版本号、不许自称正式版**。
- **准备发版**时,先给用户这五样,批准后才动:
  1. **版本建议**(`python3 scripts/release_preflight.py` 给的候选值;
     **还没有正式发布过时版本号必须由用户显式指定**,预检会把它列成阻塞项);
  2. **变更说明**(四类小节;破坏性变化写在 `### Changed` 里、以 `**破坏性**` 开头并链接发布文档);
  3. **准确提交的完整 40 位 SHA**;
  4. 那次提交的 **CI 成功证据**(status context + run 链接)—— 不是"本地跑过了";
  5. **未验证项与遗留风险**。
- ⛔ **未获用户明确批准,不创建、不推送正式 tag,不发布 Gitea Release。**
  CI **不**自动升版本、**不**打 tag、**不**发布。
- 正式发版用**注释 tag**(`v<版本号>`)指向验收过的提交;Gitea Release 关联该 tag,
  说明**来自 CHANGELOG**,不另写一份。
- 自查命令:`python3 scripts/check_changelog.py --tags --require-remote`(结构 + tag 一致性,**真查远端**);
  `python3 scripts/release_preflight.py`(**只读**预检,不给"可以发"就是不能发)。
- ⚠️ **开发版本没打 tag 是正常的**:日常 CI 不许因此变红。