背景介紹 -- 因業務需求,咱們須要維護一個proto倉庫用於先後端同窗的數據結構定義,然而在倉庫使用的過程當中常常發生proto文件格式錯誤的狀況,致使系統對應的頁面會發生崩潰。因此,咱們打算在proto倉庫中啓用pre-commit檢驗的方案,來下降這類事件發生的機率。git
pre-commit你們可能不是熟悉,可是咱們基本都享受過它帶來的便利。pre-commit是Git自帶有的hook,它能夠在咱們commit以前先對提交的內容進行遍歷、檢測亦或是其餘操做。json
舉一個最多見的例子,在開發JS項目時,若是咱們用的是社區提供的腳手架通常會都配有eslint,而後咱們在提交代碼時可能會由於某些代碼風格不符合eslint的規範而提交失敗。後端
這就是利用pre-commit能夠辦到的事,它能夠確保提交到倉庫內的代碼風格一致。查看Git的文檔能夠知道,在Git不只提供commit階段的鉤子,還有prepare-commit-msg、post-commit、pre-rebase...幾乎在提交代碼到推送代碼的每一個階段都留有相應的鉤子,咱們能夠按需進行觸發。api
在每一個git倉庫根目錄下都有一個隱藏的.git文件夾,內部有一個名爲"hooks"的文件夾,顧名思義這個文件夾是用來存放各種鉤子的。進去以後會發現,有不少.sample爲後綴的文件,這些是git自帶的鉤子模板,裏面內置了一些鉤子的案例供你們參考,當咱們編寫好屬於本身的鉤子方法後,只須要將.sample後綴去掉便可。bash
提示:任何正確命名的可執行腳本均可以正常使用 —— 你能夠用 Ruby 或 Python,或其它語言編寫它們。 -- 自定義 Git - Git 鉤子服務器
在執行驗證以前,首先應該須要確認驗證的目標文件(被修改或新增的文件)。關於修改與新增的文件列表,咱們能夠經過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文件上傳至服務端進行檢測,以保證檢測內容的絕對正確。
因爲直接讀取推送文件內容的方式沒法達到指望的效果,咱們嘗試將整個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文件上傳到指定地方。其中須要注意幾個參數:
將文件順利上傳以後就完成了代碼提交端的任務了,剩下就是看服務端檢測的結果。若是檢測經過則退出pre-commit繼續提交動做,反之則提示proto文件的錯誤的部分,並中斷代碼提交。
到這,也許你們心中仍有個大大的問號。通篇的大前提是修改.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
複製代碼