使用 Woodpecker 与 Gitea 搭建纯开源的 CI 流程|极限降本

最近开源了一个挂机冒险游戏《模拟龙生》,有热心同学不仅帮忙做优化,还连夜在给游戏加页面,泪目。详见文末小结部分。

一、前言

image-20240115204716944

大家好,这里是白泽。这篇文章是《Woodpecker CI 设计分析》的续接,将通过阅读 Woodpecker 和 Gitea 的源代码,解决前一篇文章最后遗留的问题,并最终实现本地部署 Woodpecker 和 Gitea,实现持续集成(CI)全流程使用开源技术,极限降本。

image-20240115214403769

遗留问题:

通过 docker-compose 部署 Woodpecker 并将 GitHub 作为 Forge 平台,模拟 CI 流程的时候,遇到了一个告警:提示创建 webhook 需要一个公共可访问的 Host 地址,本地部署的 Woodpecker 无法通过 webhook 监听 GitHub 中 repo 的变动,也就无法使用持续集成的能力了。

?? 因此这里将架构图中的 Forge 部分的 GitHub 源换成 Gitea 源,全部使用开源项目,彻底拥抱开源,极限降本。

image-20240116173320678

二、本地部署 Gitea

访问 Gitea 的官方文档,找到 Installation with Docker 部分的内容:

version: "3"
?
networks:
  gitea:
    external: false
?
services:
  server:
    image: gitea/gitea:main-nightly
    container_name: gitea
    environment:
      - USER_UID=1000
      - USER_GID=1000
    restart: always
    networks:
      - gitea
    volumes:
      - ./gitea:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3000:3000"
      - "222:22"

通过 docker-compose 部署运行:

docker-compose up -d

?? 访问 http://0.0.0.0:3000 后,点击安装,然后注册用户,便可以进入到 Gitea 的使用页面,到现在为止看似没有任何问题

image-20240120154144766

三、OAuth 是什么

在 GitHub/Gitea 的上下文中,OAuth App 是一种允许开发者使用 GitHub/Gitea 用户帐户进行身份验证和访问的应用程序。开发者可以创建自己的 OAuth App,并通过 GitHub/Gitea 的 OAuth 流程获得对用户帐户的有限访问权限。

OAuth App 在 GitHub/Gitea 上注册后,会获得一个客户端 ID 和一个客户端密钥(Client Secret)。这些凭据用于在用户授权后获取访问令牌,以便应用程序可以代表用户执行某些操作。

在当前场景中,Woodpecker 为了获得访问 Gitea 的能力(监听仓库变动等),需要在 Gitea 的 Setting 注册一个 Oauth App。

image-20240120160646053

这里的 Client IDClient Secret 将用于下一节 Woodpecker 的部署。并且 Redirect URI 设置为 Woodpecker 的授权地址。

这里涉及到一些配置均是与 OAuth2 授权流程相关的,包括下文部署 Woodpecker 的时候,也会涉及。

?? 由于篇幅原因,下一篇文章我将讲解在本地部署成功后,使用 Gitea 用户登录 Woodpecker 平台时,OAuth2 认证的流程,以解释这些配置的作用。这里按下不表,先照着操作即可。

四、本地部署 Woodpecker

由于《Woodpecker CI 设计分析》这篇文章已经讲解了如何通过 docker-compose 本地部署,这里不再赘述这种方式,但出于学习目的,这里我们通过项目源代码进行编译运行。

相比 docker,单步调试能帮助你快速理清楚开源项目的流程。

4.1 部署 & 运行

  • 克隆项目
git clone [email protected]:woodpecker-ci/woodpecker.git
  • 查看 Woodpecker 的文档,找到 Preparation for local development

?? 很多同学学习或者参与开源项目的第一步就被拦住了,如何成功本地编译运行一个开源项目,是成功的一半,接下来跟着白泽一步一步阅读文档,希望你今后也能独立完成启动其他项目的流程。

  1. 要求 Golang >= 1.20 版本。
  2. Install make:make 是一个构建工具,用于自动化项目的编译和构建过程。它通常使用名为 Makefile 的文件来定义一系列规则和依赖关系,以描述如何生成目标文件(通常是可执行文件或库)。
  3. Install Node.js & pnpm:需要 Node 环境,以及 Node 的包管理工具 pnpm,也是按照文档要求安装即可。
  4. 最重要的:在项目根目录创建 .env 文件,存放配置变量,下面是白泽创建的配置列表,照着官方文档的改,除了要修改 HOST 相关变量,还要注意将 GitHub 源切换成 Gitea 源。
WOODPECKER_OPEN=true
WOODPECKER_ADMIN=baize
?
# if you want to test webhooks with an online forge like GitHub this address needs to be accessible from public server
WOODPECKER_HOST=http://0.0.0.0:8000
?
# github (sample for a forge config - see /docs/administration/forge/overview for other forges)
-WOODPECKER_GITHUB=true
-WOODPECKER_GITHUB_CLIENT=<redacted>
-WOODPECKER_GITHUB_SECRET=<redacted>
+WOODPECKER_GITEA=true
+WOODPECKER_GITEA_URL=http://0.0.0.0:3000
+WOODPECKER_GITEA_CLIENT=<redacted>
+WOODPECKER_GITEA_SECRET=<redacted>
?
# agent
WOODPECKER_SERVER=0.0.0.0:9000
WOODPECKER_AGENT_SECRET=a-long-and-secure-password-used-for-the-local-development-system
WOODPECKER_MAX_WORKFLOWS=1
?
# enable if you want to develop the UI
# WOODPECKER_DEV_WWW_PROXY=http://0.0.0.0:8010
?
# used so you can login without using a public address
WOODPECKER_DEV_OAUTH_HOST=http://0.0.0.0:8000
?
# disable health-checks while debugging (normally not needed while developing)
WOODPECKER_HEALTHCHECK=false
?
# WOODPECKER_LOG_LEVEL=debug
# WOODPECKER_LOG_LEVEL=trace
  1. 编译启动项目(前一篇文章已经讲过 Woodpecker 的架构图了,server & agent 的概念不再赘述),当然这篇文章的第二张图片是帮助大家回忆的 Woodpecker 架构图。
# 启动server服务
go run ./cmd/server/main.go
# 启动agent服务
go run ./cmd/agent/main.go

4.2 使用

  • 访问 http://0.0.0.0:8000 进入 Woodpecker 登录页面,点击 Login 图标。?? 但是再次返回到这个页面,无法跳转至 Gitea 授权认证页面!

image-20240120163321446

4.2.1 docker 运行的弊端

?? 还记得这篇文章在通过 docker-compose 部署 Gitea 成功后,白泽提了一句看似没有任何问题

由于白泽已经踩过坑了,因此上文 Woodpecker 已经是本地编译运行的,也是方便你接下来跟着我单步调试。要是 Woodpecker 也用 docker 部署,一旦遇到问题,看 docker 日志排错效率太低了!

# 在点击Login未果后,我们在server端的控制台看到了包含这条信息的日志,全局搜索到它的位置,定位到错误日志的打印函数,从而定位到断点应该打在哪里
cannot authenticate user

image-20240120170445778

接下来需要你以 debug 的形式启动 Woodpecker Server 以及 Agent,在 ./server/router/router.go#64 行打下断点。

image-20240120165854326

重新访问 http://0.0.0.0:8000 点击 Login,然后跟踪 Woodpecker Server 的断点。

我们发现是 froge.Login() 这个方法运行的到了 err,所以去查看针对 Gitea 的方法实现: server/forge/gitea/gitea.go:119

?? 由于 Woodpecker Server 错误日志提示当前 Gitea 的版本是 dev,不由得让白泽联想,莫非是两个开源项目之间有版本依赖的联系!

带着一些信息,我们打断点到这个函数。

// code.gitea.io/sdk/[email protected]/version.go:91
func (c *Client) checkServerVersionGreaterThanOrEqual(v *version.Version) error {
    if c.ignoreVersion {
        return nil
    }
    // 向 Gitea 发送 Http 请求,获取 version
    if err := c.loadServerVersion(); err != nil {
        return err
    }
    // 对比是否大于等于1.11.0(development)无法比较
    if !c.serverVersion.GreaterThanOrEqual(v) {
        c.mutex.RLock()
        url := c.url
        c.mutex.RUnlock()
        return fmt.Errorf("gitea server at %s is older than %s", url, v.Original())
    }
    return nil
}

然后就有了日志中的 ErrUnknownVersion development 这个错误。

由于通过 docker-compose 部署 Gitea 的时候用的是 main 分支的最新内容,在获取 version 的时候,得到了字符串 development 而非一个具体的发布版本号。这里我们查看一下 Gitea 的 main 分支的 version,信息对称。

?? 程序员的幸福时刻:这程序如我所愿!

image-20240120172155074

解决方案:

  • 本地运行 Gitea,将这个配置改成一个数值型的。
  • 还是通过 docker-compose 部署 Gitea,但是将镜像版本设定为 >= 1.11.0 的发布版,而非开发版即可,比如 image: gitea/gitea:1.21.4 即可。
4.2.2 用 Gitea 账户登录 Woodpecker
  • 输入 Gitea 用户名密码(需要提前在 Gitea 中注册,当然你能创建 OAuth App 自然已经有了账户)。

image-20240120163427557

  • 授权 woodpecker-test-app 应用访问 Gitea。

image-20240120163828839

  • 登录成功,进入 Woodpecker 管理页面,此时显示 Gitea 登录账号的 Repo 列表,空空如也(毕竟 Gitea 都是刚部署的,Repo 还没有创建过)。

image-20240120172518892

  • Gitea 创建 test-repo

image-20240120173346401

  • 返回 http://0.0.0.0:8000/repos 点击 Add repository,点击 Enable

image-20240120173834629

?? 官方文档:When you activate your repository, Woodpecker automatically adds webhooks to your forge (e.g. GitHub, Gitea, …).

所以可以编写 pipeline 配置文件,去触发 webhook 了。

image-20240120174902046

4.2.3 Woodpecker + Gitea 实现 CI
  • test-repo 写了一个 hello world 打印函数和一个单元测试。

image-20240120180606227

  • test-repo 的根目录创建 .woodpecker/build.yaml:内容就打印一句日志。
steps:
  build:
    image: debian:stable-slim
    commands:
      - echo building
      - sleep 5
  • 将代码推送到 Gitea,在 Woodpecker 页面点击 Run pipeline

image-20240120192034223

  • 提示无法连接到 http://localhost:3000/baize/test-repo.git/ 这个仓库,当然现在我们已经确保的是:Woodpecker 是可以访问到 Gitea 的,毕竟都拉取到了 baize 这个账号创建的 test-repo

image-20240120192121947

此时 Woodpecker Server 的日志:

image-20240120192526574

此时 Woodpecker Agent 的日志:

image-20240120192601615

  • 为了解决问题:必须要探究 pipeline 运行函数的实现!毕竟直接看这日志太过抽象,还是得打断点。

?? 有了上面打断点调试的经验,这里发生问题我们不用慌乱,休息一下,下篇文章白泽带你解析 Woodpecker 的核心机制:Pipeline 运行逻辑。

五、小结

?? 未完待续,欢迎追更。

公众号 「白泽talk」,我也开源了一个 Go 学习仓库:包含 Go 各阶段学习文章、读书笔记、电子书、简历模板等,欢迎 star。

最近开源了一个挂机冒险游戏《模拟龙生》,还有热心同学不仅帮忙做优化,还连夜在给游戏加页面,泪目。

image-20240121103235404

现在游戏体量只有500-600行代码,感兴趣的同学可以一起来维护。游戏的大致玩法在这篇文章中。后续游戏有阶段性变化功能合入主分支,则会继续通过文章向各位介绍使用的技术和玩法。(学习娱乐两不误)

下一步的计划是添加本地存档功能,预计今天完成。

白泽目前正在打造一个氛围良好的行业交流群(游戏交流群),文章的更新也会提前预告,欢迎加入:622383022。