- CI/CD
- DevOps
【免费下载链接】woodpecker
Woodpecker is a simple, yet powerful CI/CD engine with great extensibility.
本篇指南面向希望深度参与 Woodpecker CI/CD 引擎开发的贡献者,完整梳理 docs/versioned_docs/version-3.16/92-development/07-guides.md 中三大核心主题:基于 Xorm 的数据库迁移机制、官方镜像常量的管理规范,以及 Server / Agent / CLI 三种组件的本地镜像构建流程。读完本篇,你将掌握如何为 Woodpecker 添加一条可自动执行、可回滚、可测试的数据库迁移,理解模型变更与表结构同步的底层原理,并能从源码编译出可运行、可推送的官方容器镜像。
一、ORM 选型:Xorm 与 xormigrate
Woodpecker 服务端的数据库访问统一基于 Xorm 这一 Go 语言 ORM 实现。围绕 Xorm,项目还引入了src.techknowlogick.com/xormigrate作为数据库迁移管理库,两者共同构成了服务端数据层的基石。
从 migration.go 的导入可以看出,迁移任务本身就是一个xormigrate.Migration结构体的切片,其中MigrateSession回调接收一个*xorm.Session,开发者在这个回调里直接编写针对该数据库方言的 SQL 或 Xorm 操作。Xorm 的另一大职责是模型到表结构的自动同步:当模型结构体字段发生变化(如新增一个 struct tag 属性)时,底层 ORM 会基于结构体字段标签自动处理列的新增与调整,无需手写 DDL。
二、添加一条新的数据库迁移
2.1 放置位置与命名规范
所有迁移任务都放在server/store/datastore/migration/目录下,文件名遵循NNN_描述.go的编号前缀约定。截至当前仓库版本,迁移编号已推进到030,例如:
- 000_legacy_to_xormigrate.go:历史遗留迁移记录转换,负责把旧版
migrations表内容导入 xormigrate 体系后删除旧表; - 001_add_org_id.go:为 users 表新增
user_org_id列,并为每个用户创建对应的 org 记录; - 030_deduplicate_log_entries.go:按
(step_id, line)去重 log_entries,为唯一索引腾出空间。
新文件需要自行定义包级变量,典型结构如下(以001_add_org_id.go为模板):
package migration import ( "fmt" "src.techknowlogick.com/xormigrate" "xorm.io/xorm" ) var addOrgID = xormigrate.Migration{ ID: "add-org-id", MigrateSession: func(sess *xorm.Session) error { // 1. 同步涉及的新模型 // 2. 查询旧数据 // 3. 逐条转换并更新 return nil }, }其中ID字段是迁移的唯一标识,一旦执行成功会被持久化到迁移记录表中,后续启动不会再重复执行。
2.2 两条硬性规则
原文档明确强调了两条必须遵守的约束:
- 不要自行管理事务:在
MigrateSession内部禁止调用sess.Begin()、sess.Commit()或sess.Close()。会话与事务的生命周期由底层的 xormigrate 迁移管理器统一接管——如果某条迁移失败,管理器会尝试回滚该条迁移并终止后续执行(见 migration.go 顶部注释)。 - 新增模型必须注册:如果给数据库添加的是全新模型(而非在现有模型上加字段),必须将该模型加入 migration.go 中的
allBeans变量,才能保证新表被创建。例如目前allBeans中注册了Agent、Pipeline、Config、Repo、Secret、Cron、Forge、Org等 18 个模型。
2.3 注册到迁移队列
编写完迁移文件后,需要把新定义的迁移变量追加到migrationTasks切片的末尾(位于 migration.go),保证它们按顺序执行。该切片头部注释写着「APPEND NEW MIGRATIONS / They are executed in order and if one fails Xormigrate will try to rollback that specific one and quits」,即:迁移按声明顺序依次执行,单条失败会尝试回滚该条并中止。
2.4 执行流程与自动记录
服务端启动时会调用Migrate(ctx, engine, allowLong)(见 migration.go),完整流程如下:
- 通过
xormigrate.New(e, migrationTasks)构建迁移管理器; - 检查旧版
migrations表是否存在且为空,若是则执行InitSchema初始化(初始化回调为空操作,因为模型同步随后统一进行); - 调用
m.Migrate()依次执行尚未执行过的迁移; - 迁移全部成功后调用
syncAll(e),遍历allBeans逐个执行sess.Sync(bean),将最新模型结构同步到真实数据库表(见 syncAll)。
每成功执行一条迁移,xormigrate 就会把该迁移的ID写入迁移记录表,因此下次启动时它会被自动跳过。这也是「服务端启动自动执行迁移、且不会重复执行」的机制来源。
2.5 常用迁移辅助函数
迁移目录中的 common.go 提供了一批跨数据库方言的辅助函数,避免每个迁移重复编写方言判断:
| 函数 | 作用 | 支持方言 |
|---|---|---|
renameTable(sess, old, new) | 重命名整张表 | MySQL / PostgreSQL / SQLite |
dropTableColumns(sess, table, cols...) | 删除若干列(自动先清理相关索引) | MySQL / PostgreSQL / SQLite |
alterColumnDefault(sess, table, column, def) | 修改列的默认值 | MySQL / PostgreSQL(SQLite 为 no-op) |
alterColumnNull(sess, table, column, null) | 修改列的空值约束 | MySQL / PostgreSQL(SQLite 为 no-op) |
renameColumn(sess, table, column, newName) | 重命名列 | MySQL / PostgreSQL / SQLite |
注意dropTableColumns的函数头注释明确要求「YOU MUST COMMIT THE SESSION AT THE END」,但如上文所述,会话提交应由迁移管理器负责,因此该函数注释更多是对历史实现(该段代码源自 Gitea)的保留说明。由于 SQLite 不支持直接删列,common.go中还提供了removeColumnFromSQLITETableSchema与normalizeSQLiteTableSchema,通过重写建表 SQL 的方式模拟删列,二者都有对应的单元测试验证(见 common_test.go)。
2.6 方言差异处理范例
030_deduplicate_log_entries.go 是展示方言差异处理的绝佳范例:它先为log_entries创建临时索引以加速删除,再针对三种数据库给出不同语法:
- MySQL:
DELETE a FROM log_entries a JOIN log_entries b ON ...; - PostgreSQL:
DELETE FROM log_entries a USING log_entries b WHERE ...; - SQLite:
DELETE FROM log_entries AS a WHERE EXISTS (SELECT 1 ...)。
该迁移的注释还揭示了一个重要经验:去重类迁移不能标记为 Long,因为UNIQUE(step_id, line)索引会在每次启动时由模型同步创建,跳过清理会导致同步失败。
2.7 迁移测试
迁移目录提供了一套完整的测试设施:
- migration_test.go 中的
TestMigrate会分别针对「全新数据库」和「旧数据库转储」两种场景执行完整迁移链:SQLite 使用内存库与预置转储./test-files/sqlite.db,PostgreSQL 使用pg_dump --inserts生成的./test-files/postgres.sql还原现场; - 测试驱动通过环境变量选择:
WOODPECKER_DATABASE_DRIVER指定方言(默认sqlite3),WOODPECKER_DATABASE_DATASOURCE指定 MySQL/PostgreSQL 连接串(见 migration_test.go)。
因此,新增迁移后建议在提交前至少本地跑一遍TestMigrate,并尽量在 MySQL 与 PostgreSQL 上各验证一次方言 SQL 的正确性。
三、官方镜像常量:统一管理、必须锁定精确 tag
Woodpecker 所有官方默认镜像都集中定义在 shared/constant/constant.go,例如:
const ( // DefaultClonePlugin can be changed by 'WOODPECKER_DEFAULT_CLONE_PLUGIN' at runtime. // renovate: datasource=docker depName=woodpeckerci/plugin-git DefaultClonePlugin = "docker.io/woodpeckerci/plugin-git:2.10.1" ) // TrustedClonePlugins can be changed by 'WOODPECKER_PLUGINS_TRUSTED_CLONE' at runtime. var TrustedClonePlugins = []string{...} // TaskTimeout is the time till a running task is counted as dead. var TaskTimeout = time.Minute管理规范可以总结为三点:
- 集中存放:任何官方默认镜像地址必须写入该常量文件,不得散落在业务代码里;
- 精确锁定:镜像必须使用精确 tag(如
:2.10.1),不允许浮动 tag; - 可运行时覆盖:部分常量预留了环境变量覆盖入口,例如克隆插件可通过
WOODPECKER_DEFAULT_CLONE_PLUGIN修改,受信任克隆插件列表可通过WOODPECKER_PLUGINS_TRUSTED_CLONE调整;常量上方renovate: datasource=docker depName=woodpeckerci/plugin-git注释则是依赖机器人自动升级版本的配置标记。
四、本地构建镜像
以下构建命令均以仓库根目录为工作目录执行。构建产物输出到dist/目录(由 Makefile 中DIST_DIR ?= dist决定),make vendor、make build-*等目标定义均可直接查看仓库根目录 Makefile。
4.1 构建 Server 镜像
Server 组件体积最大、依赖最重,构建分为三步:
### build web component make vendor cd web/ pnpm install --frozen-lockfile pnpm build cd .. ### define the platforms to build for (e.g. linux/amd64) # (the | is not a typo here) export PLATFORMS='linux|amd64' make cross-compile-server ### build the image docker buildx build --platform linux/amd64 -t username/repo:tag -f docker/Dockerfile.server.multiarch.rootless --push .几点说明:
make vendor对应 Makefile 中的vendor目标,内部执行go mod tidy && go mod vendor;- 前端先行:
cross-compile-server依赖build-ui,而build-ui会在web/下执行pnpm install --frozen-lockfile; pnpm build,随后build-server还会触发generate-openapi(见 Makefile),因此在干净环境下首次构建耗时会较长; PLATFORMS中的竖线不是笔误:Makefile 的cross-compile-server目标会按;分隔多个平台,再按|切分 os/arch 对(如linux|amd64),并分别生成TARGETOS、TARGETARCH_XGO与TARGETARCH_BUILDX供 xgo 与 buildx 使用(见 Makefile);- xgo 平台限制:
cross-compile-server依赖xgo这个 Go 跨编译工具,而 xgo 镜像只提供amd64版本,所以必须在 amd64 主机上执行该目标;check-xgo目标会在缺 xgo 时自动执行go install src.techknowlogick.com/xgo@latest(见 Makefile); - 若确实无法使用 xgo,可尝试
build-server目标直接在本机构建,但它在部分操作系统(如 macOS)上会失败; - 最终
docker buildx build使用 docker/Dockerfile.server.multiarch.rootless 构建并--push推送多架构镜像。
4.2 构建 Agent 镜像
Agent 组件不包含 Web UI,构建更轻量:
### build the agent make build-agent ### build the image docker buildx build --platform linux/amd64 -t username/repo:tag -f docker/Dockerfile.agent.multiarch --push .build-agent使用CGO_ENABLED=0静态编译(见 Makefile),产出dist/woodpecker-agent二进制,镜像构建基于 docker/Dockerfile.agent.multiarch。你也可以通过TARGETOS、TARGETARCH环境变量覆盖目标平台(Makefile 默认取go env GOOS/go env GOARCH)。
4.3 构建 CLI 镜像
CLI 组件同样不含 UI,构建方式与 Agent 类似:
### build the CLI make build-cli ### build the image docker buildx build --platform linux/amd64 -t username/repo:tag -f docker/Dockerfile.cli.multiarch.rootless --push .build-cli同样以CGO_ENABLED=0静态编译(见 Makefile),产出dist/woodpecker-cli,镜像构建基于 docker/Dockerfile.cli.multiarch.rootless。若只需一键产出三个组件的本地二进制,可直接执行make build(它依次调用build-agent、build-server、build-cli,见 Makefile)。
五、小结
本篇围绕 Woodpecker 开发指南的核心内容展开:在数据层,理解了「Xorm 模型同步 + xormigrate 迁移任务」的双轨机制,掌握了新迁移的编写位置、注册方式、事务禁忌与方言差异处理,并梳理了迁移执行的完整调用链与测试方法;在配置与构建层,明确了官方镜像常量必须集中存放、锁定精确 tag 的规范,以及 Server / Agent / CLI 三个组件从 Makefile 目标到docker buildx build --push的完整本地镜像构建流程。无论是新增数据库字段、添加新模型,还是为自定义版本打镜像,以上内容都能直接作为可复用的实战参考。
- CI/CD
- DevOps
【免费下载链接】woodpecker
Woodpecker is a simple, yet powerful CI/CD engine with great extensibility.
相关推荐
Woodpecker 开发指南:数据库迁移、官方镜像常量与本地镜像构建实战
Woodpecker 开发指南:数据库迁移、官方镜像常量与本地镜像构建实战 本篇指南面向希望深入 Woodpecker 源码或为其贡献代码的开发者,聚焦开发文档
CI/CDDevOpsWoodpecker 容器镜像仓库(Registry)配置完全指南:私有镜像拉取、全局仓库与本地镜像构建
Woodpecker 容器镜像仓库(Registry)配置完全指南:私有镜像拉取、全局仓库与本地镜像构建 本篇指南以 Woodpecker CI/CD 引擎 v
CI/CDDevOpsCoze Studio 项目实体适配层(@coze-studio/project-entity-adapter)使用指南:基于 React Hooks 的项目 CRUD 弹窗封装
Coze Studio 项目实体适配层(@coze studio/project entity adapter)使用指南:基于 React Hooks 的项目
CI/CDDevOps
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考