GitHub

GitHub Actionsのneeds|ジョブ依存の設計とoutputs受け渡しの実務

GitHub Actionsのneeds|ジョブ依存の設計とoutputs受け渡しの実務

needsの記述自体は1語だけです。ところが、依存を足したのに前段の出力が空で返る、テストが落ちたはずのデプロイが走る、逆に条件付きで飛ばした前段のせいで後続が丸ごと消える、といった詰まり方をします。原因の多くは記法ではなく、直接依存しかneedsコンテキストに入らない規則と、失敗やスキップが連なり全体へ伝わる範囲を取り違えているところにあります。この記事では、依存グラフの組み方、outputsによる値の受け渡しとサイズ上限、伝播の断ち方、そしてジョブをどこで割るかの判断基準までを扱いました。ワークフローの基本構造はGitHub Actionsとは?できること・使い方とCI/CD自動化の解説記事、パイプライン全体の考え方はCI/CDとは?仕組みとパイプラインの判断基準を先に押さえてください。

まとめ:needsで先に決める4つの前提

第一に、needsを書かなければ全ジョブが同時に走り出します。順序は暗黙には生まれません。ビルドの後にテストを置きたいなら、その一本一本を明示的に宣言する必要があります。

第二に、needsコンテキストへ入るのは直接の依存だけです。公式リファレンスは「依存ジョブの依存ジョブ」を含まないと明記しています。孫の出力を読みたければ、実行順としては既に確定していても、その名前をneedsの配列へ書き足さなければ届きません。

第三に、値の受け渡しには上限があります。出力は1ジョブあたり1MB、1回の実行で合計50MBまでで、サイズはUTF-16換算の概算です。ビルド成果物のような塊はここへ載せず、アーティファクトへ回します。

第四に、失敗もスキップも、その地点から先の連なり全体へ伝わります。片方だけ止めることはできません。後始末や通知を必ず走らせたいなら、条件式で明示的に受け止める設計が要ります。

needsが作る依存グラフと既定では全ジョブが並列に走るという前提

最初に、順序がどこから生まれるのかを揃えます。ここを飛ばすと、後段の出力や伝播の話が場当たりの暗記になってしまうためです。

文字列と配列の2通りで書くneedsの記法と実行順の決まり方

公式の定義は「このジョブが走る前に成功して完了していなければならないジョブを指定する」ものです。値は文字列でも文字列の配列でも受け付けます。1本だけならneeds: build、複数ならneeds: [build, lint]と書きます。

jobs:
  build:
    runs-on: ubuntu-24.04
    steps:
      - run: make build
  lint:
    runs-on: ubuntu-24.04
    steps:
      - run: make lint
  test:
    needs: [build, lint]
    runs-on: ubuntu-24.04
    steps:
      - run: make test

この書き方ではbuildとlintが同時に走り、両方が成功してからtestが始まります。全体の所要時間は、直列の合計ではなく最も長い経路で決まりました。逆にneedsを一切書かなければ、3つとも同時に走り出します。テストがビルド成果物を前提にしていれば、そこで初めて壊れます。

直接の依存だけがneedsコンテキストに入り孫の出力は届かない規則

ここが最も踏みやすい段差です。needsコンテキストは「現在のジョブの直接の依存として定義された全ジョブの出力」を持ちますが、公式は「暗黙的に依存しているジョブ、たとえば依存ジョブの依存ジョブは含まない」と明記しています。

たとえばsetup→build→deployと連ねたとき、deployからneeds.setup.outputs.versionを読んでも空文字が返ります。実行順としてはsetupが先に終わっているにもかかわらず、です。プロパティの参照は存在しない名前でもエラーにならず空文字へ落ちるため、失敗の見た目は「値が入っていない」だけになります。

対処は2つあります。deployのneedsへsetupを書き足して直接依存にするか、buildが自分のoutputsへ受け取った値を再送出して中継するかです。前者は依存グラフに線が1本増えるだけで実行順を変えず、記述も短く済みます。中継はbuildの責務を膨らませるため、値が3つ4つと増えると読みにくくなりました。

循環参照と同一ワークフロー内という制約から外れる依存の作り方

needsで結べるのは同じワークフローファイルの中のジョブに限られます。互いを指し合う循環を書くと、その実行は開始前に落ちます。ジョブ名の綴りを間違えた場合も同様で、ランナーが1台も起動しないまま構文の検証で止まりました。

別ファイルのワークフローを待たせたいときは、needsではなくon: workflow_runで発火側を連鎖させるか、呼び出し可能なワークフローとして取り込む形です。トリガー側の選び方はGitHub Actionsのトリガー(on)の設計、共通処理を切り出して呼ぶ形はReusable Workflowsとworkflow_callの使い方に整理があります。呼び出し可能なワークフローは呼び出し側から見れば1つのジョブなので、needsの対象としてそのまま扱えます。

outputsとGITHUB_OUTPUTでジョブ間に値を渡す実装の順序

ジョブごとにランナーが分かれるため、変数もファイルも自動では引き継がれません。値を渡す経路はoutputs、ファイルを渡す経路はアーティファクトと、はっきり分かれています。

ステップの出力をジョブのoutputsへ写す2段構えの実装記法

受け渡しは2段です。まずステップ側で$GITHUB_OUTPUTへ書き出し、次にジョブのoutputsでそのステップ出力へ名前を付け替えます。受け取る側はneeds経由で読みます。

jobs:
  build:
    runs-on: ubuntu-24.04
    outputs:
      image_tag: ${{ steps.meta.outputs.tag }}
    steps:
      - id: meta
        run: echo "tag=sha-${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
  deploy:
    needs: build
    runs-on: ubuntu-24.04
    steps:
      - run: echo "deploying ${{ needs.build.outputs.image_tag }}"

ステップにidを付け忘れるとsteps.meta.outputs.tagが解決できず、ジョブの出力は空のまま通ります。式を含む出力はジョブの終了時にランナー上で評価される仕様なので、途中のステップで書き換えても最終値だけが送られました。環境変数そのものの使い分けはenv・vars・secretsの使い分けと受け渡しが扱っています。

1ジョブ1MB・1実行50MBという出力サイズの上限と切り替え先

公式が示す上限は明快です。出力は1ジョブあたり最大1MB、1回のワークフロー実行に含まれる全出力の合計は最大50MBで、サイズはUTF-16エンコーディングを基準に概算されます。日本語を含むJSONを丸ごと載せると、見た目の文字数より早く枠を使います。

渡すもの 経路 目安
タグ・版番号・真偽値 outputs 数十バイト
マトリックス定義のJSON outputs 数KB程度まで
ビルド成果物・レポート アーティファクト MB以上
依存キャッシュ actions/cache 再生成できる分

判断軸は大きさだけではありません。後続が「値として分岐に使う」ならoutputs、「ファイルとして開く」ならアーティファクトです。受け渡しの設計と保持期間の決め方はGitHub Actionsのアーティファクトの受け渡し設計にまとめてあります。

シークレットを含む出力が警告1行だけ残して落ちる仕様と回避策

シークレットを含む出力はランナー上で伏せ字にされ、GitHub Actions側へ送られません。落ちたことはログに1行だけ残ります。「Skip output {output.Key} since it may contain secret.」という警告文がそれです。

厄介なのは、ジョブ自体は成功で終わる点です。後続は空文字を受け取って進み、認証や接続の段になって初めて失敗します。判定はシークレットの値と一致するかどうかで行われるため、意図せず巻き込まれることもありました。前段で発行した一時トークンを後段へ渡す設計は、この仕様を踏むと動きません。OIDCで各ジョブが個別に資格情報を取りに行く形へ組み替えるのが確実です。

マトリックスの出力が名前衝突で上書きされる順序の非保証と対策

マトリックスを使うと、出力は展開された全ジョブから合成されます。ここで公式が明確に警告しているのが順序です。「Actionsはマトリックスジョブが走る順序を保証しない。出力名が一意であることを確認せよ。さもなければ最後に走ったマトリックスジョブが出力値を上書きする」と書かれています。

つまりresultのような固定名を全セルで使うと、受け取れるのは1つだけで、しかもどのセルの値かは実行ごとに変わります。回避は名前へマトリックス変数を織り込むことです。output_${{ matrix.version }}のように展開すれば衝突しません。受け取る側はtoJSON(needs.build.outputs)で全体を1つのJSONとして読めます。組み合わせの展開そのものはマトリックスビルドのinclude・excludeの調整を参照してください。

失敗とスキップが依存の連なりを下流へ伝播する範囲と伝播の断ち方

依存を張ると、成功だけでなく失敗も伝わります。どこまで伝わるのかを先に確定させておくと、後始末ジョブの置き場所が自動的に決まります。

失敗地点から先の全ジョブがスキップされる伝播の範囲と例外条件

公式の記述は2文です。「あるジョブが失敗またはスキップされた場合、それを必要とする全ジョブはスキップされる」「互いを必要とする一連のジョブを含む実行では、失敗またはスキップはその地点から先の依存チェーン内の全ジョブに適用される」。直後の1本だけではありません。

ここで見落としやすいのが、失敗とスキップが同じ扱いになる点です。条件付きで前段を飛ばしただけのつもりでも、下流は連鎖して消えます。ドキュメント更新のときだけビルドを飛ばす設計にしたら、通知ジョブまで一緒に消えていた、という形で表面化しました。needs.build.resultが返す値はsuccess・failure・cancelled・skippedの4つで、この2つを区別できるのは結果を名指しで比べたときだけです。

always()より否定形のcancelled()を選ぶ公式の推奨と使い分け

依存が満たされなくても走らせたいときは、ジョブのifに条件式を書いて既定の前提を外します。公式が例として挙げているのはalways()ですが、実務での既定は否定形のcancelled()です。always()は取り消し操作でも止まらないため、詰まった処理がタイムアウトまで居座ります。

jobs:
  notify:
    needs: [build, test, deploy]
    if: ${{ !cancelled() }}
    runs-on: ubuntu-24.04
    steps:
      - run: bash scripts/notify.sh

式の評価規則そのもの、たとえばステータス関数を1つ書いた時点で暗黙のsuccess()が外れることや、文字列と真偽値の比較で足をすくわれる型キャストはGitHub Actionsの条件分岐(if)の評価規則で詳しく扱っています。本記事では、条件式は伝播を断つ弁として使う、という役割だけを押さえます。

後始末と通知のジョブを依存グラフの末端へ置く配置の型と実行条件

伝播の性質を踏まえると、置き場所は1通りに絞れます。後始末や通知は末端に置き、途中の全ジョブをneedsの配列へ並べ、否定形のcancelled()で受けます。途中に挟むと、そのジョブ自身が伝播の中継点になってしまうためです。

並べる対象は「直接依存だけがneedsコンテキストに入る」という先の規則とも噛み合います。通知本文へ前段の版番号やテスト件数を載せたいなら、どのみち全部を配列へ書く必要があるからです。実行の重複を抑える側の制御はconcurrencyのgroup設計とcancel-in-progressと組み合わせて考えてください。

ビルドとテストとデプロイをジョブに割るか1本へ収めるかの判断

ここからは公式が答えを持たない領域です。needsで細かく割るほど依存グラフは読みやすくなりますが、割った回数だけ支払う固定費があります。判断の材料を数えられる形にしておきます。

ジョブを割るたびに増えるランナー起動と復元という固定費の見積もり方

ジョブは1本ごとに別のランナーで動きます。したがって割るたびに、ランナーの割り当て、チェックアウト、言語ランタイムの用意、依存キャッシュの復元が繰り返されます。Node.jsの中規模プロジェクトを想定するなら、この前準備の目安は1ジョブあたり30秒から1分です。

ビルド1本を4ジョブへ割れば、前準備は4回分です。並列化で縮む時間がこの固定費を上回らなければ、分割は総所要時間を延ばす方向にしか働きません。加えて課金は実行時間に対して発生するため、消費分数は素直に増えます。分単価と無料枠の考え方はGitHub Actionsの料金と2026年改定後の分単価で確認できます。

2026年6月に入ったステップ並列とneedsによる分割の使い分け

判断の前提が2026年6月25日に変わりました。GitHubがステップの並列実行を提供開始し、background・wait・wait-all・parallelの4つのキーワードが使えるようになったためです。同じジョブの中で複数のステップを同時に走らせ、必要な地点で合流させられます。

steps:
  - uses: actions/checkout@v6
  - parallel:
      - name: フロントをビルドする
        run: npm run build:frontend
      - name: バックエンドをビルドする
        run: npm run build:backend
  - run: npm test

1ジョブ内で同時に走れるバックグラウンドステップは最大10本で、超えた分は枠が空くまで待たされます。バックグラウンドステップの出力と環境変数の変更は、それを含むwaitかwait-allを通過するまで後続から見えません。合成アクションの中では使えない制約もあります。

使い分けの線は明確です。同じ作業ディレクトリと同じ依存を共有したまま並列化したいならステップ側、実行環境そのものを分けたいならジョブ側です。フロントとバックエンドのビルドを同時に走らせる程度なら、ジョブへ割ってアーティファクトで橋渡しするより、1ジョブ内のparallelのほうが速く、記述も短く収まりました。

ジョブを割ってよいのは環境・並列・承認の3条件に限る運用方針

受託開発で引き渡すパイプラインでは、ジョブ分割を次の3条件に絞ることを勧めます。第一に実行環境が違うとき。WindowsとLinuxで検証する、あるいはDockerイメージのビルドだけ別のランナーサイズを使う、といった場合です。第二に並列化の利得が前準備の固定費を明確に上回るとき。目安として、並列で縮む時間が2分を超えるかどうかで線を引きます。第三に承認や環境保護を挟むとき。デプロイの手前で人の判断を入れるなら、そこは必ずジョブの境界になります。

逆に、この3つのどれにも当たらない分割は見送ってください。「ビルドとテストは別物だから」という理由だけでジョブを割るのは、読みやすさと引き換えに毎回の固定費と受け渡しの記述を買う取引です。特に、前段の出力を1つ渡すためだけにoutputsとneedsを書く構成は、後から読む人にとって最も追いにくい形になりました。同じ処理が1ジョブ内の連続したステップで済むなら、そのほうが壊れません。なお、テストが依存するDBやキャッシュはジョブを割らずにサービスコンテナ(services)として同じジョブへ付ける形が扱いやすくなります。

ジョブの粒度は、引き渡した後で最も直しにくい部分でもあります。テストの追加やデプロイ先の変更は局所で済みますが、依存グラフの組み替えはワークフロー全体の書き換えになるためです。既存パイプラインの引き取りや内製化の段取りを含めて相談したい場合は、保守運用・内製化支援で個別の構成に沿って設計から見直せます。テスト実行そのものの組み方はGitHub Actionsでビルド・自動テストを設定する方法が土台になります。

よくある質問

needsの記法と伝播について、実装中に詰まりやすい点をまとめます。

needsには複数のジョブを指定できますか?

指定できます。値は文字列でも文字列の配列でも受け付けるため、1本ならneeds: build、複数ならneeds: [build, lint]と書きます。配列に並べた全ジョブが成功して完了するまで、そのジョブは開始しません。並べた順序に意味はなく、待ち合わせの条件として扱われます。

依存ジョブの依存ジョブの出力を読めますか?

読めません。needsコンテキストに入るのは直接の依存として定義されたジョブだけで、公式リファレンスも暗黙の依存は含まないと明記しています。存在しないプロパティの参照は空文字として評価されるためエラーにもなりません。読みたいジョブ名をneedsの配列へ書き足すのが確実です。

前段が失敗してもデプロイの後始末を走らせるには?

ジョブのifへ条件式を書き、既定の成功判定を外してください。if: ${{ !cancelled() }}と書けば、前段が失敗しても取り消し以外は走ります。always()でも動きますが、取り消し操作でも止まらずタイムアウトまで居座るため、公式は否定形を促しています。

ジョブの出力に大きなデータを載せても問題ありませんか?

上限に触れます。出力は1ジョブあたり最大1MB、1回の実行の合計で最大50MBで、サイズはUTF-16換算の概算です。ビルド成果物やテストレポートのような塊はアーティファクトへ回してください。outputsへ載せてよいのは、後続が分岐や引数に使う短い値だけです。

マトリックスのジョブから出力を受け取るとどうなりますか?

展開された全ジョブの出力が合成されます。ただし実行順は保証されないため、同じ出力名を全セルで使うと最後に走ったジョブの値で上書きされます。名前へmatrixの値を織り込んで一意にしたうえで、受け取る側はtoJSONで全体をJSONとして読むのが安全です。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.09 テックブログ IDCFクラウド(IDCフロンティア)不正アクセス・ランサムウェア:影響先・復旧・データは戻るか
  2. 2026.10.08 テックブログ 大阪公立大学のランサムウェア被害と仮想化基盤の停止|全授業休講に至った経緯とバックアップを守る設定
  3. 2024.11.08 テックブログ OpenAPI GeneratorでJavaコードを自動生成する方法|CLI導入からSpring・ライブラリ選択まで
  4. 2026.10.09 テックブログ ニッスイのサイバー攻撃で日水物流の入出荷停止|委託先クラウド障害に荷主が備える手順
  5. 2026.10.09 テックブログ 京王電鉄のランサムウェア被害とグループ共通基盤:決済・ポイント・予約が止まった範囲と遮断の初動

RELATED POSTS 関連記事

目次