1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GitHub Actions - 複数 jobs, 複数 stepsの処理結果をまとめて判定する

Last updated at Posted at 2024-12-31

内容

オブジェクトフィルタcontainsを利用して、
複数Jobs、複数Stepsの処理終了後に処理結果をまとめて判定する方法を記載する

stepsの処理結果をまとめて判定する

steps.<step_id>.outcomeを使う

jobs:
  test_A:
    name: test A
    runs-on: ubuntu-latest
    outputs:
      test_result: ${{ steps.test_result.outputs.result }}
    steps:
      - uses: actions/checkout@v4

      - name: test 1
        id: test_1
        shell: bash
        # continue-on-error: true  # stepが失敗した場合も後続のstepを実行したい場合に指定する
        run: |
          ...

      - name: test 2
        id: test_2
        shell: bash
        # continue-on-error: true  # stepが失敗した場合も後続のstepを実行したい場合に指定する
        run: |
          ...

      - name: test result
        id: test_result
        if: ${{ always() }}  # 前段のstepが失敗しても実行する
        uses: actions/github-script@v7
        env:
          TEST_FAILED: ${{ contains(steps.*.outcome , 'failure') }}  # idが振られたstepの全結果に失敗が含まれている場合にtrueを返す
        with:
          result-encoding: string
          script: | # 結果を文字列に変換
            const result = ${{ env.TEST_FAILED }} ? 'Failed' : 'Successful';
            return result;

stepのidは必須プロパティではないが、steps.<step_id>.outcomeは、step idが定義されているstepしか参照できない

${{ contains(steps.*.outcome , 'failure') }}で前段の複数stepから成るテスト結果をまとめている
steps コンテキストoutcomeをオブジェクトフィルタでリスト化した上で、
containsでリストにfailureが含まれているかをtrue/falseで取得している
steps.<step_id>.outcomeにはsuccessfailurecancelledskippedのいずれかが入る

continue-on-error: trueを指定し、stepが失敗した場合、steps.<step_id>.outcomeには、step結果が"失敗"から"成功"に変更される前の"失敗"が入っている
逆に変更された後の"成功"を取得したい場合は、steps.<step_id>.conclusionを使用する

continue-on-error

stepで失敗した場合も後続のstepを実行したい場合は、continue-on-error: trueを使用する
ただし、該当stepが失敗した場合も該当stepに加え、Jobが成功扱いとなる

./.github/workflows/reuse_test_a.yml
    steps:
      - uses: actions/checkout@v4

      - name: test 1
        id: test_1
        shell: bash
        continue-on-error: true  # stepが失敗した場合もstepとJobは成功扱いになる
        run: |
          ...
jobs:
  test_A:
    name: test A
    uses: ./.github/workflows/reuse_test_a.yml
    secrets: inherit

  test_B:
    name: test B
    needs: [test_A]  # test_Aが失敗した場合もtest_Bは実行される
    uses: ./.github/workflows/reuse_test_b.yml
    secrets: inherit

test_Btest_Aに依存しているが、test_Aが失敗した場合も成功扱いとなるのでtest_Bが実行されてしまう

success(), failure()を使う

jobs:
  test_A:
    name: test A
    runs-on: ubuntu-latest
    outputs:
      test_result: ${{ steps.xxx.outputs.result }}  # 単純にstepのoutputsで返却は無理
    steps:
      - uses: actions/checkout@v4

      - name: test 1
        id: test_1
        shell: bash
        run: |
          ...

      - name: test 2
        id: test_2
        shell: bash
        run: |
          ...

      - name: test result success
        id: test_result_success
        if: ${{ success() }}  # 前段のstepが全て成功の場合に実行
        uses: actions/github-script@v7
        with:
          result-encoding: string
          script: |
            return 'Successful';

      - name: test result failure
        id: test_result_failure
        if: ${{ failure() }}  # 前段のstepに失敗が有った場合に実行
        uses: actions/github-script@v7
        with:
          result-encoding: string
          script: |
            return 'Failed';

ステータスチェック関数を使ってもsteps.<step_id>.outcomeを使うパターンと似たようなことは可能だが、
結果の判定stepが2つになるため、単純にJobのoutputsとして返却することはできない

Jobsの処理結果をまとめて判定する

needs.<job_id>.resultを使う

jobs:
  test_A:
    name: test A
    uses: ./.github/workflows/reuse_test_a.yml
    secrets: inherit

  test_B:
    name: test B
    uses: ./.github/workflows/reuse_test_b.yml
    secrets: inherit

  test_result:
    name: test result
    needs: [test_A, test_B]
    if: ${{ always() }}
    uses: ./.github/workflows/reuse_test_result.yml
    secrets: inherit
    with:
      failed: ${{ contains(needs.*.result, 'failure') }}  # needsで指定したJobの全結果に失敗が含まれている場合にtrueを渡す

${{ contains(needs.*.result, 'failure') }}で依存Job(needsで指定したJob)の結果をまとめている
needs コンテキストresultをオブジェクトフィルタでリスト化した上で、
containsでリストにfailureが含まれているかをtrue/falseで取得している
needs.<job_id>.resultにはsuccessfailurecancelledskippedのいずれかが入る

needs.<job_id>.outputs.<output name>

以下のようにneeds.<job_id>.outputs.<output name>をオブジェクトフィルタでリスト化することはできない
全jobに該当のoutputが含まれていることが保障されないためだと思われる

jobs:
  test_A:
    name: test A
    uses: ./.github/workflows/reuse_test_a.yml
    secrets: inherit

  test_B:
    name: test B
    uses: ./.github/workflows/reuse_test_b.yml
    secrets: inherit

  test_result:
    name: test result
    needs: [test_A, test_B]
    if: ${{ always() }}
    uses: ./.github/workflows/reuse_test_result.yml
    secrets: inherit
    with:
      result_list: ${{ needs.*.outputs.test_result }}  # ※ これは不可

needsオブジェクト

{
  "test_A": {
    "result": "success",
    "outputs": {
      "test_result": "Successful"
    }
  },
  "test_B": {
    "result": "failure",
    "outputs": {
        "test_result": "Failed"
    }
  }
}

needs.<job_id>.outputs.<output name>をまとめて参照する場合、
needsオブジェクトをtoJSONでJSONに変換すると参照することができる

  test_result:
    name: test result
    needs: [test_A, test_B]
    if: ${{ always() }}
    runs-on: ubuntu-latest
    env:
      NEEDS: ${{ toJSON(needs) }}  # JSON化
    steps:
      - name: test result
        uses: actions/github-script@v7
        with:
          result-encoding: string
          script: |
            const needs = ${{ env.NEEDS }};
            console.log(needs);
            const test_result = Object.keys(needs).map(jobId => needs[jobId].outputs.test_result);
            console.log(test_result);

console.log出力結果

2024-12-31-14-42-47.png

以下のようにtoJSONせずに参照することはできない

  test_result:
    name: test result
    needs: [test_A, test_B]
    if: ${{ always() }}
    runs-on: ubuntu-latest
    steps:
      - name: test result
        uses: actions/github-script@v7
        with:
          result-encoding: string
          script: |
            const needs = ${{ needs }};  // これは不可
            console.log(needs);
            const test_result = Object.keys(needs).map(jobId => needs[jobId].outputs.test_result);
            console.log(test_result);

console.log出力結果

2024-12-31-14-58-26.png

reusable-workflowsのinputsにtoJSON(needs)を渡して参照することは可能

  test_result:
    name: test result
    needs: [test_A, test_B]
    if: ${{ always() }}
    uses: ./.github/workflows/reuse_needs_output.yml
    secrets: inherit
    with:
      needs: ${{ toJSON(needs) }}
./.github/workflows/reuse_needs_output.yml
name: needs output

on:
  workflow_call:
    inputs:
      needs:
        required: true
        type: string
jobs:
  test_result:
    name: test result
    runs-on: ubuntu-latest
    steps:
      - name: test result
        uses: actions/github-script@v7
        with:
          result-encoding: string
          script: |
            const needs = ${{ inputs.needs }};
            console.log(needs);
            const test_result = Object.keys(needs).map(jobId => needs[jobId].outputs.test_result);
            console.log(test_result);
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?