TechBlog

GitHub Actions入門:CI/CDパイプラインをゼロから構築する実践ガイド

by あくえり
#GitHub Actions #CI/CD #自動化 #DevOps #GitHub
GitHub Actions入門 CI/CDガイド
目次

CI/CDとは何か

CI(継続的インテグレーション) とは、コードをリポジトリにプッシュするたびに自動でテストを実行し、バグを早期に発見する仕組みです。

CD(継続的デリバリー/デプロイ) とは、テストが通ったコードを自動で本番環境やステージング環境にデプロイする仕組みです。

CI/CDがない開発の問題点:

  • テストを手動で実行し忘れてバグを本番にリリースする
  • デプロイ手順をドキュメント化しても、手順を間違える
  • チームメンバー全員がデプロイできないため、特定の人に依存する

GitHub Actionsを使えば、これらをコード(YAML)で定義して自動化できます。

GitHub Actionsの基本概念

ワークフロー(Workflow)

.github/workflows/ディレクトリに置くYAMLファイルです。「何をトリガーに」「どのような処理を」実行するかを定義します。

トリガー(on)

ワークフローを起動するイベントです。代表的なものは以下のとおりです。

トリガー説明
push指定ブランチへのプッシュ時
pull_requestプルリクエストの作成・更新時
workflow_dispatch手動でワークフローを起動
schedulecronで定期実行

ジョブ(jobs)

ワークフロー内の処理単位です。デフォルトで並列実行されますが、needsで依存関係を定義して順次実行もできます。

ステップ(steps)

ジョブ内の個々の処理です。シェルコマンドの実行(run)またはアクション(uses)を指定します。

アクション(Action)

再利用可能なステップのテンプレートです。GitHubが公式に提供するもの(actions/checkoutなど)や、コミュニティが公開するものがあります。

Node.jsプロジェクトのCI設定

まず最もよく使われる構成、Node.jsプロジェクトのテスト自動実行を構築します。

# .github/workflows/ci.yml

name: CI

# トリガー:mainへのプッシュ、またはすべてのPR作成・更新時
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

# 同じブランチで新しいワークフローが起動したら古いものをキャンセル
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  test:
    name: Lint & Test
    runs-on: ubuntu-latest

    # Node.jsの複数バージョンでテスト(マトリックス戦略)
    strategy:
      matrix:
        node-version: [20, 22]

    steps:
      # ① リポジトリのコードをチェックアウト
      - name: Checkout repository
        uses: actions/checkout@v4

      # ② Node.jsのセットアップ(npmキャッシュも有効化)
      - name: Setup Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'

      # ③ 依存パッケージをインストール(package-lock.jsonに従い完全再現)
      - name: Install dependencies
        run: npm ci

      # ④ リントチェック
      - name: Run ESLint
        run: npm run lint

      # ⑤ 型チェック(TypeScriptプロジェクトの場合)
      - name: Type check
        run: npm run type-check

      # ⑥ テスト実行(カバレッジも計測)
      - name: Run tests
        run: npm run test -- --coverage

      # ⑦ カバレッジレポートをアーティファクトとして保存
      - name: Upload coverage report
        uses: actions/upload-artifact@v4
        if: matrix.node-version == 20  # Node 20の場合のみ
        with:
          name: coverage-report
          path: coverage/
          retention-days: 7

package.jsonのスクリプト設定

上記のワークフローに対応するnpmスクリプトです。

{
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "lint": "eslint . --ext .ts,.tsx",
    "type-check": "tsc --noEmit",
    "test": "vitest run",
    "test:watch": "vitest",
    "test:coverage": "vitest run --coverage"
  }
}

ビルドとデプロイの自動化

テストが通ったら本番にデプロイするパイプラインを構築します。ここではCloudflare Pagesへのデプロイを例にします。

# .github/workflows/deploy.yml

name: Deploy to Cloudflare Pages

on:
  push:
    branches: [main]  # mainへのプッシュ時のみデプロイ

jobs:
  # ジョブ1: テスト(先に実行)
  test:
    name: Run Tests
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - run: npm test

  # ジョブ2: ビルドとデプロイ(testジョブが成功した場合のみ実行)
  deploy:
    name: Build & Deploy
    runs-on: ubuntu-latest
    needs: test  # testジョブの完了を待つ

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npm run build
        env:
          # GitHub Secretsから環境変数を注入
          PUBLIC_API_URL: ${{ secrets.PUBLIC_API_URL }}

      - name: Deploy to Cloudflare Pages
        uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          command: pages deploy dist/ --project-name=my-project

      # デプロイ完了をSlackに通知
      - name: Notify Slack
        if: always()  # 成功・失敗どちらでも実行
        uses: slackapi/slack-github-action@v1
        with:
          channel-id: 'C0123456789'
          slack-message: |
            ${{ job.status == 'success' && '✅ デプロイ成功' || '❌ デプロイ失敗' }}
            Branch: ${{ github.ref_name }}
            Commit: ${{ github.sha }}
        env:
          SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}

シークレットの設定方法

APIトークンなどの機密情報はGitHub Secretsに保存します。

  1. GitHubリポジトリの「Settings」→「Secrets and variables」→「Actions」を開く
  2. 「New repository secret」をクリック
  3. 名前(例: CLOUDFLARE_API_TOKEN)と値を入力して保存

ワークフロー内では${{ secrets.CLOUDFLARE_API_TOKEN }}のように参照します。シークレットの値はログに表示されずマスクされます。

PRにテスト結果を自動コメント

プルリクエスト時にテスト結果や差分カバレッジをPRコメントとして自動投稿することで、レビューが効率化します。

# .github/workflows/pr-check.yml

name: PR Check

on:
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    # PRへのコメント権限を付与
    permissions:
      pull-requests: write

    steps:
      - uses: actions/checkout@v4
        with:
          # PR のベースブランチも取得(差分カバレッジ計算に必要)
          fetch-depth: 0

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - run: npm ci

      - name: Run tests with coverage
        run: npm run test -- --coverage --reporter=json
        continue-on-error: true  # テスト失敗でもPRコメントを投稿する

      - name: Post coverage comment
        uses: davelosert/vitest-coverage-report-action@v2
        with:
          json-summary-path: ./coverage/coverage-summary.json
          json-final-path: ./coverage/coverage-final.json

定期実行(cron)でのスケジュール実行

毎日深夜に依存パッケージの脆弱性チェックを行う例です。

# .github/workflows/security-scan.yml

name: Security Scan

on:
  schedule:
    # 毎日 UTC 00:00(JST 09:00)に実行
    - cron: '0 0 * * *'
  workflow_dispatch:  # 手動でも実行できるようにする

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci

      - name: Run npm audit
        run: npm audit --audit-level=high
        # 高リスク以上の脆弱性があればワークフローが失敗する

      - name: Check for outdated packages
        run: npm outdated || true  # 旧バージョンがあっても失敗扱いにしない

キャッシュを活用してビルドを高速化

npm ciは毎回node_modulesを再インストールするため時間がかかります。setup-nodecache: 'npm'オプションを使うと、package-lock.jsonのハッシュをキーにしてキャッシュが効きます。

手動でキャッシュを設定する場合は以下のようにします。

- name: Cache node_modules
  uses: actions/cache@v4
  with:
    path: ~/.npm
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-

hashFiles('**/package-lock.json')package-lock.jsonのハッシュ値を計算します。ファイルが変わらなければ同じキャッシュが再利用され、変わった場合は新しくキャッシュが作成されます。

よくあるつまずきポイント

YAML のインデントエラー

GitHubActions のワークフローはYAMLの書式に厳密です。タブではなくスペースを使い、インデントを2または4スペースに統一してください。yamllintコマンドで事前チェックできます。

# yamllintのインストールと検証
pip install yamllint
yamllint .github/workflows/ci.yml

パーミッションエラー

デフォルトではワークフローのパーミッションは読み取り専用です。PRへのコメント投稿やリリースの作成など書き込みが必要な場合は明示的にpermissionsを指定します。

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

GitHub CI/CD実践ガイド 持続可能なソフトウェア開発を支えるGitHub Actionsの設計と運用

GitHub Actionsの設計思想から実践的なCI/CDパイプライン構築まで体系的に学べる一冊。再利用可能なワークフローや大規模運用のノウハウも収録されています。

※ アフィリエイトリンクを含みます

まとめ

GitHub Actionsの核心は「コードとして定義された自動化」です。

  • .github/workflows/にYAMLを置くだけでCI/CDが動く
  • トリガー(on) でいつ実行するかを定義する
  • ジョブ(jobs)needsで連鎖させてパイプラインを作る
  • シークレット でAPIトークンなどを安全に管理する
  • キャッシュ を活用して実行時間を短縮する

まずはnpm testを自動実行するシンプルなCI設定から始め、慣れてきたら自動デプロイやSlack通知を追加していくことをおすすめします。

共有: