ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Woodpecker Addon 开发指南:基于 go-plugin 与 RPC 的 Forge / 日志存储扩展机制

2026/9/29 3:18:28 拓冰建站 浏览量
Woodpecker Addon 开发指南:基于 go-plugin 与 RPC 的 Forge / 日志存储扩展机制 CI/CDDevOps【免费下载链接】woodpeckerWoodpecker is a simple, yet powerful CI/CD engine with great extensibility.项目地址https://gitcode.com/gh_mirrors/wo/woodpecker点击查看免费下载Woodpecker 的服务端server为代码托管平台Forge与日志存储Log store提供了独立的插件化扩展能力即 Addon。本文以官方开发文档为主体结合仓库中 server/forge/addon 与 server/services/log/addon 的源码实现系统讲解 Addon 的架构原理、接口约定、编写流程与调试方式帮助读者掌握如何用 Go 语言为 Woodpecker 编写一个可独立运行的 Forge 或日志存储插件。Addon 是什么给 Forge 与日志存储开一扇插件门Woodpecker server 目前支持两类 AddonForge Addon以独立进程方式实现go.woodpecker-ci.org/woodpecker/v3/server/forge.Forge接口替代或补充内置的 GitHub、GitLab、Gitea、Forgejo、Bitbucket 等代码托管平台适配器Log store Addon以独立进程方式实现go.woodpecker-ci.org/woodpecker/v3/server/service/log.Service接口自定义流水线日志的存储后端。⚠️ 注意Addon 仍处于实验阶段experimental其实现可能在任何时候发生变化或破坏兼容性请谨慎用于生产环境并留意 Woodpecker 后续版本的接口变更。Addon 与 server 之间通过RPC通信底层基于 HashiCorp 的go-plugin库 中有依赖声明。go-plugin 的核心模型是插件是一个完全独立的子进程server 通过启动该进程并与它建立 RPC 连接把接口调用转发到插件进程内执行。从源码结构看Addon 的实现由三个部分组成以 Forge Addon 为例server/forge/addon/plugin.go定义握手配置HandshakeConfig与Plugin类型负责把forge.Forge实现桥接到 go-plugin 的 RPC 服务端/客户端server/forge/addon/server.goServe入口与RPCServer在插件进程内接收 RPC 请求并转发到你的 Forge 实现server/forge/addon/client.goLoad函数在 server 进程中加载并连接插件子进程返回一个可像本地对象一样调用的forge.Forge。工作原理握手、Magic Cookie 与 RPC 桥接go-plugin 的进程间通信并非黑魔法它依赖一套显式的握手协议。在 server/forge/addon/plugin.go 中可以看到 Forge Addon 的握手配置const pluginKey forge var HandshakeConfig plugin.HandshakeConfig{ ProtocolVersion: 1, MagicCookieKey: WOODPECKER_FORGE_ADDON_PLUGIN, MagicCookieValue: woodpecker-plugin-magic-cookie-value, }日志存储 Addon 的握手配置与之对称见 server/services/log/addon/plugin.gopluginKey为logMagic Cookie Key 为WOODPECKER_LOG_ADDON_PLUGIN协议版本同样为 1。Magic Cookie 的作用是防止 server 误启动一个无关进程go-plugin 会要求子进程在标准输出/环境中携带约定的 Cookie 值只有握手成功的进程才会被当作合法插件接受。Plugin类型实现了 go-plugin 的plugin.Plugin接口通过Server方法暴露RPCServer在插件侧提供服务通过Client方法返回RPC在 server 侧代理调用。在 server 侧server/forge/addon/client.go 的Load(file string)函数展示了加载流程func Load(file string) (forge.Forge, error) { client : plugin.NewClient(plugin.ClientConfig{ HandshakeConfig: HandshakeConfig, Plugins: map[string]plugin.Plugin{ pluginKey: Plugin{}, }, Cmd: exec.Command(file), Logger: logger.AddonClientLogger{ Logger: log.With().Str(addon, file).Logger(), }, }) // TODO: defer client.Kill() rpcClient, err : client.Client() if err ! nil { return nil, err } raw, err : rpcClient.Dispense(pluginKey) if err ! nil { return nil, err } extension, _ : raw.(forge.Forge) return extension, nil }Load接收插件可执行文件的路径file通过exec.Command(file)把插件作为子进程拉起然后 Dispense 出实现了forge.Forge的代理对象。也就是说Addon 最终会编译成一个独立的可执行文件由 server 按需启动。日志中的 addon 标识字段由于 Addon 运行在独立进程其日志与 server 自身日志混在一起时难以区分。因此Load在构造客户端日志器时使用了log.With().Str(addon, file).Logger()给日志打上名为addon、值为插件文件名的字段。这正对应官方文档中查看日志定位问题组件的建议——当怀疑某个 Bug 来自 Addon 时先看日志里addon字段标记了哪个插件文件再到对应的独立 Addon 仓库去提 Issue而不是在主仓库提。序列化细节参数如何穿越进程边界由于 RPC 只能传递可序列化数据所有接口参数和返回值都被转换为 JSON。以 Forge Addon 为例server/forge/addon/args.go 定义了专门的传输结构体例如argumentsRepo、argumentsFileDir、argumentsStatus、argumentsBranchesPullRequests等并提供了modelUser/modelRepo这类扩展模型它们内嵌标准模型同时显式携带Token、Secret、Expiry、Hash、Perm等字段保证在 JSON 序列化时不丢失敏感信息与权限字段见 args.go 中modelUser/modelRepo的asModel()与FromModel转换函数。值得注意的还有Hook方法HTTP 请求无法直接通过 RPC 传输因此在 client.go 中会把http.Request拆解为Method、URL、Header、Form、Body字段序列化后发送插件侧再重新组装为http.NewRequest见 server.go。编写 Forge Addon一个最小可运行的示例官方文档给出的是 Forge Addon 的最小骨架。核心思路是直接 import Woodpecker 的 Go 包go.woodpecker-ci.org/woodpecker/v3在main函数中调用对应 addon 包提供的Serve方法传入实现了服务接口的结构体剩下的进程连接工作全部交给Serve完成。package main import ( context net/http go.woodpecker-ci.org/woodpecker/v3/server/forge/addon forgeTypes go.woodpecker-ci.org/woodpecker/v3/server/forge/types go.woodpecker-ci.org/woodpecker/v3/server/model ) func main() { addon.Serve(config{}) } type config struct { }config必须实现go.woodpecker-ci.org/woodpecker/v3/server/forge.Forge接口。接口定义位于 server/forge/forge.go共 17 个方法覆盖了 Forge 集成的全部能力维度能力维度接口方法用途说明身份标识Name()/URL()返回 Forge 驱动唯一标识如github与实例根 URLOAuth 登录Login()两段式 OAuth 流程先返回重定向 URL再用 code 换取用户信息组织与团队Teams()/Org()/OrgMembership()查询用户所属组织/团队及成员权限仓库查询Repo()/Repos()按 remoteID 或 owner/name 获取单个仓库或分页获取用户可见仓库配置文件获取File()/Dir()在特定 commit上读取流水线配置文件支持多文件目录Webhook 处理Hook()/Activate()/Deactivate()解析入站 webhook、创建/删除指向 Woodpecker 的 webhook状态与认证Status()/Netrc()向 Forge 上报工作流状态、为私有仓库克隆生成 .netrc 凭据分支与 PRBranches()/BranchHead()/PullRequests()分支列表、分支最新 commitcron 功能依赖与 PR 列表编写时要注意几点约定均来自 forge.go 的接口注释并发安全实现必须支持并发调用方法接收context.Context以便取消/超时不要在实现中保存用户态数据用户上下文通过*model.User参数传入错误语义不支持的接口返回types.ErrNotImplemented可跳过的 webhook 事件返回types.ErrIgnoreEvent资源不存在返回types.ErrRecordNotExistHook 返回值语义(repo, pipeline, nil)表示执行该事件的流水线(repo, nil, nil)表示事件合法但无需执行(nil, nil, ErrIgnoreEvent)表示忽略(nil, nil, error)表示非法 webhook 或解析错误且必须校验 webhook 签名防伪造。为什么不能在 Addon 中访问 server 全局配置文档特别强调Addon 无法访问 Woodpecker 的全局变量例如 server 配置因为 Addon 运行在完全独立的进程中与 server 不共享内存空间。因此Addon 需要的所有配置如 API Token、私有服务地址等都必须由你在 Addon 中自行解析环境变量或你自己的配置文件来获得。日志存储 Addon接口与 RPC 方法日志存储 Addon 的服务接口定义在 server/services/log/service.gotype Service interface { LogFind(step *model.Step) ([]*model.LogEntry, error) LogAppend(step *model.Step, logEntries []*model.LogEntry) error LogDelete(step *model.Step) error StepFinished(step *model.Step) }对应地server/services/log/addon/server.go 中的RPCServer暴露了 4 个 RPC 方法LogFind查询某 step 的日志、LogAppend追加日志条目、LogDelete删除日志、StepFinishedstep 结束时的通知回调。编写方式与 Forge Addon 一致——在main中调用该包导出的Servepackage main import ( go.woodpecker-ci.org/woodpecker/v3/server/services/log go.woodpecker-ci.org/woodpecker/v3/server/services/log/addon ) func main() { addon.Serve(myLogStore{}) } // myLogStore 必须实现 log.Service 接口 type myLogStore struct{}Addon 类型速查表官方文档将两类 Addon 的入口包与需实现的服务接口归纳如下类型Addon 包服务接口Forgego.woodpecker-ci.org/woodpecker/v3/server/forge/addongo.woodpecker-ci.org/woodpecker/v3/server/forge.Forge日志存储go.woodpecker-ci.org/woodpecker/v3/server/service/log/addongo.woodpecker-ci.org/woodpecker/v3/server/service/log.Service说明源码中的实际包路径为server/services/log/addon目录为 server/services/log/addonserver/service/log与server/services/log在模块导入语义上等价引用时以实际仓库目录为准。调试与故障排查建议结合文档与源码排查 Addon 问题时可按以下顺序看日志定位组件server 日志中带有addon字段的行来自 Addon 进程见 client.go 的日志构造先确认问题发生在哪个组件确认归属如果问题出在 Addon 本身请到该 Addon 的独立仓库提 Issue而不是主仓库检查进程与握手Addon 是独立可执行文件检查插件二进制是否可执行、能否成功完成 Magic Cookie 握手协议版本为 1Cookie Key/Value 必须与 server 端一致核对序列化边界若某个字段在插件侧消失了检查它是否属于标准model.User/model.Repo之外、必须通过args.go中扩展模型显式携带的字段如 OAuth Token、Perm 等。更进一步查看 Forge 接口的完整方法签名与语义约定server/forge/forge.go查看 Forge Addon 的 RPC 服务端实现每个方法的参数反序列化与调用转发server/forge/addon/server.go查看 Forge Addon 的客户端加载与代理实现server/forge/addon/client.go查看日志存储 Addon 的完整 RPC 方法server/services/log/addon/server.go参考内置 Forge 实现如 server/forge/gitea、server/forge/github、server/forge/gitlab来了解Forge接口在实际平台适配中如何落地。由于 Addon 仍处于实验阶段编写前请务必锁定你所用 Woodpecker 版本的模块版本如本仓库的go.woodpecker-ci.org/woodpecker/v3并在升级时重新核对接口签名避免因实验性 API 变动导致编译或运行失败。赞分享CI/CDDevOps【免费下载链接】woodpeckerWoodpecker is a simple, yet powerful CI/CD engine with great extensibility.项目地址https://gitcode.com/gh_mirrors/wo/woodpecker点击查看免费下载相关推荐VeleroArkdelete backup 命令完全指南备份删除机制、参数详解与实战用法VeleroArk delete backup 命令完全指南备份删除机制、参数详解与实战用法 导读 本文围绕 Velero其前身名为 ArkCLI 中CI/CDDevOpsWoodpecker Addons 插件开发实战基于 go-plugin 扩展 Forge 与日志存储Woodpecker Addons 插件开发实战基于 go plugin 扩展 Forge 与日志存储 Woodpecker 服务端通过 Addons插件CI/CDDevOpsWoodpecker Addons 扩展机制深度解析基于 go-plugin 的 Forge 与日志存储插件开发指南Woodpecker Addons 扩展机制深度解析基于 go plugin 的 Forge 与日志存储插件开发指南 Woodpecker 服务器通过 AddCI/CDDevOps上一篇猫抓视频下载教程三步存下任意网页视频资源嗅探一次讲清下一篇OOTDiffusion 虚拟试衣 AI 模型下载与本地部署完整指南零基础一天跑通创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考