Git / GitHub、プリコミットが何であるか、そしてそれを右にフックする方法をユーザーに伝える必要はないと思います。 すぐにポイントを取得しましょう。
ネットワークには多くのフックの例があり、そのほとんどはシェル上にありますが、1人の重要な点に注意を払った著者は一人もいません。フックはプロジェクトからプロジェクトにドラッグする必要があります。 一見-大丈夫です。 しかし、突然20のプロジェクトに既に存在するフックに変更を加える必要があります...または、突然WindowsからLinuxに開発を移行し、フックをPowerShellに移行する必要があります...どうすればよいですか?
??????? 利益 ...
「そのような方が良い:8パイと1キャンドル!」
もちろん、例は非常に誇張されていますが、それらの助けを借りて、回避したい不便が確認されました。 フックをすべてのプロジェクトに沿ってドラッグしないようにします。頻繁に「仕上げる」必要はありませんでしたが、同時に次のこともできました。
- リポジトリに送信されたコードの有効性を確認します(たとえば、PEP8要件への準拠、ドキュメントの入手可能性など)。
- プロジェクトの包括的な検証を実行します(単体テストなど)。
- エラーが発生した場合にコミット操作を中止し、解析の詳細なログを表示します。
そして、それは次のように見えました:
python pre-commit.py --check pep8.py --test tests.py
フック自体が単なるスターターであり、すべての
特別なストリートマジックはそれが起動するスクリプトによって実行されることは明らかです。 そのようなスクリプトを書きましょう。 興味があれば、猫にようこそ。
pre-commit.py
ただし
、完成したサンプルをネットワークからダウンロードして開発
を開始
する前に
、それらを取得するパラメーターを検討してください。 同時に、例を使用してすべてがどのように機能するかを説明します。
これらのパラメーターは、スクリプトの主な動作を定義します。
- -cまたは--check [script1 ... scriptN] -検証スクリプトを実行します。 スクリプトはpre-commit.pyと同じディレクトリに配置する必要があります。 それ以外の場合は、フルパスを指定する必要があります。 各スクリプトは、現在のコミットから「供給」されます。
- -tまたは--test [test1 ... testN] -単体テストおよび現在のコミットファイルを必要としないその他のスクリプトを実行します。 テストは現在のプロジェクトディレクトリに配置する必要があります。 それ以外の場合は、フルパスを指定する必要があります。
両方のパラメーターはオプションです(1種類のチェックのみを残す可能性のため)が、どちらも指定しない場合、
pre-commit.pyはコード「1」(エラー)で終了します。
そして、補助パラメーターを追加します(すべてオプション):
- -eまたは--exec path_to_interpreter ---checkおよび--testからスクリプトを実行するインタープリターへのフルパス(ファイル名付き)。 パラメーターが指定されていない場合、 pre-commit.pyを実行するインタープリターが使用されます。
- -vまたは--verbose-詳細ログを有効にします。 指定しない場合、実行がエラーコードで終了したこれらのスクリプトのコンソール出力がログに書き込まれます。
- -oまたは--openlog path_to_viewer-ログを表示するプログラムへのフルパス(ファイル名を含む)。
- -fまたは--forcelog-ログを強制的に開きます。 指定しない場合、エラーが検出された場合にのみログが開かれます。 --openlogが指定されている場合、パラメーターは適用可能です。
ロジックは明確です。スクリプト自体の作成を開始できます。
コマンドラインオプション
まず、コマンドラインパラメーターパーサーを構成します。 ここでは、ベース
Pythonパッケージに含まれているため、
argparseモジュールを使用します(または「指で」については、
ここと
ここで 詳しく説明し
ます )。
次のパラメーターを使用してスクリプトを実行します。
c:\python34\python c:\dev\projects\pre-commit-tool\pre-commit.py --check c:\dev\projects\pre-commit-tool\pep8.py --test tests.py
そして、画面に
paramsの内容を表示します:
{'exec': 'c:\\python34\\python.exe', 'forcelog': False, 'test': ['tests.py'], 'check': ['c:\\dev\\projects\\pre-commit-tool\\pep8.py'], 'openlog': None, 'verbose': False}
これで、すべてのパラメーターの値が
params辞書にあり、同じ名前のキーで簡単に取得できます。
メインパラメータの存在のチェックを追加します。
すべては問題ありませんが、柔軟性を損なうことなく、生活を少し簡素化できます。 99%のケースで、検証スクリプトは1つであり、たとえば 'pep8.py'と呼ばれ、権限内のユニットテストスクリプトは毎回同じものが呼び出されます(多くの場合、1つでもあります)。 同様に、ログ表示の場合-常に同じプログラムを使用します(メモ帳にします)。 パーサーの設定を変更しましょう:
そして、デフォルト設定を追加します。
if params.get('check') is not None and len(params.get('check')) == 0:
変更後、パーサー構成コードは次のようになります。
これで、スクリプトの起動ラインが短くなりました。
c:\python34\python c:\dev\projects\pre-commit-tool\pre-commit.py --check --test --openlog
パラメータの内容:
{'check': ['c:\\dev\\projects\\pre-commit-tool\\pep8.py'], 'openlog': 'notepad', 'test': ['tests.py'], 'verbose': False, 'exec': 'c:\\python34\\python.exe', 'forcelog': False}
パラメータが獲得されました。
ログ
ログオブジェクトを設定します。 ログファイル
「pre-commit.log」は、現在のプロジェクトのルートに作成されます。 Gitの場合、作業ディレクトリはプロジェクトのルートであるため、ファイルへのパスは指定しません。 また、操作ごとに新しいファイルを作成するモードを示し(以前のログを保存する必要はありません)、ログの形式を設定します-メッセージのみ:
import logging log_filename = 'pre-commit.log' logging.basicConfig( filename=log_filename, filemode='w', format='%(message)s', level=logging.INFO) to_log = logging.info
コードの最後の行は、私たちの生活を少し簡略化します
。logging.infoの代わりにコードでさらに使用するエイリアスを作成します。
シェル
子プロセスを繰り返し起動し、その出力をコンソールに読み込む必要があります。 このニーズを実装するために、
shell_command関数を作成します。 彼女の責任は次のとおりです。
- サブプロセスの開始( Popenを使用);
- サブプロセスのコンソールからのデータの読み取りとその変換。
- サブプロセスがエラーコードで終了した場合、読み取りデータをログに書き込みます。
関数は引数を取ります:
- commandはPopenの引数です。 実際、シェルで実行されるもの。 ただし、行全体(「python main.py」)ではなく、リスト(['python'、 'main.py'])で設定することをお勧めします。
- force_report-ログへの出力を制御します。 値をとることができます: True-ログへの出力を強制、False-エラーコードを受信した場合に出力、 None-ログへの出力を無効にします。
from subprocess import Popen, PIPE def shell_command(command, force_report=None):
頭の修正
現在のコミットのファイルのリストは、
コンソールコマンドGit-"diff"を使用して簡単に取得できます。 この場合、変更されたファイルまたは新しいファイルが必要です。
from os.path import basename
その結果、
ターゲットには次のようなものが含まれます。
['C:\\dev\\projects\\example\\demo\\daemon_example.py', 'C:\\dev\\projects\\example\\main.py', 'C:\\dev\\projects\\example\\test.py', 'C:\\dev\\projects\\example\\test2.py']
最も痛い段階が完了しました-それはさらに簡単になります。
検証チェック
ここではすべてが簡単です
。--checkで指定されたすべてのスクリプトを実行し、
ターゲットのリストを使用してそれぞれを実行します。
if params.get('check') is not None: for script in params.get('check'): code, report = shell_command( [params.get('exec'), script] + targets, params.get('verbose')) if code != 0: result_code = code
検証チェックに合格しなかったコードのログの内容の例:
[ SHELL ] C:\python34\python.exe c:\dev\projects\pre-commit-tool\pep8.py C:\dev\projects\example\demo\daemon_example.py (code: 1):
C:\dev\projects\example\demo\daemon_example.py:8:80: E501 line too long (80 > 79 characters)
テストを実行する
単体テストでも同じことを行いますが、
ターゲットはありません。
if params.get('test') is not None: for script in params.get('test'): code, report = shell_command( [params.get('exec'), script], params.get('verbose')) if code != 0: result_code = code
[UPD]ログを表示します
グローバル結果コードと
--openlogおよび
--forcelogパラメーターに応じて、ログを表示するかどうかを決定します。
if params.get('openlog') and (result_code > 0 or params.get('forcelog')):
ご注意 Python 2.6(以降)および3.xのバージョンで動作します。 2.6より前のバージョン-テストは実行されませんでしたまた、スクリプトの最後で結果コードを
Gitシェルに返すことを忘れないでください。
exit(result_code)
それだけです スクリプトを使用する準備ができました。
悪の根
フックとは、「pre-commit」と呼ばれるファイル(拡張子なし)で、ディレクトリ<project_dir> /。Git / hooks /に作成する必要があります。
Windowsで正しく実行するための重要なポイントがいくつかあります。
1.ファイルの最初の行は#!/ Bin / shです
そうでない場合、次のエラーが表示されます。
GitHub.IO.ProcessException: error: cannot spawn .git/hooks/pre-commit: No such file or directory
2.パスを指定するときに標準の区切り文字を使用すると、同様のエラーが発生します。
GitHub.IO.ProcessException: C:\python34\python.exe: can't open file 'c:devprojectspre-commit-toolpre-commit.py': [Errno 2] No such file or directory
これは3つの方法で処理できます。二重バックスラッシュを使用するか、二重引用符を使用するか、「/」を使用します。 たとえば、Windowsはこれを食べて窒息しません。
もちろん、これはお勧めできません:)好きな方法を使用してください。
受入試験
「猫で」トレーニングします。
テストコミットには、新しい名前が変更された\変更および削除されたファイルがあります。 また、コードを含まないファイルも含まれます。 コード自体に設計エラーが含まれており、単体テストの1つに合格しません。 検証、テスト、詳細ログを開くフックを作成しましょう:
c:/python34/python c:/dev/projects/pre-commit-tool/pre-commit.py -c -t test.py test2.py -vfo
そして、コミットを実行してみてください。 数秒後、
Gitデスクトップはエラー
を通知します:
そして次のウィンドウで、メモ帳には次のように表示されます。
[ SHELL ] git diff --cached --name-only --diff-filter=ACM (code: 0):
.gitattributes1
demo/daemon_example.py
main.py
test.py
test2.py
[ SHELL ] C:\python34\python.exe c:\dev\projects\pre-commit-tool\pep8.py C:\dev\projects\example\demo\daemon_example.py C:\dev\projects\example\main.py C:\dev\projects\example\test.py C:\dev\projects\example\test2.py (code: 1):
C:\dev\projects\example\demo\daemon_example.py:8:80: E501 line too long (80 > 79 characters)
C:\dev\projects\example\demo\daemon_example.py:16:5: E303 too many blank lines (2)
C:\dev\projects\example\demo\daemon_example.py:37:5: E303 too many blank lines (2)
C:\dev\projects\example\demo\daemon_example.py:47:5: E303 too many blank lines (2)
C:\dev\projects\example\main.py:46:80: E501 line too long (90 > 79 characters)
C:\dev\projects\example\main.py:59:80: E501 line too long (100 > 79 characters)
C:\dev\projects\example\main.py:63:80: E501 line too long (115 > 79 characters)
C:\dev\projects\example\main.py:69:80: E501 line too long (105 > 79 characters)
C:\dev\projects\example\main.py:98:80: E501 line too long (99 > 79 characters)
C:\dev\projects\example\main.py:115:80: E501 line too long (109 > 79 characters)
C:\dev\projects\example\main.py:120:80: E501 line too long (102 > 79 characters)
C:\dev\projects\example\main.py:123:80: E501 line too long (100 > 79 characters)
[ SHELL ] C:\python34\python.exe test.py (code: 1):
Test 1 - passed
Test 2 - passed
[!] Test 3 FAILED
[ SHELL ] C:\python34\python.exe test2.py (code: 0):
Test 1 - passed
Test 2 - passed
詳細なログなしでのみ、同じコミットを繰り返します。
c:/python34/python c:/dev/projects/pre-commit-tool/pre-commit.py -c -t test.py test2.py -fo
結果:
[ SHELL ] C:\python34\python.exe c:\dev\projects\pre-commit-tool\pep8.py C:\dev\projects\example\demo\daemon_example.py C:\dev\projects\example\main.py C:\dev\projects\example\test.py C:\dev\projects\example\test2.py (code: 1):
C:\dev\projects\example\demo\daemon_example.py:8:80: E501 line too long (80 > 79 characters)
C:\dev\projects\example\demo\daemon_example.py:16:5: E303 too many blank lines (2)
C:\dev\projects\example\demo\daemon_example.py:37:5: E303 too many blank lines (2)
C:\dev\projects\example\demo\daemon_example.py:47:5: E303 too many blank lines (2)
C:\dev\projects\example\main.py:46:80: E501 line too long (90 > 79 characters)
C:\dev\projects\example\main.py:59:80: E501 line too long (100 > 79 characters)
C:\dev\projects\example\main.py:63:80: E501 line too long (115 > 79 characters)
C:\dev\projects\example\main.py:69:80: E501 line too long (105 > 79 characters)
C:\dev\projects\example\main.py:98:80: E501 line too long (99 > 79 characters)
C:\dev\projects\example\main.py:115:80: E501 line too long (109 > 79 characters)
C:\dev\projects\example\main.py:120:80: E501 line too long (102 > 79 characters)
C:\dev\projects\example\main.py:123:80: E501 line too long (100 > 79 characters)
[ SHELL ] C:\python34\python.exe test.py (code: 1):
Test 1 - passed
Test 2 - passed
[!] Test 3 FAILED
エラーを修正し、コミットを繰り返します。ここに待望の結果があります
。Git デスクトップは誓いません。メモ帳には空の
pre-commit.logが表示されます。 利益。
ここで完成した例を
見ることができ
ます 。
[UPD]結論の代わりに
もちろん、このスクリプトは万能薬ではありません。 必要なすべてのチェックがテストスクリプトをローカルで実行することに限定されている場合に役立ちます。 複雑なプロジェクトでは、
継続的インテグレーション(またはCI)の概念が通常使用されますが、ここでは
Travis (LinuxおよびOS X用)とそのアナログ
AppVeyor (Windows用)が助けになります。
[UPD]別の選択肢は、
オーバーコミットです。
Gitフックを管理するための非常に機能的なツール。 ただし、微妙な違いがあります-
オーバーコミットを機能させるには、
Rubyインタープリターをローカルにデプロイする必要があります。
適切なコーディングと正しいコミットを行います。