GithubActionsでrunにifを書かない方法で、ステップごとに実行判断をするロジックの書き方・使い方(実例あり)
お詫び
Qiitaの元記事にて、区切り線を「---」で書いている場所があり、これがZennの記法に干渉して一部うまく表示できない記事がある事を認識しています。
全ての記事を精査しきれていないため、お手数ですがお見かけの際は教えていただけると大変喜びます。
公式リファレンス
Github Docs
本稿ではstepsコンテキストを使っています
実行結果によって処理をする・しないを分岐する
先に結論を言うと、runでif文を書けるので、shellを書くのと同じ感覚で書けばやりたい事は実現できます。
ロギングがしたいならechoでログを出すか、ログファイルも更新してpushすればGithubActionsを探しに行かなくてよくなります。
が、ここではGithubActionsの流儀に則って制御を考えます。
また、runで実行すると、途中でエラーになった時にどこで失敗したのか特定するのが難しくなるので、運用を考えるならなるべく切り分けたいからです。
やり方は、if: 条件
をいれるだけです。
問題は、条件に何を書くかです。
条件分岐で使えるもの
色々なものが使えますが、ここでやりたいのは「前回実行したステップの結果を受け取りたい」というものです。
たとえば、前回実行した結果が全て完了したとか、特定の処理をしたかどうか。
解決アプローチとして、3つのパターンがあります。
- そもそもstepをまとめてしまう
- 前stepの結果を判定するロジックをrunで書く
- 【今回採用】stepを実行する前にif条件を入れる
大事な事なのでもう一度繰り返します。
うまく行かない場合は、今回採用する方法にこだわる必要はありません。
実務で採用する場合にまで、後でできる事を今こだわるとハマって進捗率を下げてしまう、なんて事も考えられます。
状況に合わせて活用してください。
ソース例と解説
今回の制御で見る公式ドキュメントの位置
Thanks:
if: steps.(ステップのid).outcome == "success"
が最もわかりやすいです。
以下のような使い方ができます。
- name: 確実に成功する処理
id: test1
continue-on-error: true # 処理が失敗しても継続させる
run: echo "成功"
- name: 実行される事を確認
if: steps.test1.outcome == "success"
run: echo "処理される"
- name: 確実に失敗する処理
id: test2
continue-on-error: true # 処理が失敗しても継続させる
run: exit 1
- name: 実行される事を確認
if: steps.test2.outcome == "success"
run: echo "スキップ"
- name: 確実に失敗する処理
id: test2
continue-on-error: true # 処理が失敗しても継続させる
run: exit 1
- name: 実行される事を確認
if: steps.test2.outcome == "success"
run: echo "スキップ"
適当なリポジトリを作って、.github/workflows
以下に置いて検証してみてください。
実践例
この方法は、Ubuntu20.04にデフォルトで入っているpythonのバージョンが古くて動かないスクリプトがある場合、処理をしないための例です。
参考
Ubuntu22.04 LTSではどうなるか気になるところです。
ソースコード
runs-on: ubuntu-latest
steps:
# 以下、ステップ
- name: PosixPath.with_stemが使えないので、スクリプトを実行できるか判断する
id: check_python
continue-on-error: true
run: |
# python3のマイナーバージョンが9未満か判定
if [ "$(python3 --version | cut -d"." -f2)" -lt "${minor}" ]
then
exit 1
fi
- name: スクリプトを実行
if: steps.check_python.outcome == 'success'
run: python3 (実行スクリプト)
check_pythonでexit 1
を実行しなければスクリプトを実行します。
応用例
この方法は、Ubuntu20.04にデフォルトで入っているpythonのバージョンが古くて動かないスクリプトを動かすための例です。
もっといい方法があるので、以下はあくまで実践例です。
今回のGAとは関係ないですが、WSLに入れる時に参考になりました。
ソースコード
# 実行環境
runs-on: ubuntu-latest
steps:
# 以下、ステップ
- name: 判定ロジックは同じ。上記では`exit 1`で終わらせたが、こちらはインストールする
id: check_python
env:
minor: 9
run: |
pyc=$(python3)
# python3のマイナーバージョンが9未満の時だけ実施する
if [ "$(python3 --version | cut -d"." -f2)" -lt "${minor}" ]
then
sudo apt update && sudo apt install software-properties-common
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt install python3.${minor}
pyc=python3.${minor}
sudo apt update && sudo apt install python3.${minor}-distutils
fi
echo "::set-output name=pyc::${pyc}"
- name: pythonスクリプト実行
env:
# id: check_pythonの出力からpycを取得してenvに入れる
pyc: ${{ steps.check_python.outputs.pyc }}
run: |
${pyc} -m pip install --upgrade pip
${pyc} -m pip install pathlib
${pyc} (実行スクリプト)
pyc
にはpython3
かpython3.9
か、どちらかが入っています。
重要なのは、実行するステップではpyc
に何が入っているか気にしなくても良い事です。
より厳密なチェックをするなら、which "${pyc}"
のパスが有効である事を検証するのも良いかもしれません。
良い方法
環境構築系は自分で頑張るより、uses
を探した方が楽です。
pythonのバージョンを指定する方法はあります。
steps:
- uses: actions/setup-python@v3
with:
python-version: '3.9'
- name: pythonスクリプト実行
run: |
python -m pip install --upgrade pip
python -m pip install pathlib
python (実行スクリプト)
あれだけ長いコードが、こんなに圧縮できるのでuses
は非常に優秀です。
uses
で使えるものは以下の通り
今回使ったもの
使えるもの
自分でuses
を作れます
uses
を見ると、Githubユーザー/リポジトリ
の関係であることに気付きます。
これは具体例を挙げるのが難しい(私が具体例を知らないため)ですが、オリジナルの環境構築ができるのは便利です。
メンテナンスを考えると決して楽な選択ではありませんが、どうしてもという場合には使えそうです。
使用例
私がやりたかったこと
pushされるたびにCHANGELOG.mdを自動で生成してPRを出すスクリプトを書きたかったんですが、
- github-changelogs(node)
- github-changelog-generator(ruby)
を使って自動生成する仕組みを作ろうとしていました。
わざわざ自前で環境構築をやってコマンド打ち込んで…とやると大変なので、CIでなんとかならないかと思っていたところでした。
いきなり難しい事をやると手がつかないので、手始めに上記のように簡単なCIを色々作ってみてGithubActionsのナレッジも蓄積してきたので、そろそろ挑戦しようと思っていました。
実務ではなく趣味でやっているCIだと、CIの勉強だけだと運用経験が積めず、なかなか学びが進まないので非常に時間がかかりましたが、ようやくここまで来れました。
本稿の内容を踏まえて、プログラマーを目指す方へ
ぶっちゃけ本題です。
冗長になりすぎたので、別記事にします。
Discussion