はじめに
聞いて聞いて!
isort
って「I sort」(私が並べる)なんだよ! nikkieです。
Pythonのimport文をアルファベット順に並べ替えてくれるisort、https://t.co/cwwhd4aRy9
— nikkie にっきー (@ftnext) 2022年1月1日
i sort your imports, so you don't have to.
(私が並べ替えるから、あなたはしなくていいよ)
から来ているのか!
iはimportかなーと思ってたら"私"だった、ちょっとアイうたっぽい(AI/愛/I)
タイトルの通り、GitHub ActionsでPythonのコードにblack
やisort
をかけてコミットを作り、GitHubのリポジトリにプッシュする方法を調べました。
目次
- はじめに
- 目次
- 背景
- 結論:設定ファイルの例
- bashの || (OR) 演算子
- 整形しないときもコミットを作ってプッシュすると実装
- git remote set-urlは要らない?
- git configで設定する情報
- git configを1つのstepに切り出す
- blackやisortの引数の指定ーpyproject.toml、便利!
- 終わりに(次回予告?)
背景
GitHubのリポジトリで発生したイベントを契機に処理を実行できるGitHub Actions、便利ですよね。
私がよく設定するのはテストコードの実行なのですが、ふと「PEP 8に反したコードであれば、GitHub Actionsの処理の中で、しれっと直してしまえばいいのではないか」と思い付きました。
1人でPythonを書いている分にはPEP 8に反したコードをGitHubにプッシュすることはめったにありません1。
複数人の開発シーンになると、PEP 8に反したコードがリポジトリにプッシュされることも(私の経験上)起こりえます(我々愚かな人類の"気をつける"には限界がありますからね)。
このあたりは、保存時にblack
やisort
で整形するように全員のエディタを設定したり、pre-commit
を導入したりといった仕組み化で解決できると思いますが、今回はCI側でしれっと整形するというアイデアを実装してみました。
CIツールの中でも手軽だと感じているGitHub Actionsでまず試してみました。
やり方(コマンド)を展開することで、GitHub Actions以外のCIツールでも設定できると思います。
結論:設定ファイルの例
このActionの動きは次のようになります:
- リポジトリにプッシュされたコードが
isort
で整形する必要があれば、整形してコミットを作りプッシュする - また、
black
で整形する必要があれば、整形してコミットを作りプッシュする
「整形する必要があれば、整形してコミットを作りプッシュする」の実装
black
の場合を例にすると、実装は次のようになります。
※単体でも動くように取り出したので、結論のyamlと比べると余計な処理が入っています
black --check --diff . || black -v .
git config --global user.name $GITHUB_ACTOR
git config --global user.email $GITHUB_ACTOR@users.noreply.github.com
git commit -am "[style] Format Python code with black in GitHub Actions"
git push
順番に見ていきましょう。
bashの ||
(OR) 演算子
「blackで整形する必要がある場合は整形する」は||
演算子で表せます。
A、Bをコマンドとしたとき、A || B
は
- コマンド
A
の終了コードが0のとき、コマンドB
は実行されない(短絡評価) - コマンド
A
の終了コードが0以外のとき、コマンドB
を実行
という動作をします2。
「blackで整形する必要があるか」の確認は、--check
オプションで実装できます。
- 整形する必要なし:終了コードは0
- 整形する必要あり:終了コードは0以外
以上から、A || B
を以下のように書けばいいですね。
- コマンド
A
:black --check .
- コマンド
B
:black .
整形したらどうなるかを示すために--check
と一緒に--diff
オプションも指定しています。
整形しないときもコミットを作ってプッシュすると実装
「blackで整形する必要がある場合は、整形だけでなくコミットを作りプッシュまで行う」ように実装する案も浮かびます。
ですが、今回は整形する必要の有無によらず、コミットを作りプッシュするコマンドを実行します。
これは以下の理由によります:
- ワーキングツリーがclean(ファイルに差分がない)のときに
git commit
してもエラー終了しない - リモートリポジトリに先行するコミットがないためになにも
git push
できなくてもエラー終了しない
分岐はblack --check --diff . || black -v .
の部分だけと最小限にし、整形してもしていなくても常にコミットを作りプッシュするようにしました。
この実装には、以下のIssueのコメントを参考にしています。
https://github.com/actions/toolkit/issues/908#issuecomment-919609272
you can use
black --check .
as a condition
git remote set-url
は要らない?
GitHub Actionsでコミットを作り、プッシュする方法を調べると、git remote set-url
というコマンドも登場します。
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY
この secrets.GITHUB_TOKEN
に何を設定するかがよく分からず3調べていたところ、actions/checkout@v2
の効能により、このコマンドが不要になることが分かりました。
Version 2 of checkout resolves the detached HEAD state issue and simplifies pushing to origin.
git config
で設定する情報
コミットを作るのに必要なuserのnameとemailを設定します。
環境変数GITHUB_ACTOR
には、ワークフローを開始した人かアプリの名前4が入るそうです。
これを使うと、nikkieがコミットをプッシュしてワークフローを起動したとき、ワークフローが作るコミットもnikkieが作ったことになります。
また、email
の@users.noreply.github.com
の部分ですが、メールアドレスをプライベートにする仕組みでした。
GitHub上で有効にし、さらにローカルのリポジトリにも設定すると、メールアドレスを隠せるそうです5(今回初めて知りました!)
git config
を1つのstepに切り出す
isort
で整形するstepでもblack
で整形するstepでも、コミット前にgit config
で設定するのはDRYじゃないですよね。
これはGitHub Actionsの性質を使ってDRYにできました!(ここも「聞いて聞いて!」)
1つのジョブで実行中のすべてのステップは同じランナー上で実行されるので、ステップ間でファイルシステムを通じて情報をやりとりすることができます。(Software Design 2020年10月号 (Kindle の位置No.1934-1935))
git config
コマンドで一度だけuserのnameとemailを設定すれば、その情報はファイルシステムに書き込まれているので、後続のステップですぐにgit commit
できます。
black
やisort
の引数の指定ーpyproject.toml
、便利!
black
やisort
に渡す引数もありますよね。
black -l 79 .
(1行あたりの文字数を指定)isort --profile black .
(blackと競合しないように指定)
整形の有無のチェックでも整形時も引数を指定するのはDRYじゃないと感じます。
これをDRYにする方法としてpyproject.toml
に引数を書くという方法がありました!
[tool.black] line-length = 79 [tool.isort] profile = "black"
リポジトリルートのpyproject.toml
を見てblack
やisort
が動くので、YAMLファイルのコマンドの記載がスッキリしました。
終わりに(次回予告?)
色々調べながら「Pythonのコードを整える」Actionをゼロから組み上げました!
ですが、GitHub Actionsでコードを整えるニーズって色んな人が感じていそうですよね。
実際、Actionを公開している方もいます。
psf/black@stable
6isort/isort-action
こういったActionを組合せても実現できたのですが、1箇所どうしても使いづらい点が解消できず、ゼロから組み上げることにしました。
試行錯誤の過程はまたの機会にアウトプットします。
-
エディタの保存をトリガーに
black
やisort
で整形するように設定しています。VSCodeの場合:https://code.visualstudio.com/docs/python/editing#_formatting↩ - シェルについては Shell Tools and Scripting · Missing Semester が最近のオススメです。↩
- 自動トークン認証 - GitHub Docsによると設定は不要のようです(どうりで情報が出てこなかったわけだ)↩
- 「The name of the person or app that initiated the workflow.」ref: https://docs.github.com/ja/actions/learn-github-actions/environment-variables#default-environment-variables↩
- コミットメールアドレスを設定する - GitHub Docs↩
- Blackのドキュメントで案内されています。GitHub Actions integration - Black 23.3.0 documentation↩