Woodpecker,一款很新的 CI CD 工具

Posted by Elizabeth Huang on Thu, Jun 27, 2024

Woodpecker 是一款簡單的 CI/CD 工具,基於 container 處理 pipeline

雖然非常小眾,但如果想要又小又簡單,可以在本地執行,可以使用插件的 CI 工具,那 Woodpecker 是個新選擇

架構

架構

  • Forge:Github, GitLab等。發PR等 event 可觸發 Woodpecker pipeline
  • Server:核心,負責與Forge溝通、提供使用者介面(User 可以透過瀏覽器使用)、透過gRPC與Agent溝通
  • Agent:負責執行pipeline的workflow。多個 Agent 可以並行處理多個 workflow

注意,Woodpecker 必須被安裝在可以被公共網路(public internet)存取的地方,否則無法觸發 Webhook event。如果想當免費仔,可以在本地安裝,手動觸發 pipeline

pipeline

  • Pipeline:由一些workflow組成。由push, tag等事件觸發
  • Workflow:一組動作(step)組成的某種功能。可以有多個 workflow 檔,但 artifact 只在 step 能夠互相存取,workflow 之間無法互相存取,需要借助 AWS S3 等外部儲存空間
  • Service:在執行 step 時會需要用到的服務。例如許多 Web 專案會用到的 Redis, DB 等等
  • Matrix:矩陣。Woodpecker 會使用每種組合執行一次 workflow。例如定義了兩種 GO 版本和兩種 Redis 版本,那組合共有四種(2 * 2 = 4)。在測試版本相容性的時候很好用
  • Plugin:可以從官方商店找能用的Plugin。另外因為 Woodpecker 是從 Drone fork 出來的,因此也可以用 Drone 的 Plugin ,但有可能需要微調。官方範例裡的plugin/slack就是用 Drone 的 Plugin

Forge

Github

注意要使用 Woodpecker 的 Repository,可以是private,但該帳號必須擁有這個 Repository 的權限,如果這個帳號是 Contributor,就無法在這個專案上使用

要連接到 Github,必須先取得 OAuth Client ID 和 Secret。下面介紹如何取得

登入 Github 後,點擊右上角的頭像,選擇 Settings

Github settings

進入 Settings 後選擇 Developer Settings

Developer Settings

新增 OAuth APP,然後填入資料

Homepage URL就是Woodpecker的FQDN,如https://wookpecker.example.com

Authorization callback URL就是Homepage URL加上/authorize

Create a new OAuth APP

建立好 APP 後,需要建立 Client secret,然後記下 Client ID 和 Client secret,安裝的時候會用到

Client ID and Client Secret

Docker compose 安裝

官方有提供多種安裝方法,如 Docker、K8S、二進制檔

可以安裝在本地或是 VPS 等 Server 上,這邊示範在本地透過 Docker compose 安裝

首先,產生WOODPECKER_AGENT_SECRET,用於 Server 和 Agent 之間通訊的 Key

使用以下命令產生用十六進制輸出的32位元組的隨機字串

1openssl rand -hex 32

再來建立 compose.yaml

 1# compose.yaml
 2version: '3'
 3
 4services:
 5  woodpecker-server:
 6    image: woodpeckerci/woodpecker-server:latest
 7    ports:
 8      - 8000:8000
 9    volumes:
10      - woodpecker-server-data:/var/lib/woodpecker/
11    environment:
12      # Forge 中每個帳號都可以登入 Woodpecker
13      - WOODPECKER_OPEN=true
14      # 需要設FQDN,如https://wookpecker.example.com
15      - WOODPECKER_HOST=${WOODPECKER_HOST}
16      # Forge 使用 Github
17      - WOODPECKER_GITHUB=true
18      # Github OAuth client ID
19      - WOODPECKER_GITHUB_CLIENT=${WOODPECKER_GITHUB_CLIENT}
20      # Github OAuth secret
21      - WOODPECKER_GITHUB_SECRET=${WOODPECKER_GITHUB_SECRET}
22      # 剛才產生的隨機字串
23      - WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET}
24
25  woodpecker-agent:
26    image: woodpeckerci/woodpecker-agent:latest
27    command: agent
28    restart: always
29    depends_on:
30      - woodpecker-server
31    volumes:
32      - /var/run/docker.sock:/var/run/docker.sock
33    environment:
34      - WOODPECKER_SERVER=woodpecker-server:9000
35      # 剛才產生的隨機字串
36      - WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET}
37
38volumes:
39  woodpecker-server-data:

WOODPECKER_HOST與 Github OAuth 的Homepage URL相同,是 FQDN,如https://wookpecker.example.com

WOODPECKER_GITHUB_CLIENTWOODPECKER_GITHUB_SECRET就是剛才建立 Github OAuth 取得的兩個字串

Docker compose 可以讀取 .env file,這些變數可以貼在裡面就好

1# .env
2WOODPECKER_HOST=http://localhost:8000
3WOODPECKER_GITHUB_CLIENT=
4WOODPECKER_GITHUB_SECRET=
5WOODPECKER_AGENT_SECRET=

設定完成後,啟動 Woodpecker

1docker-compose up -d

Try it out

準備試用的 Repository

首先準備一個 Repository 來試用 Woodpecker

這邊複製了 swaggo 的 Example,需求檢查 swagger file 是否有忘記更新的情況

.woodpecker.yaml

建立好專案後,在根目錄建立.woodpecker.yaml(實際上可以有好幾個workflow檔,這邊為求簡單只建立一個檔案)

多行 commands 可以用列點的方式,Woodpecker 會依次執行。如果有比較複雜的情況,可以寫一個腳本由 command 執行

以下 command 表示:

  1. 安裝swag
  2. 將 swagger json 和 yaml file 的 checksum 輸出成變數
  3. 更新 swagger files
  4. 將更新後的 swagger json 和 yaml file 的 checksum 輸出成變數
  5. 比對是否一致,不一致的話 exit 1
 1steps:
 2  - name: check swagger
 3    image: golang
 4    commands:
 5      - go install github.com/swaggo/swag/cmd/swag@latest
 6      - export SWAGGER_JSON_CHECKSUM_BEFORE=$(md5sum docs/swagger.json | cut -d ' ' -f 1)
 7      - export SWAGGER_YAML_CHECKSUM_BEFORE=$(md5sum docs/swagger.yaml | cut -d ' ' -f 1)
 8      - swag init
 9      - export SWAGGER_JSON_CHECKSUM_AFTER=$(md5sum docs/swagger.json | cut -d ' ' -f 1)
10      - export SWAGGER_YAML_CHECKSUM_AFTER=$(md5sum docs/swagger.yaml | cut -d ' ' -f 1)
11      - if [ "$SWAGGER_JSON_CHECKSUM_BEFORE" != "$SWAGGER_JSON_CHECKSUM_AFTER" ]; then exit 1; fi
12      - if [ "$SWAGGER_YAML_CHECKSUM_BEFORE" != "$SWAGGER_YAML_CHECKSUM_AFTER" ]; then exit 1; fi
13      - exit 0

設定好後,push 到 Repo

Dashboard

http://localhost:8000 進入 Dashboard

Woodpecker Dashboard

點擊 Login 後,會要求 OAuth 認證,點擊 Authorize

登入以後,點擊 Add repository 按鈕,選擇剛才建立的 Repository,點選Enable

要手動觸發 pipeline,點擊 Run pipeline 按鈕,然後選擇 branch 並執行

Trigger a pipeline

執行結果 可以看到 pipeline 的第一個步驟都是 clone 專案,因此不需要在 .woodpecker.yaml 裡面寫 clone code 的步驟

Pipeline result

試試 Matrix

我們先在main裡面加上這幾行

 1import (
 2	"fmt"
 3	// ...
 4)
 5func main() {
 6	for i := range 10 {
 7		fmt.Println(10 - i)
 8	}
 9	fmt.Println("go1.22 has lift-off!")
10	// ...
11}

這個只有在 GO 1.22 裡面才會正常執行

然後修改.woodpecker.yaml,加上 golint 檢查,以及matrix

 1# .woodpecker.yaml
 2steps:
 3  # ...
 4  - name: golang ci
 5    image: golang:${GO_VERSION}
 6    commands:
 7      - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.59.1
 8      - golangci-lint --version
 9      - golangci-lint run
10
11matrix:
12  GO_VERSION:
13    - 1.21.11
14    - 1.22.4

再次手動執行,可以很清楚地看到 GO 1.21.11 的版本會噴錯

Pipeline fail

另外,如果有多個 agent,多個 matrix 可以並行執行,節省時間!金錢的力量

Wrap up

Github action 有免費好用的每月額度,小小的個人專案可以輕鬆達到 CI/CD 的功能;而 Woodpecker 的主動權在使用者身上,可以自己選擇機器,只需要機器的錢, pipeline 可以無限次執行,甚至可以有更進階的自定義功能

本篇的範例 Code 在我的 Github repository