持續集成(CI)是軟件開發過程中的一個重要部分,在這個過程中,共享的代碼存儲庫會由於將團隊成員的新工作集成到其中而不斷地發生變化。
為了確保代碼的高質量並減少潛在的錯誤,每次集成通常由一個自動化的構建和測試過程來驗證。
在本文中,我們將學習如何使用Github Actions設置該過程,從而使項目自動構建和運行單元測試。該項目使用Golang編寫,並使用PostgreSQL作為其主要數據庫。
一、Github Actions如何運作
Github Action是Github提供的一項服務,具有與其他CI工具(如Jenkins,Travis或CircleCI)相似的功能。
Workflow
為了使用Github Actions,我們必須定義一個工作流程(workflow)。 工作流基本上是由一個或多個作業組成的自動化過程。它可以通過3種不同的方式觸發:
- 通過Github存儲庫上發生的事件
- 通過設置一個重復的時間表
- 手動單擊repository UI上的run workflow按鈕
要創建工作流,我們只需要將.yml文件添加到存儲庫的.github/workflows文件夾中。例如,這是一個簡單的工作流文件ci.yml:
name: build-and-test
on:
push:
branches: [ master ]
schedule:
- cron: '*/15 * * * *'
jobs:
build:
runs-on: ubuntu-latest
這個工作流的名稱為build-and-test。我們可以使用on關鍵字定義如何觸發它。
在這個流程中,有一個事件將在更改被推送到master分支時觸發工作流,以及還有一個每15分鍾運行一次工作流的定期調度觸發器。
然后,在工作流yaml文件的jobs部分中定義要運行的作業列表。
Runner
為了運行jobs,我們必須為每個job指定一個runner。 runner是偵聽可用作業的服務器,並且一次只能運行一個作業。
我們可以直接使用Github托管的runner,或者指定我們自己的自托管runner。

runners將運行這些jobs,然后將其進度,日志和結果報告回Github,這樣我們就可以很容易地在存儲庫的UI上進行檢查。
我們使用run-on關鍵字指定要使用的runner,如上圖那樣。在此示例工作流程中,將使用Github托管的運行程序來安裝Ubuntu最新版本。
Job
job是在同一runner上執行的一組步驟。
通常,工作流(workflow)中的所有job都是並行運行的,除非你有一些相互依賴的job,否則它們將按順序運行。
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Build server
run: ./build_server.sh
test:
needs: build
runs-on: ubuntu-latest
steps:
- run: ./test_server.sh
在此示例中,有2個job:
-
第一個是
build,它包含2個步驟:檢查代碼和構建服務器。 -
第二個是
test,它將運行應用程序的測試。在這里,使用
need關鍵字來表示test依賴於build,因此只有在應用程序成功構建后才能運行測試。這個
test只有一個步驟,運行test_server.sh腳本。
Step
step是在每個job中一個接一個連續運行的單個任務。 一個步驟可以包含1個或多個action。
action基本上是一個獨立的命令,例如上面的run test_server.sh命令。 如果一個step包含多個action,它們將按順序運行。
關於action的一個有趣的事情是它可以被重復利用。 因此,如果有人已經編寫了我們需要的github action,則可以在workflow中使用它。
讓我們看一下這個例子:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Build server
run: ./build_server.sh
在這里,使用steps關鍵字列出要在jobs中運行的所有步驟。
第一步是從Github上將代碼轉移到runner服務器。要做到這一點,只需使用Github actions團隊已經編寫好的Github actions checkout@v2
第二步是構建應用服務器。在本例中,我們提供了自己的操作,它只是運行存儲庫中的build_server.sh腳本。
總結
在開始編碼之前,讓我們做一個簡短的總結:

- 我們可以通過3種方式觸發工作流(workflow):事件,調度計划或手動。
- 一個工作流由一個或多個任務(jobs)組成。
- 一個任務由多個步驟(steps)組成。
- 每個步驟可以有1個或多個動作(actions)。
- 工作流(workflow)中的所有任務(jobs)通常並行運行,除非它們相互依賴,在這種情況下,它們是串行運行的。
- 每個任務(jobs)由特定服務器(runner)單獨運行。
- 服務器(runner)會將進度,日志和任務結果報告回github。 我們可以直接在Github存儲庫的UI上檢查它們。
二、實戰,為Golang+Postgres設置workflow
結合https://github.com/shisuizhe/simple-bank食用。
現在讓我們學習如何為Golang應用程序設置一個真實的workflow,以便它可以連接到Postgres,並在每當git push到github時運行所有單元測試。
使用workflow模板
在Github倉庫中,選擇Actions標簽。

Github知道我們的項目主要是用Go編寫的,因此建議我們為Go設置工作流程。 讓我們單擊此設置按鈕。
可以看到,存儲庫下的.github / workflows文件夾下將創建一個新文件go.yml:

name: Go
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: ^1.13
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Get dependencies
run: |
go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
- name: Build
run: go build -v .
- name: Test
run: go test -v .
我們可以在這里直接使用Github編輯器編輯這個文件。但是,我更喜歡先將文件添加到本地項目目錄下,然后在推送到Github之前在本地編輯好它。
創建一個workflow的yaml文件
進入項目根路徑,創建一個新文件夾.github/workflows
mkdir -p .github/workflows
然后在這個文件夾中為工作流程創建一個新的yaml文件。 可以隨意命名,這里,我命名為ci.yml。
touch .github/workflows/ci.yml
將github提供的模板內容拷貝到ci.yml,並做一些修改。
配置觸發事件
定義可以觸發此工作流程的事件。 通常,只要有內容修改被push到master分支,或者有合並到master分支的拉取請求,我們都希望運行測試。
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
還有很多其他事件可以配置。請參考Github Actions文檔。
設置任務
接下來設置jobs。 在Github提供的模板中,只有1個job名為build,其運行在Ubuntu runner上。我把它重命名為test,因為這個名稱表示它的主要目的。
jobs:
test:
name: Test
runs-on: ubuntu-latest
這個任務job還有幾步需要做:
-
步驟1:安裝Go環境。
在runner服務器上安裝go,在這一步中,只需要使用現有的Github操作,即
setup-go @ v2。steps: - name: Set up Go 1.x uses: actions/setup-go@v2 with: go-version: ^1.15 id: go使用with關鍵字為該操作提供輸入參數。 在這里,要求它使用特定版本的Go,例如1.15版。
id字段只是此步驟的唯一標識符。 如果要在其他上下文中引用此步驟,則可能需要它。
-
步驟2:轉移代碼
將這個存儲庫的代碼轉移到runner服務器上。 為此,我們還重復使用現有操作:checkout @ v2。
- name: Check out code into the Go module directory uses: actions/checkout@v2 -
步驟3:獲取依賴關系
獲取項目正在使用的所有依賴項或外部軟件包。
- name: Get dependencies run: | go get -v -t -d ./... if [ -f Gopkg.toml ]; then curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh dep ensure fi但是,在這里,我們不需要這一步,因為當構建應用程序或運行測試時,go mod會自動下載缺少的庫。 因此,將其刪除!
構建步驟也是不必要的,因為應用程序將運行go test時會自動構建。
- name: Build run: go build -v . -
步驟4:運行測試
最后一步是運行單元測試。 為此,我們已經在Makefile中定義了一個
make test命令。 因此,在此步驟中要做的就是調用它:- name: Test run: make test
將workflow推送到github
至此,我們完成了CI workflow的第一個基本版本:
name: ci-test
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: ^1.15
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Test
run: make test
由於我們尚未設置Postgres數據庫,因此它可能無法正常工作。 但讓我們將其推送到Github上看看其運行方式:
git add .
git commit -m "init ci workflow"
git push origin master
現在,回到Github存儲庫頁面並選擇Actions。所有步驟(steps)均在右側列出。Setup job,Setup Go和Checkout code步驟成功完成,這里,Test雖然已經完成,但是失敗了。

打開步驟Test,查看日志可以看到:

正如日志中看到的,代碼不能連接到Postgres的端口5432,因為我們還沒有在工作流中設置它。現在就開始吧!
添加Postgres服務
讓我們搜索github action postgres,然后打開有關創建Postgres服務容器的Github Action官方文檔頁面。

從上圖可以看到Postgres被聲明為該job的外部服務。 讓我們復制此代碼塊並將其粘貼到我們的工作流文件中。
因此,我們使用services關鍵字來指定要與我們的job一起運行的外部服務列表。在本例中,只需要1個服務,即Postgres。
並且,由於項目中使用的是Postgres版本12,因此我們將此Docker映像名稱設置為postgres:12。 可以在Docker Hub上查看此Postgres鏡像的可用版本和標簽。
services:
postgres:
image: postgres:12
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: 123456
POSTGRES_DB: simple_bank
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
接下來,我們需要為訪問數據庫的認證設置一些環境變量。
請記住,我們在本地Postgres容器中使用user="postgres",password="123456"和database="simple_bank"。 因此,我們在此處為CI工作流程設置相同的值。
options選項非常重要,因為runner服務器使用它來檢查Postgres是否已成功啟動,以便它知道何時運行工作流程中的后續步驟。
添加運行數據庫遷移步驟
現在已經定義好了Postgres服務,但是為了讓測試成功運行,還需要運行db遷移,為我們的應用程序創建正確的數據庫模型。
因此,在Check out code ...步驟之后定義一個新步驟。 名稱為Run migrations。 它唯一需要做的就是運行make migrationup。
好了,現在讓我們嘗試將這個新的工作流更改推送到Github,看看會發生什么。

在圖中箭頭處可以看到一條日志說postgres service is healthy。Setup Go步驟也成功。然后它轉移新的代碼到runner。
但是,數據庫遷移失敗了,因為沒有安裝golang-migrate CLI工具來運行遷移。
安裝 golang-migrate 工具
因此,讓我們搜索golang migrate,然后打開此Github頁面文檔。
根據您使用的操作系統,有幾個選項。 因為我們正在使用Ubuntu作為runner,因此我將復制此curl命令以下載構建遷移的二進制文件。
curl -L https://github.com/golang-migrate/migrate/releases/download/$version/migrate.$platform-amd64.tar.gz | tar xvz
現在,在工作流程中,添加一個新的Install golang-migrate步驟在Run migrations之前。 然后,在run操作中,粘貼curl命令。
- name: Install golang-migrate
run: curl -L https://github.com/golang-migrate/migrate/releases/download/v4.12.2/migrate.linux-amd64.tar.gz | tar xvz
它將下載zip文件,並解壓得到名為migration .linux-amd64的遷移二進制文件。 現在,為了使migration命令起作用,我們必須將該二進制文件移動到/usr/bin文件夾中。
因為這一步驟包含不止一個命令。在這里可以使用管道符|來指定多行命令。讓我們將這個move命令添加到步驟中:
- name: Install golang-migrate
run: |
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.12.2/migrate.linux-amd64.tar.gz | tar xvz
sudo mv migrate.linux-amd64 /usr/bin/
which migrate
請注意,只有超級用戶才能更改/usr/bin文件夾的內容,因此我們必須使用sudo運行此命令。
還增加了1個命令:which migrate,僅用於檢查migrate CLI二進制文件是否已成功安裝,是否可以在runner中使用。
現在,workflow是完美配置了嗎?如果就這一樣push到github的話,再次測試還是會失敗。
因為項目中的Makefile指定了遷移命令的二進制文件名為migrate,而不是上面設置的migrate.linux-amd64,所以還要做修改:
- name: Install golang-migrate
run: |
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.12.2/migrate.linux-amd64.tar.gz | tar xvz
sudo mv migrate.linux-amd64 /usr/bin/migrate
which migrate
好了,讓我們重新看看完整的workfolw配置文件:
name: ci-test
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
test:
name: Test
runs-on: ubuntu-latest
services:
postgres:
image: postgres:12
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: 123456
POSTGRES_DB: simple_bank
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: ^1.15
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Install golang-migrate
run: |
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.12.2/migrate.linux-amd64.tar.gz | tar xvz
sudo mv migrate.linux-amd64 /usr/bin/migrate
which migrate
- name: Run migrations
run: make migrateup
- name: Test
run: make test
提交測試:

Nice!最終我們的CI測試工作流程成功運行。
通過編寫一個Github Action工作流程來運行需要連接到外部Postgres服務的Golang單元測試,讓我們已經了解了持續集成。
Github Actions還可以做更多的事情。 查看其官方文檔以了解有關它們的更多信息。
That's all,see you next time.
