如何使用 Jenkins 對 .NET Core 實現個人的持續整合 ?
在使用 TDD 開發時,儘管單元測試執行速度較快,但可能僅執行一部分的單元測試而已;而整合測試與驗收測試更慢,實務上不可能真的花時間去等待測試結果,而是希望在背景不斷地執行,在本機配合 Docker 執行 Jenkins 後,只要每次有新的 commit,就會自動執行所有測試,並將測試結果傳送到 Slack。
Version
macOS High Sierra 10.13.3
Docker for Mac 17.20.0-ce-mac49 (21995)
.NET Core 2.1.4
Jenkins 2.89.4
Slack 3.0.5
Rider 2017.3.1
User Story
我們希望每次 git push
到 Git 後,Jenkins 就會自動跑 Unit Test,若失敗則會通知 Slack。
Task
- Git 直接使用本機的 git repository
- Jenkins 使用本機的 Docker
- Slack 可裝在本機或行動裝置
建立 .NET Core 專案
NumberService.cs
1 | namespace NumberLib |
isEven()
若為偶數傳回 true
,奇數傳回 false
。
NuberServiceTest.cs
1 | using NumberLib; |
提供兩個測試案例,分別測試傳回 true
與 false
。
執行 Unit Test
直接在 Rider 執行單元測試,確認結果正確。
建立 Git Repository
要實現持續整合,Git 是其中的關鍵,唯有透過版控,才能在每個版本的變動觸發 Jenkins 自動執行測試。
直接在 Rider 建立 Git repository。
設定 Jenkins
建立與執行 Jenkins Container
若要 macOS 執行 Jenkins,最簡單的方法是透過 Docker,但只使用官方的 Jenkins image 是不夠的,因為我們必須在 Jenkins 的 Linux 環境跑單元測試,也就是說 Linux 必須要有 .NET Core SDK 環境。1 1關於建立 image 建立,詳細請參考 如何建立含有 .NET Core SDK 的 Jenkins Docker Image ?
1 | $ docker run --name MyJenkinsCore -p 8080:8080 -p 50000:50000 -v jenkins_home:/var/jenkins_home -v /Users/oomusou/Code:/var/code oomusou/jenkins-core |
在建立 container 時,較特別的為
1 | -v /Users/oomusou/Code:/var/code |
-v
是設定 host os 的目錄,相當於 container 內對應的目錄。
如此就能使用 macOS 的開發工具,如 Rider 在 /Users/oomusou/Code
,但 Jenkins 跑單元測試時是在 Linux 的 /var/code
目錄下。
Manage Jenkins
- 左側選
Manage Jenkins
- 右側選
Configure Global Security
Configure Global Security
- 不勾選
Enable security
- 不勾選
CSRF Protection
因為 Jenkins 只是跑在本機,所以使用較寬鬆的 security 設定
建立 Jenkins Job
每一個自動化,在 Jenkins 稱為 Job。
- 若是第一次使用 Jenkins,可使用首頁的
create new jobs
建立新 job - 若已經有其他 job,可使用左上角的
New Item
建立新 job
- 輸入 Job 名稱
- 選擇
Freestyle project
- 選擇
Build
- 選擇
Add build step
- 選擇
Execute shell
切換到 /var/code
的單元測試目錄下,使用 dotnet test
執行單元測試。
目前是在 Linux 下跑單元測試,也可彌補之前只在 macOS 跑單元測試,但 production 是 Linux 的缺憾
- 選擇
Build Now
,第一次執行 job - Jenkins 執行單元測試,得到
藍燈
設定 Git
目前為止,Jenkins Job 已經設定好,但必須手動使用 Build Now
執行 Job,我們希望的是每次 Git commit,就會自動執行 Jenkins job,不用我們操心。
新增 post-commit
post-commit
1 | #!/bin/sh |
到專案的 .git/hooks
目錄下,新增 post-commit
,讓每次 Git commit 時執行 Jenkins job。
Jenkins 的網址規則為 /job/[Job名稱]/build
。
最後記得使用 chmod +x post-commit
讓 post-commit
有執行權限。
建立新 Commit
- 將
false
改成true
- 新增 commit
Jenkins 自動執行單元測試,得到 紅燈
。
這個
紅燈
是我們預期的,因為由false
改成true
,單元測試一定會失敗
設定 Slack
目前為止,每次 Git commit 都會自動執行 Jenkins 單元測試,唯每次都必須回到 Jenkins 才知道單元測試是否成功,若能將結果推送到 Slack,我們就能把 Slack 當成持續整合的訊息平台。
新增 Channel
- 按下
Channels
右側的+
新增 channel
- Privacy : 設定為
Public
或Private
channel - Name : 設定 channel 名稱
- Purpose : channel 的功能描述,可以不輸入
- Send invites to : 設定 channel 成員,可以稍後再設定
- 按
Create Channel
開始建立 channel
- 按
Got It!
進入 channel
- 正式進入 channel,將來 Jenkins 訊息會傳進此 channel
新增 Notification
- 選擇右上方的
option
- 選擇
Add an app
新增 Jenkins App
Slack 將開啟瀏覽器
- 稍微往下捲輸入
Jenkins
- 選擇
Jenkins CI
新增 Configuration
- 按
Add Configuration
加入 Jenkins CI
新增 Integration
- 按
Add Bitbucket Integration
正式加入整合 Jenkins
Slack 設定完成
- 介紹 Jenkins 設定流程
Slack 部分已經設定完成,接下來是 Jenkins 的設定
Slack 網頁先不要關閉,稍後會用到
設定 Jenkins
Manage Jenkins
- 左側選擇
Manage Jenkins
- 右側選擇
Manage Plugins
安裝 Slack Plugin
- 選擇
Avaliable
tab - 選擇
Slack Notification Plugin
- 按
Download now and Install after restart
新增 Webhook
- 左側選擇
Manage Jenkins
- 右側選擇
Configure System
在 Global Slack Notifier Settings
下設定
- Base URL : 貼一段 Slack 所提供的 URL
- Integration Token : 貼一段 Slack 所提供的 token
Q : 要貼什麼 Base URL 與 token 呢 ?
回到 Slack 最後的網頁往下捲到 Step 3
- 將
Base URL
與Integration Token
複製貼上
最後按 Save
存檔。
設定 Job
- 選擇要發 Slack 通知的 job
- 按
Configure
設定
在 Jenkins 執行完 build
動作後,無論成功或失敗,將結果通知 Slack
- 選擇
Post-build Actions
- 選擇
Add post-build action
- 選擇
Slack Notification
選擇希望 Jenkins 通知 Slack 的動作 :
- 選擇
Notify Failure
、Notify Success
與Notify Unstable
建議不用選擇太多 action,
success
確認 Jenkins 還活著,Failure
與Unstable
確認 CI 失敗即可
新增一個 Git commit,就可發現 Slack 收到 Jenkins 測試成功 綠燈
的訊息。
Conclusion
- 實務上若 Git repository 放在 GitHub 或 Bitbucket,則可將 Jenkins 放到雲端主機的 VM 上,大致上的流程與設定與本文類似
- 本文雖然只針對單元測試示範,但也適用於整合測試與驗收測試
- 將較耗時的整合測試與驗收測試交給 Jenkins 處理,在開發時只要針對單元測試即可,若 Slack 出現紅燈,再回來關注整合測試與驗收測試失敗的部分