Files
姚章浩 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

119 lines
9.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Gitea 版工程规范(eng-standards 适配版)
> **性质**:跨仓库的系统级规范,按**本机真实环境**(自建 Gitea 1.26.2 + act_runner + ci-bridge + 阿里云测试/生产)改写。
> **来源**:评估并适配 `eng-standards-main.zip`(原稿是 GitLab/glab 口径),模板问题与修正见 `04-模板评估与修正.md`。
> **状态**:本次**只制定规范**。不部署生产、不运行附件原脚本、不改任何仓库。
> **正本仓库**:`zheromiku/eng-standards-main`(Gitea,https://gitea.zheromiku.top/zheromiku/eng-standards-main);本机工作副本 `~/eng-standards/`。
>
> **怎么读**:`README` 三十秒版 → `01-规范` 全文 → `02-流程说明与Runbook`(怎么操作)→ `03-待配置事项`(还差什么)→ `04-模板评估与修正`(原稿哪里不对)→ `05-应用机制`(怎么让它以后默认生效)→ `06-接入既有工程`(手头这个仓怎么接进来)→ `07-发版操作说明`(打 tag 与建 Release 的步骤)。
## 交付物
| 文件 | 是什么 |
|---|---|
| `README.md` | 本文件:索引 + 三十秒版 + 本环境**唯一取值表** |
| `01-规范-Git与CI-CD(Gitea版).md` | **正本**:分支/提交/评审/触发、自动检查与测试环境部署、生产发布与回滚、版本与产物 |
| `02-流程说明与Runbook.md` | 日常动作的**逐步命令**(提交前、看 CI、接入新仓、发布、回滚) |
| `03-待配置事项.md` | 逐项:现状 / 要做什么 / 为什么 / 怎么验证 / 谁来做 |
| `04-模板评估与修正.md` | 附件 15 处问题的证据与改法(含实测反证) |
| `05-应用机制-让规范在以后所有工程生效.md` | 三渠道(技能 / 仓内指针 / 生成器+守门)、已配 vs 待配、验证记录 |
| `06-接入既有工程.md` | **既有仓怎么接进来**:前置体检(含 PR 锁死陷阱)、一条命令、试点记录、验收判据 |
| `07-发版操作说明(tag与Release).md` | **怎么发版**:三态区分、四类小节变更记录(含破坏性变化的固定写法)、构建身份、只读预检、批准后打注释 tag + 建 Release 与读回判据、常见错误表 |
| `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 <n>` 才是显式批准的合并 |
| `docs/architecture/` | 两份设计依据草案:`universal-ci-standard.md`(通用 CI 规范)、`knowledge-governance-ci-pilot.md`(试点交接)。**已生效的部分在 `01-规范` 里**;它们是过程记录,不是配置 |
## 0. 三十秒版(只记六句)
1. **结论只来自可复现的命令**。数字、清单、成败一律由脚本打印或断言,禁止手抄进文档(原稿 §0-1,保留)。
2. **每个校验都要有反向自检**:故意弄坏一处,它必须红。永远返回 0 的校验器比没有校验更糟。
3. **密钥只从 Gitea Secrets / 目标机文件进**,不入库、不回显、不进命令行参数(会进 `ps`)。
4. **提交前跑本仓门禁命令**(唯一入口,如 `make verify`);CI 不是第一次验证,CI 只是"再来一遍"。
**CI 与 CD 分开**:推送流水线只做检查/测试/构建/证据,**不含部署**;部署由每个项目自己设计。
5. **生产部署只部署"测试验收过的那一份产物"**(同 sha、同镜像 ID)。这条是本环境新增的硬约束,见 §6.1。
6. **占位不许假报成功**:没实现的步骤要 `exit 1`(红的占位是设计),不要"打印 TODO 然后返回成功"。
## 1. 本环境事实(**唯一取值表**;以命令输出为准,不要手抄到别处)
```bash
# 取这张表的命令(改完环境重跑,别靠记忆)
python3 ~/.hermes/scripts/gitea_api.py GET /api/v1/version
python3 ~/.hermes/scripts/gitea_api.py GET /api/v1/repos/search?limit=50
docker ps --format '{{.Names}}\t{{.Image}}' | grep -E 'runner|bridge'
curl -s http://127.0.0.1:8899/healthz
```
| 项 | 值 |
|---|---|
| Gitea | 自建 **1.26.2**;LAN `http://192.168.50.223:3000`;公网别名 `https://gitea.zheromiku.top` |
| 账号 / 组织 | `zheromiku`;组织 `Weknora`、`aiball` |
| 仓库 | `Weknora/{dobotai-platform, knowledge-governance, knowledge-adapter-service, Enterprise-Knowledge-Base}`;`aiball/{protocol-docs, software-pc, upper-tool(空)}`;`zheromiku/{hello, protocol-docs(空)}` |
| CI 引擎 | Gitea Actions(act_runner,**仓库级** Runner 容器) |
| Runner | `runner-weknora-kg-01`→knowledge-governance、`runner-weknora-dp-01`→dobotai-platform、`runner-weknora-ka-01`→knowledge-adapter-service、`runner-software-01`(aiball 组织级,覆盖 protocol-docs/software-pc) |
| Runner 配置 | `capacity: 1`(同时只跑 1 个作业)、`force_pull: false`、labels `ubuntu-latest/24.04/22.04` → `docker://docker.gitea.com/runner-images:*`、注册令牌**文件挂载**(`/run/secrets/registration_token:ro`)、挂 `docker.sock` |
| 工具链/缓存 | 主机 `/srv/aiball/ci/toolcache` 只读挂载为容器内 `/opt/citools`;可写缓存 `/srv/aiball/ci/toolcache/cache` → `/opt/citools-cache` |
| ci-bridge | `/srv/aiball/ci/bridge/bridge.py`,监听 `0.0.0.0:8899`;容器内可达地址 `http://172.17.0.1:8899`;局域网 `http://192.168.50.134:8899`;Tailscale `http://100.92.49.78:8899` |
| bridge 接口 | `POST /api/ci/report`(作业结果→落盘+微信通知)、`POST /api/ci/publish`(静态产物 tar.gz→URL)、`GET /p/<ns>/<repo>/<sha>/…`、`GET /r/<id>`、`GET /healthz` |
| bridge 落盘 | 报告 `/srv/aiball/ci/reports/`(含 `pending/`、`undelivered/`);预览 `/srv/aiball/ci/previews/` |
| 仓库变量/密钥 | `CI_BRIDGE_URL`(variable)、`CI_BRIDGE_TOKEN`(secret)——仅 Weknora 三仓已配 |
| 提交闸门 | 本机 hook `~/.githooks/commit-msg`(`core.hooksPath`)+ CI 侧 `.gitea/scripts/ci_gate.py`(同一套规则,绕不过) |
| 仓库接入脚本 | `/srv/aiball/runner/attach_repo.sh <owner/repo> <runner-name>`、`/srv/aiball/runner/deploy_repo_runner.sh` |
| Gitea API 客户端 | `~/.hermes/scripts/gitea_api.py`(库/CLI)、`~/.local/bin/giteactl`(`runs/show/logs/watch/rerun`)、`giteacli`(**只读用途,本机 token 无 read:user**) |
| 测试服务器 | 阿里云 ECS `120.76.217.51`(key `~/.ssh/weknora-aliyun-test-ed25519`) |
| 生产服务器 | 阿里云 ECS `120.76.218.137`(key `~/.ssh/weknora-aliyun-prod-ed25519`) |
| 生产布局 | `/opt/dobotai/{config/dobotai.env, container/{compose.yaml,runtime.env,migrations/,backups/,ops/}, secrets/}`;镜像由 `container/runtime.env` 的 `DOBOTAI_IMAGE=` 选择 |
| 健康/版本 | `http://127.0.0.1:8090/healthz`(`{"status":"ok"}`)、`http://127.0.0.1:8090/version`(`version/commit_sha/build_time`) |
| 数据库 | **永远是阿里云 RDS(PostgreSQL 17.9)**;目标机 Docker 只跑应用容器,禁止本地库 |
| 构建受限 | 目标机 **不可达 Docker Hub** → 用 daocloud 镜像源;Go 用 `GOPROXY=https://goproxy.cn,direct` |
| 本机网络 | 出网走 mihomo;境外全 000 时先查 `~/.clashctl` 节点(`PUT /proxies/三毛机场 {"name":"<活节点>"}`) |
> ⚠️ 上表里**只有一个数字是"规范"**(如"生产路径 /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`
(实现随项目走)而明确报"自检跑不了"—— 那是**故意**的:不许拿一份空清单去跑出莫名其妙的结论。
## 完整调用链(在**项目仓**里跑:空工作区 → 最小 bootstrap → 仓内脚本 → 提交范围闸门)
```bash
python3 scripts/ci_chain_fixture.py
```
它起一个本地 HTTP git 服务,并把 workflow 里 **bootstrap 那段 `run:` 原样抽出来**在**空目录**里执行
(所以 YAML 一旦改坏,这里立刻红):正例验证"空工作区能启动、检出到事件完整 SHA、协议保留、
新分支范围非空、合规提交通过",负例复现"第一步调用仓内脚本 = 127"、"取不到事件 SHA 必须失败"、
"含违规提交的新分支判红且**红在那条提交上**"。
⚠️ **模板 workflow 的第一步必须是最小 bootstrap**(只依赖 runner 给的环境变量):
空工作区里仓内脚本还不存在 —— `grep -n "name: 检出代码" -A3 templates/gitea-build.yml` 可核对。