🐧

GitHub Actions の timeout-minutes の linter 及び一括設定ツール

2024/06/29に公開

GitHub Actions の timeout-minutes に関する lint rule 及び一括で timeout-minutes を設定するツールを作ったので紹介します。

https://github.com/suzuki-shunsuke/ghatm

timeout-minutes とは

https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idtimeout-minutes

timeout-minutes は GitHub Actions の job 及び step (workflow は対応していないはず) の設定項目の一つで、 job 及び step のタイムアウトです。
timeout-minutes で設定した時間以内に job 及び step が完了しない場合に強制的に終了し失敗扱いになります。
デフォルトは 360 分です。

ただし、 reusable workflow を使っている job は timeout-minutes をサポートしていません。

なぜ timeout-minutes を設定すべきなのか

https://exercism.org/docs/building/github/gha-best-practices#h-set-timeouts-for-workflows

デフォルトの 6 時間はかなり長く、ほとんどの job はそこまで長い時間はかかりません。
何らかの理由で処理がハングすると強制終了するまで延々と job は実行され続け、無駄にリソースを消費します。
タイムアウトを適切に設定することで早期に問題に気づき、ワークフローをリトライするなどして問題を解決できます。

適切な timeout の値はなにか

適切な timeout は job や step 毎に異なりますが、殆どの場合 30 分もあれば十分でしょう。
もちろん 30 分で終わらないようなものは 60 分とか適切に伸ばす必要がありますし、直ぐ終わるような処理であれば 10 ~ 20 分とかより短く設定してもよいでしょう。
また step 毎に設定することも出来ますが、個人的には job 単位で十分だと思います。
あまり細かく設定しても消耗してしまうのである程度余裕をもって設定するのが良いでしょう。

timeout-minutes が設定されているかチェックする lint rule

自分は ghalint という GitHub Actions の linter を開発していますが、 v0.2.12 から job の timeout-minutes が必須になりました。

https://github.com/suzuki-shunsuke/ghalint
https://github.com/suzuki-shunsuke/ghalint/blob/main/docs/policies/012.md

現状除外設定はありません。 timeout-minutes を設定しないほうが良いようなケースが特に思い浮かばないからです。

また ghalint は 自分が開発する lintnet という汎用の linter にも module として移植されており、そちらの module にも同様の lint rule が追加されています。

https://github.com/lintnet/lintnet

https://github.com/lintnet-modules/ghalint/tree/main/workflow/job_timeout_minutes_is_required

これらを CI で実行することで timeout-minutes の設定を強制できます。

timeout-minutes を一括で設定

timeout-minutes が設定されていない多くの workflow がある場合、それらを全部手で修正するのは大変です。
そこで timeout-minutes が設定されていない job に自動で設定するツールを開発しました。

https://github.com/suzuki-shunsuke/ghatm

設定ファイルや環境変数などを必要としない非常にシンプルなツールです。
使い方は簡単で、 workflow があるリポジトリのルートディレクトリで ghatm set というコマンドを実行するだけです。

ghatm set

そうすると timeout-minutes が設定されていない job に自動で timeout-minutes: 30 という設定が追加されます。
30 という値はコマンドラインオプションで変更できます。

ghatm set -t 60

既に timeout-minutes が設定されている job には何の影響もありません。
あくまで一括で同じ値を設定するだけなので、特定の job だけ別の値を設定するといったことには対応していません。
それをしたい場合はその job だけ手で修正すればよいでしょう。

このツールは YAML のコメントやインデント、空行、アンカーなどを維持しつつ、 timeout-minutes を設定する行を追加します。
YAML にパッチを当てるツールやライブラリの中にはこれらを維持しないものもありますが、 ghatm ではそのへんを維持できるように工夫しています。

pull_request イベントなどをフックして ghatm を CI で実行し、自動で feature branch に変更を push することで timeout-minutes の設定を自動化することも出来ます。
ただし、自動で設定された timeout-minutes の値が適切とも限らないので場合によっては修正が必要でしょう。

multi-gitter と ghatm で全リポジトリをまとめて修正

ghatm を使うと簡単に timeout-minutes を設定できますが、多くのリポジトリに対し手作業で ghatm を実行し pull request を作成するのは大変です。
そこで multi-gitter というツールを使うと特定の GitHub Organization ないし User の全リポジトリ(対象を絞ることも可能)に対しまとめて pull request を作成したり、作成した pull request をまとめてマージしたり出来ます。

https://github.com/lindell/multi-gitter

自分が実際に multi-gitter を使って大量のリポジトリに timeout-minutes を設定したログはこちらです。

https://github.com/suzuki-shunsuke/batch-bulk-ghatm

https://github.com/suzuki-shunsuke/batch-bulk-ghatm/issues/1

multi-gitter も ghatm も aqua でインストールできます。

今回は multi-gitter の run, merge コマンドを使いました。

  • run: PR の作成
  • merge: PR の merge

幾つかポイントを書いておきます。

multi-gitter は今回はじめて使いましたが、便利ですね。
例えば pinact を実行して GitHub Actions のバージョンを固定するのもまとめて出来そうです。

https://github.com/suzuki-shunsuke/pinact

Discussion