rcmdnk's blog
Last update

Input/Output: Change Your Environment, Change Your Life

workflow_dispatchはGitHub Actionsを手動で起動するための機能ですが、ここでは inputsパラメーターを指定して実行時に指定できるようになっています。

そこではデフォルトの値を指定できますが、それらの値をpushなどのイベント時にも使いたい、という話。

workflow_dispatch

GitHub Actionsではpushpull_requestのレポジトリの変更時やschedule.cronといった定期的な時刻を トリガーとしてジョブを実行できます。

workflow_dispatchもトリガーの1つで、これをonの中で指定するとWorkflowのページにRun workflowボタンが設置され 手動で実行できるようになります。

手動で実行するだけなら最後のpushイベントの時に実行されたworkflowを再実行すれば大概の事は 事足りると思いますが、workflow_dispatchでは入力パラメーターを指定して実行できる点が特有の機能になります。

.github/workflows/dispatch.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
---
name: dispatch workflow

on:
  workflow_dispatch:
    inputs:
      var1:
        type: string
        description: 'string variable'
        required: false
        default: 'v1'
      var2:
        type: boolean
        description: 'boolean variable'
        required: false
        default: false

jobs:
  test:
  ...
  steps:
    - name: ...
       run: echo ${{ inputs.var1 }}, ${{ inputs.var2 }}
...

みたいな感じのworkflowを用意すればRun workflowボタンの中にvar1に対して入力欄や var2に対してチェックボックスが出来て値を変更できるようになります。

それらの値は各ステップの中で条件判定に使ったり直接値を使ったりすることが出来るようになります。

inputsの値をpushイベントなどで使う

workflowで

1
2
3
4
5
6
7
on:
  push:
  pull_request:
  workflow_dispatch:
    inputs:
      var1:
...

みたいな感じでonのトリガーとしてpushpull_requestなど他のトリガーと一緒に定義することは可能で ジョブを共有できます。

ただし、inputsに関する値はworkflow_dispatchでトリガーがかかった場合しか使えず、 その他の場合は参照しても値が入っていません。 defaultで指定したものも入っていません。

なのでworkflowの中で必ずこれらの値を使いたいと言う場合には、別途初期値を設定しておく必要があります。

簡単には環境変数として入れてしまう方法があります。

.github/workflows/test.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
---
name: push/pull_reaust/dispatch

on:
  push:
  pull_request:
  workflow_dispatch:
    inputs:
      var1:
        type: string
        description: 'string variable'
        required: false
        default: 'v1'
      var2:
        type: boolean
        description: 'boolean variable'
        required: false
        default: false

env:
  var1: ${{ inputs.var1 || 'string variable' }}
  var2: ${{ inputs.var1 || false }}

jobs:
  var_test:
  runs-on: ubuntu-latest
  steps:
    - name: inputs check
      run: echo ${{ inputs.var1 }}, ${{ inputs.var2 }}
    - name: env check
       run: echo ${{ env.var1 }}, ${{ env.var2 }}
    - name: inputs boolean check
      if: $ {{ inputs.var2 }}
      run: echo inputs.var2 is true
    - name: env boolean check
      if: $ {{ env.var2 }}
      run: echo env.var2 is true

こんな感じでglobalなenvとしてinputsを使った変数を定義します。 inputs側のdefaultの値を知る術が無いので上のように二箇所で同じ初期値を設定する感じになって ちょっと面倒な感じにはなってしまいます。

逆にenvを最初に定義してinputsに入れられれば最初に定義するだけで済みそうなものですが、

1
2
3
4
5
6
7
8
9
10
11
12
13
env:
  var1: 'string variable'

on:
  push:
  pull_request:
  workflow_dispatch:
    inputs:
      var1:
        type: string
        description: 'string variable'
        required: false
        default: ${{ env.var1 }}

みたいにすると Unrecognized named-value: 'env'. Located at...といったworkflowの構文エラーが出て実行されません。

というわけで2度書きせざるを得ない状態です。

上の書き方で、 GitHub Actionsの中のコンテクストでは、||がorですが、 以下のようにするとinputs.var1が空のとき、否定判定になって後ろのstring variableが 入ります。

1
var1: ${{ inputs.var1 || 'string variable' }}

ここで文字列を使う場合はダブルクォートは使えずに必ずシングルクォートで囲う必要があります。

式 - GitHub Docs

また、booleanとしてtruefalseが使えますが、 これらはクォート無しで書けるものの、env自体が文字列としての扱いにしかならないので 上の場合、env boolean checkは必ず通ってしまいます。 (truefalseという文字列として空文字ではない、として判定されるため。)

if文などにenvで直接渡したい場合にはifの方で

1
2
3
    - name: env boolean check
      if: $ {{ env.var2 == 'true' }}
      run: echo env.var2 is true

のように文字列として'true'と比較する必要があります。

trigger workflowを別に作ってdispatchする

workflow_dispatchを設定しておくとWebインターフェースから手動で行う以外にも API経由で実行することもできます。

なので他のレポジトリのworkflowを動かすことも出来たりします。

これを使って同じレポジトリの中でworkflow_dispatch用のworkflowと他のpushとか用のものを作って、 push用の方からworkflow_dispatch用のworkflowを実行してやるということが考えられます。

追記: 2023/02/27

ただし、dispatchするにはブランチかタグの指定しかできず、pull requestで作られる最終的なmergeの状態は 渡すことが出来ないためtest.yml側でon.pull_requestを指定することは出来ません 1

なのでpull requestもチェックしたい場合はやはり上のように同じファイル内で変数を再定義する必要があります。

追記ここまで

とりあえず上のworkflowで、workflow_dispatch以外のトリガー部分は削除します。 また、envを消してinputsを直接使うようにしておきます。

.github/workflows/dispatch.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
---
name: dispatch

on:
  workflow_dispatch:
    inputs:
      var1:
        type: string
        description: 'string variable'
        required: false
        default: 'v1'
      var2:
        type: boolean
        description: 'boolean variable'
        required: false
        default: false

jobs:
  var_test:
  runs-on: ubuntu-latest
  steps:
    - name: inputs check
      run: echo ${{ inputs.var1 }}, ${{ inputs.var2 }}
    - name: inputs boolean check
      if: $ {{ inputs.var2 }}
      run: echo inputs.var2 is true

これを.github/workflows/dispatch.ymlとして導入したとします。

別の.github/workflows/test.ymlとかのファイルを以下のような内容で作ります。

.github/workflows/test.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
---
name: test

on:
  push:

jobs:
  test:
    runs-on: ubuntu-latest
    permissions:
      actions: write
    steps:
      - uses: convictional/[email protected]
        continue-on-error: true
        id: dispatch
        with:
          owner: ${{ github.repository_owner }}
          repo: ${{ github.event.repository.name }}
          ref: ${{ github.ref }}
          github_token: ${{ github.token }}
          workflow_file_name: dispatch.yml
      - name: Write workflow url
        run: |
          echo Main job URL: ${{ steps.dispatch.outputs.workflow_url }} >> $GITHUB_STEP_SUMMARY
      - name: Check status
        run: |
          if [ "${{ steps.dispatch.outputs.conclusion }}" != "success" ];then
            exit 1
          fi

まず、APIでdispatchするためにトークンが必要ですが、 同じレポジトリであればAction実行時に用意される$($GITHUB_TOKEN)が使えます。 permissionsとしてactions: writeを加えておくことで他のworkflowを実行する権限が付けられます。

直接APIのURLを叩いても良いですが、それようのactionがMarketplaceにあるのでそれを使います。

Trigger Workflow and Wait · Actions · GitHub Marketplace

上のwithで渡される引数のところで、workflow_file_nameworkflow_dispatchが書かれたworkflowのファイル名を指定します。

これでpush時にこのtest.ymlが実行され、その中でdispatch.ymlも実行されます。

上のように書くと、dispatch.ymlが終わるとそのstepも終わって次に行くようになっています。

testの方のページからもdispatchの方のジョブページに簡単に飛べるようにGITHUB_STEP_SUMMARYに書き出してジョブの概要欄に見えるようにもしておきます。

この情報はログの中にもありますが、いちいち開かずともすぐに見に行けるように。

最後はdispatchの方のジョブが失敗したらtestの方も失敗のステータスになるようにチェックを入れます。

trigger-workflow-and-waitではpropagate_failureという引数があってこれをtrueにすれば dispatchが失敗したらstep自体も失敗になるようにもできます。

上の例ではジョブ概要にリンクを書き出したいので最初のstepでは失敗しても継続して最後で再度チェックするようなことをしています。

この辺が冗長だと思えば

.github/workflows/test.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
---
name: test

on:
  push:

jobs:
  test:
    runs-on: ubuntu-latest
    permissions:
      actions: write
    steps:
      - uses: convictional/[email protected]
        continue-on-error: true
        id: dispatch
        with:
          owner: ${{ github.repository_owner }}
          repo: ${{ github.event.repository.name }}
          ref: ${{ github.ref }}
          github_token: ${{ github.token }}
          workflow_file_name: dispatch.yml
          propagate_failure : true

とするだけでも良いかと。

まとめ

pushなどをworkflow_dispatchと同じファイルに書く場合に比べ、 別のトリガー用ファイルを用意した場合:

  • Pros
    • デフォルト値を各部分はinputsのところだけで統一できる。
    • envの設定をそっくり落とせる
    • inputsの値を直接使えるので、booleanの値をifなどでそのままbooleanとして評価できる。
  • Cons
    • ジョブが2つ起動される。
      • 特にプライベートレポジトリだと実行時間が最大倍になる(dispatch.yml側もシングルタスクの場合)ので余計なコストがかかる可能性がある。
    • ジョブの詳細ログがtestの方では見れずdispatchの方にいって確認する必要がある。

といった感じ。

シンプルなのはenvを使った最初の方かな、と思いますし、 特にプライベートレポジトリで使う場合には必ずそっちを使ったほうが良いです。

一方で別ファイルを用意すると、デフォルト値を一箇所に集約できるので、 もし大量のinputsの値を扱うような場合にはこっちの方が良いかとは思います。

もしかしたらもっと上手い方法があるかもしれませんが、もしあれば教えてもらえるとうれしいです。

GitHub Actionsは今でも結構頻繁にアップデートがありますし、一方でドキュメントの更新は結構滞ってる感もあって 隠し機能的になってる部分もあるので、なにかできるかもしれませんし、 そのうちしれっとアップデートしてもっと簡単に出来る方法が追加されるかもしれません。

Sponsored Links
  1. convictional/trigger-workflow-and-wait のREADMEとかを見るとrefにThe reference of the workflow run. The reference can be a branch, tag, or a commit SHA. が指定できるように書いてありますが、 GitHubのCreate a workflow dispatch event の方には The git reference for the workflow. The reference can be a branch or tag name.となっていて、 実際にPRのSHA($)を指定すると

    No ref found for: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    

    みたいなエラーが出ます。

Sponsored Links

« GitHub Actionsで行ったtestのcoverageの結果をジョブの概要として出力する Pythonレポジトリ用のpre-commit環境を整える »

}