--- tags: git, ci title: 探索藏在Git當中的Git Hook description: 研究Git Hook在CI的應用 author: Frank --- # 探索藏在Git當中的Git Hook [![](https://img.shields.io/badge/s716134-githook_demo-green?logo=gitlab)](https://gitlab.com/s716134/githook-demo/-/commits/githook/) 如果希望在git生命週期中,在本機增加一些CI[^CI]任務的話,或許可以不必特地架一台CI Server[^CI_SERVER]去處理,只需要使用到本篇的主角「Git Hook」,就可以達到基本的CI任務了。 [TOC] ## 1. 什麼是Git Hook? Git提供的一組客製化條件觸發腳本的規則,統稱這個規則為Git Hook。這些規則適用於Git的幾個重要操作環節,例如: 提交(commit)、合併(merge)等[^CUSTOMIZING_GIT]。 * 提交流程觸發(Committing-Workflow Hooks) * 郵件流程觸發(Email Workflow Hooks) * 其它客戶端觸發(Other Client Hooks) * 伺服器端觸發(Server-Side Hooks) :::warning :warning: 為了聚焦在本文所探討的議題上,所以僅會介紹幾個基本的Committing的自定義Git Hook。 ::: ## 2. 為什麼需要Git Hook? 在講求快速開發的時代,軟體提交的數量也隨之大量增加。若還是寄望所有的CI工作都交由CI Server來處理,那很快就會面臨到機器效能不足的嚴峻議題。另一方面,組織也希望減少在CI上的成本,同時又期許軟體產出的品質提升。事實上,我認為「**許多的CI工作,是可以分派給開發人員自己負責,而不是完全將其推卸給其它人或機器**」。Git Hook則是我認為目前在現代開發模式中,最適合拿來利用、推廣的一項工具。 ## 3. 哪些人會需要使用到Git Hook? * **開發人員** 正如先前所提到,使用Git Hook來達到基礎的CI任務,可以讓開發人員可以對自己交付的Source Code負責任。除此之外,只要定義好處發腳本的場景,開發人員並不需要額外再特別去學一套新的工具、語言。 * **測試人員** 測試人員如果可以定義好哪些Git場景(commit、push...)去觸發執行哪些測試工具、案例(JUnit、JMeter...),相信不僅方便留存測試紀錄,並且可以節省許多繁複的操作。 :::info :bulb: Git Hook腳本可以使用任何程式語言去編寫,常見的例如: Shell、Ruby、Perl或Python等。 ::: ## 4. 我該在哪裡使用Git Hook? 一般來說,Git在初始化專案的時候,會自動產生一組Git Hook範例腳本,並將其存放在`.git/hooks/`底下。~~幸運的話~~可以在`.git/hooks/`底下看到下面這些範例Git Hook腳本: ```bash= applypatch-msg.sample pre-applypatch.sample pre-rebase.sample commit-msg.sample pre-commit.sample pre-receive.sample fsmonitor-watchman.sample pre-merge-commit.sample prepare-commit-msg.sample post-update.sample pre-push.sample update.sample ``` 對Git而言,僅會認hooks底下符合這些命名規範的檔案名稱,且不需要任何副檔名。所以這些腳本預設是關閉的,如果要啟用它們,**僅需要重新命名將`.sample`移除即可**。如果要固定使用同一組Git Hook腳本,可以透過`core.hooksPath`來指向執行Git Hook的目錄[^SHARE]。 :::info :bulb: 只有最開始使用`git init`初始化的資料夾當中,會出現這些範例Git Hook腳本。這其實是因為`.git/`內的檔案,並不會被Git主動納入版本控管。 ::: ## 5. 哪些時機可以觸發Git Hook? ### 5.1 使用`pre-commit`讓顯示訊息一致 :::danger **案例描述 :** 因為會在不同的電腦、工具中,對相同專案使用Commit紀錄修改,有時候會忘記自己使用哪個名字、信箱,所以時常導致在同一個專案出現多名角色。 ![](https://i.imgur.com/xUcYVMU.png) ::: :::success **解決方法[^CASE1] :** 利用簡單的Shell Script判斷使用者資訊,沒有設定好就不給Commit。 ```bash= #!/bin/sh username=$(git config user.name) if [ "$username" != "LAI, CHUN JING" ] then cat <<\EOF [ERROR] Are you LAI, CHUN JING?" EOF exit 1 fi ``` ::: ### 5.2 使用`pre-push`觸發執行回歸測試 :::danger **案例描述 :** 因為專案的急迫性,有時候開發完新功能或是修改完BUG後,就會急急忙忙地將程式推上版。但是,若是組織沒有一個完善的CI Server,且自己也忘記在推版前執行測試案例,去確認原有功能在修改後是否保持完整,那很可能就會不小心推了一版「改A壞B」的程式上去。 ![](https://i.imgur.com/bfjl2o9.png =600x130) ::: :::success **解決方法[^CASE2] :** ```bash= #!/bin/sh echo "Running mvn clean test for errors" DIR=`pwd` CWD="$DIR/app_example" MAIN_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" cd $CWD MVN_RESULT=$(mvn clean test 2>&1) if [ $? -ne 0 ]; then echo echo "${MVN_RESULT}" | ((tee /dev/fd/5 | grep -A 10 -B 2 "Reactor Summary:" >/dev/fd/4) 5>&1 | sed -n -e '/^Failed tests:/,/Tests run:.*$/ p' ) 4>&1 echo echo "Error while testing the code" cd $CWD exit 1 fi ``` ::: <!-- ### 5.2 使用`pre-push`或`post-merge`觸發Docker打包Image :::danger **案例描述 :** ::: :::success **解決方法[^CASE3] :** ```bash= ::: --> :::warning :warning: 實際上觸發Git Hook不止這些時機[^TASKS][^TASKS2],這裡僅是把個人比較熟悉的場景列上來。 ::: ## 6. 故障排除 ### 6.1 hook was ignored because it's not set as executable 若是出現這個訊息,代表commiter並沒有執行`.git/hooks/`的權限,這時就可以使用`chmod ug+x .git/hooks/*`這行指令,給予使用者及群組擁有`執行(x)`的權限。 [^CI]: [What is Continuous Integration?](https://www.atlassian.com/continuous-delivery/continuous-integration) [^CI_SERVER]: [為什麼我們需要CI Server?](https://ithelp.ithome.com.tw/articles/10102375) [^CUSTOMIZING_GIT]: [Customizing Git - Git Hooks](https://git-scm.com/book/zh-tw/v2/Customizing-Git-Git-Hooks) [^SHARE]: [How to Share Git Hooks with the Team](https://chamikakasun.medium.com/how-to-share-git-hooks-with-the-team-37424603dd91) [^CASE1]: 不可考,突然找不到來源,待補上 [^CASE2]: [mallocator/git.hook.mvn_clean](https://gist.github.com/mallocator/34332f7a6a68d15a419c) [^CASE3]: [Using a post-merge git hook to clean up old branches](https://www.liquidlight.co.uk/blog/using-a-post-merge-git-hook-to-clean-up-old-branches/) [^TASKS]: [Understanding Git Hooks](https://codeburst.io/understanding-git-hooks-in-the-easiest-way-bad9afcbb1b3) [^TASKS2]: [What hooks are supported by GitKraken?](https://support.gitkraken.com/working-with-repositories/githooks/)