GitHub Actionsの複数Jobで構成されるWorkflowの正しい結論を知る方法


GitHub Actionsで複数Jobが動くWorkflowがあったとして、この最終的な結論を知る方法について調べてみた。

そもそもどうして複数Jobが動くWorkflowの結論が知りたいのかというと、例えば次のようにBuild->Test->Publishのようにシリアルに実行されるJobで構成されるWorkflowがあったとして、最後にその成功・失敗いづれかの結果をSlackで通知したかったから。なお、通知の際は、成功・失敗のステータスを区別する必要があった。

```
Build Job => Test Job => Publish Job => Slack通知 (成功・失敗いづれも通知)
```

## ダメだった例

まずはダメだった例を紹介する。下記のようにJob群（build、test、publish）と、always()とneedsを使って、それらJob群の結果を取得するJob（`slack_notification`）を用意した。 下記の、slack_notificationのように、needsでその前のJobとの依存関係を設定した上で、Job実行条件にalways()を指定することで、その前のJob群がどこかでfailしてスキップされたとしても必ずslack_notificationは実行されるようになる。 ただし、結論からいうと、これは要件を満たさない。slack_notificationの前のJobが成功・失敗関係なく、得られる`job.status`は必ず`success`。`job.status`とは現在実行中のJobのステータスであるため`success`となる。当たり前の話だった。

```yaml
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: exit 0
  test:
    runs-on: ubuntu-latest
    needs: build
    steps:
      - run: exit 1
  publish:
    runs-on: ubuntu-latest
    needs: test
    steps:
      - run: exit 0

  slack_notification:
    if: always()
    needs: publish
    runs-on: ubuntu-latest
    steps:
      - run: |
          echo "let's see the workflow conclusion"
          echo ${{ job.status }}
```
このWorkflowの実行結果は下記の通りで、test Jobで失敗しているにも関わらず、slack_notification Jobではステータスをjob.statusから取得しているため`success`と表示されてしまっている。

![ng-case.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103420/640a2fdc-05fa-00b8-995b-d52f2b049dc0.png)



## 方法1: GitHub API v3 Workflow Jobを使う

GitHub API v3のWorkflow Job APIは、全てのJobの実行結果を`conclusion`として返却してくれる。このAPIを上記のslack_notificationでたたけば、そのWorkflowにおいて、それまでのJobの実行結果が成功なのか失敗なのか一覧取得でき、そこから最終結果を判断することが可能となる。

https://developer.github.com/v3/actions/workflow-jobs/


## 方法2: Workflow Conclusion Actionを使う

方法１で紹介したGitHub API v3のWorkflow Job APIを内部で利用して、Workflow内のJobの結果を全て取得し、その最終結果を環境変数（`WORKFLOW_CONCLUSION`）に入れてくれる便利なActionがある。それが、[workflow-conclusion-action](https://github.com/technote-space/workflow-conclusion-action)。正しいJob群の最終結果が得られるので、上記の要件は満たせるわけです。上記のサンプルを[workflow-conclusion-action](https://github.com/technote-space/workflow-conclusion-action)をつかって変更すると次のようになる。

```yaml
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: exit 0
  test:
    runs-on: ubuntu-latest
    needs: build
    steps:
      - run: exit 1
  publish:
    runs-on: ubuntu-latest
    needs: test
    steps:
      - run: exit 0

  slack_notification:
    if: always()
    needs: publish
    runs-on: ubuntu-latest
    steps:
      - uses: technote-space/workflow-conclusion-action@v1
      - run: |
          echo "let's see the workflow conclusion"
          echo ${{ env.WORKFLOW_CONCLUSION }}
```

このWorkflowの実行結果は下記の通り。今度はslack_notification Jobで、ステータスが無事`success`と表示された 🎉🎉🎉

![ok-case.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103420/16a5c20b-2219-f532-afb4-f613ac4ef1b0.png)

まとめると、基本的に、方法１も２もGitHub ｖ３ API Workflow Jobを利用したアプローチ。APIの仕様に準じてパラメータを組み立て、結果をパースすることが苦ではない人は方法１、楽をしたい人は方法２を選択ください。

最後に、slack_notification Jobの中身を、目的のslack通知の内容に書き換えると次のようになる

```yaml
  slack-notification:
    if: always()
    needs:  publish
    runs-on: ubuntu-latest
    steps:
      - uses: technote-space/workflow-conclusion-action@v1
      - name: Slack Notification
        uses: sonots/slack-notice-action@v3
        with:
          title: My deployment
          status: ${{ env.WORKFLOW_CONCLUSION }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SLACK_WEBHOOK_URL: ${{ secrets.MY_GITHUB_CHANNEL_WEBHOOK }}
```

本記事で紹介したサンプルコード
- https://gist.github.com/yokawasa/b3e4c49c66ba9069f3904163ecf841d6