Gitea AGENTS.md 深度解析:让 AI 编程 Agent 遵循项目规范的 20 条硬规则
【免费下载链接】giteaGit with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry and CI/CD项目地址: https://gitcode.com/GitHub_Trending/gi/gitea
Gitea 仓库根目录的 AGENTS.md 是一份专为 AI 编程 Agent(以及希望模仿 Agent 行为的贡献者)定制的协作规范:它约束了 PR 描述写法、Conventional Commits 类型选择、Agent 署名 trailer、注释哲学、前后端代码风格、lint 命令与测试运行方式。读完本文,你将掌握 Gitea 对 AI 辅助开发设定的全部 20 条规则、每条规则背后的仓库实现依据(如 Makefile 目标、locale 同步机制、e2e 测试脚本),以及如何在实际贡献中准确执行这些约束。
1. 文档定位与总则
AGENTS.md 采用极简的逐条规则(bullet rule)格式,共 20 条,可归纳为五个主题域:
| 主题域 | 覆盖规则 |
|---|---|
| 事实与版本控制纪律 | 先验证再断言、不重写 git 历史 |
| 信息与文档导航 | 用make help列开发目标、读docs目录 |
| PR 与提交规范 | PR 描述、issue 引用、Conventional Commits、Agent 署名 |
| 代码风格 | 注释哲学、版权头、locale、TS 非空断言、Go 现代特性、Tailwind 工具类 |
| 质量保障 | lint/生成命令、修复根因而非禁用检查、单测运行与性能预算 |
1.1 先验证,再断言(Never assume, verify before claiming)
第一条规则即"不要假设,先验证再下结论"。这是对 Agent 最常见的幻觉式输出(凭空声称某函数存在、某参数默认值是多少)的针对性约束。在 Gitea 仓库中,验证手段是明确的:用 Makefile 提供的make help目标查看全部开发目标——该目标通过awk解析 Makefile 中带##注释的行来生成帮助输出(见 Makefile#L183-L187),还会额外打印test-e2e、test-backend[#TestSpecificName]、test-integration[#TestSpecificName]三个参数化目标的用法说明。因此规则要求 Agent 在声称"某命令可以执行"之前,先运行make help核实目标确实存在。
1.2 文档导航:docs目录是权威来源
第二条规则要求 Agent 在动手前阅读docs目录中的开发者文档。当前仓库docs目录包含以下文件,构成完整的开发文档体系:
- development.md:从源码构建 Gitea 与日常开发工作流(
make build依次执行frontend与backend两个子目标); - testing.md:四类自动化测试(单元测试、集成测试、e2e 测试、迁移测试)的运行方法;
- guidelines-backend.md、guidelines-frontend.md、guidelines-refactoring.md:分领域的编写与重构指南;
- community-governance.md、release-management.md:社区治理与版本发布管理。
这条规则实际上把 AGENTS.md 与docs目录建立了一个两级文档结构:AGENTS.md 管"Agent 行为",docs管"项目事实",Agent 必须以后者为准来回答技术性问题。
1.3 不重写 git 历史
规则明确:除非被要求,绝不重写 git 历史;更新 PR 一律通过追加新提交并正常git push完成。这避免了 Agent 使用rebase/amend后强推(force-push)造成的协作事故,也符合 CONTRIBUTING.md 中"PR 会被 squash-merge、PR 标题即最终提交信息"的合并流程——历史整洁由合并方保证,贡献方只需线性追加提交。
2. PR 与提交信息规范
2.1 PR 描述:最小化,只写 what 与 why
规则要求 PR 描述保持最小:只说明"做了什么"和"为什么",禁止罗列任务清单或文件列表;UI 变更必须附截图,修改既有 UI 时须附 before/after 对比;目标控制在 1000 字符以内。这与 CONTRIBUTING.md 的 PR 章节完全一致:PR 标题描述"问题"而非"修法",首条评论作为 PR 摘要,功能 PR 的截图与"用法说明 + 测试说明"是合并的硬性前提。
2.2 引用 issue 与 PR 必须用完整 URL
规则要求通过完整 URL 而非编号引用 issue/PR。对 Agent 而言这是可检索性约束:编号#1234脱离仓库上下文后无法被外部 LLM 或搜索引擎解析,完整 URL 则自含项目、平台与定位信息。
2.3 Conventional Commits 与 Gitea 专属的enhance类型
规则要求提交信息与 PR 标题使用 Conventional Commits 格式,并特别指出用户可见的小型增强应使用 Gitea 专属的enhance类型。CONTRIBUTING.md 给出了完整类型表(build、ci、chore、docs、feat、enhance、fix、perf、refactor、revert、style、test),其中enhance的定义是:
小型或琐碎的用户可见改进或 UX 打磨(如措辞修改、颜色调整、间距/边距微调、占位符、小的 UI 行为改进)。
它与feat(较大的用户可见功能)构成大小之分。示例包括:
fix(web): prevent avatar upload crash on empty file feat(api): add pagination to repo hooks list enhance(repo): improve diff toolbar spacing ci(workflows): lint PR titles in CI实现层面的佐证:CI 会对合法 Conventional Commits 标题自动打type/…标签——feat/enhance/fix/docs/test前缀会分别获得对应标签(例如enhance(web): …获得type/enhancement),且标签随标题编辑保持同步;其他前缀则不自动打标,由合并者负责。可见enhance不是文档约定而已,而是与 CI 打标管线、changelog 生成直接联动的机制。
2.4 Agent 署名:Assisted-bytrailer,而非Co-Authored-By
规则规定:提交信息中必须添加形如下式的 trailer:
Assisted-by: AGENT_NAME:MODEL_VERSION并明确禁止使用Co-Authored-By或Signed-off-by来表达 Agent 参与。这一设计的含义是:
- 人类作者仍是唯一的
Author/Co-Authored-By主体,Agent 贡献以独立的Assisted-by元数据记录,格式固定为"代理名:模型版本"两段,便于后续按工具与模型维度统计 AI 辅助情况; - 不用
Signed-off-by是因为 DCO 签名语义上声明的是"本人有权提交此代码",把 Agent 放进该 trailer 会混淆责任主体。
2.5 Issue/PR 评论中的署名位置
在 issue 与 PR 的评论中,Agent 署名必须放在单行结尾(trailing line),不得作为 PR 描述的一个章节。这避免了署名块喧宾夺主,也与 PR 描述"最小化"的原则保持一致。
3. 代码风格规则
3.1 注释哲学:几乎不写注释
规则对注释的约束极强:
- 几乎不写注释;要写就写短的、尽量同行(same-line)的,为未来读者解释"为什么";
- 绝不复述代码本身、变更过程或生成该代码的 prompt("Never narrate code, the change or the prompt"——后者是专门针对 Agent 生成代码时习惯留"这是根据用户要求 X 生成"类注释的现象);
- 保留仍然适用的既有注释;
- 如果需要写段落级注释,说明实现大概率过于复杂,应当重新设计。
这一条与 Gitea 后端指南的基调一致,核心是"复杂度的告警器":长注释通常不是文档不足,而是实现有问题的信号。
3.2 新.go文件的版权头
新建.go文件须写入当年年份的版权头。CONTRIBUTING.md 给出了标准格式:
// Copyright <current year> The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT且此后仅当版权主体变化时才修改。AGENTS.md 将其细化为 Agent 的操作性指令:"加入当前年份"。
3.3 locale:只编辑locale_en-US.json
规则规定在options/locale目录下只能编辑 locale_en-US.json,其他语言的 locale 文件由 Crowdin 同步流程自动覆盖更新,手动修改其他语言文件会在下次同步时被冲掉。CONTRIBUTING.md 的 Translation 章节确认了这一点:仓库内只维护英文翻译,其余语言达到约 25% 的翻译覆盖率后回同步进仓库。Agent 如果"好心"顺手修了其他语言的文案,属于无效甚至有害的改动。
3.4 TypeScript:值必存在时用!而非?./??
前端规则要求:当某个值在类型与运行时逻辑上必然存在时,使用非空断言!而不是可选链?.或空值合并??。其语义是:?./??会对读者暗示"这里可能是空",如果实际不可能为空,防御式写法反而掩盖了真实不变量;!则把不变量显式声明出来,把防御留给真正可空的边界。
3.5 Go:优先使用现代语言特性
规则要求在 Go 代码中尽可能使用现代语言特性。结合 go.mod 声明的语言版本可推断,这指向for range int循环、min/max/clear内置函数、泛型等随新版 Go 引入的能力——即新代码不应停留在旧式写法(如手写 64 位整数取模的循环、临时变量交换等)。
3.6 Tailwind:tw-*工具类优先于内联样式与子项边距
前端样式规则:
- 优先使用
tw-*(Tailwind)工具类,而非内联style; - 优先使用
flex-*(如tw-gap-*间距类)而不是给每个子元素逐一加tw-ml-*/tw-mr-*边距; - 当需要提升优先级时,才回退到带
!important的tw-*写法。
这与 Gitea 前端"全面 Tailwind 化"的方向一致,gap优先于逐项 margin 也是响应式布局更稳健的通用实践。
4. Lint、格式与生成命令
4.1 四条"改完就跑"的命令
规则将常见改动类型映射到固定的收尾命令:
| 改动类型 | 必须执行的命令 | 说明 |
|---|---|---|
编辑.go文件 | make fmt | 格式化 Go 与模板代码 |
编辑go.mod | make tidy | 运行go mod tidy并重新生成 license 文件 |
| API 变更 | make generate-swagger | 从代码注释重新生成 swagger 规范 |
| 有 diff 的任意文件 | make lint-go/lint-js/lint-css/lint-templates | 对改动范围做 lint |
这些目标均可在 Makefile 中逐一验证存在:
fmt(Makefile#L199)实际是golangci-lint fmt加上对templates下*.tmpl的 sed 规整(去掉{{、(后与}}、)前的多余空白);配套的fmt-check会git diff检查是否还有未格式化的差异并让 CI 失败;tidy(Makefile#L420)执行go mod tidy -compat=<go.mod 声明版本>,还包含一段针对上游 Go issue 的 workaround(tidy 丢失toolchain行时用go mod edit -toolchain恢复),随后重新生成 license 清单;generate-swagger(Makefile#L228)调用go-swagger从 Go 源码注释生成规范,并把非go:前缀的输出行视为告警直接失败——即生成过程不干净就不算成功;- lint 目标族(Makefile#L276-L363):
make lint汇总了lint-frontend、lint-backend、lint-templates、lint-swagger、lint-spell、lint-md、lint-actions、lint-json、lint-yaml、lint-shell,AGENTS.md 只要求 Agent 按改动范围挑对应的细分目标(lint-js、lint-css、lint-go、lint-templates),避免全量 lint 的噪音与耗时。
4.2 修复根因,而非禁用检查
规则要求:遇到问题时修复原因本身,而不是禁用 linter 或削弱测试;确不可避免时,使用最小作用域并附行尾注释说明理由。这是对 Agent 常见"绕过行为"(加nolint、注释掉断言、放宽期望值让测试变绿)的直接封杀,最小作用域 + 行尾理由两个约束保证了例外可审计。
5. 测试规则
5.1 三类测试的单测运行方式
规则给出了 Gitea 三种自动化测试各自的单测命令,全部可在仓库中核实:
# Go 单元测试:精确匹配测试名 go test -run '^TestName$' ./modulepath/ # TypeScript 单元测试(Vitest):按路径过滤 pnpm exec vitest <path-filter> # Playwright e2e:只跑指定测试文件 GITEA_TEST_E2E_FLAGS='<filepath>' make test-e2e- Go 侧使用
^...$锚定正则防止误匹配其他测试; make test-e2e的实现链为test-e2e -> playwright frontend backend,最终由 tools/test-e2e.sh 消费GITEA_TEST_E2E_FLAGS环境变量,并自动在本地与容器两种 Playwright 运行模式间探测(非 Ubuntu/Debian 的 Linux 上自动回退到容器模式);- Makefile 还提供了参数化目标的替代写法:
make test-backend#TestXxx会经$(subst .,/,$*)把.转换为/后传给-run(Makefile#L404-L406),集成测试同理有test-integration#%目标。
5.2 测试数量、速度与确定性预算
规则最后两条是量化与定性结合的测试纪律:
- 数量与速度:写"最少、最快"的测试来覆盖行为,能扩展现有测试就不要新建;逻辑可隔离时优先单元测试。仓库层面有对应支撑——单元测试通过
GO_TEST_PACKAGES过滤掉modelmigration、tests等重量级包(Makefile#L112),且集成测试默认走 SQLite(非 CI 环境下GITEA_TEST_DATABASE缺省为sqlite,见 Makefile#L30-L36),无需外部数据库服务; - 性能预算:单个集成测试目标 < 2 秒,单个 e2e 测试 < 4 秒。这是硬性的单测耗时预算,超出即视为实现或测试设计有问题;
- 确定性等待:等待条件满足必须等待确定性条件(如元素出现、请求完成、日志就绪),禁止
sleep硬等待——这与 e2e 脚本中wait_for_container采用"轮询端口 + 30 秒超时上限"的模式(tools/test-e2e.sh)是同一哲学; - 语义化定位器:e2e 测试优先使用
getByRole/getByLabel一类语义定位器,而非脆弱的 CSS 选择器或 XPath,保证 UI 样式类名变化不会击穿测试。
6. 规则落地的完整工作流
把 20 条规则串起来,Agent(或贡献者)在 Gitea 仓库完成一次合规改动的完整闭环是:
- 核实:
make help确认目标存在;阅读docs下对应指南;引用外部对象一律完整 URL; - 实现:Go 用现代特性、新文件带当年版权头;TS 用
!表达不变量;样式用tw-*与flex-*;注释几乎不写、只解释"为什么"; - 文案:只改 locale_en-US.json;
- 收尾命令:按改动面执行
make fmt/make tidy/make generate-swagger,再跑对应的lint-*目标; - 测试:
go test -run '^TestName$' ./modulepath/、pnpm exec vitest <path-filter>或GITEA_TEST_E2E_FLAGS='<filepath>' make test-e2e定向验证,遵守 2s/4s 预算与确定性等待; - 提交:Conventional Commits 标题(用户可见小改进用
enhance),追加提交而非重写历史,提交信息尾部加Assisted-by: AGENT_NAME:MODEL_VERSION; - PR:最小化描述(what/why,<1000 字符),UI 变更附 before/after 截图,评论中的 Agent 署名放在单行结尾。
这套规范的工程价值在于:它把"人类评审者不会重复提醒的隐性知识"(哪些命令必须跑、哪个 locale 文件不能碰、Agent 该怎么署名、测试有多快才算合格)显式化为机器可消费的规则集,使 AI 辅助产出在进入人工评审前就满足 Gitea 的格式、标签与流程预期,从而降低 PR 往返成本。
【免费下载链接】giteaGit with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry and CI/CD项目地址: https://gitcode.com/GitHub_Trending/gi/gitea
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考