GitHub Actions ワークフローでジョブ毎のステータスを Slack 通知する

前回のフォローアップ的な内容です。

blog.kondoumh.com

複数ジョブからなるワークフローの最後に Slack などに通知する時、ワークフロー全体の成功・失敗だけでなく、どのジョブで失敗したかを通知したくなります。通知のリンクからワークフローの実行結果を開けば分かりますが、通知時点で分かる方がよいでしょう。

ジョブ毎にステータスをワークフローレベルの変数に記録して最後に通知したいところですが、そのようなグローバル変数は提供されていません。そのかわり、job.outputs を使えばジョブの出力を後続ジョブに伝搬できます。

https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idoutputs

各ジョブに必ず実行される最終スッテップを定義して、echo "::set-output name=<output_name>::<output of job>"job.status を記録します。ジョブの outputs${{ steps.<step_id>.outputs.<output_name> }} の形式で output を定義します。以下の例では、ジョブ名とジョブステータスを message という名前で記録して outputs/done に保持するようにしています。

  Build:
    runs-on: ubuntu-latest
    name: Build
    outputs:
      done: ${{ steps.check.outputs.message }}
    steps:
    - name: Build
      run: echo Build
    - id: check
      if: ${{ always() }}
      run: echo "::set-output name=message::Build ${{ job.status }}"

全ジョブの結果出力用ジョブを書いてみます。needs で対象の全ジョブを指定します。${{ needs.<job_name>.outputs.<output_name> }} で記録したジョブの出力が取れます。

  Aggregate:
    if: ${{ always() }}
    runs-on: ubuntu-latest
    needs: [Build, TestA, TestB, Deploy]
    steps:
    - name: Aggregate
      run: |
        echo ${{ needs.Build.outputs.done }}
        echo ${{ needs.TestA.outputs.done }}
        echo ${{ needs.TestB.outputs.done }}
        echo ${{ needs.Deploy.outputs.done }}

出力結果。

f:id:kondoumh:20210130144146p:plain

文字だと地味なので Slack への通知は見やすいようにステータスを絵文字に変換してみます。

  Build:
    runs-on: ubuntu-latest
    name: Build
    outputs:
      done: ${{ steps.check.outputs.message }}
    steps:
    - name: Build
      run: echo Build
    - id: check
      if: ${{ always() }}
      run: echo "::set-output name=message::Build ${{ (job.status == 'success' && '✅') || '❌' }}"

Slack 通知用のジョブ。

  Notify_succeed:
    if: ${{ success() }}
    runs-on: ubuntu-latest
    needs: [Build, TestA, TestB, Deploy]
    steps:
    - name: Notify to Slack channel
      uses: rtCamp/action-slack-notify@v2
      env:
        SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
        SLACK_USERNAME: GitHUb Actions
        SLACK_TITLE: 'Workflow #${{ github.run_number }} succeeded'
        SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
        SLACK_MESSAGE: '${{ needs.Build.outputs.done }} ${{ needs.TestA.outputs.done }} ${{ needs.TestB.outputs.done }} ${{ needs.Deploy.outputs.done }}'

実行結果。Deploy は Test B の失敗により実行されません。

f:id:kondoumh:20210130141441p:plain

Slack の通知です。Test B で失敗したことが分かります。

f:id:kondoumh:20210130140714p:plain

ワークフロー定義の全体を掲載しておきます。

name: Multi Stage Build
on:
  push:

jobs:
  Build:
    runs-on: ubuntu-latest
    name: Build
    outputs:
      done: ${{ steps.check.outputs.message }}
    steps:
    - name: Build
      run: echo Build
    - id: check
      if: ${{ always() }}
      run: echo "::set-output name=message::Build ${{ (job.status == 'success' && '✅') || '❌' }}"

  TestA:
    runs-on: ubuntu-latest
    needs: Build
    outputs:
      done: ${{ steps.check.outputs.message }}
    name: Run Test A
    steps:
    - name: Test A
      run: echo Test A
    - id: check
      if: ${{ always() }}
      run: echo "::set-output name=message::Test A ${{ (job.status == 'success' && '✅') || '❌' }}"

  TestB:
    runs-on: ubuntu-latest
    needs: Build
    outputs:
      done: ${{ steps.check.outputs.message }}
    name: Run Test B
    steps:
    - name: Test B
      run: |
        echo Test B
    - id: check
      if: ${{ always() }}
      run: echo "::set-output name=message::Test B ${{ (job.status == 'success' && '✅') || '❌' }}"

  Deploy:
    runs-on: ubuntu-latest
    needs: [TestA, TestB]
    outputs:
      done: ${{ steps.check.outputs.message }}
    steps:
    - name: Deploy
      run: echo Deploy
    - id: check
      if: ${{ always() }}
      run: echo "::set-output name=message::Deploy ${{ (job.status == 'success' && '✅') || '❌' }}"

  Notify_succeed:
    if: ${{ success() }}
    runs-on: ubuntu-latest
    needs: [Build, TestA, TestB, Deploy]
    steps:
    - name: Notify to Slack channel
      uses: rtCamp/action-slack-notify@v2
      env:
        SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
        SLACK_USERNAME: GitHUb Actions
        SLACK_TITLE: 'Workflow #${{ github.run_number }} succeeded'
        SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
        SLACK_MESSAGE: '${{ needs.Build.outputs.done }} ${{ needs.TestA.outputs.done }} ${{ needs.TestB.outputs.done }} ${{ needs.Deploy.outputs.done }}'

  Notify_failure:
    if: ${{ failure() }}
    runs-on: ubuntu-latest
    needs: [Build, TestA, TestB, Deploy]
    steps:
    - name: Notify to Slack channel
      uses: rtCamp/action-slack-notify@v2
      env:
        SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
        SLACK_USERNAME: GitHUb Actions
        SLACK_TITLE: 'Workflow #${{ github.run_number }} failed'
        SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
        SLACK_COLOR: danger
        SLACK_MESSAGE: '${{ needs.Build.outputs.done }} ${{ needs.TestA.outputs.done }} ${{ needs.TestB.outputs.done }} ${{ needs.Deploy.outputs.done }}'