再利用WorkflowのSecrets、inheritでも孫には自動で届かない

|著者: QUASA編集チーム|2 分で読めます
再利用WorkflowのSecrets、inheritでも孫には自動で届かない

GitHub Actionsの再利用可能Workflowでは、secrets: inheritでSecretを渡しても、次の呼び出しへは自動転送されない。GitHubの構文リファレンスは、入れ子の再利用可能WorkflowへSecretを渡す際、呼び出しジョブでsecretsを再指定するよう定めている。A→B→Cなら、AがBに渡しただけではCには届かない。

Cでも同じSecretを使うには、BがCを呼ぶジョブで名前を指定して渡すか、secrets: inheritを指定する。値を受け取る経路とは別に、実行ジョブのenvironmentが同名Secretを上書きするか、GITHUB_TOKENに必要な権限が残っているかも確認する必要がある。

A→B→Cの最小構成

以下は、同じリポジトリの.github/workflowsにA.yml、B.yml、C.ymlを置く仮の設定例だ。Aは起点のWorkflow、BとCはon: workflow_callを持つ再利用可能Workflowとする。リポジトリにRELEASE_KEYというSecretがあり、CではDEPLOY_KEYという名前で使いたい場合を考える。

  • A.yml:jobs.call_b.usesに./.github/workflows/B.ymlを指定する。同じ呼び出しジョブのsecretsをinheritにすると、Aから直接呼び出すBへ利用可能なSecretsを渡す設定になる。
  • B.yml:jobs.call_c.usesに./.github/workflows/C.ymlを指定する。Cに渡すには、その呼び出しジョブにsecretsを設け、DEPLOY_KEY: ${{ secrets.RELEASE_KEY }}と対応付ける。Bが受け取ったRELEASE_KEYを、Cが求める名前で渡す設定だ。
  • C.yml:on.workflow_call.secretsにDEPLOY_KEYを定義する。渡し忘れを検出したければrequired: trueを指定し、値を使うジョブでは${{ secrets.DEPLOY_KEY }}を参照する。

Aから受け取った名前をBでもDEPLOY_KEYにしたいなら、Aの呼び出しジョブでDEPLOY_KEY: ${{ secrets.RELEASE_KEY }}と明示してもよい。その場合、BからCへの指定はDEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}になる。呼び出し元で参照する名前と呼び出し先が受け取る名前を分けて考えると、どの境界で値が途切れたかを追いやすい。

ここでいうA→B→Cは条件を示すための例であり、実行結果を報告するものではない。各呼び出しはジョブのusesで行い、呼び出す側のジョブに通常のステップを並べる形にはしない。

明示渡しとinheritの到達範囲

明示渡しでは、呼び出し先が受け取るSecret名ごとに、呼び出し元の値を対応付ける。CがDEPLOY_KEYだけを必要とするなら、B→Cでその名前だけを指定すれば、受け渡す対象が設定から分かる。名前を指定したSecretは、Cのon.workflow_call.secretsで宣言した名前と一致させる必要がある。

inheritは呼び出し元が利用できるSecretsを直接の呼び出し先へまとめて渡す。同じorganization内、または同じenterprise内で認められた呼び出し関係で使える。inheritで受け取ったSecretは、呼び出し先のon.workflow_call.secretsに明記していなくても参照できるが、そこから別のWorkflowを呼ぶときは新たな受け渡し設定が要る。

上の仮の構成で、呼び出し境界ごとの結果を並べると次のとおりだ。

  • A→Bがinherit、B→Cにsecrets指定なし:BにはAから渡したSecretが届く。Cへの受け渡しは設定されていない。
  • A→Bがinherit、B→CでDEPLOY_KEYを明示渡し:Bが参照できるSecretを指定した名前でCへ渡す。Cが受け取るのは、Bがその呼び出しで指定したSecretだ。
  • A→Bで必要なSecretだけを明示渡し、B→Cがinherit:CにはBが利用できるSecretsを渡す。AがBに渡していないSecretが、この指定だけでAから届くわけではない。

CのDEPLOY_KEYをrequired: trueにすれば、呼び出し元がその名前を渡していない設定を検出できる。ただし、requiredが確認するのは受け渡しの有無であり、渡した値が空文字でないことまでは保証しない。値そのものが処理に必須なら、使用するジョブで空値を検出して失敗させる。Secretの中身をログに出す必要はない。

environment secretが同名ならジョブ側が優先する

再利用可能Workflowの実行ジョブにenvironmentを設定した場合、その環境にある同名SecretがリポジトリまたはorganizationのSecretより優先する。GitHubの再利用手順は、この優先順位が呼び出し元のsecrets: inheritにも、名前を指定した受け渡しにも適用されると説明している。再利用可能Workflowを呼ぶだけのジョブにはenvironmentを指定できず、環境は呼び出し先の実行ジョブに設定する。

たとえばAからBへリポジトリのDEPLOY_KEYを渡し、Bの実行ジョブがenvironment: productionを指定しているとする。productionにもDEPLOY_KEYが登録されていれば、そのジョブの${{ secrets.DEPLOY_KEY }}はproduction側の値を参照する。Aのsecrets設定だけを見ても、実行時に使われる値は確定しない。

Secretが環境にしか登録されていない構成でも、呼び出し元からの受け渡し指定を省くと、再利用可能Workflow側で空文字として解決され、実行時にエラーが出ない場合がある。名前を指定して渡す方法も用意されている。required: trueは渡された値の中身を検査しないため、環境名、同名Secret、呼び出し時のsecrets指定をそれぞれ確認する必要がある。

GITHUB_TOKENの権限とenvは別に追う

SecretがCに届いても、GITHUB_TOKENの権限不足で処理が失敗することがある。GitHubの再利用Workflow制約によれば、入れ子のWorkflowに渡るGITHUB_TOKEN権限は維持または縮小のみで、下位から昇格できない。また、呼び出し元を含めて接続できるWorkflowは最大10階層、最上位の単一Workflowファイルから呼べる固有の再利用可能Workflowは、入れ子を含め最大50件だ。

仮にAの呼び出しジョブでpermissionsにpackages: readだけを与えたなら、BやCでpackages: writeを指定しても、Cにパッケージ公開用の書き込み権限は生まれない。Cで書き込みが必要な構成なら、Aの呼び出しジョブから必要な権限を与え、途中の呼び出しで縮小していないか確認する。DEPLOY_KEYなどのSecretを渡す設定と、GITHUB_TOKENのpermissionsは別々に扱う。

呼び出し元のWorkflow全体で定義したenvも、呼び出し先には伝播しない。BやCがその設定値を必要とするなら、Workflowの入力として渡すか、利用できるvarsを用意する。envに置いた通常の設定値、secretsに置いた機密値、permissionsで定めるトークン権限は、似た失敗を招いても参照する設定箇所が異なる。

空値と権限不足の切り分け

Cで値が空に見える場合は、まずCが参照するSecret名と、Bの呼び出しジョブが渡す名前を照合する。A→Bにinheritがあっても、B→Cに指定がなければそのSecretは転送されない。Bが別名で受け取っているなら、B→Cの対応付けで参照する名前も合わせる。

次に、値を使うジョブのenvironmentを確認する。同名のenvironment secretがあれば、そのジョブで使う値は呼び出し元から渡したリポジトリやorganizationの値と異なり得る。受け渡し自体が欠けている可能性と、同名Secretの優先順位によって値が変わる可能性を分けて調べる。

値は利用できるのにGitHub Packagesなどへの書き込みが失敗するなら、Aの呼び出しジョブからCまでのpermissionsを追う。下位にpackages: writeを書き足しても、上位でpackages: readまでに絞られていれば権限は増えない。Secret名、実行ジョブのenvironment、各段階のpermissionsを順に見れば、どの設定が処理を止めているかを特定できる。

関連記事:

共有:

ニュースレターを購読

Web3、AI、暗号資産の最新ニュースを受信箱にお届けします。

0