# 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 ` 才是显式批准的合并 | | `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////…`、`GET /r/`、`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 `、`/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` 可核对。