GitHub Actions Reusable Workflowのoutputsが呼び出し元で空になるときの確認

GitHub ActionsのReusable Workflowでoutputsが空になる原因を、step・job・workflow_callの受け渡し順に沿って確認します。

AIものさし編集部

Reusable Workflow内のstepでは値が出ているのに、呼び出し元の次のjobでは空になる。この症状は、値を作る処理よりも、step outputをjob outputへ、job outputをworkflow_call outputへ公開する「境界の受け渡し」が一段抜けているときに起きやすいです。呼び出し元ではReusable Workflowを呼び出すjob自体のIDをneedsに指定し、workflow側で公開したoutput名を参照します。

確認は、値を作ったstepから呼び出し元の利用箇所まで、4層を順番にたどると切り分けやすくなります。先に結論を言えば、step内の値は自動的に呼び出し元へ渡りません。step ID、jobのoutputs、on.workflow_call.outputs、callerのneeds.<job_id>.outputs.<name>が名前でつながっているかを確認してください。

まず4層の名前と受け渡しを図にする

Reusable Workflowのoutputは、次の流れです。


called workflow内のstep

  └─ steps.<step_id>.outputs.<output_name>

       ↓ job側で公開

called workflow内のjob

  └─ jobs.<job_id>.outputs.<job_output_name>

       ↓ workflow_callで公開

Reusable Workflowの外部output

  └─ on.workflow_call.outputs.<workflow_output_name>.value

       ↓ caller側の呼び出しjobが受け取る

caller workflowの後続job

  └─ needs.<call_job_id>.outputs.<workflow_output_name>

名前はそれぞれ別の階層です。stepに付けるid、GITHUB_OUTPUTへ書く名前、job outputのキー、workflow_call outputのキー、callerのjob IDを混同しないでください。ある階層で値が存在しても、その上の階層へ明示的にmapしていなければ次の階層では参照できません。

最小構成で一度だけ値を通す

次の例は、呼び出されたworkflowで安全な固定文字列を作り、呼び出し元の後続jobで表示する最小例です。実運用のトークン、個人情報、顧客データなどはoutputに入れず、まず「確認用」というような機密でない短い値で経路だけを検証してください。

Reusable Workflow(例:.github/workflows/reusable.yml):


name: Reusable workflow

on:

  workflow_call:

    outputs:

      result:

        description: "確認用の結果"

        value: ${{ jobs.build.outputs.result }}

jobs:

  build:

    runs-on: ubuntu-latest

    outputs:

      result: ${{ steps.emit.outputs.result }}

    steps:

      - id: emit

        shell: bash

        run: echo "result=確認用" >> "$GITHUB_OUTPUT"

呼び出し側(例:.github/workflows/caller.yml):


name: Caller

on:

  workflow_dispatch:

jobs:

  call-reusable:

    uses: ./.github/workflows/reusable.yml

  inspect:

    runs-on: ubuntu-latest

    needs: call-reusable

    steps:

      - name: Read reusable workflow output

        env:

          RESULT: ${{ needs.call-reusable.outputs.result }}

        run: printf 'result=%s\n' "$RESULT"

この例では、step IDのemitからresultを取得し、job buildのoutputs.resultへ割り当てます。Reusable Workflowのon.workflow_call.outputs.resultではjobs.build.outputs.resultを参照します。呼び出し側はReusable Workflowを呼び出したjobのID call-reusableをneedsへ書き、外部に公開したresultを読みます。途中のどこかで名前を変えたら、そのmap先も合わせて直します。

どの境界で値が消えたか切り分ける

最初に、値を生成するstepの実行結果を確認します。stepがskipされていないか、失敗していないか、outputを書き出す行まで到達したかを見ます。step outputを使う場合は、そのstepにidがあり、参照式が同じIDを使っている必要があります。表示名nameは人向けのラベルで、steps.<id>.outputs.<name>の<id>の代わりにはなりません。

次に、値がGITHUB_OUTPUTへ書かれているかを確認します。現在の形式は、同じstepの後続処理から参照できるstep outputとして、環境変数GITHUB_OUTPUTが示すファイルへ「name=value」を追記する形です。シェルの引用符が不適切で、値が途中で分割されたり、式の評価前に空になったりしていないかも確認します。複数行値は単純な1行形式では扱えないため、公式の複数行記法に従うか、成果物ファイルを使う方法を検討します。

続いて、job outputsのmapを調べます。Reusable Workflow内にstep outputがあっても、jobs.<job_id>.outputsで参照する設定がなければ、workflow_callへ渡すjob-level outputはありません。ここではjob IDとstep IDを正確に区別し、式の大文字小文字やoutput名の綴りを照合します。job outputsはjobの完了時に後続jobから使われるので、同じjob内で後続stepが必要な値を読む設計ならstep outputを参照します。

三つ目に、on.workflow_call.outputsを確認します。ここで定義したoutput名がReusable Workflowの外部インターフェースです。valueはcalled workflow内のjob-level outputを参照します。step outputを直接指定したり、存在しないjob IDを参照したり、descriptionだけを追加してvalueを設定し忘れたりすると、呼び出し側が読む値につながりません。

最後にcaller側を確認します。後続jobがReusable Workflowを呼び出すjobにneedsで依存しているか、needs.<呼び出しjob ID>.outputs.<workflow_callで定義した名前>を参照しているかを見ます。別のjob IDや、called workflow内のjob IDを使っても呼び出し元からは解決できません。Reusable Workflowを呼び出すjobは通常のstep内で呼び出すのではなく、jobs配下のjobとしてusesを指定します。

症状から探す確認ポイント

表は横にスクロールできます

症状まず見る場所確認すること
stepのログには値があるがjob outputが空job outputssteps.<id>.outputs.<name>のID・名前とmap
called workflow内のjobでは値があるがcallerで空workflow_call outputsvalueが正しいjobs.<job_id>.outputs.<name>を参照しているか
callerの式が空または評価できないcaller jobneedsが呼び出しjob IDを指定し、外部output名を使っているか
特定の実行だけ空step/job条件ifによるskip、失敗、continue-on-error、依存関係、空文字を返す経路
matrix呼び出しだけ値が期待と違うmatrix output仕様どの完了runの値が最終outputになるか、空文字を返すrunがあるか
workflowをネストしたときだけ空各workflow境界A→B、B→Cの各段階で、次のworkflowへ必要なoutputを明示しているか

失敗したjobの画面だけで判断せず、runごとに呼び出されたworkflow、job、stepの順にログを開きます。名前を一度に全部変更すると切り分けが難しくなるため、まず現在の名前を表に書き出し、1つのmapだけ直して再実行してください。直した後は、呼び出し元で確認した値と、その値を作ったstepの値が一致するか確認します。

4層の対応表を作る

編集前に次の空欄を埋めると、どこで参照が切れているか見つけやすくなります。

表は横にスクロールできます

層記入する名前参照例
step ID値を出すstepのidemit
step outputGITHUB_OUTPUTに書くキーresult
job ID / job output出力元jobと公開キーbuild / result
workflow outputworkflow_callで公開するキーresult
caller job IDreusable workflowを呼ぶjobcall-reusable
caller参照needsとworkflow output名needs.call-reusable.outputs.result

同じresultという名前を全階層で使う必要はありません。名前を変えるときは、対応表にそれぞれのmapを明記してください。短く同じ名前を使う例は読みやすい反面、階層の取り違えを隠すことがあります。大きなworkflowでは、job outputにjob_result、workflow outputにfinal_resultのような異なる名前を付け、どの境界の値かを判別しやすくする選択肢もあります。

matrix、skip、空文字の注意点

matrix strategyでReusable Workflowを複数回呼び出すときは、通常の単一実行と同じように「最後のrunの値」とだけ考えないでください。GitHub公式仕様では、outputはmatrixのうち最後に正常終了し、かつ値を設定したReusable Workflowの実行結果になります。最後に正常終了した実行が空文字を設定した場合、その直前に正常終了して値を設定した実行の値が採用されます。この動作を前提に集約が必要なら、各matrix結果を一つの文字列outputに上書きする設計ではなく、artifactや別の集約方法が適切か検討してください。

条件付きstepやjobでは、値を作る経路自体が実行されない場合があります。空文字は必ずしもマッピング設定の誤りを意味しません。if条件、needsによる依存、失敗時の扱い、デフォルト値の有無を見て、値が設定されない分岐を確認します。複数の分岐から同じoutputを返すなら、各分岐で同じ契約(型・意味・空値の扱い)を保てるかを決めます。未実行と「意図的に空」を同じ値で表すと、呼び出し元で原因を区別できなくなるため、必要ならstatus outputを別に設計します。

ネストしたReusable Workflowでは、ワークフローAがBを呼び、BがCを呼ぶ場合、AからCの値を直接参照できるわけではありません。各境界で外部に公開し、次のworkflowが受け取れるようにします。A→Bのmap、B→Cのmap、最上位callerの参照を一つずつ確認してください。

秘密情報をoutputへ載せない

workflow outputやログは、秘密を運ぶための代替手段ではありません。アクセストークン、署名鍵、個人情報をoutputへ出して、値が渡るか試すのは避けてください。GitHub Actionsにはsecretをmaskする仕組みがありますが、maskを機密データの一般的な受け渡し方法として扱わないでください。Reusable Workflowが必要とするsecretは、公式のinputs/secrets契約で渡し、outputには非機密の状態・識別子・集計結果など、呼び出し元が扱ってよい値だけを含めます。

確認用テストは「ok」のような固定値でよく、実運用値を出力する必要はありません。出力の内容がログに表示される設定なら、機密でないことを確認します。複数行の出力、JSON、ユーザー入力を含む出力では、区切り文字や改行による解釈の違いにも注意し、必要に応じてartifactsや明示的なファイル形式を選びます。

小さい変更で安全に再現する

原因を切り分ける際は、本番相当のtokenや実データを使わず、固定文字列を出す小さなworkflowを一時ブランチで動かします。再実行できるワークフローであっても、意図しない副作用を避けるため、読み取り専用の権限、テスト用の値、必要最小限のjobで確認してください。実行しないまま「動作確認済み」と書かず、記事内のYAMLは公式仕様に沿った例として提示します。

テストでは三箇所を照合します。第一にstep outputを作る処理が成功したか。第二にReusable Workflowのjob outputとworkflow_call outputの値が、同じ非機密文字列になったか。第三にcallerのneeds.<job_id>.outputs.<name>で同じ値を読めたかです。ログに値を出さない方針なら、後続処理の条件分岐やテスト用の一致確認で値を検証し、表示を最小限にします。

GITHUB_OUTPUTへ書く値の形式

1行の値なら、値の中の改行やシェル展開を意識して、公式のGITHUB_OUTPUT形式で書きます。例えば固定値なら次のようにできます。


- id: emit

  shell: bash

  run: echo "result=ok" >> "$GITHUB_OUTPUT"

変数を入れるときは、未信頼文字列をそのままシェルコードへ連結しないでください。環境変数として渡し、シェルで引用して扱い、改行や区切り文字を含む場合は公式の複数行output形式を用います。複雑な構造の大きなデータはjob outputに詰め込むより、artifactなど明示的なデータ受け渡し方法を選んだほうが扱いやすい場合があります。ここで説明しているのは伝達経路の確認であり、任意のバイナリや巨大データをoutputsに適用できるという意味ではありません。

値を書けたように見えても、output名が式と一致しなければ別のキーを読んでいるだけです。手入力で名前を変更する場合は、GITHUB_OUTPUTの左辺、steps参照、jobs.outputsのキー、workflow_call.outputsのキー、callerの式を同じ表で照合してください。YAMLのインデントがずれると、設定が期待する階層に入らないことがあります。workflowファイルの構文とGitHub上のrunログの両方で確認します。

よくある修正パターン

job outputsのmapがない場合: 該当step outputをjobs.<job_id>.outputsへ公開し、そのjob outputをworkflow_call側で参照します。別のjobのstep outputを直接使うことはできないため、値を作ったjobとmapするjobの関係も確認します。

workflow_callのvalueがない場合: 公開したい外部outputのvalueを設定し、正しいjob-level outputへつなぎます。descriptionだけでは値は返りません。

callerが別jobをneedsにしている場合: Reusable Workflowを呼び出すjob IDをneedsへ指定します。呼び出しjobがskipされる条件や、後続jobのif条件も確認します。

名前が途中で変わっている場合: 4層のどこで変換しているかを対応表に記録してから、必要な1か所だけ修正します。式全体を置換する前に、各キーが定義される場所と参照される場所を検索してください。

修正後のworkflowを実行できる権限がない場合は、静的にYAMLと名前の対応を点検した結果と、実行検証が未実施であることを分けて記録してください。記事だけから特定リポジトリの権限設定やGitHub Enterprise Serverのバージョンを決めつけることはできません。

確認チェックリスト

  • 値を作るstepが実行され、意図した値を書いている

  • stepに一意なidがあり、式の参照と一致している

  • step outputの名前がGITHUB_OUTPUTのキーと一致している

  • job outputsが正しいstep outputをmapしている

  • on.workflow_call.outputs.valueが正しいjob outputを参照している

  • callerがReusable Workflowのcall jobをneedsに指定している

  • callerがworkflow_callで公開したoutput名を使っている

  • if、skip、失敗、matrix、空文字の経路を確認している

  • secretや個人情報をoutput・ログへ出していない

  • 非機密の固定値で各境界を確認し、実運用値を使った未検証を避けている

次に行うこと

まず出力元workflowで、step IDとGITHUB_OUTPUTのキーを確認します。続いてjob outputs、workflow_call.outputs.value、callerのneeds参照を順に対応表へ記録し、空になった最初の境界だけ修正してください。matrixや条件分岐がある場合は、値を設定しない経路もテストします。変更を適用する前に、secretを含まない固定値で再現可能な最小ケースを作り、修正後はstep・Reusable Workflow output・caller outputの三点で一致を確かめます。

出典

確認日:2026-10-15

注:この記事は公式仕様に基づく一般的な切り分け手順です。特定のrepositoryやworkflowでの実行・hands-on検証は行っていません。