Git -- Pre-commit鉤子的使用

背景介紹 -- 因業務需求,咱們須要維護一個proto倉庫用於先後端同窗的數據結構定義,然而在倉庫使用的過程當中常常發生proto文件格式錯誤的狀況,致使系統對應的頁面會發生崩潰。因此,咱們打算在proto倉庫中啓用pre-commit檢驗的方案,來下降這類事件發生的機率。git

什麼是pre-commit?

pre-commit你們可能不是熟悉,可是咱們基本都享受過它帶來的便利。pre-commit是Git自帶有的hook,它能夠在咱們commit以前先對提交的內容進行遍歷、檢測亦或是其餘操做。json

舉一個最多見的例子,在開發JS項目時,若是咱們用的是社區提供的腳手架通常會都配有eslint,而後咱們在提交代碼時可能會由於某些代碼風格不符合eslint的規範而提交失敗。後端

這就是利用pre-commit能夠辦到的事,它能夠確保提交到倉庫內的代碼風格一致。查看Git的文檔能夠知道,在Git不只提供commit階段的鉤子,還有prepare-commit-msg、post-commit、pre-rebase...幾乎在提交代碼到推送代碼的每一個階段都留有相應的鉤子,咱們能夠按需進行觸發。api

如何觸發pre-commit(如何觸發Git Hook)

在每一個git倉庫根目錄下都有一個隱藏的.git文件夾,內部有一個名爲"hooks"的文件夾,顧名思義這個文件夾是用來存放各種鉤子的。進去以後會發現,有不少.sample爲後綴的文件,這些是git自帶的鉤子模板,裏面內置了一些鉤子的案例供你們參考,當咱們編寫好屬於本身的鉤子方法後,只須要將.sample後綴去掉便可。bash

提示:任何正確命名的可執行腳本均可以正常使用 —— 你能夠用 Ruby 或 Python,或其它語言編寫它們。 -- 自定義 Git - Git 鉤子服務器

怎麼編寫一個可用的hook腳本

  • 肯定目標文件

在執行驗證以前,首先應該須要確認驗證的目標文件(被修改或新增的文件)。關於修改與新增的文件列表,咱們能夠經過git的diff方法來查出。數據結構

#!/bin/bash
STAGE_FILES=$(git diff --cached --name-only --diff-filter=ACM -- '*.proto')

if [[ $STAGE_FILES = "" ]] ; then
    echo "無須要檢查的proto文件"   
    exit 0
fi
複製代碼
  • 文件內容讀取

若是改動的文件中有proto文件,則會進入下一個流程。咱們先將檢測到的改動文件打印出來,供提交者確認,隨後進行驗證流程。app

echo "$STAGE_FILES 待檢查"

for proto in $STAGE_FILES;do
    PROTO_TEXT=$(cat $proto)
    echo "$proto 檢查中..."
done;

exit 0
複製代碼
  • 文件內容推送至服務端
request=`curl -s -H "Content-type: application/json" -H "X-Proto-Commit: precommit" -X POST -d '{"proto_text":"'$PROTO_TEXT'","name":"'$proto'"}' http://localhost:7777/api/proto/preCommit`
複製代碼
  • 服務端返回處理
STATUS=`echo -e "'$request'" | grep "error"`
if [[ $STATUS = "" ]];then
    echo "$proto 檢查完成..."   
else
    echo $STATUS
    exit 1
fi
複製代碼

咱們將會以第一步中獲得的變動文件列表爲循環體,將每一個變動的proto文件中的內容都讀取出來,並經過curl的方式將其發送到,負責提供檢測能力的服務端,等待服務端返回檢測結果。curl

看起來,一個簡易的pre-commit-proto能力就完成了,可是在實際使用的時候發現,有某些proto文件提交到檢測端一定會報錯,就算這個文件自己並沒有任何錯誤,服務端仍然會返回錯誤。post

通過屢次測試,最後定位到是編碼格式的問題,因爲curl自帶的encode的功能對中文的支持有限,因此咱們決定將整個proto文件上傳至服務端進行檢測,以保證檢測內容的絕對正確。

如何經過curl上傳proto文件並驗證?

因爲直接讀取推送文件內容的方式沒法達到指望的效果,咱們嘗試將整個proto文件上傳到服務端進行解析。

- request=`curl -s -H "Content-type: application/json" -H "X-Proto-Commit: precommit" -X POST -d '{"proto_text":"'$PROTO_TEXT'","name":"'$proto'"}' http://localhost:7777/api/proto/preCommit`
+ request=`curl -k -s -F "file=@$proto" http://localhost:7777/api/proto/preCommit`
複製代碼

咱們只需將request變量後面的執行語句微調,便可將proto文件上傳到指定地方。其中須要注意幾個參數:

  • -F: 用來向服務器上傳二進制文件。
  • -k: 指定跳過 SSL 檢測。(可選,因爲示例中不是https接口,因此加上了)
  • -s: 靜默模式,不輸出任何東西(可選,看自身需求)

將文件順利上傳以後就完成了代碼提交端的任務了,剩下就是看服務端檢測的結果。若是檢測經過則退出pre-commit繼續提交動做,反之則提示proto文件的錯誤的部分,並中斷代碼提交。

如何在一個新的克隆中啓用pre-commit

到這,也許你們心中仍有個大大的問號。通篇的大前提是修改.git文件夾內的內容,可是既然是git項目,就確定表明着除本身之外的人也能運行項目,這種狀況下pre-commit就失效了。由於.git文件夾是屬於本地的,無法存儲到版本庫中,咱們要用別的方法將寫好的pre-commit腳步裝載到其本地的.git/hooks中。

#!/bin/bash
cp pre-commit .git/hooks/pre-commit
chmod 777 .git/hooks/pre-commit
echo 'protobuf預檢初始化完成'
exit 0
複製代碼

咱們逐行讀一下上面的代碼塊,首先是聲明bash腳本。緊接着將項目根目錄的'pre-commit'文件複製到指定路徑'.git/hooks/pre-commit'。

這還沒完,若是沒有修改文件權限,咱們會發現git commit的時候還是沒有觸發鉤子,因此將'.git/hooks/pre-commit'修改爲修改成777(可讀可寫可執行)。

後記

至此,一個簡單的pre-commit鉤子的開發與觸發就完成了,git提供的其餘鉤子用法也大同小異,咱們只須要選擇適合需求的鉤子,而後將開發好的腳本替換到hooks文件夾內便可。下面把完整的代碼附上~

init.sh

#!/bin/bash
cp pre-commit .git/hooks/pre-commit
chmod 777 .git/hooks/pre-commit
echo 'protobuf預檢初始化完成'
exit 0

pre-commit.sh

#!/bin/bash
STAGE_FILES=$(git diff --cached --name-only --diff-filter=ACM -- '*.proto')

if [[ $STAGE_FILES = "" ]] ; then
    echo "無須要檢查的proto文件"   
    exit 0
fi

echo "$STAGE_FILES 待檢查"

for proto in $STAGE_FILES;do
    PROTO_TEXT=$(cat $proto)
    echo "$proto 檢查中..."
    request=`curl -k -s -F "file=@$proto" http://localhost:7777/api/proto/preCommit`
    CODE=`echo -e "'$request'" | grep "code" | awk -F ":" '{print $2}' | awk -F "," '{print $1}'`
    if [[ $CODE > 0 ]];then
        echo "預查服務故障,請重試"
        exit 1
    fi
    STATUS=`echo -e "'$request'" | grep "error"`
    if [[ $STATUS = "" ]];then
        echo "$proto 檢查完成"
    else
        echo $STATUS
        exit 1
    fi
done;

exit 0
複製代碼
相關文章
相關標籤/搜索