1
0

More than 1 year has passed since last update.

Git hooksを使用してpyproject.tomlのversion情報とgit tagの更新をコミットをトリガーに自動化する

Last updated at Posted at 2023-06-10

はじめに

本記事は以下ブログ記事の転記です。

記事で書かれているpoetrygit-hookgit-tagの説明は割愛します。
詳細は公式ドキュメントを確認してください。

補足情報

2023/09/06

しばらく運用していて、コミットごとにgit tagするのは冗長だと感じることが増えてきました。

コミットごとにpyproject.tomlgit tagを更新(インクリメント)するのであれば良いですが、毎回更新しなくてよい場合は、後述する.pre-commit-config.yamlupdate-pyprojectフックを一式コメントアウトしてください。

動作としては以下の通りです。

  • pyproject.tomlversionキーを更新する場合は、コミットするとpyproject.tomlversionキーを取得して、git tagが実行される。
  • pyproject.tomlversionキーを更新しない場合は、git tagは実行されない。

要約

  • git commitをトリガーにpyproject.tomltool.poetryテーブルのversionキーとgit tagを同時に更新する
  • git hookでバージョン更新用のスクリプトをフックに設定する
  • git hookとしてpre-commitpost-commitの2つを使用する
  • post-commitでは最後にgit pushを実行する

作成したもの

以下のスクリプトをgit hookで実行します。

1. update_pyproject_version.py

  • pyproject.tomlのバージョンを更新するためのPythonスクリプト

2. run_git_tag_base_pyproject.py

  • pyproject.tomltool.poetryテーブルのversionキーを取得してgit tagを実行するためのPythonスクリプト

3. .pre-commit-config.yaml

  • git フックスクリプトをセットアップするためのコンフィグファイル

4. pre-commitフック

  • poetryコマンド、静的解析パッケージ、update_pyproject_version.pyを含む、pre-commitで実行するカスタムスクリプト
  • .pre-commit-config.yaml作成後にpoetry run pre-commit installコマンドで作成します。

5. post-commitフック

  • run_git_tag_base_pyproject.pygit commit後に実行するカスタムスクリプト
  • create_post-commit.shを実行することで作成します。

実行例

以下のプロジェクトを例に上記スクリプトの実行例を示します。

1.コミット前の情報

0.2.8までバージョンが追加されている状態です。

1.1. ローカルタグ情報

video-grid-merge % git tag
v0.2.1
v0.2.2
v0.2.3
v0.2.4
v0.2.5
v0.2.6
v0.2.7
v0.2.8

1.2. リモートタグ情報

% git ls-remote --tags origin
0ae7fa7be0457a3512c51c7a231f24e724570ffd        refs/tags/v0.2.1
197a04bcf528e4c0ef5c297dff061d4d34ffea2b        refs/tags/v0.2.2
01f88f943ee8330034530c14fb417bf45c5e651b        refs/tags/v0.2.3
4212bfa0733f2c2c809f9db37333096b752a451e        refs/tags/v0.2.4
52b72640df2f31edbef5c21e0fec149d67eafe04        refs/tags/v0.2.5
6de440811f4adbbd614f622ebed74450dfeaa951        refs/tags/v0.2.6
03b75ea8f4688c3e3b8e02c285093b9736a38d24        refs/tags/v0.2.7
054237a84bbe9233099c55af4ff2443145fbb4d8        refs/tags/v0.2.8

1.3. pyproject.toml

[tool.poetry]
name = "video-grid-merge"
version = "0.2.8"
description = "This project allows you to use FFmpeg to arrange video files stored in a specified folder in an NxN grid layout and generate the output."
authors = ["7rikaz_h785 <7rikaz.h785.stat2ltas41lcijad@gmail.com>"]
readme = "README.md"
license = "MIT"
packages = [{include = "video_grid_merge"}]

[tool.poetry.scripts]
#video-grid-merge = "video_grid_merge.__main__:main"
#delete-temporaliy-files = "video_grid_merge.local_code.delete_files:main"

[tool.taskipy.tasks]
vgmrun = "python video_grid_merge"
vgmrn = "python video_grid_merge/rename_files.py"
vgmrm = "python video_grid_merge/delete_files.py"
vgmtest = "pytest -s -vv --cov=video_grid_merge --cov-branch --cov-report term-missing --cov-report html"
isort = "poetry run isort video_grid_merge tests"
black = "poetry run black video_grid_merge tests"
flake8 = "poetry run flake8 video_grid_merge tests"
mypy = "poetry run mypy"

[tool.poetry.dependencies]
python = "^3.10"
mdformat = "^0.7.16"
tomlkit = "^0.11.8"

[tool.poetry.group.dev.dependencies]
flake8 = "^6.0.0"
black = "^23.3.0"
isort = "^5.12.0"
mypy = "^1.3.0"
pytest = "^7.3.1"
flake8-pyproject = "^1.2.3"
taskipy = "^1.11.0"
pytest-cov = "^4.1.0"
pytest-mock = "^3.10.0"
pre-commit = "^3.3.2"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

[tool.black]
target-version = ['py310']

[tool.isort]

[tool.flake8]
ignore = ["E402","E501","W503"]

[tool.mypy]
files = ["video_grid_merge","tests"]
python_version = "3.10"
strict = true
warn_return_any = false
ignore_missing_imports = true
scripts_are_modules = true

[tool.pytest.ini_options]
testpaths = ["tests",]

2. プロジェクトのインストール

git clone https://github.com/7rikazhexde/video-grid-merge.git
cd video-grid-merge
poetry install 

プロジェクトは以下のようになります。
run_git_tag_base_pyproject.pyupdate_pyproject_version.pyはciという名前のディレクトリに格納されています。

video-grid-merge % tree -L 2
.
├── LICENSE
├── README.md
├── ci
│   ├── run_git_tag_base_pyproject.py
│   └── update_pyproject_version.py
├── create_post-commit.sh
├── poetry.lock
├── pyproject.toml
├── requirements-dev.txt
├── requirements.txt
├── tests
│   ├── __init__.py
│   ├── test_data
│   └── test_main.py
└── video_grid_merge
    ├── __init__.py
    ├── __main__.py
    ├── delete_files.py
    ├── local_code
    ├── media
    └── rename_files.py

3. pre-commitスクリプトとpost-commitスクリプトの作成

.git/hooks以下は下記の通りです。

video-grid-merge % tree -L 2 .git/hooks 
.git/hooks
├── applypatch-msg.sample
├── commit-msg.sample
├── fsmonitor-watchman.sample
├── post-update.sample
├── pre-applypatch.sample
├── pre-commit.sample
├── pre-merge-commit.sample
├── pre-push.sample
├── pre-rebase.sample
├── pre-receive.sample
├── prepare-commit-msg.sample
├── push-to-checkout.sample
└── update.sample

poetry install後はpre-commitスクリプトとpost-commitスクリプトは存在しないため、下記コマンドを実行してインストールします。(一部記載を環境変数に置き換えています。)

3.1. pre-commit作成

video-grid-merge % poetry run pre-commit install
Configuration file exists at $HOME/Library/Preferences/pypoetry, reusing this directory.

Consider moving TOML configuration files to $HOME/Library/Application Support/pypoetry, as support for the legacy directory will be removed in an upcoming release.
pre-commit installed at .git/hooks/pre-commit

3.2. post-commit作成

video-grid-merge % chmod +x ./create_post-commit.sh
video-grid-merge % ./create_post-commit.sh 
[video-grid-mergeのディレクトリパス]/.git/hooks/post-commit created with execution permission.

再度.git/hooks以下を確認するとpre-commitとpost-commitが追加されていることを確認できます。

video-grid-merge % tree -L 2 .git/hooks 
.git/hooks
├── applypatch-msg.sample
├── commit-msg.sample
├── fsmonitor-watchman.sample
├── post-commit # 追加
├── post-update.sample
├── pre-applypatch.sample
├── pre-commit # 追加
├── pre-commit.sample
├── pre-merge-commit.sample
├── pre-push.sample
├── pre-rebase.sample
├── pre-receive.sample
├── prepare-commit-msg.sample
├── push-to-checkout.sample
└── update.sample

4. git commit例

4.1. 失敗例

作成したgit hookはgit commitをトリガーに実行されますが、pre-commitでは.pre-commit-config.yamlのプラグインオプションであるfail_fastにより、各hook処理が成功(=Passed)しない場合には実行を停止させています。これはupdate-pyprojectフック(update_pyproject_version.py)で失敗した情報を取得できないため、エラーの場合も実行させないことで、バージョンの更新もさせないようにしています。

次に失敗時の例として、.pre-commit-config.yamlファイルで改行を増やして、git add後にgit commitした場合、trailing-whitespaceフックにより指摘箇所は修正されますが、pre-commitフックの実行は停止されます。

> git -c user.useConfigOnly=true commit --quiet
trim trailing whitespace.................................................Failed
- hook id: trailing-whitespace
- exit code: 1
- files were modified by this hook

Fixing README.md

4.2. 成功例

差分は下記の通りです。
git diff.png

ここで再度、git commitを実行するとpyproject.tomlgit tag0.2.9で更新してgit pushしていることが確認できます。(実行結果の表示について、vscodeのコミットボタン押下でも問題なく動作しますが、デフォルトではログ出力しないため、git commitコマンドを実行しています。)

% git commit -m "docs(README): Changed installation description"
trim trailing whitespace.................................................Passed
fix end of files.........................................................Passed
mixed line ending........................................................Passed
check toml...........................................(no files to check)Skipped
check yaml...........................................(no files to check)Skipped
poetry-check.........................................(no files to check)Skipped
- hook id: poetry-check
poetry-lock..............................................................Passed
- hook id: poetry-lock
- duration: 2.39s

Configuration file exists at $HOME/Library/Preferences/pypoetry, reusing this directory.

Consider moving TOML configuration files to $HOME/Library/Application Support/pypoetry, as support for the legacy directory will be removed in an upcoming release.
Updating dependencies
Resolving dependencies... (0.9s)

poetry-export........................................(no files to check)Skipped
- hook id: poetry-export
poetry-export........................................(no files to check)Skipped
- hook id: poetry-export
isort................................................(no files to check)Skipped
black................................................(no files to check)Skipped
flake8...............................................(no files to check)Skipped
mypy.................................................(no files to check)Skipped
mdformat.................................................................Passed
Update pyproject.toml version............................................Passed
Configuration file exists at $HOME/Library/Preferences/pypoetry, reusing this directory.

Consider moving TOML configuration files to $HOME/Library/Application Support/pypoetry, as support for the legacy directory will be removed in an upcoming release.
v0.2.1
v0.2.2
v0.2.3
v0.2.4
v0.2.5
v0.2.6
v0.2.7
v0.2.8
v0.2.9
Enumerating objects: 7, done.
Counting objects: 100% (7/7), done.
Delta compression using up to 4 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 451 bytes | 451.00 KiB/s, done.
Total 4 (delta 3), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (3/3), completed with 3 local objects.
To https://github.com/7rikazhexde/video-grid-merge.git
   054237a..c6f792c  main -> main
Total 0 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/7rikazhexde/video-grid-merge.git
 * [new tag]         v0.2.9 -> v0.2.9
.git/hooks/post-commit end!!!
[main c6f792c] docs(README): Changed installation description
 2 files changed, 5 insertions(+), 5 deletions(-)

4.3. git log(コミット後)

video-grid-merge % git log --stat
commit c6f792c39cebb9ee6c21c6ae7c040573f4ddd995 (HEAD -> main, tag: v0.2.9, origin/main, origin/HEAD)
Author: 7rikaz_h785 <7rikaz.h785.stat2ltas41lcijad@gmail.com>
Date:   Fri Jun 9 21:53:03 2023 +0900

    docs(README): Changed installation description

 README.md      | 8 ++++----
 pyproject.toml | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

4.4. ローカルタグ情報(コミット後)

video-grid-merge % git tag
v0.2.1
v0.2.2
v0.2.3
v0.2.4
v0.2.5
v0.2.6
v0.2.7
v0.2.8
v0.2.9 # 追加

4.5. リモートタグ情報(コミット後)

% git ls-remote --tags origin
0ae7fa7be0457a3512c51c7a231f24e724570ffd        refs/tags/v0.2.1
197a04bcf528e4c0ef5c297dff061d4d34ffea2b        refs/tags/v0.2.2
01f88f943ee8330034530c14fb417bf45c5e651b        refs/tags/v0.2.3
4212bfa0733f2c2c809f9db37333096b752a451e        refs/tags/v0.2.4
52b72640df2f31edbef5c21e0fec149d67eafe04        refs/tags/v0.2.5
6de440811f4adbbd614f622ebed74450dfeaa951        refs/tags/v0.2.6
03b75ea8f4688c3e3b8e02c285093b9736a38d24        refs/tags/v0.2.7
054237a84bbe9233099c55af4ff2443145fbb4d8        refs/tags/v0.2.8
c6f792c39cebb9ee6c21c6ae7c040573f4ddd995        refs/tags/v0.2.9 # 追加

注意事項

git commit時にコミットキャンセルした場合はpre-commitは正常終了していますので、update-pyprojectフック(update_pyproject_version.py)は動作します。

結果、pyproject.tomltool.poetryテーブルversionキーは更新されますので、その状態で再度コミットするとさらに0.0.1分加算します。なので、この場合はversion部分を手動で変更前の値に戻す必要があります。現状はコミットキャンセル処理を考慮していないのでこのようになっています。

また、update_pyproject_version.pyではv[major].[minor].[pathch]でバージョン定義していますが、major0以上[minor]と[pathch]0から999にしています。もし、0.1.0から0.2.0にminorバージョンを更新するためには0.1.999とする必要があります。

それぞれ、今後改善したい部分ですが、対応は未定です。

コード補足

コードはGistにコミットしていますが、下記リポジトリにもコミットしています。
試験用のため更新は非定期で、将来的にはリポジトリを変更するかもしれませんが、利用したい方は自己責任の範囲でフォークやDLをして使用してください。

まとめ

本記事では、git hookでpythonプロジェクトのバージョン管理を自動化する記事を紹介しました。

pre-commitではバージョン更新用のフック以外にも静的解析ツールのフックも利用しています。ツールに信頼を置くことが前提になりますが、コードの品質を上げるためには必要だと思います。

一方でツールが増えると管理が複雑になるため、一元管理し、人による操作はなるべく減らすことが必要だと感じています。DockerやGitHub Action等、便利なツールは他にもありますが、個人的には使用経験が浅いので、今後はgit hook以外にもツールを活用して、より効率的なコーディングができるようにしていきたいと思っています。

最後に、本記事がどなたかの参考になれば幸いです。

参考

以下の記事を参考にさせていただきました。

以上です。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0