關於 web service, unity, blogger 等軟體工程筆記

Integration of Jenkins and GitBlit

Edit icon 沒有留言
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 自行從操作介面設定,如下圖操作:

Repository setting in GitBlit

另一種是修改 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 的設置。

Build triggers in 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 SCM 流程

每次 Jenkins 收到通知後 (/git/notifyCommit),會先向 Git server 要求最新版本,當發現版本不同才會啟動建置作業。

Reference

沒有留言: