☰
Woodpecker 开发者指南:数据库迁移(Xorm ORM)、官方镜像常量与本地镜像构建实战
2026/9/28 2:57:29 网站建设 项目流程
  • CI/CD
  • DevOps

【免费下载链接】woodpecker

Woodpecker is a simple, yet powerful CI/CD engine with great extensibility.

项目地址:https://gitcode.com/gh_mirrors/wo/woodpecker
点击查看免费下载

本篇指南面向希望深度参与 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 两条硬性规则

原文档明确强调了两条必须遵守的约束:

  1. 不要自行管理事务:在MigrateSession内部禁止调用sess.Begin()、sess.Commit()或sess.Close()。会话与事务的生命周期由底层的 xormigrate 迁移管理器统一接管——如果某条迁移失败,管理器会尝试回滚该条迁移并终止后续执行(见 migration.go 顶部注释)。
  2. 新增模型必须注册:如果给数据库添加的是全新模型(而非在现有模型上加字段),必须将该模型加入 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),完整流程如下:

  1. 通过xormigrate.New(e, migrationTasks)构建迁移管理器;
  2. 检查旧版migrations表是否存在且为空,若是则执行InitSchema初始化(初始化回调为空操作,因为模型同步随后统一进行);
  3. 调用m.Migrate()依次执行尚未执行过的迁移;
  4. 迁移全部成功后调用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

管理规范可以总结为三点:

  1. 集中存放:任何官方默认镜像地址必须写入该常量文件,不得散落在业务代码里;
  2. 精确锁定:镜像必须使用精确 tag(如:2.10.1),不允许浮动 tag;
  3. 可运行时覆盖:部分常量预留了环境变量覆盖入口,例如克隆插件可通过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.

项目地址:https://gitcode.com/gh_mirrors/wo/woodpecker
点击查看免费下载

相关推荐

上一篇:终极代码美化工具:JS Beautifier 完全指南
下一篇:Vue Konva实战指南:打造精美Canvas图形应用

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询