Gitea 源码开发工作流:从 make build 到 Swagger 校验的完整构建与调试指南
Gitea 采用前后端分离的构建体系:Go 编写后端、TypeScript/Vue 编写前端,通过 Makefile 统一编排 make build、make watch、make lint 等开发命令。本篇基于仓库中的 docs/development.md 与 Makefile 的实际内容,系统讲解从源码构建、热重载开发,到格式化/Lint 检查、SVG 图标生成、Swagger API 文档更新、配置项扩展与数据库迁移的完整日常开发流程,帮助你在本地环境中独立完成 Gitea 的构建、调试与提交前自检。
从源码构建 Gitea
构建开发版 Gitea 的核心命令只有一条:
make build
开发构建不需要任何 build tags:SQLite 支持默认编译进二进制(在 Makefile 中可以看到 CGO_TAGS := sqlite_mattn pam,只有当 TAGS 中包含这些 tag 时才会自动把 CGO_ENABLED 置为 1),本地开发完全够用。bindata tag 的作用是把前端资源嵌入二进制,仅在制作自包含的分发构建时才需要,日常开发应省略。
从 Makefile 可以看到 build 目标的实际编排:
build: frontend backend ## build everything
frontend:依赖public/assets/.vite/manifest.json,即执行pnpm exec vite build,把web_src/js、web_src/css编译产物输出到public/assets下(FRONTEND_DEST变量定义于 Makefile)。backend:先执行generate-backend(运行go generate,见 Makefile),再编译出可执行文件gitea。最终调用为go build -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)',其中LDFLAGS会注入main.Version与main.Tags两个变量——这正是 main.go 中声明的:
// these flags will be set by the build flags
var (
Version = "development" // program version for this build
Tags = "" // the Golang build tags
)
版本号的推导逻辑也很清晰(Makefile):tag 构建去掉 v 前缀作为正式版本号;release/v1.2 分支会得到 1.2-nightly;普通分支(如 main)则通过 git describe 生成形如 1.28.0+dev-356-ge47d0b66ea 的开发版本号。
使用 make help 可以列出全部可用 target(该 target 通过解析 Makefile 中 ## 注释生成帮助文本,见 Makefile)。持续集成如何构建和检查 Gitea,可以直接参考仓库内 .github/workflows 目录下的 workflow 文件(例如 pull-db-tests.yml 等)。
持续构建:让改动自动生效
开发过程中频繁修改源码,可以用 watch 命令实现文件变更自动重新构建:
# 同时监听前端和后端
make watch
# 只监听前端(启动 Vite dev server)
make watch-frontend
# 只监听后端(Go)
make watch-backend
从 Makefile 可以看到三者的实现细节:
make watch实际是执行bash tools/watch.sh(见 tools/watch.sh),由脚本协调前后端两个监听进程;make watch-frontend启动NODE_ENV=development pnpm exec vite,即 Vite 开发服务器,日志级别由FRONTEND_DEV_LOG_LEVEL(默认warn)控制;make watch-backend则通过 air 工具实现:GITEA_RUN_MODE=dev go run github.com/air-verse/air@v1.67.4 -c .air.toml,监听配置定义在仓库根目录的 .air.toml。
一个需要注意的坑:监听全部后端源文件可能触及 macOS / Linux 默认的文件描述符上限(open files limit),导致 air 报 EMFILE 类错误。解决方法是提高当前 shell 的限制,或写入 shell 启动文件使其持久生效:
ulimit -n 12288
格式化、Lint 与一致性检查
持续集成会拒绝未通过格式化、Lint 或一致性检查的 PR,因此提交前务必在本地先跑:
# 1. 先格式化
make fmt
# 2. 再 Lint:全量或分端
make lint # lint everything
make lint-backend # 只查后端
make lint-frontend # 只查前端
make fmt 做了两件事(Makefile):用 golangci-lint 的 fmt 子命令格式化 Go 代码,并用 sed 规则整理 templates 下所有 .tmpl 文件的空白(去除 {{、( 之后的以及 }}、) 之前的多余空格)。
make lint 的完整覆盖面(Makefile)包括:
| 目标 | 检查内容 | 工具 |
|---|---|---|
lint-js |
web_src/js、tools、tests/e2e 下的 JS/TS |
ESLint(--max-warnings=0)+ vue-tsc |
lint-css |
CSS 及 Vue 组件样式 | stylelint |
lint-go |
全部 Go 源码 | 通过 tools/lint-go-all.go 调度 golangci-lint |
lint-spell |
拼写检查(Go 目录 + 模板 + 英文本地化文件等) | misspell,自定义字典 assets/misspellings.csv |
lint-swagger |
Swagger/OpenAPI 规范 | spectral |
lint-yaml / lint-json / lint-shell / lint-actions |
各类配置文件 | yamllint、ESLint、shellcheck(容器内执行)、actionlint + zizmor |
其中 lint-go 的调度脚本是 tools/lint-go-all.go,配置基于仓库根目录的 .golangci.yml;前端则使用 eslint.config.ts 与 stylelint.config.ts。
许多问题可以自动修复:
make lint-fix # 全量自动修复
make lint-backend-fix # 只修复后端
make lint-frontend-fix # 只修复前端
CI 所执行的“组合一致性检查”对应 make checks,它在 Makefile 中拆分为两条支线:
checks-frontend: lockfile-check svg-check
checks-backend: tidy-check swagger-check openapi3-check fmt-check swagger-validate security-check
即后端侧除了格式与 Swagger/OpenAPI 校验,还会检查 go mod tidy 结果是否已提交(tidy-check),并用 govulncheck 做依赖安全扫描(security-check)。
构建与新增 SVG 图标
Gitea 的 SVG 图标通过 make svg 构建:它先清空 public/assets/img/svg,再运行 node tools/generate-svg.ts 把图标源编译到目标目录(Makefile)。
- 图标源文件目录:
web_src/svg - 输出目录:
public/assets/img/svg以及options/fileicon(SVG_DEST_DIRS定义于 Makefile)
CI 通过 svg-check 强制图标产物与源保持同步:重新生成后 git add 目标目录并 diff,若存在未提交的差异则直接失败(Makefile)。因此新增自定义图标的正确姿势是:在 web_src/svg 下添加源文件,运行 make svg,把生成的产物一并提交。
更新 API 文档(Swagger)
当你创建或修改 API 路由时,必须使用 go-swagger 注释更新 Swagger 文档。API 路由、请求/响应结构体与 swagger 定义之间的配合方式,详见 后端开发指南。
改动端点后,重新生成并校验规范,然后提交更新后的 JSON:
make generate-swagger # 从代码注释生成规范
make swagger-validate # 校验规范有效性
从 Makefile 可以看到生成链的细节:
- 输入模板:
templates/swagger/v1-input.json; - 产物:
templates/swagger/v1-swagger.generated.json(Swagger 2.0)与templates/swagger/v1-openapi3.generated.json(由go run build/generate-openapi.go转换,见 build/generate-openapi.go); - 生成时排除
gitea.dev/sdk包(SWAGGER_EXCLUDE),且要求生成过程零 warning; swagger-validate会拦截任何WARNING:输出,校验失败即退出非零。
CI 用下面的命令验证提交的规范与代码保持同步:
make swagger-check
其实现(Makefile)就是重新执行 generate-swagger,然后对 v1-swagger.generated.json 做 git diff——有 diff 就报错要求重新生成并提交。OpenAPI3 规范同理(openapi3-check)。
新增配置项的完整流程
在 Gitea 中增加一个配置项,仅修改 modules/setting 下的解析文件是不够的,完整的清单是:
- 在
modules/setting相应文件中读取新选项; - 更新示例配置文件 custom/conf/app.example.ini,让部署者能看到新选项;
- 在官方文档仓库的配置速查表(config cheat sheet)中补充文档说明(该速查表托管在独立的文档仓库中,不在本仓库内)。
这样做的意义在于:Gitea 的安装向导与文档均以 app.example.ini 为配置项的“单一事实来源之一”,遗漏更新会导致新配置无法被自助发现。
数据库迁移
对 models/ 目录下数据库持久化结构体做了破坏性变更(增删字段、改类型、加索引等)时,需要在 modelmigration/ 下添加一个新的迁移文件。该目录按版本组织为 v1_6/ 到 v1_27/ 以及 v28/ 等子包,每个迁移是一个带固定编号的 Go 文件(例如 v1_27/v342.go 是最新编号),迁移注册总表见 modelmigration/migrations.go。
迁移的测试运行方式见 测试指南:
make test-migration
迁移测试还会用到各迁移对应的 fixture 数据(位于 modelmigration/fixtures)。
测试概览
单元测试、集成测试、端到端测试与迁移测试的详细方法见 docs/testing.md,此处仅给出与日常开发最相关的入口:
make test-backend # Go 单元测试(本地默认 SQLite)
make test-backend#TestName # 跑单个后端测试
make test-integration # 集成测试
make test-integration#TestName # 跑单个集成测试
make test-e2e # Playwright 端到端测试
make test-migration # 迁移测试
值得注意的实现细节:
- Makefile 中,本地(非 CI)运行
test-*目标时默认GITEA_TEST_DATABASE=sqlite,无需任何外部数据库服务; - 集成测试使用编译好的
gitea二进制而非直接go test(Makefile 有注释说明:testlogger 会把 gitea 日志转发到t.Log,直接-v会刷屏); - 跑单个测试的
#选择器本质是把TestName转成go test -run的正则($(subst .,/,$*))。
IDE 配置
Visual Studio Code
仓库在 contrib/development/vscode 提供了 launch.json、tasks.json 和 settings.json 三个文件。使用方法是:在 Gitea 仓库根目录新建 .vscode 目录,把该目录内容复制进去,之后即可用 Ctrl+Shift+B 构建 gitea 可执行文件、F5 进入调试模式。更多说明见 contrib/development/README.md(该 README 说明支持 Debian、Ubuntu、Red Hat、Fedora、SUSE Linux、macOS 与 Windows)。
GoLand
在 main.go 的 func main() 上点击 Run Application 箭头,即可启动一个可调试的 Gitea 实例。
关键配置:Run/Debug Configuration 中的 Output Directory 必须设置为 Gitea 项目根目录(即包含 main.go 与 go.mod 的目录)。否则工作目录会是 GoLand 的临时目录,导致 Gitea 在开发模式下无法加载动态资源(如 templates/ 下的模板文件)。
提交你的改动
推送分支并发起 PR 即可;评审流程与 PR 要求见 CONTRIBUTING.md。提交前建议按本文顺序做一次完整自检:
make fmt
make lint
make checks
make test-backend
如果 PR 涉及 API 端点,追加 make generate-swagger && make swagger-validate;涉及 models/ 结构体,追加 make test-migration。需要帮助时可加入官方 Discord 的 #Develop 频道。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00