GitHub Actions

CI/CDGitHubYAML自動化DevOpsワークフロークラウドマーケットプレイス

CI/CDツール

GitHub Actions

概要

GitHub Actionsは、GitHubネイティブのCI/CDプラットフォームです。YAML設定によるワークフロー定義、豊富なマーケットプレイス、GitHub統合により、開発からデプロイまでの自動化を実現します。

詳細

GitHub Actions(ギットハブ アクション)は、Microsoftが提供するGitHubプラットフォーム統合型のCI/CD(継続的インテグレーション・継続的デリバリー)サービスです。2019年にリリースされて以来、ソフトウェア開発ライフサイクルの自動化において中心的な役割を果たしています。リポジトリに.github/workflows/ディレクトリ内のYAMLファイルでワークフローを定義することで、コードのビルド、テスト、デプロイを自動化できます。GitHub Marketplaceでは11,000以上の再利用可能なアクションが提供されており、Node.js、Python、Java、.NETなど幅広い言語とフレームワークをサポートします。Linux、macOS、Windows、ARM、GPUランナーを提供し、マトリックスビルドや並列実行により効率的なCI/CDパイプラインを構築可能です。セルフホストランナーにも対応し、企業環境での柔軟な運用を実現します。2024年以降はセキュリティ機能が強化され、Artifact Attestations、暗号化シークレット、依存関係スキャンにより安全な開発プロセスを提供しています。

メリット・デメリット

メリット

  • GitHubとの完全統合: リポジトリ、Issues、Pull Requestとシームレス連携
  • 豊富なマーケットプレイス: 11,000以上の再利用可能なアクション
  • 多様な実行環境: Linux、macOS、Windows、ARM、GPUランナー対応
  • 無料利用枠: パブリックリポジトリは無制限、プライベートでも月2,000分
  • スケーラビリティ: マトリックスビルドと並列実行による高速処理
  • セキュリティ: 暗号化シークレット、Artifact Attestations対応
  • 柔軟性: セルフホストランナーとカスタムアクション作成
  • 設定の版管理: YAMLファイルでワークフロー設定をバージョン管理

デメリット

  • GitHubロックイン: GitHub以外のプラットフォームでは使用不可
  • 料金: プライベートリポジトリでの大規模利用は高コスト
  • 学習コスト: YAML記法とワークフロー概念の理解が必要
  • デバッグの複雑さ: ワークフロー失敗時の原因特定が困難
  • 実行時間制限: ジョブあたり最大6時間の制限
  • ストレージ制限: アーティファクトとキャッシュの保存期間制限
  • ネットワーク制約: 外部APIアクセスの制限やレート制限

主要リンク

書き方の例

基本的なNode.jsワークフロー

# .github/workflows/node.yml
name: Node.js CI

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

jobs:
  test:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [18.x, 20.x, 22.x]

    steps:
    - uses: actions/checkout@v4
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v4
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'npm'
    - run: npm ci
    - run: npm run build --if-present
    - run: npm test

TypeScriptプロジェクトのCI/CD

# .github/workflows/typescript.yml
name: TypeScript CI/CD

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

env:
  NODE_VERSION: '20.x'

jobs:
  lint-and-test:
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      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: Type check
      run: npm run type-check
      
    - name: Lint
      run: npm run lint
      
    - name: Run tests
      run: npm run test:coverage
      
    - name: Upload coverage reports
      uses: codecov/codecov-action@v3
      with:
        file: ./coverage/lcov.info

  build:
    needs: lint-and-test
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version: ${{ env.NODE_VERSION }}
        cache: 'npm'
    - run: npm ci
    - run: npm run build
    
    - name: Upload build artifacts
      uses: actions/upload-artifact@v4
      with:
        name: build-files
        path: dist/
        retention-days: 30

Docker イメージビルドとデプロイ

# .github/workflows/docker.yml
name: Docker Build and Deploy

on:
  push:
    branches: [main]
    tags: ['v*']

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
      
    steps:
    - name: Checkout repository
      uses: actions/checkout@v4
      
    - name: Log in to Container Registry
      uses: docker/login-action@v3
      with:
        registry: ${{ env.REGISTRY }}
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}
        
    - name: Extract metadata
      id: meta
      uses: docker/metadata-action@v5
      with:
        images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
        tags: |
          type=ref,event=branch
          type=ref,event=pr
          type=semver,pattern={{version}}
          type=semver,pattern={{major}}.{{minor}}
          
    - name: Build and push Docker image
      uses: docker/build-push-action@v5
      with:
        context: .
        push: true
        tags: ${{ steps.meta.outputs.tags }}
        labels: ${{ steps.meta.outputs.labels }}

  deploy:
    needs: build-and-push
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
    - name: Deploy to production
      uses: appleboy/[email protected]
      with:
        host: ${{ secrets.HOST }}
        username: ${{ secrets.USERNAME }}
        key: ${{ secrets.SSH_KEY }}
        script: |
          docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main
          docker stop myapp || true
          docker rm myapp || true
          docker run -d --name myapp -p 80:3000 ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main

マルチプラットフォーム対応

# .github/workflows/multi-platform.yml
name: Multi-Platform CI

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

jobs:
  test:
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        python-version: ['3.9', '3.10', '3.11', '3.12']
        exclude:
          - os: macos-latest
            python-version: '3.9'
          - os: windows-latest
            python-version: '3.12'
            
    runs-on: ${{ matrix.os }}
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v4
      with:
        python-version: ${{ matrix.python-version }}
        
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
        pip install pytest
        
    - name: Run tests
      run: pytest tests/ -v
      
    - name: Platform-specific commands
      shell: bash
      run: |
        if [[ "${{ matrix.os }}" == "ubuntu-latest" ]]; then
          echo "Running Ubuntu-specific commands"
          sudo apt-get update
        elif [[ "${{ matrix.os }}" == "windows-latest" ]]; then
          echo "Running Windows-specific commands"
          choco --version
        elif [[ "${{ matrix.os }}" == "macos-latest" ]]; then
          echo "Running macOS-specific commands"
          brew --version
        fi

条件付きワークフローとリリース自動化

# .github/workflows/release.yml
name: Release

on:
  push:
    tags:
      - 'v*'

jobs:
  release:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      
    steps:
    - uses: actions/checkout@v4
      with:
        fetch-depth: 0
        
    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: '20.x'
        registry-url: 'https://registry.npmjs.org'
        
    - name: Install dependencies
      run: npm ci
      
    - name: Build
      run: npm run build
      
    - name: Run tests
      run: npm test
      
    - name: Generate changelog
      id: changelog
      run: |
        # Generate changelog from git commits
        echo "CHANGELOG<<EOF" >> $GITHUB_OUTPUT
        git log $(git describe --tags --abbrev=0 HEAD^)..HEAD --pretty=format:"- %s (%h)" >> $GITHUB_OUTPUT
        echo "EOF" >> $GITHUB_OUTPUT
        
    - name: Create GitHub Release
      uses: actions/create-release@v1
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      with:
        tag_name: ${{ github.ref_name }}
        release_name: Release ${{ github.ref_name }}
        body: |
          ## Changes in this release
          ${{ steps.changelog.outputs.CHANGELOG }}
        draft: false
        prerelease: ${{ contains(github.ref_name, 'beta') || contains(github.ref_name, 'alpha') }}
        
    - name: Publish to NPM
      if: ${{ !contains(github.ref_name, 'beta') && !contains(github.ref_name, 'alpha') }}
      run: npm publish
      env:
        NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

セキュリティスキャンとコード品質

# .github/workflows/security.yml
name: Security and Quality

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 0 * * 1' # 毎週月曜日

jobs:
  security:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Run dependency vulnerability scan
      uses: actions/dependency-review-action@v3
      if: github.event_name == 'pull_request'
      
    - name: Run CodeQL Analysis
      uses: github/codeql-action/init@v2
      with:
        languages: javascript
        
    - name: Autobuild
      uses: github/codeql-action/autobuild@v2
      
    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v2
      
    - name: Run Trivy vulnerability scanner
      uses: aquasecurity/trivy-action@master
      with:
        scan-type: 'fs'
        scan-ref: '.'
        format: 'sarif'
        output: 'trivy-results.sarif'
        
    - name: Upload Trivy scan results
      uses: github/codeql-action/upload-sarif@v2
      if: always()
      with:
        sarif_file: 'trivy-results.sarif'