timeout-minutes の指定忘れを指摘する GitHub Actions を書いた

GitHub Actions の timeout-minutes を明示的に指定しなかったことで minute quota 溶かしをやらかしたので作りました。

背景

パブリックなリポジトリで GitHub Actions を使っていると出くわすことはないかと思われますが、プライベートなリポジトリで GitHub Actions を書いていると timeout-minutes を指定し損ねたジョブが延々と走り続けて minute quota (Team plan だと 3,000 分/月) を浪費してしまう 😭 みたいなとても悲しいできごとが発生することがまれにあります。

それというのも、timeout-minutes を指定しなかった場合のデフォルトのタイムアウトが 360 分 (すなわち 6 時間) という長大な値となっているがために、下手こいて終わらないジョブを 9 回走らせてしまうだけでこの quota を綺麗サッパリ食べ尽くしてしまうという落とし穴1が GitHub Actions には存在するからですね2

対策

こんな凄惨で悲しいできごとを二度と起こしてはいけない…! というわけで何某かの再発防止をしたいわけですが、生憎リポジトリや組織の単位で GitHub Actions ジョブのデフォルトタイムアウトを設定することが現時点ではできない模様です。したがって個別のジョブに timeout-minutes を設定していく以外の対策が存在せず、その対策も人間が作業するとなるとどうしても「設定漏れ」のリスクが生じてしまいます。

そういうわけで、今回は (minute quota の浪費を防止するために minute quota を 1 分/回消費するという若干本末転倒感が否めないのですが…) timeout-minutes が指定されていないジョブが存在したら fail させる GitHub Actions を作ってみたのでした。

使い方

例えば以下のようなワークフローを用意して、GitHub Actions を利用している各リポジトリの .github/workflows ディレクトリに配置するだけです。Slack 通知が不要であれば - name: Slack notification 以降の行を削って構いません。

name: Enforce timeout-minutes

on: push

jobs:
  enforce-timeout-minutes:
    runs-on: ubuntu-latest
    timeout-minutes: 2

    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Enforce timeout-minutes
        id: enforce-timeout-minutes
        uses: komiya-atsushi/action-enforce-timeout-minutes@v1.0.0

      - name: Slack notification
        uses: rtCamp/action-slack-notify@v2
        env:
          SLACK_MESSAGE: ${{ steps.enforce-timeout-minutes.outputs.message }}
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
        if: ${{ failure() }}

余談: TypeScript での GitHub Actions 開発

GitHub Actions の開発は様々な手段があるのですが、今回は TypeScript を利用しました。

JavaScript であれば公式ドキュメントとして Creating a JavaScript action が存在するのでそれを少し参考にしつつ、TypeScript 固有の事情については typescript-action という公式 (?) のテンプレート的なリポジトリを大いに参考にしました。


  1. これはどう考えても、なんとかしてアップセル的な課金をさせようとする GitHub によって巧妙に仕組まれた罠だと思うのですがどうなんでしょうか? 

  2. というか、GitHub Teams の課金はユーザー数によって増加するのになんで各種 quota はユーザー数に比例して増加、とはならないんでしょうかね…?