0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

資料 - ワークフローとアクション

Posted at

資料 - ワークフローとアクション

1. はじめに

GitHub Actionsは、リポジトリ内でCI/CDパイプラインを構築できる強力な自動化プラットフォームです。本記事では、GitHub Actionsの公式ドキュメントをもとに、実務で必要となる全ての機能を体系的に解説します。

2. 目次

  1. はじめに
  2. 目次
  3. ワークフローの基本構造
  4. イベントトリガーの仕組み
  5. 変数とコンテキスト
  6. 式評価とステータスチェック
  7. ワークフローコマンド
  8. 依存関係のキャッシング
  9. 再利用可能なワークフロー
  10. 環境とデプロイメント保護
  11. カスタムアクションの作成
  12. 実践的なパターン
  13. トラブルシューティング
  14. まとめ

3. ワークフローの基本構造

3.1 YAMLファイルの配置

ワークフローファイルは .github/workflows/ ディレクトリに配置します。ファイル拡張子は .yml または .yaml です。

3.2 基本的な構成要素

name: "CI パイプライン"

on: 
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

env:
  NODE_VERSION: '20'
  DATABASE_URL: ${{ secrets.DATABASE_URL }}

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: "リポジトリのチェックアウト"
        uses: actions/checkout@v5
      
      - name: "Node.js のセットアップ"
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
      
      - name: "依存関係のインストール"
        run: npm ci
      
      - name: "テストの実行"
        run: npm test

3.3 ワークフロー実行の流れ

3.4 主要なキーワード

キーワード 説明 必須
name ワークフロー名 任意
on トリガーイベント 必須
jobs 実行するジョブの定義 必須
runs-on ランナーの種類 必須
steps ジョブ内の処理ステップ 必須
env 環境変数 任意
permissions トークンの権限設定 任意
concurrency 並行実行制御 任意

4. イベントトリガーの仕組み

4.1 主要なイベント一覧

GitHub Actionsは30種類以上のイベントに対応していますが、実務でよく使用するものを紹介します。

4.2 push イベント

コミットがプッシュされたときにトリガーされます。

on:
  push:
    branches:
      - main
      - 'releases/**'
    paths:
      - '**.js'
      - '!docs/**'
    tags:
      - v1.*

フィルタリングオプション:

  • branches / branches-ignore: ブランチ名でフィルタ
  • paths / paths-ignore: ファイルパスでフィルタ
  • tags / tags-ignore: タグ名でフィルタ

4.3 pull_request イベント

プルリクエストに関連するアクションでトリガーされます。

on:
  pull_request:
    types:
      - opened
      - synchronize
      - reopened
    branches:
      - main
    paths:
      - 'src/**'

アクティビティタイプ:

  • opened: プルリクエスト作成時
  • synchronize: プルリクエストへのプッシュ時
  • reopened: プルリクエスト再オープン時
  • closed: プルリクエストクローズ時
  • labeled / unlabeled: ラベル変更時
  • assigned / unassigned: アサイン変更時
  • review_requested: レビュー依頼時

4.4 schedule イベント

cron式を使用して定期実行します。

on:
  schedule:
    # 毎日午前9時(UTC)に実行
    - cron: '0 9 * * *'
    # 月曜日と木曜日の午前5時30分(UTC)に実行
    - cron: '30 5 * * 1,4'

cron式の構文:

┌─────────── 分 (0 - 59)
│ ┌───────── 時 (0 - 23)
│ │ ┌─────── 日 (1 - 31)
│ │ │ ┌───── 月 (1 - 12)
│ │ │ │ ┌─── 曜日 (0 - 6, 日曜=0)
│ │ │ │ │
* * * * *

4.5 workflow_dispatch イベント

手動でワークフローを実行する場合に使用します。

on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'デプロイ先の環境'
        required: true
        type: choice
        options:
          - development
          - staging
          - production
      logLevel:
        description: 'ログレベル'
        required: true
        default: 'warning'
        type: choice
        options:
          - info
          - warning
          - debug
      dryRun:
        description: 'ドライラン実行'
        required: false
        type: boolean
        default: false

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: "デプロイ実行"
        run: |
          echo "環境: ${{ inputs.environment }}"
          echo "ログレベル: ${{ inputs.logLevel }}"
          echo "ドライラン: ${{ inputs.dryRun }}"

4.6 workflow_run イベント

他のワークフロー完了時にトリガーされます。

on:
  workflow_run:
    workflows: ["ビルドワークフロー"]
    types:
      - completed
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    steps:
      - name: "成功時のデプロイ"
        run: echo "デプロイを開始します"

4.7 イベントのフィルタリングパターン

4.7.1 グロブパターンの使用

on:
  push:
    branches:
      # feature/で始まる全てのブランチ
      - 'feature/**'
      # releases/10 のような形式
      - 'releases/[0-9]+'
      # v1.0.0 のようなタグ
    tags:
      - 'v[0-9]+.[0-9]+.[0-9]+'

4.7.2 否定パターン

on:
  push:
    branches:
      - 'releases/**'
      # releases/**-alpha を除外
      - '!releases/**-alpha'

5. 変数とコンテキスト

5.1 デフォルト環境変数

GitHub Actionsが自動的に設定する環境変数です。

jobs:
  show-info:
    runs-on: ubuntu-latest
    steps:
      - name: "環境変数の表示"
        run: |
          echo "リポジトリ: $GITHUB_REPOSITORY"
          echo "ブランチ: $GITHUB_REF_NAME"
          echo "SHA: $GITHUB_SHA"
          echo "アクター: $GITHUB_ACTOR"
          echo "ワークフロー名: $GITHUB_WORKFLOW"
          echo "実行番号: $GITHUB_RUN_NUMBER"

主要な環境変数:

変数名 説明
GITHUB_REPOSITORY リポジトリの所有者と名前 yamada-taro/my-app
GITHUB_REF ブランチまたはタグの完全な ref refs/heads/main
GITHUB_REF_NAME ブランチまたはタグ名 main
GITHUB_SHA コミット SHA ffac537e6cbbf934b08745a378932722df287a53
GITHUB_ACTOR ワークフローを開始したユーザー名 yamada-taro
GITHUB_WORKSPACE ワークスペースディレクトリのパス /home/runner/work/my-app/my-app
RUNNER_OS ランナーのOS Linux, Windows, macOS

5.2 コンテキストの使用

コンテキストは ${{ }} 構文でアクセスします。

5.2.1 github コンテキスト

jobs:
  context-demo:
    runs-on: ubuntu-latest
    steps:
      - name: "GitHub コンテキストの使用"
        run: |
          echo "イベント名: ${{ github.event_name }}"
          echo "リポジトリ: ${{ github.repository }}"
          echo "ブランチ: ${{ github.ref_name }}"
          echo "アクター: ${{ github.actor }}"
          echo "ジョブID: ${{ github.job }}"

5.2.2 env コンテキスト

env:
  GLOBAL_VAR: "グローバル変数"

jobs:
  env-demo:
    runs-on: ubuntu-latest
    env:
      JOB_VAR: "ジョブレベル変数"
    steps:
      - name: "環境変数の使用"
        env:
          STEP_VAR: "ステップレベル変数"
        run: |
          echo "グローバル: ${{ env.GLOBAL_VAR }}"
          echo "ジョブ: ${{ env.JOB_VAR }}"
          echo "ステップ: ${{ env.STEP_VAR }}"

優先順位: ステップ > ジョブ > ワークフロー

5.2.3 secrets コンテキスト

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: "APIキーの使用"
        env:
          API_KEY: ${{ secrets.API_KEY }}
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
        run: |
          echo "APIキーは自動的にマスクされます"
          echo $API_KEY

5.2.4 steps コンテキスト

jobs:
  multi-step:
    runs-on: ubuntu-latest
    steps:
      - name: "バージョン番号の生成"
        id: version
        run: echo "number=1.0.${{ github.run_number }}" >> $GITHUB_OUTPUT
      
      - name: "ビルド結果の出力"
        id: build
        run: |
          echo "status=success" >> $GITHUB_OUTPUT
          echo "artifact=app-v1.0.tar.gz" >> $GITHUB_OUTPUT
      
      - name: "前ステップの出力を使用"
        run: |
          echo "バージョン: ${{ steps.version.outputs.number }}"
          echo "ビルド状況: ${{ steps.build.outputs.status }}"
          echo "アーティファクト: ${{ steps.build.outputs.artifact }}"

5.2.5 needs コンテキスト

jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      build_id: ${{ steps.build.outputs.id }}
      artifact_name: ${{ steps.build.outputs.name }}
    steps:
      - id: build
        run: |
          echo "id=${{ github.run_number }}" >> $GITHUB_OUTPUT
          echo "name=app-build-${{ github.run_number }}.zip" >> $GITHUB_OUTPUT
  
  test:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: "ビルド情報の使用"
        run: |
          echo "ビルドID: ${{ needs.build.outputs.build_id }}"
          echo "アーティファクト: ${{ needs.build.outputs.artifact_name }}"

5.2.6 matrix コンテキスト

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node: [18, 20, 22]
        include:
          - os: ubuntu-latest
            node: 22
            experimental: true
    steps:
      - name: "マトリックス情報の表示"
        run: |
          echo "OS: ${{ matrix.os }}"
          echo "Node.js: ${{ matrix.node }}"
          echo "実験的: ${{ matrix.experimental }}"

5.3 設定変数(vars コンテキスト)

リポジトリ、組織、環境レベルで設定した変数にアクセスします。

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: "設定変数の使用"
        run: |
          echo "環境: ${{ vars.ENVIRONMENT_NAME }}"
          echo "APIエンドポイント: ${{ vars.API_ENDPOINT }}"
          echo "リージョン: ${{ vars.AWS_REGION }}"

5.4 コンテキストの利用可能範囲


6. 式評価とステータスチェック

6.1 基本的な演算子

jobs:
  conditional:
    runs-on: ubuntu-latest
    steps:
      - name: "等価比較"
        if: github.ref == 'refs/heads/main'
        run: echo "mainブランチです"
      
      - name: "不等価比較"
        if: github.event_name != 'schedule'
        run: echo "スケジュール実行ではありません"
      
      - name: "AND条件"
        if: github.ref == 'refs/heads/main' && github.event_name == 'push'
        run: echo "mainブランチへのpushです"
      
      - name: "OR条件"
        if: github.event_name == 'push' || github.event_name == 'pull_request'
        run: echo "pushまたはpull_requestです"

6.2 組み込み関数

6.2.1 contains 関数

jobs:
  check-labels:
    runs-on: ubuntu-latest
    if: contains(github.event.issue.labels.*.name, 'bug')
    steps:
      - name: "バグラベル付きのIssue"
        run: echo "このIssueにはbugラベルが付いています"

6.2.2 startsWith / endsWith 関数

jobs:
  branch-check:
    runs-on: ubuntu-latest
    steps:
      - name: "featureブランチのチェック"
        if: startsWith(github.ref, 'refs/heads/feature/')
        run: echo "featureブランチです"
      
      - name: "JSファイルのチェック"
        if: endsWith(github.event.head_commit.modified[0], '.js')
        run: echo "JavaScriptファイルが変更されました"

6.2.3 format 関数

jobs:
  format-demo:
    runs-on: ubuntu-latest
    steps:
      - name: "フォーマット済みメッセージ"
        run: |
          MESSAGE=$(echo '${{ format("ビルド {0} が {1} で完了しました", github.run_number, github.ref_name) }}')
          echo $MESSAGE

6.2.4 hashFiles 関数

jobs:
  cache-dependencies:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      
      - name: "依存関係のキャッシュ"
        uses: actions/cache@v4
        with:
          path: ~/.npm
          key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}

6.2.5 toJSON / fromJSON 関数

jobs:
  json-demo:
    runs-on: ubuntu-latest
    steps:
      - name: "JSONへの変換"
        run: echo '${{ toJSON(github) }}' > github-context.json
      
      - name: "JSONからの変換"
        env:
          MATRIX_JSON: '{"include":[{"os":"ubuntu-latest","node":20},{"os":"windows-latest","node":18}]}'
        run: echo '${{ fromJSON(env.MATRIX_JSON) }}'

6.3 ステータスチェック関数

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: "テスト実行"
        id: test
        run: npm test
        continue-on-error: true
      
      - name: "成功時の処理"
        if: success()
        run: echo "全てのステップが成功しました"
      
      - name: "失敗時の処理"
        if: failure()
        run: echo "いずれかのステップが失敗しました"
      
      - name: "必ず実行される処理"
        if: always()
        run: echo "結果に関わらず実行されます"
      
      - name: "キャンセル時の処理"
        if: cancelled()
        run: echo "ワークフローがキャンセルされました"

複合条件の例:

steps:
  - name: "テスト失敗かつmainブランチ"
    if: failure() && github.ref == 'refs/heads/main'
    run: echo "mainブランチでテストが失敗しました"
  
  - name: "成功またはスキップ"
    if: success() || skipped()
    run: echo "成功またはスキップされました"

6.4 三項演算子の代替パターン

GitHub Actionsには三項演算子がありませんが、&&|| を組み合わせて実現できます。

env:
  ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'production' || 'development' }}
  
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: "環境の表示"
        run: echo "デプロイ先: ${{ env.ENVIRONMENT }}"

7. ワークフローコマンド

ワークフローコマンドは、ランナーとの通信を行うための特殊な構文です。

7.1 メッセージの出力

7.1.1 デバッグメッセージ

jobs:
  debug-example:
    runs-on: ubuntu-latest
    steps:
      - name: "デバッグメッセージの出力"
        run: |
          echo "::debug::これはデバッグメッセージです"
          echo "::debug::変数の値: ${{ github.sha }}"

デバッグメッセージを表示するには、リポジトリシークレット ACTIONS_STEP_DEBUGtrue に設定します。

7.1.2 通知メッセージ

steps:
  - name: "通知の作成"
    run: |
      echo "::notice::ビルドが正常に完了しました"
      echo "::notice file=app.js,line=10::この行に注意してください"

7.1.3 警告メッセージ

steps:
  - name: "警告の作成"
    run: |
      echo "::warning::非推奨の機能が使用されています"
      echo "::warning file=config.yml,line=5,col=10,title=非推奨::古い設定形式です"

7.1.4 エラーメッセージ

steps:
  - name: "エラーの作成"
    run: |
      echo "::error::ビルドに失敗しました"
      echo "::error file=app.js,line=42,col=15,title=構文エラー::予期しないトークンです"
      exit 1

7.2 ログのグループ化

steps:
  - name: "グループ化されたログ"
    run: |
      echo "::group::依存関係のインストール"
      npm install
      echo "::endgroup::"
      
      echo "::group::テストの実行"
      npm test
      echo "::endgroup::"
      
      echo "::group::ビルドの実行"
      npm run build
      echo "::endgroup::"

7.3 値のマスキング

jobs:
  mask-demo:
    runs-on: ubuntu-latest
    steps:
      - name: "シークレットのマスク"
        run: |
          TOKEN="ghp_secret_token_12345"
          echo "::add-mask::$TOKEN"
          echo "トークン: $TOKEN"
      
      - name: "環境変数のマスク"
        env:
          PASSWORD: "my_secret_password"
        run: |
          echo "::add-mask::$PASSWORD"
          echo "パスワードは表示されません: $PASSWORD"

7.4 環境変数の設定

jobs:
  env-file:
    runs-on: ubuntu-latest
    steps:
      - name: "環境変数の設定"
        run: |
          echo "BUILD_TIME=$(date +'%Y-%m-%d %H:%M:%S')" >> $GITHUB_ENV
          echo "BUILD_NUMBER=${{ github.run_number }}" >> $GITHUB_ENV
      
      - name: "環境変数の使用"
        run: |
          echo "ビルド時刻: $BUILD_TIME"
          echo "ビルド番号: $BUILD_NUMBER"

複数行の値:

steps:
  - name: "複数行の環境変数"
    run: |
      {
        echo 'JSON_RESPONSE<<EOF'
        curl https://api.example.com/data
        echo EOF
      } >> $GITHUB_ENV
  
  - name: "値の使用"
    run: echo "$JSON_RESPONSE"

7.5 出力パラメータの設定

jobs:
  generate:
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.version.outputs.number }}
      artifact: ${{ steps.build.outputs.name }}
    steps:
      - name: "バージョン番号の生成"
        id: version
        run: echo "number=1.0.${{ github.run_number }}" >> $GITHUB_OUTPUT
      
      - name: "アーティファクト名の生成"
        id: build
        run: echo "name=app-${{ github.sha }}.tar.gz" >> $GITHUB_OUTPUT
  
  deploy:
    needs: generate
    runs-on: ubuntu-latest
    steps:
      - name: "出力の使用"
        run: |
          echo "バージョン: ${{ needs.generate.outputs.version }}"
          echo "アーティファクト: ${{ needs.generate.outputs.artifact }}"

7.6 ジョブサマリーの追加

jobs:
  summary-demo:
    runs-on: ubuntu-latest
    steps:
      - name: "サマリーの作成"
        run: |
          echo "## テスト結果 :rocket:" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "- **成功**: 42件" >> $GITHUB_STEP_SUMMARY
          echo "- **失敗**: 3件" >> $GITHUB_STEP_SUMMARY
          echo "- **スキップ**: 5件" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "### 失敗したテスト" >> $GITHUB_STEP_SUMMARY
          echo "1. test_user_authentication" >> $GITHUB_STEP_SUMMARY
          echo "2. test_api_response" >> $GITHUB_STEP_SUMMARY
          echo "3. test_database_connection" >> $GITHUB_STEP_SUMMARY

7.7 システムPATHの追加

steps:
  - name: "カスタムパスの追加"
    run: |
      echo "$HOME/.local/bin" >> $GITHUB_PATH
      echo "/opt/custom-tools/bin" >> $GITHUB_PATH
  
  - name: "追加したパスの確認"
    run: echo $PATH

8. 依存関係のキャッシング

8.1 キャッシュの基本

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      
      - name: "Node.js キャッシュ"
        id: cache
        uses: actions/cache@v4
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-
      
      - name: "キャッシュヒット時のメッセージ"
        if: steps.cache.outputs.cache-hit == 'true'
        run: echo "キャッシュが見つかりました"
      
      - name: "依存関係のインストール"
        if: steps.cache.outputs.cache-hit != 'true'
        run: npm ci

8.2 キャッシュの仕組み

8.3 複数パスのキャッシュ

- name: "複数ディレクトリのキャッシュ"
  uses: actions/cache@v4
  with:
    path: |
      ~/.npm
      ~/.cache
      node_modules
      .next/cache
    key: ${{ runner.os }}-multi-${{ hashFiles('**/package-lock.json') }}

8.4 言語別のキャッシュ例

8.4.1 Node.js

- name: "Node.js セットアップとキャッシュ"
  uses: actions/setup-node@v4
  with:
    node-version: '20'
    cache: 'npm'

- run: npm ci

8.4.2 Python

- name: "Python セットアップとキャッシュ"
  uses: actions/setup-python@v5
  with:
    python-version: '3.11'
    cache: 'pip'

- run: pip install -r requirements.txt

8.4.3 Java (Gradle)

- name: "Java セットアップとキャッシュ"
  uses: actions/setup-java@v4
  with:
    distribution: 'temurin'
    java-version: '17'
    cache: 'gradle'

- run: ./gradlew build

8.4.4 Ruby

- name: "Ruby セットアップとキャッシュ"
  uses: ruby/setup-ruby@v1
  with:
    ruby-version: '3.2'
    bundler-cache: true

- run: bundle exec rake test

8.5 キャッシュの制限事項

  • 保存期間: 7日間アクセスがないキャッシュは自動削除
  • サイズ制限: リポジトリあたり10GB(デフォルト)
  • 最大キャッシュ数: 制限なし(サイズ制限内)
  • キャッシュキー: 512文字まで

8.6 キャッシュの削除順序


9. 再利用可能なワークフロー

9.1 基本的な再利用可能ワークフロー

呼び出されるワークフロー (.github/workflows/reusable-test.yml):

name: "再利用可能なテストワークフロー"

on:
  workflow_call:
    inputs:
      node-version:
        description: 'Node.js のバージョン'
        required: true
        type: string
      test-command:
        description: 'テストコマンド'
        required: false
        type: string
        default: 'npm test'
    outputs:
      test-result:
        description: 'テスト結果'
        value: ${{ jobs.test.outputs.result }}
    secrets:
      npm-token:
        description: 'NPM トークン'
        required: false

jobs:
  test:
    runs-on: ubuntu-latest
    outputs:
      result: ${{ steps.test.outputs.status }}
    steps:
      - uses: actions/checkout@v5
      
      - name: "Node.js セットアップ"
        uses: actions/setup-node@v4
        with:
          node-version: ${{ inputs.node-version }}
      
      - name: "依存関係のインストール"
        env:
          NPM_TOKEN: ${{ secrets.npm-token }}
        run: npm ci
      
      - name: "テスト実行"
        id: test
        run: |
          ${{ inputs.test-command }}
          echo "status=success" >> $GITHUB_OUTPUT

呼び出し側のワークフロー (.github/workflows/main.yml):

name: "メインワークフロー"

on:
  push:
    branches: [main]

jobs:
  test-node-18:
    uses: ./.github/workflows/reusable-test.yml
    with:
      node-version: '18'
      test-command: 'npm test'
    secrets:
      npm-token: ${{ secrets.NPM_TOKEN }}
  
  test-node-20:
    uses: ./.github/workflows/reusable-test.yml
    with:
      node-version: '20'
      test-command: 'npm run test:coverage'
    secrets:
      npm-token: ${{ secrets.NPM_TOKEN }}
  
  report:
    needs: [test-node-18, test-node-20]
    runs-on: ubuntu-latest
    steps:
      - name: "テスト結果の表示"
        run: |
          echo "Node 18: ${{ needs.test-node-18.outputs.test-result }}"
          echo "Node 20: ${{ needs.test-node-20.outputs.test-result }}"

9.2 シークレットの継承

jobs:
  call-workflow:
    uses: ./.github/workflows/deploy.yml
    secrets: inherit

9.3 マトリックス戦略との組み合わせ

jobs:
  test:
    strategy:
      matrix:
        node: [18, 20, 22]
        os: [ubuntu-latest, windows-latest]
    uses: ./.github/workflows/test.yml
    with:
      node-version: ${{ matrix.node }}
      os: ${{ matrix.os }}

9.4 ネストされた再利用可能ワークフロー

最大10レベルまでネスト可能です。

9.5 YAML アンカーとエイリアス

YAMLのアンカー機能を使って、設定の重複を避けることができます。

jobs:
  test: &base_job
    runs-on: ubuntu-latest
    timeout-minutes: 30
    env:
      NODE_VERSION: '20'
    steps:
      - uses: actions/checkout@v5
      - name: "Node.js セットアップ"
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
      - run: npm test
  
  # 同じ設定を再利用
  integration-test: *base_job

環境変数での使用:

env: &shared_env
  NODE_ENV: production
  API_URL: https://api.example.com
  
jobs:
  build:
    runs-on: ubuntu-latest
    env: *shared_env
    steps:
      - run: npm run build
  
  deploy:
    runs-on: ubuntu-latest
    env: *shared_env
    steps:
      - run: npm run deploy

10. 環境とデプロイメント保護

10.1 環境の定義

jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    environment:
      name: staging
      url: https://staging.example.com
    steps:
      - name: "ステージング環境へのデプロイ"
        run: |
          echo "デプロイ先: ${{ vars.DEPLOY_URL }}"
          echo "APIキー: ${{ secrets.API_KEY }}"
  
  deploy-production:
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://www.example.com
    steps:
      - name: "本番環境へのデプロイ"
        run: echo "本番デプロイを実行"

10.2 デプロイメント保護ルール

10.2.1 必須レビュアー

リポジトリ設定で、環境ごとに最大6名のレビュアーを設定できます。

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production  # レビュアー承認が必要
    steps:
      - name: "デプロイ実行"
        run: echo "承認後にデプロイされます"

10.2.2 待機タイマー

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production  # 待機時間が設定されている
    steps:
      - name: "デプロイ実行"
        run: echo "待機時間経過後にデプロイされます"

待機時間は1分~43,200分(30日)の範囲で設定可能です。

10.2.3 デプロイメントブランチとタグ

環境設定で、特定のブランチやタグからのみデプロイを許可できます。

# production 環境は main ブランチからのみデプロイ可能
on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: "デプロイ"
        run: echo "main ブランチからのデプロイ"

10.3 環境変数とシークレット

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: "環境固有の設定を使用"
        env:
          # 環境変数
          REGION: ${{ vars.AWS_REGION }}
          CLUSTER: ${{ vars.ECS_CLUSTER }}
          # 環境シークレット
          ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY_ID }}
          SECRET_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        run: |
          echo "リージョン: $REGION"
          echo "クラスター: $CLUSTER"
          aws ecs update-service ...

10.4 デプロイメントのフロー


11. カスタムアクションの作成

11.1 アクションの種類

GitHub Actionsでは3種類のカスタムアクションを作成できます。

  1. JavaScriptアクション: Node.js で実装
  2. Dockerコンテナアクション: Dockerコンテナで実行
  3. コンポジットアクション: 複数のステップを組み合わせ

11.2 JavaScriptアクション

action.yml:

name: '挨拶アクション'
description: '名前を受け取って挨拶を返します'
author: '山田太郎'

inputs:
  who-to-greet:
    description: '挨拶する相手の名前'
    required: true
    default: '世界'
  greeting-style:
    description: '挨拶のスタイル'
    required: false
    default: 'formal'

outputs:
  greeting-message:
    description: '生成された挨拶メッセージ'
  timestamp:
    description: '実行時のタイムスタンプ'

runs:
  using: 'node20'
  main: 'index.js'
  pre: 'setup.js'
  post: 'cleanup.js'

branding:
  icon: 'message-circle'
  color: 'blue'

index.js:

const core = require('@actions/core');
const github = require('@actions/github');

async function run() {
  try {
    const whoToGreet = core.getInput('who-to-greet');
    const greetingStyle = core.getInput('greeting-style');
    const timestamp = new Date().toISOString();
    
    let greeting;
    if (greetingStyle === 'formal') {
      greeting = `こんにちは、${whoToGreet}様`;
    } else {
      greeting = `やあ、${whoToGreet}!`;
    }
    
    console.log(`挨拶を生成しました: ${greeting}`);
    
    core.setOutput('greeting-message', greeting);
    core.setOutput('timestamp', timestamp);
    
    // デバッグ情報
    core.debug(`実行時刻: ${timestamp}`);
    core.info(`リポジトリ: ${github.context.repo.owner}/${github.context.repo.repo}`);
    
  } catch (error) {
    core.setFailed(error.message);
  }
}

run();

使用例:

jobs:
  greeting:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      
      - name: "挨拶の実行"
        id: greet
        uses: ./
        with:
          who-to-greet: '田中花子'
          greeting-style: 'casual'
      
      - name: "出力の表示"
        run: |
          echo "メッセージ: ${{ steps.greet.outputs.greeting-message }}"
          echo "時刻: ${{ steps.greet.outputs.timestamp }}"

11.3 Dockerコンテナアクション

action.yml:

name: 'Dockerコンテナアクション'
description: 'Dockerコンテナで実行されるアクション'
author: '山田太郎'

inputs:
  command:
    description: '実行するコマンド'
    required: true

runs:
  using: 'docker'
  image: 'Dockerfile'
  args:
    - ${{ inputs.command }}
  env:
    CUSTOM_VAR: 'value'

Dockerfile:

FROM alpine:3.18

RUN apk add --no-cache bash curl jq

COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]

entrypoint.sh:

#!/bin/bash

set -e

COMMAND=$1

echo "実行コマンド: $COMMAND"
echo "環境変数: $CUSTOM_VAR"

eval "$COMMAND"

echo "コマンド実行完了"

11.4 コンポジットアクション

action.yml:

name: 'Node.js セットアップとテスト'
description: 'Node.js のセットアップ、依存関係のインストール、テスト実行を行います'
author: '山田太郎'

inputs:
  node-version:
    description: 'Node.js のバージョン'
    required: false
    default: '20'
  working-directory:
    description: '作業ディレクトリ'
    required: false
    default: '.'
  cache:
    description: 'キャッシュを有効にするか'
    required: false
    default: 'true'

outputs:
  test-result:
    description: 'テスト結果'
    value: ${{ steps.test.outputs.result }}

runs:
  using: 'composite'
  steps:
    - name: "Node.js セットアップ"
      uses: actions/setup-node@v4
      with:
        node-version: ${{ inputs.node-version }}
        cache: ${{ inputs.cache == 'true' && 'npm' || '' }}
    
    - name: "依存関係のインストール"
      shell: bash
      working-directory: ${{ inputs.working-directory }}
      run: |
        echo "::group::npm install"
        npm ci
        echo "::endgroup::"
    
    - name: "Lint実行"
      shell: bash
      working-directory: ${{ inputs.working-directory }}
      run: |
        echo "::group::ESLint"
        npm run lint
        echo "::endgroup::"
      continue-on-error: true
    
    - name: "テスト実行"
      id: test
      shell: bash
      working-directory: ${{ inputs.working-directory }}
      run: |
        echo "::group::Tests"
        npm test
        echo "result=success" >> $GITHUB_OUTPUT
        echo "::endgroup::"
    
    - name: "カバレッジレポート"
      if: always()
      shell: bash
      working-directory: ${{ inputs.working-directory }}
      run: |
        if [ -d "coverage" ]; then
          echo "## テストカバレッジ" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "カバレッジレポートが生成されました" >> $GITHUB_STEP_SUMMARY
        fi

使用例:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      
      - name: "テスト実行"
        uses: ./
        with:
          node-version: '20'
          working-directory: './backend'
          cache: 'true'

11.5 アクションのメタデータ詳細

11.5.1 inputs の詳細設定

inputs:
  deployment-environment:
    description: 'デプロイ先の環境'
    required: true
    type: choice
    options:
      - development
      - staging
      - production
  
  version:
    description: 'バージョン番号'
    required: false
    default: 'latest'
  
  dry-run:
    description: 'ドライランモード'
    required: false
    default: 'false'
  
  api-key:
    description: 'APIキー'
    required: true
    deprecationMessage: 'このパラメータは非推奨です。代わりに secrets を使用してください'

11.5.2 ブランディング設定

branding:
  icon: 'check-circle'
  color: 'green'

利用可能なアイコン: Feather icons v4.28.0
利用可能なカラー: white, black, yellow, blue, green, orange, red, purple, gray-dark


12. 実践的なパターン

12.1 マトリックス戦略

12.1.1 基本的なマトリックス

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node: [18, 20, 22]
        # 3 OS × 3 Node = 9 ジョブ
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
      - run: npm test

12.1.2 マトリックスの拡張(include)

strategy:
  matrix:
    os: [ubuntu-latest, windows-latest]
    node: [18, 20]
    include:
      # 特定の組み合わせに設定を追加
      - os: ubuntu-latest
        node: 22
        experimental: true
      # 新しい組み合わせを追加
      - os: macos-latest
        node: 20
        experimental: false

12.1.3 マトリックスの除外(exclude)

strategy:
  matrix:
    os: [ubuntu-latest, windows-latest, macos-latest]
    node: [18, 20, 22]
    exclude:
      # Windows + Node 18 を除外
      - os: windows-latest
        node: 18
      # macOS + Node 22 を除外
      - os: macos-latest
        node: 22

12.1.4 マトリックスの動的生成

jobs:
  setup:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.set-matrix.outputs.matrix }}
    steps:
      - id: set-matrix
        run: |
          MATRIX='{"include":[
            {"os":"ubuntu-latest","node":18},
            {"os":"ubuntu-latest","node":20},
            {"os":"windows-latest","node":20}
          ]}'
          echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
  
  test:
    needs: setup
    runs-on: ${{ matrix.os }}
    strategy:
      matrix: ${{ fromJSON(needs.setup.outputs.matrix) }}
    steps:
      - run: echo "OS: ${{ matrix.os }}, Node: ${{ matrix.node }}"

12.2 並行実行制御(concurrency)

12.2.1 ブランチ単位での制御

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: "デプロイ実行"
        run: echo "同じブランチの実行は1つのみ"

12.2.2 環境単位での制御

jobs:
  deploy-production:
    runs-on: ubuntu-latest
    concurrency:
      group: production-deployment
      cancel-in-progress: false
    steps:
      - name: "本番デプロイ"
        run: echo "本番環境への同時デプロイを防止"

12.2.3 条件付きキャンセル

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: ${{ !contains(github.ref, 'release/') }}

12.3 条件付き実行の高度な使用

12.3.1 プルリクエストのマージ判定

jobs:
  on-merge:
    runs-on: ubuntu-latest
    if: github.event.pull_request.merged == true
    steps:
      - name: "マージ後の処理"
        run: echo "プルリクエストがマージされました"

12.3.2 ラベルによる実行制御

jobs:
  deploy-if-labeled:
    runs-on: ubuntu-latest
    if: contains(github.event.pull_request.labels.*.name, 'deploy')
    steps:
      - name: "デプロイ実行"
        run: echo "deployラベルが付いています"

12.3.3 複数条件の組み合わせ

jobs:
  complex-condition:
    runs-on: ubuntu-latest
    if: |
      github.event_name == 'push' &&
      github.ref == 'refs/heads/main' &&
      !contains(github.event.head_commit.message, '[skip ci]')
    steps:
      - name: "条件を満たした場合の処理"
        run: echo "全ての条件を満たしています"

12.4 ジョブ間のデータ受け渡し

12.4.1 outputs を使った受け渡し

jobs:
  prepare:
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.version.outputs.number }}
      sha-short: ${{ steps.sha.outputs.short }}
      should-deploy: ${{ steps.check.outputs.deploy }}
    steps:
      - uses: actions/checkout@v5
      
      - id: version
        run: echo "number=$(cat package.json | jq -r .version)" >> $GITHUB_OUTPUT
      
      - id: sha
        run: echo "short=${GITHUB_SHA:0:7}" >> $GITHUB_OUTPUT
      
      - id: check
        run: |
          if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
            echo "deploy=true" >> $GITHUB_OUTPUT
          else
            echo "deploy=false" >> $GITHUB_OUTPUT
          fi
  
  build:
    needs: prepare
    runs-on: ubuntu-latest
    steps:
      - name: "ビルド情報の表示"
        run: |
          echo "バージョン: ${{ needs.prepare.outputs.version }}"
          echo "コミットSHA: ${{ needs.prepare.outputs.sha-short }}"
  
  deploy:
    needs: [prepare, build]
    if: needs.prepare.outputs.should-deploy == 'true'
    runs-on: ubuntu-latest
    steps:
      - name: "デプロイ実行"
        run: echo "バージョン ${{ needs.prepare.outputs.version }} をデプロイ"

12.4.2 artifacts を使った受け渡し

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      
      - name: "ビルド実行"
        run: |
          npm run build
          tar -czf build.tar.gz dist/
      
      - name: "ビルド成果物のアップロード"
        uses: actions/upload-artifact@v4
        with:
          name: build-artifact
          path: build.tar.gz
          retention-days: 5
  
  test:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: "ビルド成果物のダウンロード"
        uses: actions/download-artifact@v4
        with:
          name: build-artifact
      
      - name: "テスト実行"
        run: |
          tar -xzf build.tar.gz
          npm test
  
  deploy:
    needs: [build, test]
    runs-on: ubuntu-latest
    steps:
      - name: "ビルド成果物のダウンロード"
        uses: actions/download-artifact@v4
        with:
          name: build-artifact
      
      - name: "デプロイ実行"
        run: |
          tar -xzf build.tar.gz
          aws s3 sync dist/ s3://my-bucket/

12.5 エラーハンドリング

12.5.1 continue-on-error の使用

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: "重要なテスト"
        run: npm test
      
      - name: "オプショナルなLint"
        run: npm run lint
        continue-on-error: true
      
      - name: "カバレッジチェック"
        run: npm run coverage
        continue-on-error: true
      
      - name: "必ず実行される処理"
        if: always()
        run: echo "結果に関わらず実行"

12.5.2 マトリックスでのエラーハンドリング

strategy:
  matrix:
    node: [18, 20, 22]
    include:
      - node: 22
        experimental: true
  fail-fast: false

steps:
  - name: "テスト実行"
    run: npm test
    continue-on-error: ${{ matrix.experimental == true }}

12.6 セキュリティのベストプラクティス

12.6.1 最小権限の原則

permissions:
  contents: read
  pull-requests: write
  issues: write

jobs:
  comment:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
    steps:
      - name: "プルリクエストへのコメント"
        uses: actions/github-script@v8
        with:
          script: |
            github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              body: 'テストが完了しました'
            })

12.6.2 シークレットの安全な使用

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: "シークレットの使用"
        env:
          API_KEY: ${{ secrets.API_KEY }}
        run: |
          # シークレットは自動的にマスクされる
          curl -H "Authorization: Bearer $API_KEY" https://api.example.com
      
      - name: "シークレットを含むファイルの作成"
        run: |
          echo "${{ secrets.SSH_PRIVATE_KEY }}" > private_key
          chmod 600 private_key
          # 使用後は削除
          rm private_key
        shell: bash

12.6.3 pull_request_target の安全な使用

# 危険: フォークからの任意のコードを実行
on: pull_request_target

jobs:
  dangerous:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5  # フォークのコードをチェックアウト
        with:
          ref: ${{ github.event.pull_request.head.sha }}
      - run: npm install  # 任意のコードが実行される可能性
      - run: npm test

# 安全: 信頼できるコードのみ実行
on: pull_request_target

jobs:
  safe:
    runs-on: ubuntu-latest
    steps:
      # デフォルトブランチのコードを使用
      - uses: actions/checkout@v5
      
      # フォークからの入力は検証する
      - name: "プルリクエスト情報の取得"
        uses: actions/github-script@v8
        with:
          script: |
            const pr = context.payload.pull_request;
            console.log(`PR #${pr.number}: ${pr.title}`);

12.7 パフォーマンス最適化

12.7.1 並列実行の活用

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - run: npm run lint
  
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - run: npm test
  
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - run: npm run build
  
  # 全てのジョブが成功した場合のみ実行
  deploy:
    needs: [lint, test, build]
    runs-on: ubuntu-latest
    steps:
      - run: echo "全てのチェックが完了しました"

12.7.2 キャッシュの効果的な使用

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      
      # 複数のキャッシュを組み合わせ
      - name: "Node.js キャッシュ"
        uses: actions/cache@v4
        with:
          path: ~/.npm
          key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-npm-
      
      - name: "Next.js キャッシュ"
        uses: actions/cache@v4
        with:
          path: |
            .next/cache
            node_modules/.cache
          key: ${{ runner.os }}-next-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
          restore-keys: |
            ${{ runner.os }}-next-${{ hashFiles('**/package-lock.json') }}-
            ${{ runner.os }}-next-
      
      - run: npm ci
      - run: npm run build

13. トラブルシューティング

13.1 よくある問題と解決方法

13.1.1 ワークフローがトリガーされない

原因と対策:

  1. YAMLの構文エラー

    # 間違い
    on:
      push
        branches: [main]
    
    # 正しい
    on:
      push:
        branches: [main]
    
  2. ファイル配置の誤り

    • .github/workflows/ ディレクトリに配置されているか確認
    • ファイル拡張子が .yml または .yaml であることを確認
  3. イベントフィルタが一致しない

    # mainブランチのみトリガー
    on:
      push:
        branches:
          - main
    

13.1.2 キャッシュが効かない

# キャッシュキーにハッシュ値を含める
- uses: actions/cache@v4
  with:
    path: ~/.npm
    key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-npm-

13.1.3 ジョブがタイムアウトする

jobs:
  long-running:
    runs-on: ubuntu-latest
    timeout-minutes: 60  # デフォルトは360分
    steps:
      - name: "長時間かかる処理"
        run: ./long-script.sh

13.1.4 環境変数が参照できない

# 環境変数の設定
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: "環境変数の設定"
        run: echo "MY_VAR=value" >> $GITHUB_ENV
      
      - name: "環境変数の使用"
        run: echo $MY_VAR  # 次のステップで利用可能

13.2 デバッグ方法

13.2.1 デバッグログの有効化

リポジトリシークレット ACTIONS_STEP_DEBUGtrue に設定すると、詳細なログが出力されます。

steps:
  - name: "デバッグ情報の出力"
    run: |
      echo "::debug::デバッグメッセージ"
      echo "現在のディレクトリ: $(pwd)"
      echo "ファイル一覧:"
      ls -la

13.2.2 ランナーへのSSH接続

開発時のデバッグには、tmate を使ったSSH接続が便利です。

steps:
  - name: "SSH セッションの開始"
    uses: mxschmitt/action-tmate@v3
    if: failure()  # 失敗時のみ

14. まとめ

GitHub Actionsは、CI/CDパイプラインの構築に必要な全ての機能を提供する強力なプラットフォームです。本記事では、公式ドキュメントに基づいて以下の内容を解説しました。

14.1 主要な機能

  • ワークフロー構文: YAML形式での柔軟な定義
  • 豊富なイベント: 30種類以上のトリガーイベント
  • 変数とコンテキスト: 動的な値の参照と受け渡し
  • 式評価: 条件分岐とデータ変換
  • キャッシング: 依存関係の高速化
  • 再利用可能ワークフロー: コードの重複削減
  • 環境管理: デプロイメント保護とシークレット管理
  • カスタムアクション: 独自の自動化処理

14.2 実装のポイント

  1. 段階的な導入: シンプルなワークフローから開始し、必要に応じて機能を追加
  2. キャッシュの活用: ビルド時間の短縮
  3. 並列実行: テストやビルドの高速化
  4. セキュリティ: 最小権限の原則とシークレット管理
  5. 再利用性: 共通処理の抽出とテンプレート化

14.3 制限事項の理解

  • ワークフローファイルサイズ: 推奨される適切なサイズ
  • 同時実行制限: プランに応じた並列実行数
  • 保存期間: ログとアーティファクトの保存期間
  • キャッシュサイズ: リポジトリあたりのキャッシュ容量

GitHub Actionsを活用することで、コードの品質向上、デプロイの自動化、開発効率の改善を実現できます。本記事の内容を参考に、プロジェクトに最適なCI/CDパイプラインを構築してください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?