- 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
実行時にインストールするような作業をしているものですが。 ↩