- Gitフック
- pre-commmit
- Pythonレポジトリ用pre-commit(hookレポジトリ利用バージョン)
- 設定をpyproject.tomlに集約する
- ツールのパッケージをpoetryで管理する
- blackなどの変更結果を表示する
- 現状使っているもの
Gitフック
Gitにはフックという機能があり、commitやpushの前後で
なんらかの作業を自動で実行することができます。
Gitのレポジトリをgit initで作成したりgit cloneで取得したりすると、
.git/hooksというディレクトリが出来ていて、
その中にpre-commit.sampleなどのサンプルフックファイルが設置されるようになっています。
基本的には**pre-
なのでそれらのサンプルスクリプトをpre-commitのように名前変更してあげれば
このスクリプトがgit commit直前に実行されます。
これらのファイルはシェルスクリプトでも他の何らかの実行ファイルでもなんでも構いませんが、
必ずchmod 755 pre-commitとかして実行権限をつけておかないといけません。
commitに関して、例えば、
1 2 | |
1 2 | |
という2つのファイルを用意すると、
1 2 3 4 | |
みたいにメッセージが表示されます。-mを使わずにエディタを起動してメッセージを書く場合には
エディタを開く寸前にpre commit!が表示され、
メッセージを書いたあとにpost commit!が表示されます。
メッセージを書かずにabortされるとpost-commitは実行されずpost commit!は表示されません。
pre-commmit
このフックの中でよく使うのはpre-commitです。
commitをする際にlinterなどを実行してエラーを見つけたら
治すまでcommitを受け付けないようにするような事がよく行われます。
テストを実行することも可能ですが、毎回commit時に長いこと時間がかかるのは辛いので、
通常は簡単なチェックのみを行い、テストなどは別途実行することが多いです。
この.git/hooks/pre-commitというファイルを管理しておけば毎回同じチェックが出来るわけですが、 このファイルは.git以下にあるため、このファイル自体はGitで管理することが出来ません。
なので共有のレポジトリとかで同じpre-commitを設定したい場合でもこのファイルを 直接共有しないと同じ処理が出来ません。
そこで、pre-commitファイルを別のディレクトリで管理して
cloneしたら.git/hooksにコピーするように指定したりそういったスクリプトを用意したり、
もしくはgithooksというディレクトリでファイルを管理して
1
| |
というコマンドを実行してフックファイルを参照するディレクトリを変更するとかが考えられます。
が、これらも結構面倒。
そこで便利なのがその名も pre-commitという名前のツール。
Python製でpipで入れるか
1
| |
Homebrewで入れることも出来ます。
1
| |
Python製で環境にPythonが必要ですが(Homebrewなら一緒にインストールされる)、 レポジトリとしては何の言語でも使えます。 単にpre-commitのフックを管理するツールなので。
入れるとpre-commitというコマンドが使えるようになるので、
毎回レポジトリをcloneとかした際に
1
| |
を実行するようにします。これだけは必ずそれぞれでやらないといけないところ。
このコマンドが.git/hooks/pre-commitのファイルを作成し、
これがpre-commitコマンドをcommit直前に実行するようにしてくれます。
pre-commitコマンドは.pre-commit-config.ymlという設定ファイルを読み込んで
必要な処理を行います。
1 2 3 4 5 | |
みたいなファイルを用意するとcommitの前にblackを実行してくれます。
また、
1
| |
というコマンドで現在のGitレポジトリの中の管理ファイルに対してツールを直接実行することも出来ます。
ここでrepoで指定するレポジトリでは必ず
.pre-commit-hooks.yamlというファイルが用意されていて、
その中にあるidの値を呼ぶことでそこで設定された内容が実行されるようになっています。
hookがレポジトリによって用意されてない場合は自分でlocal環境でスクリプトなどを実行する
idを作ることも出来ます。
詳しくは pre-commitのページで。
もしチェックを通さずともcommit履歴を残したい、という場合には
1
| |
とすればpre-commitの内容は実行されずにgit commitが実行できます。
(もしくは-n。このオプションはcommit-msageによるコミットメッセージのチェックがある場合も無視されます。)
ドキュメント用のブランチなどが別にある場合などはよく使います。
また、
PRE_COMMIT_ALLOW_NO_CONFIG=1をシェル変数として設定しておくと
.pre-commit-confg.ymlがない場合は何もせずにgit commitします。
この設定はpre-commitファイルのインストール時に
1
| |
しておいても同じ事ができます1。
ブランチによっては.pre-commit-config.ymlを設置しないようなレポジトリの場合には この辺の設定をしておくと便利です。
Pythonレポジトリ用pre-commit(hookレポジトリ利用バージョン)
Python用のlinter, formatterについては
- black: 全般的なformatter。
- isort: import順をよしなに変えてくれるformatter。
- flake8: プラグインによって拡張可能なlinter。
- mypy: 型チェッカー。
あたりが有名なところです。
これらはそれぞれhook用のレポジトリが用意されているので
それを利用すると
以下のような感じの.pre-commit-config.ymlファイルを用意すれば
これらのツールがcommit時に実行されるようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | |
それぞれのargsは好きなように。
flake8に関してはプラグインとして
flake8-banditを追加で入れています。
また、commit時に実行されるツールには引数として
Gitの管理下にあるファイルのうち、hookで指定されたタイプのファイルで変更があったファイルが渡されるようになっています。
上のツールたちは基本的に.pyの様なPythonのファイルが渡されます。
mypyに関しては他のファイルの関数の返り値の型を見たりもする関係上、
変更があったファイルだけを渡すと正しく判定できないことがあります。
なので、pass_filenames: falseでpre-commitからはファイルを渡さないようにして
argsで必要な全ファイルを渡すようにしています。
mypyは結構時間がかかるツールですがcache機能もあるのでそれも考えるとツールには変更があったファイルだけより全部渡したいところ。
それぞれのオプションは各.flake8といった各種ファイルに書いても良いですが
上のように.pre-commit-config.ymlの中のargsとして書く事もできます。
このようにrepoを使って書くと、pre-commit実行時にそれらのレポジトリがpre-commit用の環境にダウンロードされ、
そこにbalckなどのツールもインストールされます。
従ってgit commit時やpre-commitを実行する際にはblackなどのコマンドは使えますが、
もしローカル環境に自分で別途それらのツールをインストールしていないと
コマンドラインから$ blackなどと打ってもコマンドがない、と言われてしまいます。
設定をpyproject.tomlに集約する
各種ツールはそれぞれの専用のファイルだったり、 globalな$HOME下や$HOME/.config/以下にあるファイルを参照しますが、 この設定が違ってしまうとレポジトリを共有して管理する際には問題になります。
基本的にはレポジトリのトップに設定ファイルがあればそれを優先するものが多いですが、 ツールごとにファイルを置くのも管理が大変です。
そこで、 Poetry などで管理されているレポジトリであればpyproject.tomlという設定ファイルがあるので ここに設定を集約します。
上のほとんどのツールはこのファイルがあればまずは一番優先して見るようになっています。
ただし、flake8に関してはpyproject.tomlを見てくれません。
が、 Flake8-pyproject を入れるとpyproject.tomlから設定を読み込んでくれるようになります 2 。
また、flake8-banditに関しては上では特に設定を書いてませんが、 プラグインとして利用する場合には.banditファイルでないと読み込んでくれません。
そこで、元のbandit を直接インストールしてpyproject.tomlを読み込むようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | |
flake8にはflake8-pyprojectを依存パッケージとして追加してflake8-banditは外します。
別途banditを追加して、
banditに関してはpyproject.tomlを読み込むのに引数が必要なのでそれを渡してあげます。
他のものは以下のようにpyproject.tomlに移します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
banditのオプションでtestsディレクトリを除くようなものも入れてみました。
これでPython用のコードをチェックするツールに関するオプションは レポジトリのpyproject.tomlの中で一元管理できるようになりました。
ツールのパッケージをpoetryで管理する
pre-commitの中でrepoを使ったツール管理をすると、
上に書いたようにコマンドラインから直接使える環境にはインストールされません。
なので例えばエディタの中で使いたい場合とかも別途インストールする必要があったりします。
そうすると環境によって違うバージョンのツールを使ったりconflictが出る可能性があります。
そこで、 Poetry で管理しているレポジトリであれば、 これらのパッケージもPoetry、つまりpyproject.tomlの中で管理するようにしてしまえばそのあたりの問題が解消されます。
まず、.pre-commit-config.ymlを以下のように書き換えます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | |
まず、全体を1つのlocalというrepoにまとめます。
これによって、この中のhooksは外部のレポジトリを見に行かずに中の設定だけを見て実行するようになります。
上の方のGitHubのレポジトリを指定していた場合にはここに書かれているlanguageやtypes_orといった設定は
すべてそれらのレポジトリ内の
.pre-commit-hooks.yamlにかかれていました。
上の各ツールの設定では基本的に各レポジトリの.pre-commit-hooks.yaml から設定を取ってきたものを書いてあります。
isortなんかはpre-commitがファイルを渡す際に--filter-filesという引数の後に渡るようにこの引数が入っています。
(なのでrepoで使う際もargsになにか加えたい場合は必ず--filter-filesを最後に載せる必要があります。)
types_orがpythonだけだったり他のものも入ってたりするのもありますが、
通常はpythonだけで良いかとは思います。
(mypyはpyiは入れて置いたほうが良いですが。)
上の場合にはid, name, entryを全部一緒にしてしまっているのであれですが、
各idのentryとなっているものが実行されるコマンドになります。
元の.pre-commit-hooks.yamlと違う点として、全て
languageをpythonからsystemに変更してあります。
languageは各種ツールをどう管理するか、を決める設定で、
pythonだとpip install .でそのレポジトリのパッケージがインストールされるようになっています。
今はすべてlocalにしたのでいずれにしろこれは使えません。
systemとすることでpre-commit側ではツールのインストールは行わず、
手元の環境にインストールされたツールをそのまま使うようになります。
そこでそれらのツールをpoetryでインストールします。
実際にレポジトリで開発中なら
1
| |
とかでパッケージをインストールしてpyproject.tomlを更新。
--group devで開発環境だけに入れるようにしておきます
3。
また、pre-commitもpipで入れられるツールなのでこれも入れてしまいます。
1
| |
banditに関してはpyproject.tomlからオプションを読み込むためにtomlが必要です。
pyproject.tomlは こんな感じになるはずです。
1 2 3 4 5 6 7 8 9 10 11 | |
最初からこのような設定の入ったpyproject.tomlを用意してpoetry installしてもOK。
この方法のメリットはpre-commitコマンド自体も開発環境にインストールされるので、
開発者はこのレポジトリをgit cloneとかで取ってきたら、
1 2 | |
として、あとは
1
| |
のようにPoetryの仮想環境の中でコマンドを実行すれば各種ツールが使えます。
またblackなどのコマンドも使えます。
1
| |
で環境に入れば直接
1 2 3 | |
などのコマンドを使えますし、これらはpyproject.tomlのオプションを読んで実行されます。
Vimのプラグインの中でツールを使ってるような場合でもこれでレポジトリが要求するツールが使えます。
blackなどの変更結果を表示する
blackはformatterとしてコードを良い感じに変更してくれますが、
pre-commitで行われるということは
git commit時に直前に行われるので変更されてしまうと
編集後とそれをblackが改善したものとの間で
実際どの様な変更が起こったか確認出来ません。
まあ、一度
1
| |
としてpre-commitを走らせずにcommitしてから改めてgit commitする、
という荒業もありますがちょっとやりすぎ。
そこでpre-commitを実行する際に変更を表示するようにします。
blackには--diffオプションを渡すとコードの変更はせずに
実際どの様な改善が出来るか、を表示してくれる機能があるのでこれを使います。
ただし、表示させつつコードも変更する、ということが出来ないので
表示用のidを1つ前に追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
こんな感じ。これで最初のblack-diffの所で変更部分が表示され、次の
blackで実際に変更されるようになります。
--colorはせっかくオプションにあるので追加で。
同様にisortにも--diffオプションがあるのでこれで同じようにisort-diffなidを直前に追加してあげれば
変更点が表示されるようになります。
現状使っているもの
以上を踏まえて以下のような.pre-commit-config.ymlと pyproject.tomlを基本としています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | |
まず、ツールとしてこれまで出ていた
- black: 全般的なformatter。
- isort: import順をよしなに変えてくれるformatter。
- flake8: プラグインによって拡張可能なlinter。
- mypy: 型チェッカー。
- bandit: セキュリティーチェッカー。
以外にPython用として
- blacken-docs: ドキュメントファイル内のPythonブロックに対して
blackをかけるformtter。 - autoflake: 未使用の変数やimportの削除をするformatter。
- autopep8: PEP8に従って動くformatter。
- flake8関連のいろいろなプラグイン
が入っています。
重複している部分もあるかと思いますが、割りとそれぞれでしか出来ないこともあるのでとりあえず必要なだけ入れてる感じです。
flake8系のエラーに関してどのツール(プラグイン)がチェックできるか、format出来るかみたいなことに関しては
以下のページにざっとわかった気になれる表があります(細かく調べると色々とほんと細かいんですが)。
autoflakeやautopep8は、デフォルトだと変更点を表示するだけですが、
--in-placeオプションをつけると実際にコードを変更します。
この変更する際には変更点を表示しないので、blackとかと同様に
最初に--in-placeなしで-diffなidで表示させ、そのあとに--in-placeありで実際にコードの変更を行っています。
Python用以外でも
- shellcheck: シェルスクリプトのlinter。
- mdformat: Markfownのformatter (いくつかのプラグインとともに)。
- pre-commit-hooks: YAMLやJSONなどのlinterやGitのmerge時のCONFLICTな状態が残っているかをチェックするもの(
check-merge-conflict)だったりいろいろなツール群。
ほとんどすべてのレポジトリにはREADME.mdといったMarkdownファイルがありますし、 Pythonのプロジェクトでもちょっとしたシェルスクリプトを開発用に置いてあることはよくあります。
なのでそれら用のツールも入れてますが、
shellcheck、mdformatはpipを使って入れられる、という点で選んでいます。
(shellcheckの方は他に同レベルで使えるツールは無いわけですが。mdformatの方は色々代替えのツールはたくさんあります。)
shellcheckは
shellcheck-py
というPythonパッケージが用意されているのでこれを使ってPoetryでshellcheckを管理できます4。
Supported hooksの中に他にもshellcheckを扱えるものがありますが、
それらはshellcheckコマンド自体は別途自分でインストールする必要があるもので、
pre-commitでもPoetryでも管理できません。
また、shellcheckの作者もpre-commit用のhookを公開してくれています。
koalaman/shellcheck-precommit: Pre-commit hook for ShellCheck
こちらはdockerを使うのでshellcheck自体は自分で入れる必要はありません。
ただしdockerが使える環境になっている必要があります。
もし開発環境が常にdockerを使えるような状態ならこれを使うのもありかもしれません。
これであればPython以外のプロジェクトでもpre-commitによってshellcheckを管理してもらえます。
コマンドラインから直接実行することは出来ませんが、
1
| |
のようにしてpre-commitのshellcheckだけを実行することは可能です。
最後のpre-commit-hooksはいろいろなツールのまとめで、これに関してはpipで管理せずにpre-commitの中で管理するものになりますが、
pre-commit公式のレポジトリでもありますし、
Supported hooks
を眺めてみて色々なごった煮の中では一番色々と便利なものが詰まっていて十分な感じがしたので。
detect-aws-credentialsとかは開発中のマシンにAWSのcredentialsなものがある場合にその中身と同じものが
レポジトリ内に書かれてないか調べるもので、
他のところからコピペしたりすると発見できなかったりします。
そもそもこの辺は常に必要なものでもなかったりするんですが、よほどファイルが大量に無い限り一瞬で終わるので
テンプレートとして入れられるだけ入れてる感じです。
JOSNファイルなんかもないレポジトリもありますが共通化するため入れてあります。
pre-commitファイル内で実行する
pre-commitコマンドに--skip-on-missing-configを引数として与えるようになる ↩-
Flake8-pyprojectの他に pyproject-flake8 というパッケージもあります。
こちらは
flake8の代わりにpflake8というコマンドをインストールし、flake8と同じように使えてかつpyroject.tomlから設定を読み込める、というものになっています。hooksも用意されているので、 flake8のところを
.pre-commit-config.yml 1 2 3 4 5
- repo: https://github.com/csachs/pyproject-flake8 rev: v0.0.1a4 hooks: - id: pflake8 additional_dependencies: [flake8-pyproject]のようにすればpyproject.tomlを読み込んで
pflake8が実行されるようになります。こちらのツールの方が古く、前はpyproject-flake8使っていましたが、 Flake8-pyprojectの方が直接flake8を使える関係上、 実際にflake8自体は同じものが使えるのと flake8にアップデートなどがあった時に 対応が早い可能性が高いこともあり今はFlake8-pyprojectを使っています。
-
Poetry 1.2から
groupというカテゴリが追加され、もともと[tool.poetry.dev-dependencies]となっていた部分は[tool.poetry.group.dev.dependencies]のような感じにするようになりました。poetry addのときのオプションも--devではなく--group devとします。Announcing Poetry 1.2.0 Blog Poetry - Python dependency management and packaging made easy ↩
-
実際には
shellcheckのバイナリをpip install実行時にインストールするような作業をしているものですが。 ↩


