内容
オブジェクトフィルタ、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にはsuccess、failure、cancelled、skippedのいずれかが入る
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が成功扱いとなる
    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_Bはtest_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にはsuccess、failure、cancelled、skippedのいずれかが入る
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出力結果
以下のように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出力結果
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) }}
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);

