GitHub Actions 講座 #2 文法の核心 — コンテキストと式、if 条件、値の受け渡し

読了 5分

前回のワークフローは上から下へ流れるだけでした。実戦のワークフローは条件によってステップをスキップし、前のステップの結果を後ろで使い、ジョブ同士が順序と値をやり取りします。今回はそれを可能にする 4 つの文法、コンテキスト、式、if 条件、値の受け渡しを扱います。この 4 つが手になじめば、以降の回のどんなワークフローでも文法でつまずくことはありません。

コンテキスト — 実行情報が入ったオブジェクト #

ワークフローの中で ${{ ... }} で囲むと式になり、式の中では実行情報を持つコンテキストにアクセスできます。よく使うものから並べます。

コンテキスト入っているもの代表的な使用例
githubイベント・リポジトリ・ブランチ情報github.ref、github.event_name、github.sha
env環境変数env.MY_VAR
steps前のステップの出力・結果steps.build.outputs.version
needs前のジョブの出力・結果needs.test.result
secretsシークレット(次回)secrets.AWS_ROLE_ARN
runnerランナー情報runner.os、runner.temp

たとえば github.event_name はこの実行を起動したイベントの名前なので、同じワークフローが push で来たのか pull_request で来たのかを区別できます。コンテキストの全一覧を暗記する必要はなく、「いまの実行に関する情報はどこかのコンテキストにある」という感覚を持って、必要なときにドキュメントを引けば十分です。デバッグのコツを 1 つ添えると、${{ toJSON(github) }} を run: echo で出力してみるのが、コンテキストの中身を丸ごと確認する一番速い方法です。

if 条件 — ステップとジョブを選んで実行 #

if はステップとジョブの両方に付けられます。代表的なパターン 3 つで実戦の大半は片付きます。

.github/workflows/deploy.yml
jobs:
  deploy:
    # 1. main ブランチへの push のときだけジョブを実行
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: ./deploy.sh

      # 2. 前のステップが失敗したときだけ実行
      - run: ./rollback.sh
        if: failure()

      # 3. 成功でも失敗でも常に実行(後片付け)
      - run: ./cleanup.sh
        if: always()

if の中では ${{ }} を省略しても式として解釈されます。重要なのは 4 つのステータス関数の区別です。デフォルトは success()(前がすべて成功したときだけ)なので、何も付けていないステップは前が失敗するとスキップされます。failure() は失敗時のみ、always() はキャンセルでない限り常に、cancelled() はキャンセル時のみ実行します。失敗の通知と後片付けが、これらの関数の定番の用途です。

ステップ間の値の受け渡し — GITHUB_OUTPUT #

前のステップで計算した値(バージョン番号、イメージタグ)を後ろのステップで使うには、出力として宣言します。ステップに id を付け、$GITHUB_OUTPUT ファイルに 名前=値 を書き込む方式です。

.github/workflows/build.yml
      - name: version を計算
        id: meta
        run: echo "version=1.4.$(date +%Y%m%d)" >> "$GITHUB_OUTPUT"

      - name: ビルド
        run: ./build.sh --version "${{ steps.meta.outputs.version }}"

受け取る側のアドレスは steps.<id>.outputs.<名前> です。環境変数として渡す $GITHUB_ENV(以降のすべてのステップの env に注入)もありますが、使い分けの基準は単純で、特定の値を特定のステップへ渡すなら OUTPUT、以降の全体で環境変数として使いたいなら ENV です。出力のほうが出どころが明示的なので、デフォルトとしては OUTPUT をおすすめします。

ジョブ間の順序と値 — needs と outputs #

ジョブはデフォルトで並列なので、順序が必要なら needs で宣言します。値を渡すには、ジョブレベルの outputs にステップの出力を引き上げます。

.github/workflows/build-deploy.yml
jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.meta.outputs.version }}
    steps:
      - id: meta
        run: echo "version=1.4.0" >> "$GITHUB_OUTPUT"

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - run: echo "デプロイするバージョン ${{ needs.build.outputs.version }}"

needs: build が順序(build の成功後に deploy)とデータの通り道(needs.build.outputs)の両方を作ります。ジョブは別々のランナーで動くため、ファイルは自動では渡らないという点だけ注意します。文字列の値は outputs で、ビルド成果物のようなファイルは #4 で扱うアーティファクトで渡します。

env — 3 層の環境変数 #

環境変数はワークフロー、ジョブ、ステップの 3 つのレベルで宣言でき、内側が勝ちます。

.github/workflows/env-demo.yml
env:
  APP_NAME: myapp          # ワークフロー全体

jobs:
  test:
    env:
      STAGE: ci            # このジョブ全体
    steps:
      - run: echo "$APP_NAME / $STAGE / $DEBUG"
        env:
          DEBUG: "true"    # このステップのみ

シェルで使う値はこのように env で宣言して $名前 で読むのが安全です。${{ }} を run スクリプトへ直接埋め込む方式は便利に見えますが、#9 で扱うスクリプトインジェクションの入口になるため、外部から来た値は必ず env を経由してシェル変数として読む習慣を今から付けておきましょう。

まとめ #

今回扱った内容です。

  • ${{ }} の式の中で github、env、steps、needs といったコンテキストから実行情報にアクセスします。toJSON() の出力がデバッグの近道です
  • if はステップ・ジョブの両方に付き、デフォルトは success() です。failure() で失敗対応、always() で後片付けを仕掛けます
  • ステップ間の値は $GITHUB_OUTPUT に書き、steps.<id>.outputs.<名前> で受け取ります
  • ジョブ間では needs が順序と値の通り道を作り、ファイルは自動では渡りません
  • 環境変数はワークフロー・ジョブ・ステップの 3 層で、外部の値は env を経由してシェル変数として読むのがインジェクションを防ぐ習慣です

次回(#3 シークレット・変数・環境)では、ワークフローに秘密の値を安全に入れる方法を扱います。secrets と vars の使い分け、そしてデプロイの承認ゲートを作る environment です。

X