`pre-commit`を`Github Actions`で実行する
はじめに
この記事は、cookiecutter-django
で使用されているpre-commit
とGithub Actions
の動作を理解するための記事です。
pre-commit
を用いて、コードの整形や型チェックを行えます。それをGithubAction
でpre-commit
を実行することで環境構築の必要をなくします。
サンプルリポジトリ
pre-commit
の内容を読む
GithubActions
で実行させる前にpre-commit
で何が実行されるようになっているのか読んでいきます。
exclude: "^docs/|/migrations/"
default_stages: [commit]
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.2.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- repo: https://github.com/psf/black
rev: 22.3.0
hooks:
- id: black
- repo: https://github.com/PyCQA/isort
rev: 5.10.1
hooks:
- id: isort
- repo: https://github.com/PyCQA/flake8
rev: 4.0.1
hooks:
- id: flake8
args: ["--config=setup.cfg"]
additional_dependencies: [flake8-isort]
# sets up .pre-commit-ci.yaml to ensure pre-commit dependencies stay up to date
ci:
autoupdate_schedule: weekly
skip: []
submodules: false
実行されるコマンド
repos
内のコマンドが順に実行されていくので下記の様になっています。
-
pre-commit-hooks
-
trailing-whitespace
- 行末のスペース削除
- 空行の削除
-
end-of-file-fixer
- ファイルの末尾を空行に変更
-
check-yaml
-
yaml
ファイルの構文チェック
-
-
-
black
- コードの整形
-
isort
- import ライブラリの整形
-
flake8
- コードのスタイルチェック
その他
-
exclude
- 除外されるファイルパターンの定義
- ここでは、ドキュメントフォルダーと DB のマイグレーションフォルダーが指定されている
-
default_stages
- 実行する段階の定義
- hooks の実行は
commit
時に制限されている
-
ci
-
pre-commit
のci
向け設定 -
autoupdate_schedule
- hook のバージョンを更新するためのスケジュール
-
weekly
が指定されており、毎週更新される
-
skip
- スキップする hookID のリストを指定
- スキップはしないように設定されている
-
submodules
- サブモジュールの再帰的チェックアウトの設定
-
false
、デフォルトもfalse
-
環境構築
まずはローカル環境でpre-commit
が実行できるように環境を構築します。
# Code quality
# ------------------------------------------------------------------------------
flake8==4.0.1 # https://github.com/PyCQA/flake8
flake8-isort==4.1.1 # https://github.com/gforcada/flake8-isort
coverage==6.3.2 # https://github.com/nedbat/coveragepy
black==22.3.0 # https://github.com/psf/black
pylint-django==2.5.3 # https://github.com/PyCQA/pylint-django
pre-commit==2.18.1 # https://github.com/pre-commit/pre-commit
上記はローカル環境用のrequirements.txt
でのコード品質部分になります。
下記の 2 つはpre-commit
で使用しないのでインストールしませんでした。
coverage
pylint-django
pre-commit
の実行テスト
整形が必要な下記のようなコードを作成し、pre-commit
を動作させます。
pre-commit install
したあとの確認になります。GithubActions
での動作のときにはpre-commit install
はしていない状況で動作確認を行います。
import random
from fizzbuzz import fizzbuzz as fb
import click
import os
@click.command()
@click.option('--num','-n',default=lambda: random.randint(1, 100),type=int,help='Number of loop')
def fizzbuzz(num: int):
fb(num)
if __name__=='__main__':
fizzbuzz()
pre-commit
動作ログ
$ >git commit -m "feat: isortなどが動作するコードに変更"
[WARNING] Unstaged files detected.
[INFO] Stashing unstaged files to C:\Users\ikura\.cache\pre-commit\patch1651315960-10368.
trim trailing whitespace.................................................Passed
fix end of files.........................................................Passed
check yaml...........................................(no files to check)Skipped
black....................................................................Failed
- hook id: black
- files were modified by this hook
reformatted hoge.py
All done! \u2728 \U0001f370 \u2728
1 file reformatted, 1 file left unchanged.
isort....................................................................Failed
- hook id: isort
- files were modified by this hook
Fixing C:\Users\ikura\repos\test-pre-commit\hoge.py
flake8...................................................................Passed
[INFO] Restored changes from C:\Users\ikura\.cache\pre-commit\patch1651315960-10368.
$ >
pre-commit
実行後のコード
import random
import click
from fizzbuzz import fizzbuzz as fb
@click.command()
@click.option(
"--num",
"-n",
default=lambda: random.randint(1, 100),
type=int,
help="Number of loop",
)
def fizzbuzz(num: int):
fb(num)
if __name__ == "__main__":
fizzbuzz()
Github Action
部分を読む
name: CI
# Enable Buildkit and let compose use it to speed up image building
env:
DOCKER_BUILDKIT: 1
COMPOSE_DOCKER_CLI_BUILD: 1
on:
pull_request:
branches: ["master", "main"]
paths-ignore: ["docs/**"]
push:
branches: ["master", "main"]
paths-ignore: ["docs/**"]
concurrency:
group: ${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
linter:
runs-on: ubuntu-latest
steps:
- name: Checkout Code Repository
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: "3.9"
cache: pip
cache-dependency-path: |
requirements/base.txt
requirements/local.txt
- name: Run pre-commit
uses: pre-commit/action@v2.0.3
# With no caching at all the entire ci process takes 4m 30s to complete!
pytest:
runs-on: ubuntu-latest
steps:
- name: Checkout Code Repository
uses: actions/checkout@v3
- name: Build the Stack
run: docker-compose -f local.yml build
- name: Run DB Migrations
run: docker-compose -f local.yml run --rm django python manage.py migrate
- name: Run Django Tests
run: docker-compose -f local.yml run django pytest
- name: Tear down the Stack
run: docker-compose -f local.yml down
jobs
の後半pytest
部分は無視する。読む分には前半部分で十分なのと簡易の動作確認としては、テスト部分の動作検証しにくい。
実行回りの設定
on:
pull_request:
branches: ["master", "main"]
paths-ignore: ["docs/**"]
push:
branches: ["master", "main"]
paths-ignore: ["docs/**"]
concurrency:
group: ${{ github.head_ref || github.run_id }}
cancel-in-progress: true
実行するタイミング(on
)
プルリクエストとプッシュ時にmaster
かmain
ブランチで実行する。またdocs
は除外
並行性(concurrency)
変更実行を許さず、同時の場合はプルリクのみ実行
実行部分
linter:
runs-on: ubuntu-latest
steps:
- name: Checkout Code Repository
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: "3.9"
cache: pip
cache-dependency-path: |
requirements/base.txt
requirements/local.txt
- name: Run pre-commit
uses: pre-commit/action@v2.0.3
- actions/checkout@3
- コードのチェックアウト
- actions/setup-python@v3
- Python の Setup
- 動作確認では、
requirements.txt
に変更
- pre-commit/action@v2.0.3
-
pre-commit
の実行
-
動作確認
pre-commit uninstall
でpre-commit
を削除し、上で作った悲しみのコードをプッシュして動作を確認してみます。
コードをプッシュ・プルリクをすると、Github Actions
で失敗し、修正が求められるようになりました。:o:
所感
Github Actions
良いなーというお気持ち
pre-commit
は最終的にはユーザーがローカルに環境を構築していなければ駄目なのですが、Github Actions
で実行することにより人が環境に関与せずチェックされるのが良いですね。
備考
-
mypy
- 型検査はコマンド実行
-
pre-commit
には含まれていない
-
coverage
- テストガバレッジはコマンド実行
参考
Discussion