はじめに: なぜCI/CDが必要なのか?

プロジェクトを進めていると、コードを修正するたびにテストを実行し、ビルドし、デプロイするという過程を繰り返す必要があります。一人で開発する時はまだ大丈夫ですが、チームで作業するとこの過程がかなり面倒になります。「私のパソコンではうまくいくのに?」という言葉を一度でも言ったことがあるなら、おそらくCI/CDの必要性を感じているはずです。

今日はGit & GitHub コラボレーションマスターシリーズの第5編として、GitHub Actionsを活用したCI/CDについて深く掘り下げていきます。初めて触れる方も理解できるように基礎から始めて、実際にプロジェクトにすぐ適用できる実践的なワークフローまで一緒に作っていきましょう。

1. CI/CDとは

1.1 CI (Continuous Integration) - 継続的インテグレーション

CIは開発者がコード変更を頻繁に、そして定期的にメインブランチに統合する開発方式です。単にコードをマージするだけでなく、統合するたびに自動的にビルドしてテストを実行し、問題がないか確認します。

例を挙げてみましょう。3人の開発者がそれぞれ異なる機能を開発しているとします。CIがなければ、それぞれが数週間別々に開発した後、一度にまとめようとした時に莫大なコンフリクトが発生する可能性があります。しかしCIを適用すれば、毎日、いや1日に何度もコードを統合して自動的に検証するため、問題を早期に発見して解決できます。

1.2 CD (Continuous Delivery/Deployment) - 継続的デリバリー/デプロイ

CDは2つの意味で使用されます。Continuous Deliveryはコード変更が自動的にテストを経てデプロイ可能な状態に準備されることを指し、Continuous Deploymentはさらに一歩進んでプロダクション環境まで自動的にデプロイされることを意味します。

まとめるとこうなります:

  • Continuous Delivery: コードがいつでもデプロイできる状態に自動準備される (デプロイボタンは人が押す)
  • Continuous Deployment: すべてのテストに通過すれば自動的にプロダクションにデプロイされる

1.3 CI/CDの実際のメリット

私が実際にCI/CDを導入した時に感じた最大の変化は「デプロイへの恐怖がなくなった」ということです。以前は金曜日の午後にデプロイすることを避けていました。問題が起きれば週末に苦労しなければならないからです。しかし自動化されたテストとデプロイパイプラインが整備されると、いつでも自信を持ってデプロイできるようになりました。

2. GitHub Actions紹介

2.1 GitHub Actionsとは?

GitHub ActionsはGitHubが直接提供するCI/CDプラットフォームです。2019年に正式リリースされて以来、急速に成長し、今では最も人気のあるCI/CDツールの1つになりました。GitHubリポジトリと完璧に統合されているため、別途の外部サービス設定なしにすぐ使用できることが最大の利点です。

GitHub Actionsの核心的な特徴をまとめると:

  • GitHubネイティブ: 別途のサービス連携なしにリポジトリですぐ使用可能
  • YAMLベース設定: 理解しやすい宣言的構文
  • マーケットプレイス: 数千個の既製アクションを再利用可能
  • 無料使用量: パブリックリポジトリは無制限、プライベートも月2,000分無料
  • 多様なランナー: Linux、Windows、macOS環境すべてサポート

2.2 他のCI/CDツールとの比較

Jenkins、CircleCI、Travis CIなど他のCI/CDツールもあります。それぞれ長所短所がありますが、GitHubを主に使用するならGitHub Actionsを選択することがほとんどの場合良い選択です。設定が簡単で、追加のインフラ構築が必要なく、GitHubのイベント(Push、PRなど)と自然に連携するからです。

3. Workflowファイル構造の理解

3.1 ワークフローファイルの場所

GitHub Actionsのワークフローファイルはリポジトリの .github/workflows/ ディレクトリに配置されます。このディレクトリにYAMLファイルを作成するとGitHubが自動的に認識して実行します。

my-project/
├── .github/
│   └── workflows/
│       ├── ci.yml
│       ├── deploy.yml
│       └── test.yml
├── src/
├── tests/
└── package.json

3.2 ワークフローの構成要素

ワークフローは次のような階層構造を持ちます:

  • Workflow (ワークフロー): 自動化された全体プロセス、1つのYAMLファイルで定義
  • Event (イベント): ワークフローを開始させるトリガー
  • Job (ジョブ): 同じランナーで実行されるステップの集まり
  • Step (ステップ): 個別の作業単位、シェルコマンドやアクションを実行
  • Action (アクション): 再利用可能な作業単位

4. 基本構文マスター

4.1 name - ワークフロー名

ワークフローの名前を指定します。GitHub Actionsタブで表示される名前なので、わかりやすく記述してください。

name: CI Pipeline

4.2 on - イベントトリガー

ワークフローがいつ実行されるかを定義します。最もよく使用されるイベントを見てみましょう。

# Pushイベント
on:
  push:
    branches: [ main, develop ]
    paths:
      - 'src/**'
      - '!src/**/*.md'

# Pull Requestイベント
on:
  pull_request:
    branches: [ main ]
    types: [ opened, synchronize, reopened ]

# スケジュール (cron構文)
on:
  schedule:
    - cron: '0 9 * * 1'  # 毎週月曜日午前9時 (UTC)

# 手動実行
on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'デプロイ環境選択'
        required: true
        default: 'staging'
        type: choice
        options:
          - staging
          - production

4.3 jobs - ジョブ定義

実際に実行するジョブを定義します。複数のジョブを定義でき、デフォルトでは並列で実行されます。

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: コードチェックアウト
        uses: actions/checkout@v4

      - name: Node.js設定
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: 依存関係インストール
        run: npm ci

      - name: ビルド
        run: npm run build

4.4 runs-on - ランナー環境指定

ジョブが実行される仮想環境を指定します。GitHubがホスティングするランナーを使用するか、セルフホスティングランナーを使用できます。

# GitHubホスティングランナー
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
runs-on: windows-latest
runs-on: macos-latest

# セルフホスティングランナー
runs-on: self-hosted

4.5 usesとrun - ステップ実行

usesは既製のアクションを使用する時、runはシェルコマンドを直接実行する時に使用します。

steps:
  # アクション使用
  - name: コードチェックアウト
    uses: actions/checkout@v4

  # シェルコマンド実行
  - name: テスト実行
    run: npm test

  # 複数行コマンド
  - name: ビルドおよびデプロイ
    run: |
      npm run build
      npm run deploy

5. よく使うアクション

5.1 actions/checkout

最も基本的なアクションで、リポジトリのコードをワークフロー環境にチェックアウトします。ほぼすべてのワークフローで最初のステップとして使用されます。

- uses: actions/checkout@v4
  with:
    fetch-depth: 0  # 全履歴を取得 (タグ、ブランチ情報が必要な場合)
    ref: ${{ github.head_ref }}  # 特定ブランチをチェックアウト

5.2 actions/setup-node

Node.js環境を設定します。バージョン指定とキャッシュ設定を同時に行えます。

- uses: actions/setup-node@v4
  with:
    node-version: '20'
    cache: 'npm'  # 自動でnode_modulesをキャッシュ
    registry-url: 'https://registry.npmjs.org'  # npm公開時に必要

5.3 actions/cache

依存関係やビルド成果物をキャッシュしてワークフロー実行時間を短縮します。

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

5.4 その他の便利なアクション

# Python設定
- uses: actions/setup-python@v5
  with:
    python-version: '3.11'
    cache: 'pip'

# Java設定
- uses: actions/setup-java@v4
  with:
    distribution: 'temurin'
    java-version: '17'

# Dockerビルドおよびプッシュ
- uses: docker/build-push-action@v5
  with:
    context: .
    push: true
    tags: user/app:latest

6. テスト自動化の実装

6.1 基本テストワークフロー

PRが作成または更新された時に自動的にテストを実行するワークフローです。

name: Test

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

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

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

      - name: 依存関係インストール
        run: npm ci

      - name: Lint検査
        run: npm run lint

      - name: 型検査
        run: npm run type-check

      - name: ユニットテスト
        run: npm test -- --coverage

      - name: テストカバレッジレポート
        uses: codecov/codecov-action@v3
        with:
          files: ./coverage/lcov.info

6.2 テスト結果レポート

テスト結果をPRにコメントとして残すと、レビュアーが素早く確認できます。

- name: テスト結果レポート
  uses: dorny/test-reporter@v1
  if: success() || failure()
  with:
    name: Jest Tests
    path: junit.xml
    reporter: jest-junit

7. デプロイ自動化

7.1 GitHub Pagesデプロイ

静的サイトをGitHub Pagesに自動デプロイするワークフローです。

name: Deploy to GitHub Pages

on:
  push:
    branches: [ main ]

permissions:
  contents: read
  pages: write
  id-token: write

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

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

      - name: ビルド
        run: |
          npm ci
          npm run build

      - name: Pagesアーティファクトアップロード
        uses: actions/upload-pages-artifact@v3
        with:
          path: ./dist

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - name: GitHub Pagesデプロイ
        id: deployment
        uses: actions/deploy-pages@v4

7.2 Vercelデプロイ

Vercelへの自動デプロイ設定です。PreviewとProduction環境を区別してデプロイできます。

name: Deploy to Vercel

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

env:
  VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
  VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Vercel CLIインストール
        run: npm install -g vercel@latest

      - name: Vercelプロジェクト情報取得
        run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }}

      - name: ビルド
        run: vercel build --token=${{ secrets.VERCEL_TOKEN }}

      - name: プロダクションデプロイ
        if: github.ref == 'refs/heads/main'
        run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }}

7.3 AWS S3 + CloudFrontデプロイ

name: Deploy to AWS

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

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

      - name: ビルド
        run: |
          npm ci
          npm run build

      - name: AWS認証情報設定
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-2

      - name: S3にアップロード
        run: aws s3 sync ./dist s3://${{ secrets.S3_BUCKET }} --delete

      - name: CloudFrontキャッシュ無効化
        run: |
          aws cloudfront create-invalidation \
            --distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} \
            --paths "/*"

8. Secrets管理

8.1 Secrets設定方法

APIキー、トークンなどの機密情報は絶対にコードに直接書いてはいけません。GitHub Secretsを使用して安全に管理してください。

設定方法:

  1. リポジトリSettingsに移動
  2. 左メニューから「Secrets and variables」 -> 「Actions」を選択
  3. 「New repository secret」をクリック
  4. 名前と値を入力して保存

8.2 Secrets使用例

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: デプロイ
        env:
          API_KEY: ${{ secrets.API_KEY }}
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
        run: |
          echo "APIキーが設定されました"
          npm run deploy

8.3 環境別Secrets管理

Environments機能を活用すると、staging、productionなど環境別に異なるsecretsを管理できます。

jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - name: ステージングデプロイ
        run: deploy --env staging
        env:
          API_URL: ${{ secrets.API_URL }}  # staging環境のAPI_URL

  deploy-production:
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: プロダクションデプロイ
        run: deploy --env production
        env:
          API_URL: ${{ secrets.API_URL }}  # production環境のAPI_URL

9. Matrixビルド

9.1 Matrix戦略とは?

Matrixビルドを使用すると、複数の環境(OS、言語バージョンなど)で同時にテストを実行できます。例えばNode.js 18、20、22バージョンですべてテストしたい場合、それぞれ別のジョブを作成する必要なくmatrixで定義すればよいです。

name: Cross-version Test

on: [push, pull_request]

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node-version: [18, 20, 22]
        exclude:
          - os: windows-latest
            node-version: 18
      fail-fast: false  # 1つが失敗しても残りは継続実行

    steps:
      - uses: actions/checkout@v4

      - name: Node.js ${{ matrix.node-version }} 設定
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}

      - run: npm ci
      - run: npm test

9.2 動的Matrix

JSONを活用して動的にmatrixを構成することもできます。

jobs:
  setup:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.set-matrix.outputs.matrix }}
    steps:
      - id: set-matrix
        run: echo "matrix={\"include\":[{\"project\":\"api\"},{\"project\":\"web\"}]}" >> $GITHUB_OUTPUT

  build:
    needs: setup
    runs-on: ubuntu-latest
    strategy:
      matrix: ${{ fromJSON(needs.setup.outputs.matrix) }}
    steps:
      - run: echo "Building ${{ matrix.project }}"

10. 実践ワークフロー例

10.1 完全なCIパイプライン

name: CI

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

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

jobs:
  lint:
    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 run lint

  type-check:
    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 run type-check

  test:
    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 -- --coverage
      - uses: codecov/codecov-action@v3

  build:
    needs: [lint, type-check, test]
    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 run build
      - uses: actions/upload-artifact@v4
        with:
          name: build
          path: dist/

10.2 自動リリースワークフロー

name: Release

on:
  push:
    tags:
      - 'v*'

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          registry-url: 'https://registry.npmjs.org'

      - run: npm ci
      - run: npm run build
      - run: npm publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

      - name: リリースノート生成
        uses: softprops/action-gh-release@v1
        with:
          generate_release_notes: true
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

10.3 PR自動ラベリング

name: PR Labeler

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  label:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/labeler@v5
        with:
          repo-token: ${{ secrets.GITHUB_TOKEN }}

10.4 スケジュールベースのセキュリティチェック

name: Security Check

on:
  schedule:
    - cron: '0 9 * * 1'  # 毎週月曜日午前9時 (UTC)
  workflow_dispatch:

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

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

      - name: 依存関係セキュリティチェック
        run: npm audit --audit-level=high

      - name: 依存関係アップデート確認
        run: npm outdated || true

      - name: Snykセキュリティスキャン
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}

まとめ: CI/CDで開発生産性を高める

今回はGitHub Actionsを活用したCI/CDパイプライン構築について見てきました。最初は設定ファイルの作成が複雑に感じるかもしれませんが、一度構築しておけば、その後は自動化の恩恵を継続的に受けることができます。

重要なのは、最初から完璧なパイプラインを作ろうとしないことです。簡単なテスト自動化から始めて、徐々に拡張していきましょう。私も最初は単にnpm testだけを実行するワークフローから始めて、必要に応じてリント、型チェック、デプロイ自動化などを1つずつ追加していきました。

次の第6編ではオープンソースへの貢献について扱います。Fork、PRワークフロー、コントリビューションガイドラインなど、実際にオープンソースプロジェクトに貢献する方法を詳しく見ていきましょう。