Github Actionsのpathsで、特定ディレクトリ下で変更があったときだけ何かする
2021-10-17
- プログラミング
あるとき、1つのディレクトリの中に、互いに独立したプロジェクトのコードを入れたくなった(monorepoというらしい?)。
しかしこれだとCI/CDツールを動かしたときに、あるプロジェクトのテストをしたいのに、別のプロジェクトまでテストする羽目になりそうだった。なぜならCI/CDツールはリポジトリの変更があったことを検知して動くが、どのディレクトリの変更なのかは検知しないのが普通だからだ。
ディレクトリごとに変更検知・テスト実行をしてくれないと、むだな処理が増えまくるので切実に困る…。
そう思って調べたところ、Github Actionsだと求める動きをするようなので、試してみることにした。
やりたいこと
リポジトリ内に、まったく無関係な3種類のプロジェクトのコード(python
, rust
, scala
)があり、ディレクトリに分かれている。
このとき、変更があったディレクトリだけでテストを走らせたい。
具体的にいうと、 python
ディレクトリ内で変更があったときにGithubにpushするとPythonのテストだけを行い、 rust
と scala
では何もしないでほしい。
また、 python
と rust
両方で変更があったときはPythonとRustのテストを行い、Scalaのテストはしないでほしい。
.
├── LICENSE
├── README.md
├── python
│ │
│ ...
├── rust
│ │
│ ...
└── scala
│
...
できたもの
結論としては、やりたいことはすべてできた。
リポジトリ
github-actions-sample-with-paths
ymlファイル
Github Actionsの設定ファイルはディレクトリごとに、合計3種類作った。どれも似たりよったりなのでpythonのものだけ貼る。
name: Test in python directory
on:
push:
paths:
- 'python/**'
defaults:
run:
working-directory: python
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup
uses: actions/setup-python@v2
with:
python-version: '3.8'
- name: Install tools
run: |
python -m pip install --upgrade pip
pip install pytest
- name: Run pytest
run: |
pytest
要点は以下の2つ。
paths
on:
push:
paths:
- 'python/**'
on.<push|pull_request>.paths
で python/**
を指定している。これにより、pythonディレクトリ下で変更があったときだけ発火する。 python/*
だと深い階層にはマッチせず意図と違う動きになるので注意。
working-directory
defaults:
run:
working-directory: python
実行するときのワーキングディレクトリを working-directory
で設定している。これは jobs.<job_id>.steps[*].run
でその都度指定してもいいが、面倒なので defaults.run
を使う。こうするとこのワークフローの run
実行時にデフォルトでこのディレクトリをワーキングディレクトリに設定する。
なぜこれを設定するかというと ワーキングディレクトリにその言語などの設定ファイルがないとテストが動かない場合がある ため。Pythonだとリポジトリルートで pytest
とやっても動くようだが(それも状況によるかも)、Rust(cargo test
)やScala(sbt test
)だとルートでやっても正常に動かない。これを防ぐために、リポジトリルートでなくプロジェクトルートをワーキングディレクトリにしている。
困ったこととか
working-directoryを設定できないactionがある
jobs.<job_id>.steps[*].uses
を使うと、自分でrunを書かなくても、誰かが公開したactionを使える。だがactionによっては、今回のユースケースに合わず、使えないものがある。
Rustのactionにactions-rs/cargoというものがある。自分も以前はこれを使ってテストなどを行っていた。今回もこれを使おうとしたのだが、このactionでワーキングディレクトリを設定することができない。どうやら working-directory
は run
のときにしか適用されず、actionを使うときは適用されないらしい。おそらくactionにワーキングディレクトリを設定する機能が実装されていなければ無理なのだろう。Rustではワーキングディレクトリを設定しないとCargo.tomlが見つからないというエラーが出てしまうため、今回はこのactionは使えない。
結局Rustではこのactionは使わず、 run
でコマンドを書いた。
このケースはactionを使わなくても簡単にできるので問題なかったが、もっと複雑なactionの場合はクリティカルな問題になるかもしれない。
なお上記の問題は以下にやりとりがある。解決するPRはできているがマージされていないっぽい。(この中で --manifest-path
を使えば一応解決できるということも書いてある。だがこれはワーキングディレクトリを設定するのとは効果が違う。)
- Support changing working directory · Issue #6 · actions-rs/cargo
- Add working-directory by MarcoPolo · Pull Request #59 · actions-rs/cargo
いちいちGithubにpushしないとGithub Actionsの設定ファイルを検証できない?
何度もコミットしてpushしてGithub Actionsを動かすのがとてつもなく不毛だと思った。
これを解決するにはactというツールを使えばいい。ローカルでGithub Actionsを実行できる。使い方はREADMEのgif見ればわかる。 act
でローカル実行、 act -n
でdry-runなど。commitしなくても、現在のコードの状況で実行してくれるので楽。
今回のように .github/workflow/
に複数ファイルがある場合、すべてのワークフローが並行して走るので、ログが入り乱れて見にくくなる。これを防ぐにはワークフローファイルを指定して実行する。下のような感じ。
act -W .github/workflows/python.yml
おわりに
やりたいことがちゃんとできたので一安心。
上に書いた actions-rs/cargo
の例もあるので、実際にこのような方法を採用するときは使いたいactionで検証してからの方がよさそう。あと act
便利。
参考
きっかけ
これらの記事を見てGithub Actionsのpathsを知った。
- Github Actions 8ヶ月使ってみてわかったことまとめ - Qiita
- GitHub Action で特定のディレクトリ配下に変更があったときのみワークフローを実行する | 35D BLOG
Github Actionsのpathsなどのドキュメント
Python
PythonでのGithub Actions記述方法はこれ見ればわかる。
Scala
ScalaでのGithub Actions記述方法はこれを見ればわかる。
今回はcacheを使う設定でymlを書いたが、これだとactによるローカル実行はできないっぽい(設定すればできるのかもしれないが…)。