在團隊協作開發中,程式碼品質是一個非常需要重視的問題,除了比較進階的重構以外,有沒有什麼自動檢查的方法可以提升程式碼品質呢?答案是有的,就是今天要討論的 pre-commit。
在程式碼 commit 進 branch 之前,如果能在 local 端阻擋一些低級失誤,就可以大大提升開發品質,在專案中使用 pre-commit 有以下好處:
- 自動化檢查程式碼排版規範快速又有效率(如 python PEP8)
- 低級的問題不會進到 code review
- 多一點時間檢查程式邏輯,而不是基本錯誤(如排版)。
- 人工檢查程式碼的時間很寶貴,減少人工即是增進效率。
- 低級的問題不會進到 CI/CD pipeline
- pipeline 應該多一點綠勾勾,而不是滿滿 debug 的痕跡。
這次示範的 pre-commit 設定是基於 python 的 pre-commit framework:pre-commit。市面上的 pre-commit framework 其實不少,例如 js 的 Husky,或是自己撰寫 git hook 的 script 也是可以的。那就廢話不多說,開始介紹吧!
一、Pre-commit 基本使用
以 framework 設置 pre-commit 非常簡單,官方文件淺顯易懂,只需要 pre-commit 的基本程式,再加上一個 yaml 設定檔即可完成!
1. Pre-commit 安裝
- 安裝工具:
$ pip3 install pre-commit # pip 安裝 pre-commit
$ pre-commit install # 安裝 pre-commit 至 .git/hooks
- 撰寫配置文件 .pre-commit-config.yaml
- 產生一個新 commit
- 需要的套件安裝會和第一次 commit 時的檢查一起完成。
2. Pre-commit 設定檔與使用流程
需要設定的只有 .pre-commit-config.yaml 這個 config 檔,將它置於專案下(也就是和 .git 同一層)即可,他的基礎格式如下:
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v1.2.3
hooks:
- ...
一個實際使用的 .pre-commit-config.yaml 範例:
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.2.3
hooks:
- id: flake8
- id: detect-aws-credentials
- id: detect-private-key
- id: check-added-large-files
- id: check-merge-conflict
- id: check-json
- id: check-yaml
- repo: https://github.com/ambv/black
rev: stable
hooks:
- id: black
language_version: python3.6
- repo: https://github.com/Lucas-C/pre-commit-hooks-bandit
rev: v1.0.4
hooks:
- id: python-bandit-vulnerability-check
args: [-l, --recursive, -x, tests]
files: .py$
- repo: https://github.com/asottile/reorder_python_imports
rev: v1.6.1
hooks:
- id: reorder-python-imports
使用這個設定檔 commit 時的檢查訊息:
修改後再次 commit:
成功 commit!
[用心去感覺] 有一些網路上的設定檔是用 sha 而不是 rev?
在官方文件有提到:rev is the revision or tag to clone at. new in 1.7.0 previously called sha,也就是這個欄位要放所指定的 repo 的版號(以前是 commit sha)。
二、常用的 Git Hooks
Pre-commit 設置的本質上就是加上一個個 Git hooks,而市面上有很多 hooks 可以挑選,以目的來區分大抵上有幾種類別:
- 程式碼規範檢查:
- black:自動格式化 (formatter)
- flake8:檢查程式碼不符合規範的地方 (checker, style & linting)。
- 安全檢查:
- detect-aws-credentials
- detect-private-key
- 格式檢查:
- check-json
- check-yaml
- 其他檢查:
- check-added-large-files
- check-merge-conflict
- ...
而每一個 hook 也可以針對各個 hook 需要的設定檔再做設定,例如 flake8 的客製化:
in .flake8 file:
[flake8]
ignore = E203, E266, E501, W503, F403, F401
max-line-length = 79
max-complexity = 18
select = B,C,E,F,W,T4,B9
exclude =
.git,
__pycache__,
build
另一個常見的用法是用 hook 啟動 unit test,不過要當 test case 質量和數量到達一定程度時比較建議使用,不然測試常常壞掉,然後不得已常常下 --no-verify 參數繞過檢查,反而得不償失。
三、Pre-commit 的注意事項
- Pre-commit 不是強制性的,不是用來強硬擋下不符規範的 commit;其主要目的是更早的發現問題,有助於維持開發流程的健康狀態。
- 方法論設計理念上,pre-commit hooks 的作用域僅限 local,其他人不應該有權限限制你 local 的 git 指令,如強硬限制你必須使用的 hooks。
- 急於 commit 可以使用 $ git commit --no-verify 繞過檢查,雖然這種作法有點不健康。
- 如果要強硬擋下的話應該使用 server 端的 pre-receive hook。
- Server 端的檢查雖然有強制性,但也有速度較慢、可能無法重現等缺點。
- Git 2.9 之後有 "global" hooks 的功能,但還是不大適合用於所有 repositories 的設定。
- 由於存放 Git hooks 的 .git/hooks 目錄不會隨著專案複製,比較適合的做法還是另外開一個 repository 分享存放設定檔,讓團隊中的成員可以自行拷貝或連結進行設定,也比較方便追蹤 hooks 的更動。
補記:Git Hooks
Git hooks 就是當某些事件發生時,觸發自定義的 script。主要分成用戶端和伺服器端的 hooks,以下是幾個常見 的 hooks 種類:
- Client-side hooks
- pre-commit
- prepare-commit-msg
- commit-msg
- post-commit
- post-checkout
- pre-rebase
- Server-side hooks
- pre-receive
- update
- post-receive
其中,pre-* 代表在某個階段之前的動作, post-* 則只能用於通知。這些 hooks 使用時通常有一些既定的目的:
- (Client-side, Pre-*):coding style checking
- (Client-side, Post-*):check branch status for safety
- (Server-side, Pre-*):protect master
- (Server-side, Post-*):notification
其設定範例可以在 .git/hooks 中找到,記得把檔名中的 .sample 刪去即可使用:
applypatch-msg.sample pre-push.sample
commit-msg.sample pre-rebase.sample
post-update.sample prepare-commit-msg.sample
pre-applypatch.sample update.sample
pre-commit.sample
References
https://pre-commit.com/
Lj Miranda - Automate Python workflow using pre-commits: black and flake8 https://ljvmiranda921.github.io/notebook/2018/06/21/precommits-using-black-and-flake8/
代碼質量和 Pre commit
https://www.jianshu.com/p/7e2d7b3d65cf
Dev Life - Pre-commit is awesome
https://blog.jerrycodes.com/pre-commit-is-awesome/
atlassian CI/CD - 3 Git hooks for continuous integration https://www.atlassian.com/fi/continuous-delivery/continuous-integration/git-hooks
使用 git hook 在 commit 前進行 unittest
https://yodalee.blogspot.com/2016/12/git-hook-unittest.html
沒有留言:
張貼留言