👌

GitHub Actions - Workflowを連鎖させてみる

2022/04/29に公開

GitHub Actionsで、Workflow内から他のWorkflowを呼び出したり、Workflowの開始・終了をEventとすることができるようになった。
なので、具体的にどのようにWorkflowを連鎖させられるのか整理してみようと思う。

on:workflow_call

Workflowのトリガー対象とするEventに、workflow_callを指定すると、他のWorkflowから呼び出すことを許可できる。
同一リポジトリ内であれば、次のように呼び出せる。

.github/workflows/call.yml
name: call
on:
  workflow_call:
jobs:
  myjob:
    runs-on: ubuntu-20.04
    steps:
      - run: echo "HELLO CALL"
.github/workflows/main.yml
name: main
on:
  push:
jobs:
  myjob:
    runs-on: ubuntu-20.04
    steps:
      - run: echo "HELLO MAIN"
  call-workflow:
    needs: [myjob]
    uses: ./.github/workflows/call.yml

実行されたWorkflowのSummaryを見ると、次のようになっている。

このとき、呼び出されたWorkflow(call.yml)は、呼び出したWorkflow(main.yml)の一部であるかのような扱いになっている。
たとえば、呼び出された側のgithub contextは、呼び出した側の値と同じになっていたり、secretsを渡さないと使えなかったりする。

なので、呼び出された時を考えずに、github contextの値を参照したりすると、意図しない動作になってしまう場合がある。
たとえば、両方のWorkflowにconcurrency: ${{ github.workflow }}を設定すると、呼び出された側の判定がconcurrency: mainとなり、永遠に待ち状態になってしまう。

また、呼び出されたWorkflowから、他のWorkflowを呼び出すことはできない。
なので、末端のJobをWorkflowとして切り出し共通利用する、ぐらいの用途に制限されてしまう感じがする。

機能は随時アップデートされているので、そのうち改善されることと期待したい。

on:workflow_run

明示的に呼び出すのではなく、Workflowの開始・終了Eventに応じて、Workflowをトリガーするにはworkflow_runが使える。

基本的な使い方は、↓で確認済み。
https://zenn.dev/umatoma/articles/0d101fefc2eee6

.github/workflows/main.yml
name: main
on:
  push:
jobs:
  myjob:
    runs-on: ubuntu-20.04
    steps:
      - run: echo "HELLO MAIN"
.github/workflows/run.yml
name: run
on:
  workflow_run:
    workflows: [main]
    types: [completed]
jobs:
  myjob:
    runs-on: ubuntu-20.04
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    steps:
      - run: echo "HELLO RUN"

この場合は、複数のWorkflowを連鎖させることができる。(3回まで)
また、独立したWorkflowとして起動するため、github contextも、それぞれ独立した値となる。

ただし、Workflow間の繋がりを可視化する仕組みが用意されていないので、連鎖の状況は確認しづらい感じがある。

まとめ

連鎖の繋がりや状態を可視化できるので、workflow_callを優先して使いたい感じがある。
ただし、github contextの扱いに注意が必要だったり、呼び出された側でworkflow_callを使えなかったり、といった制約を考慮して構成を考える必要がある。
なので、パイプライン全体を制御するWorkflow、と、再利用する各Workflow、に分ける感じだろうか。

.github/workflows/main.yml
name: main
on:
  push:
  workflow_dispatch:
jobs:
  myjob1:
    concurrency: main
    runs-on: ubuntu-20.04
    steps:
      - run: echo "HELLO MAIN"
  myjob2:
    needs: [myjob1]
    uses: ./.github/workflows/call.yml
  myjob3:
    needs: [myjob1]
    uses: ./.github/workflows/call.yml
  myjob4:
    needs: [myjob2, myjob3]
    uses: ./.github/workflows/call.yml
  myjob5:
    needs: [myjob1]
    uses: ./.github/workflows/call.yml
  myjob6:
    needs: [myjob1]
    uses: ./.github/workflows/call.yml
  myjob7:
    needs: [myjob5, myjob6]
    uses: ./.github/workflows/call.yml
  myjob8:
    needs: [myjob4, myjob7]
    concurrency: last
    uses: ./.github/workflows/call.yml

そうすることで、↓のようなパイプラインを組むことができつつ、他パイプラインで共通して呼び出すWorkflowの再利用も実現できる。
また、パイプライン全体を制御するWorkflow側で、Concurrencygithub context依存の値生成・判定を行えば、いい感じに同時実行を制御したりできそうな感じがする。

で、Workflowの開始・終了に応じた通知を、workflow_runでトリガーするようにすると、パイプラインのロジックから本質的でない通知処理を排除することができそう。

再利用する必要なければ、Jobの中にStepを書いていけばよいし、目立ったデメリットはなさそうな感じがする。

Discussion