Integration of Jenkins and GitBlit
發現需求
自從上次完成建置好第一個版本的 CI Server,拿來自動整合並建置專案 (build project),並且可以在 Jenkins 管理頁直接瀏覽或是下載建置結果。
每天會自動定時排程作業 (jobs),自動建置一版,但當專案趨近完成後,好幾個禮拜都沒有再更新還自動建置,有點浪費。
此外,有時上傳新版後後想立刻建置專案,還得自己登入 Jenkins,手動啟動建置作業,這有點麻煩啊。
後來採用輪詢 SCM (Poll SCM) 方式,設定排程 H/5 * * * *
,每五分鐘由 Jenkins 向 Git server (使用 GitBlit) 要求一次版本,發現版本不同就啟動建置作業,但當專案數量成長,建置作業跟著增加,頻繁得向 Git server 要求資料,這有點浪費資源啊。
有沒有辦法當使用者 (user) 向 Git server 上傳版本 (push) 後,讓 Git server 主動發通知給 Jenkins,然後自動執行建置呢?就像文章首圖的流程那樣,於是開始尋找可能的做法。
處理方案
GitBlit
目前內部 Git Server 採用使用 Java 開發的 GitBlit,Gitblit 1.6.2,在 GitBlit 中使用 groovy 來寫 post-receive scripts (Git push 後伺服器所要執行 scripts),利用 groovy 來完成 repository 收到新的版本後,發送訊息給 CI Server (Jenkins) 的流程。
在 gitblit/data/groovy 資夾中建立新的 script,my-jenkins-notify.groovy,或是修改內部的範例,貼上或是修改以下的程式碼:
import com.gitblit.GitBlit
import com.gitblit.Keys
import com.gitblit.models.RepositoryModel
import com.gitblit.models.UserModel
import com.gitblit.utils.JGitUtils
import org.eclipse.jgit.lib.Repository
import org.eclipse.jgit.revwalk.RevCommit
import org.eclipse.jgit.transport.ReceiveCommand
import org.eclipse.jgit.transport.ReceiveCommand.Result
import org.slf4j.Logger
def jenkinsUrl = gitblit.getString('groovy.jenkinsServer', '')
def triggerUrl = jenkinsUrl + "/git/notifyCommit?url=${url}/${repository.name}"
logger.info("jenkins hook triggered by ${user.username} for ${repository.name}, notify " + triggerUrl)
new URL(triggerUrl).getContent()
原始的 script template 可以從 Github 上找得到,以上為刪除許多註解並調整過的版本。
這段程式碼中,需修改 gitblit/data/gitblit.properties 內部的設定,開啟 gitblit.properties,尋找 web.canonicalUrl
,確認位置是否為 Git server 目前的位置。並且加入 groovy.jenkinsServer
,設定 Jenkins server 的位置,例如以下範例:
# @ gitblit/data/gitblit.properties
# Gitblit server
web.canonicalUrl = https://192.168.1.200:8082
# Jekins server
# 注意:結尾不需要加入 Slash 符號,因為前頭的範例程式碼已經有加 Slash 了
groovy.jenkinsServer = http://192.168.1.212:8080
接下來設定哪些 repository 要在收到新程式碼後 (after client push, git post-receive),要執行哪些 scripts。設定有兩種方式,一種是每個 repository 自行從操作介面設定,如下圖操作:
另一種是修改 gitblit.properties,設定全部 repository 預設收到使用者的上傳後,都要執行該 scripts。沒意外的,我們採用後者方式,才不用每一個 repository 都得設定一次,省掉之後設定的麻煩。
開啟 gitblit.properties,尋找 groovy.postReceiveScripts
,加入剛剛建立 script 名稱,若有多個 script 執行,使用空白區隔。
# @ gitblit/data/gitblit.properties
groovy.postReceiveScripts = my-jenkins-notify.groovy
以上完成 Gitblit 的設定,接下來設定 Jenkins 建置作業。
Jenkins
進入作業編輯頁,找到建置觸發程序 (Build triggers) 編輯區塊,勾選 Poll SCM,Schedule 設定空白,讓 Git server 通知 (post-commit hooks)。如下圖所示便可完成 Jenkins 的設置。
Test
若按照流程 Push 版本到 Git server 後,Jenkins 沒有自動建置的話,驗證方式先確認 Gitblit 有沒有執行 Script,檢查通知的 URL 是否正確,該 URL 是否能夠正常啟動 Job Polling SCM。
從 Gitblit log (gitblit/logs) 尋找 jenkins hook triggered 文字,從那 log 中取得 notify URL:
2016-11-15 16:32:57 [INFO ] jenkins hook triggered by siyuan for Siyuan.git, notify http://192.168.1.212:8080/git/notifyCommit?url=https://192.168.1.200:8082/Siyuan.git
直接貼到瀏覽器中,查看回應內容,如果正常有觸發作業的話,回應應該會是下面:
Scheduled polling of Siyuan’s Build
No Git consumers using SCM API plugin for: https://192.168.1.200:8082/Siyuan.git
若是回應是這樣,表示沒有任何作業被啟動:
No git jobs using repository: https://192.168.1.200:8082/Siyuan.git and branches:
No Git consumers using SCM API plugin for: https://192.168.1.200:8082/Siyuan.git
這時候檢查是否是 groovy notify URL 設定錯誤,或者是網路問題,Gitblit 無法連線到該位置。之後檢查 Jenkins 作業觸發程序設定是否有問題,所以沒有辦法自動啟動建置作業 。
咦,好似哪裡怪怪的
在建置這樣的環境時,一直有個疑問 http://Jenkins-url/git/notifyCommit 似乎是扮演直接啟動 Jenkins 作業的角色,如果一直發送要求給這個位置,那豈不會建置許多版本嗎?
針對的疑問,經過實際測試後的結果,事情不是當初想像的那樣,這樣的事情根本不會發生。其流程應該為下圖:
每次 Jenkins 收到通知後 (/git/notifyCommit),會先向 Git server 要求最新版本,當發現版本不同才會啟動建置作業。
沒有留言: