🛹

ランナー (runs-on) を動的に指定する方法 [GitHub Actions]

2022/06/05に公開

概要

ランナー (runs-on) を動的に指定する方法をいくつか紹介します。
GitHub ホストランナーの障害時はセルフホストランナーで走らせたり、CI の優先度を調整したりできます。

方法1: workflow_dispatch.inputs で指定

workflow_dispatchinputs でランナーを指定します。

runs-on: ${{ github.event.inputs.runner }}
# or
runs-on: ${{ fromJSON(github.event.inputs.runner) }}

実例

GitHub ホストランナーは頻繁に障害が起きます。
障害対策の 1 つとして障害時だけセルフホストランナーを建てて使うことができます。

.github/workflows/deploy.yml
name: Deploy

on:
  workflow_dispatch:
    inputs:
      runner:
        description: Select runner
        required: true
        default: ubuntu-20.04
        type: choice
        options:
          - ubuntu-20.04
          - self-hosted

jobs:
  deploy:
    runs-on: ${{ github.event.inputs.runner }}
    timeout-minutes: 5

    steps:
      - run: cat $GITHUB_EVENT_PATH

ラベルを複数指定することもできます。
options は文字列しか受け付けないのと fromJSON() 関数を使用するので完全な JSON を指定する必要があります。

.github/workflows/deploy.yml
name: Deploy

on:
  workflow_dispatch:
    inputs:
      runner:
        description: Select runner
        required: true
        default: '"ubuntu-20.04"'
        type: choice
        options:
          - '"ubuntu-20.04"'
          - '[ "ubuntu-22.04" ]'
          - '[ "self-hosted", "Linux" ]'

jobs:
  deploy:
    runs-on: ${{ fromJSON(github.event.inputs.runner) }}
    timeout-minutes: 5

    steps:
      - run: cat $GITHUB_EVENT_PATH

方法2: イベントのペイロードで切り替え

イベントのペイロードやコンテキストの内容によって切り替えます。

runs-on: ${{ github.event.sender.login == 'my-bot' && 'low-priority' || 'self-hosted' }}

実例

セルフホストランナーで優先度の低い CI は指定のランナーで走らせます。
固定台数で運用している場合に BOT がトリガーした CI でランナーが埋まるのを防ぎたいなど。
追加のラベルなしのランナーと low-priority ラベルの付いたランナーがあるものとします。

.github/workflows/test.yml
name: Test

on:
  pull_request:

jobs:
  test:
    runs-on: >-
      ${{
        github.event.sender.type == 'Bot'
          && fromJSON('[ "self-hosted", "low-priority" ]')
          || fromJSON('[ "self-hosted" ]')
      }}
    timeout-minutes: 5

    steps:
      - run: cat $GITHUB_EVENT_PATH

改行 (>-) についてはこちらにまとめてあります。
YAML の仕様なので if 以外でも使えます。

https://zenn.dev/snowcait/articles/9bf729f0f823df

方法3: ジョブを分ける

Ubuntu と Windows で動かす場合など steps の内容が異なる場合はジョブから分けてしまった方が良いです。

.github/workflows/os.yml
jobs:
  ubuntu:
    if: contains(github.event.pull_request.labels.*.name, 'Windows') == false
    runs-on: ubuntu-20.04
    timeout-minutes: 5

    steps:
      - run: cat $GITHUB_EVENT_PATH

  windows:
    if: contains(github.event.pull_request.labels.*.name, 'Windows') == true
    runs-on: windows-2022
    timeout-minutes: 5

    steps:
      - run: cat $Env:GITHUB_EVENT_PATH

ジョブを分けた上で Reusable workflowカスタムアクション を使ってジョブの中身を共通化することもできます。

余談

runs-on を動的に指定するのは見かけたことがなかったのでこの記事を書いていたのですが、情報の確認のためにドキュメントを眺めていたら最近(今週?)追加されたサンプルページに似たようなコードが記載されていました😹
ドキュメントが充実していっているのはいいことですね。

https://docs.github.com/ja/actions/examples/using-scripts-to-test-your-code-on-a-runner

JSON を配列にしつつ bool を暗黙的に int へキャストして配列のインデックスとして扱っているのでしょうか。
緩い比較を活用しているのだと思いますが分かりにくいのでおすすめはしません。

runs-on: ${{ fromJSON('["ubuntu-latest", "self-hosted"]')[github.repository == 'github/docs-internal'] }}

こちらの方が可読性が高いです。

runs-on: ${{ github.repository == 'github/docs-internal' && 'self-hosted' || 'ubuntu-latest' }}

Discussion