LESSON 40分

ワークフロー構文を理解しよう

ストーリー

「Hello Worldは動いたな。次はワークフローYAMLの構文を体系的に学ぼう」

木村先輩がエディタにワークフローファイルを開いた。

「YAMLの各キーワードには明確な意味がある。 on はいつ実行するか、jobs は何を実行するか、 steps はどう実行するか、runs-on はどこで実行するか。 これらを組み合わせて、思い通りのパイプラインを作れるようになろう」


ワークフローファイルの構造

全体構造

yaml
# ワークフローの名前(GitHub UIに表示される)
name: CI Pipeline

# トリガー条件
on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

# 環境変数(ワークフロー全体で使用)
env:
  NODE_VERSION: '20'
  CI: true

# 権限設定
permissions:
  contents: read
  pull-requests: write

# ジョブ定義
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm run lint

  test:
    needs: lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm test

on - トリガー条件

基本的なトリガー

yaml
# 単一のイベント
on: push

# 複数のイベント
on: [push, pull_request]

# 詳細なフィルタ
on:
  push:
    branches:
      - main
      - 'release/**'     # release/ で始まるブランチ
    paths:
      - 'src/**'          # src/ 以下の変更のみ
      - '!src/**/*.md'    # Markdownは除外
    tags:
      - 'v*'              # vで始まるタグ

スケジュール実行

yaml
on:
  schedule:
    # cron構文: 分 時 日 月 曜日
    - cron: '0 9 * * 1-5'    # 平日 9:00 UTC に実行
    - cron: '0 0 * * 0'      # 毎週日曜 0:00 UTC に実行

手動実行(workflow_dispatch)

yaml
on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'デプロイ先環境'
        required: true
        default: 'staging'
        type: choice
        options:
          - staging
          - production
      dry_run:
        description: 'ドライラン実行'
        required: false
        type: boolean
        default: false

jobs - ジョブ定義

ジョブの基本構造

yaml
jobs:
  # ジョブID(任意の名前)
  build:
    # ジョブの表示名
    name: Build Application
    # 実行環境
    runs-on: ubuntu-latest
    # タイムアウト(分)
    timeout-minutes: 15
    # ステップ
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run build

ジョブ間の依存関係(needs)

yaml
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm run lint

  test:
    needs: lint                    # lint完了後に実行
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm test

  build:
    needs: [lint, test]           # lint と test の両方が完了後に実行
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm run build

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - run: echo "Deploying..."
ジョブの実行順序

  [lint]              ← 最初に実行
    │
    ▼
  [test]              ← lint完了後
    │
    ▼
  [build]             ← test完了後
    │
    ▼
  [deploy]            ← build完了後

並列実行

needs を指定しないジョブは並列に実行されます。

yaml
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - run: npm run lint

  test-unit:
    runs-on: ubuntu-latest       # lint と並列実行
    steps:
      - run: npm run test:unit

  test-integration:
    runs-on: ubuntu-latest       # lint、test-unit と並列実行
    steps:
      - run: npm run test:integration
並列実行の図

  ┌──── [lint] ────┐
  │                 │
  ├── [test-unit] ──┤    ← 3つ同時に実行
  │                 │
  └── [test-integ] ─┘

steps - ステップ定義

ステップの詳細オプション

yaml
steps:
  # 名前付きステップ
  - name: Install dependencies
    run: npm ci

  # 条件付き実行(if)
  - name: Deploy to production
    if: github.ref == 'refs/heads/main'
    run: npm run deploy

  # 失敗しても続行
  - name: Optional lint check
    continue-on-error: true
    run: npm run lint

  # 環境変数の設定
  - name: Run tests
    env:
      DATABASE_URL: postgresql://localhost:5432/test
      NODE_ENV: test
    run: npm test

  # 作業ディレクトリの変更
  - name: Build frontend
    working-directory: ./frontend
    run: npm run build

  # シェルの指定
  - name: PowerShell step
    shell: pwsh
    run: Write-Host "Hello from PowerShell"

ステップの出力(outputs)

あるステップの出力を後続のステップで使う方法です。

yaml
steps:
  - name: Get version
    id: version
    run: |
      VERSION=$(cat package.json | jq -r '.version')
      echo "version=$VERSION" >> $GITHUB_OUTPUT

  - name: Use version
    run: echo "Version is ${{ steps.version.outputs.version }}"

環境変数とコンテキスト

環境変数の3つのスコープ

yaml
# ワークフロー全体
env:
  APP_NAME: my-app

jobs:
  build:
    # ジョブレベル
    env:
      NODE_ENV: production
    runs-on: ubuntu-latest
    steps:
      - name: Build
        # ステップレベル
        env:
          BUILD_TARGET: dist
        run: |
          echo "App: $APP_NAME"
          echo "Env: $NODE_ENV"
          echo "Target: $BUILD_TARGET"

GitHubコンテキスト

yaml
steps:
  - name: Show context
    run: |
      echo "Repository: ${{ github.repository }}"
      echo "Branch:     ${{ github.ref_name }}"
      echo "SHA:        ${{ github.sha }}"
      echo "Actor:      ${{ github.actor }}"
      echo "Event:      ${{ github.event_name }}"
      echo "Run ID:     ${{ github.run_id }}"
      echo "Run Number: ${{ github.run_number }}"

主要なコンテキスト

コンテキスト説明
githubイベント情報github.sha, github.ref
env環境変数env.NODE_ENV
secretsシークレットsecrets.AWS_ACCESS_KEY
stepsステップ出力steps.build.outputs.version
jobsジョブ出力jobs.build.outputs.artifact
runnerランナー情報runner.os, runner.arch

条件分岐(if)

よく使う条件式

yaml
jobs:
  deploy:
    runs-on: ubuntu-latest
    # mainブランチへのpushの場合のみ実行
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    steps:
      - run: echo "Deploying..."

  notify-failure:
    runs-on: ubuntu-latest
    # 前のジョブが失敗した場合のみ実行
    if: failure()
    steps:
      - run: echo "Something failed!"

  always-run:
    runs-on: ubuntu-latest
    # 常に実行(失敗しても)
    if: always()
    steps:
      - run: echo "This always runs"

条件式のパターン

条件構文
mainブランチのみif: github.ref == 'refs/heads/main'
PRのみif: github.event_name == 'pull_request'
タグのみif: startsWith(github.ref, 'refs/tags/')
前のジョブ成功時if: success()
前のジョブ失敗時if: failure()
常に実行if: always()
特定のアクターif: github.actor == 'username'

実践的なワークフロー例

Node.js プロジェクトのCI

yaml
name: Node.js CI

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

env:
  NODE_VERSION: '20'

permissions:
  contents: read

jobs:
  lint:
    name: Code Quality
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run ESLint
        run: npm run lint

      - name: Check types
        run: npm run typecheck

  test:
    name: Tests
    needs: lint
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npm test -- --coverage

      - name: Upload coverage
        if: github.event_name == 'pull_request'
        uses: actions/upload-artifact@v4
        with:
          name: coverage-report
          path: coverage/

  build:
    name: Build
    needs: test
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npm run build

      - name: Upload build artifact
        uses: actions/upload-artifact@v4
        with:
          name: build
          path: dist/

まとめ

ポイント内容
onトリガー条件(push, PR, schedule, workflow_dispatch)
jobsジョブ定義、needs で依存関係を指定
stepsuses でAction、run でコマンド、if で条件分岐
コンテキストgithub.*, secrets.*, steps.* 等で動的な値を参照

チェックリスト

  • ワークフローYAMLの全体構造を把握した
  • on でのトリガー条件の指定方法を理解した
  • needs によるジョブ間の依存関係を設定できる
  • 環境変数とコンテキストの使い方を理解した
  • if による条件分岐のパターンを把握した

次のステップへ

次のセクションでは、トリガーとイベントについてさらに深掘りします。 プッシュ、PR、スケジュールなど、さまざまなイベントの活用方法を学びましょう。


推定読了時間: 40分