ウェブエンジニア問題集
第4章

コンテキストと変数 — ${{ }}構文・env・secretsの使い分け

7
この章の目次開く

ここまでのワークフローは、毎回同じコマンドを実行するだけの「静的」なものでした。実務のワークフローでは、状況に応じたデータを扱う必要が出てきます。

  • 「いまどのブランチで動いているのか」を知りたい
  • APIキーやトークンを、コードに書かずに使いたい
  • 「mainブランチのときだけこのステップを実行」のような条件分岐をしたい

この章では、その道具立てであるコンテキスト・変数・シークレット・条件式をまとめて整理します。

${{ }} — 式を埋め込む構文

YAMLの中に ${{ }} で囲んだ式を書くと、実行時に評価されて値に置き換わります。

steps:
  - name: ブランチ名を表示する
    run: echo "いまのブランチは ${{ github.ref_name }} です"
yaml

mainブランチへのpushで動けば、ログには いまのブランチは main です と出力されます。この github.ref_name のような「実行時の情報が詰まったオブジェクト」をコンテキストと呼びます。

コンテキスト — 実行時情報の詰め合わせ

よく使うコンテキストは次の4つです。

コンテキスト中身
githubイベントやリポジトリの情報github.ref_name(ブランチ名)、github.actor(実行者)、github.event_name(イベント種別)
env自分で定義した環境変数env.NODE_ENV
secrets登録した秘密情報secrets.API_TOKEN
inputsworkflow_dispatchなどの入力値inputs.environment
学習者学習者

github コンテキストの中身って、どうやったら全部見られるの?毎回リファレンスを調べるしかない?

デバッグに便利な小技があります。toJSON 関数でコンテキストを丸ごとダンプできるんです。

  - name: githubコンテキストの中身を全部見る
    run: echo '${{ toJSON(github) }}'
yaml

「この情報どこに入ってるんだっけ?」となったら、まずこれで中身を覗くのが手っ取り早い方法です。

全コンテキストの一覧はコンテキストリファレンス(公式ドキュメント)にあります。

env — 環境変数を定義する

env: キーで環境変数を定義できます。ポイントは定義する場所によって有効範囲が変わることです。

name: Env Demo
 
on: push
 
env:
  APP_NAME: my-app          # ① ワークフロー全体で有効
 
jobs:
  build:
    runs-on: ubuntu-latest
    env:
      NODE_ENV: production  # ② このジョブ内で有効
    steps:
      - name: ビルドする
        env:
          DEBUG: 'true'     # ③ このステップ内でのみ有効
        run: |
          echo "$APP_NAME を $NODE_ENV でビルド (DEBUG=$DEBUG)"
yaml
定義場所スコープ
ワークフロー直下の env全ジョブ・全ステップ
ジョブ直下の envそのジョブの全ステップ
ステップ内の envそのステップのみ

run のシェルの中では $APP_NAME のように普通の環境変数として参照できます。一方、if:with: などYAML側で参照するときは ${{ env.APP_NAME }} と書きます。「シェルの中では $名前、YAMLの中では ${{ env.名前 }}」と覚えておきましょう。

secrets — 秘密情報を安全に扱う

デプロイ用のトークンや外部APIのキーを、YAMLに直書きするのは絶対にやってはいけません。リポジトリを見られる人全員に漏れますし、Gitの履歴に永久に残ります。

画面を見て驚く人

GitHub Actionsには専用の保管場所としてSecretsが用意されています。登録は、リポジトリの Settings → Secrets and variables → Actions → New repository secret から行います。名前(例: API_TOKEN)と値を入れて保存すると、ワークフローから参照できるようになります。

  - name: APIを呼び出す
    env:
      API_TOKEN: ${{ secrets.API_TOKEN }}
    run: curl -H "Authorization: Bearer $API_TOKEN" https://api.example.com/deploy
yaml

Secretsには次の性質があります。

  • 一度登録したら、値は誰にも見えない(再表示は不可。上書きのみ)
  • ログに出力しようとすると *** にマスクされる
  • Pull Requestを出したフォーク元の第三者からはアクセスできない

Variables — 秘密ではない設定値

同じ設定画面にVariablesというタブもあります。こちらは「秘密ではないが、YAMLにハードコードしたくない設定値」の置き場所です。

SecretsVariables
用途APIキー、トークン、パスワードアプリ名、デプロイ先URL、Nodeバージョン
参照${{ secrets.名前 }}${{ vars.名前 }}
登録後の閲覧不可(マスクされる)可能

「漏れたら困るならSecrets、そうでないならVariables」というシンプルな基準で使い分けます。

if — 条件付きでステップを実行する

if: を付けると、条件を満たすときだけジョブやステップを実行できます。

  - name: mainブランチのときだけデプロイする
    if: github.ref_name == 'main'
    run: echo "デプロイします"
yaml

if: の中は最初から式として評価されるので、${{ }} は省略できます(付けても動きます)。よく使う条件のパターンを挙げます。

# イベント種別で分岐
if: github.event_name == 'pull_request'
 
# 複数条件(&& / ||)
if: github.ref_name == 'main' && github.event_name == 'push'
 
# 前のステップが失敗しても実行する
if: failure()
 
# 成否にかかわらず必ず実行する(後片付けなど)
if: always()
yaml

failure()always()ステータス関数と呼ばれ、「テストが落ちたときだけ通知する」「一時ファイルは必ず消す」といった制御に使います。

ステップ間で値を受け渡す — outputs

あるステップの計算結果を、後のステップで使いたいことがあります。その受け渡し口がoutputsです。ステップ側では $GITHUB_OUTPUT という特殊ファイルに 名前=値 を書き込みます。

  - name: バージョンを調べる
    id: version                     # 後から参照するためのIDを付ける
    run: echo "value=$(node -p "require('./package.json').version")" >> "$GITHUB_OUTPUT"
 
  - name: バージョンを使う
    run: echo "リリースするバージョンは ${{ steps.version.outputs.value }}"
yaml

流れを整理すると:

  1. 値を作るステップに id: を付ける
  2. echo "名前=値" >> "$GITHUB_OUTPUT" で書き込む
  3. 後続ステップから steps.<id>.outputs.<名前> で参照する
メンターメンター

最初は「そういう書き方をするもの」と割り切ってコピペで構いません。ジョブをまたいだ受け渡し(jobsのoutputs)も同じ発想で、あとの章でも登場します。

まとめ

  • ${{ }} は実行時に評価される式。コンテキスト(github / env / secrets / inputs など)の値を参照できる
  • env は定義場所(ワークフロー/ジョブ/ステップ)でスコープが決まる。シェル内は $名前、YAML内は ${{ env.名前 }}
  • 秘密情報はSecretsへ。YAML直書きは厳禁、ログでは自動マスクされる
  • 秘密でない設定値はVariables(${{ vars.名前 }})に分ける
  • if: で条件実行。failure() / always() で失敗時・常時実行の制御ができる
  • ステップ間の値の受け渡しは id + $GITHUB_OUTPUT + steps.<id>.outputs.<名前>

次章では、ジョブとランナーの実行モデルを掘り下げます。複数ジョブの並列実行、needs による依存関係、そして複数バージョンでまとめてテストするマトリックスビルドを扱います。