GitHub Actionsは、GitHubに統合されたCI/CD(継続的インテグレーション/継続的デリバリー)プラットフォームです。コードのプッシュやプルリクエストをトリガーに、テスト、ビルド、デプロイを自動化できます。本記事では、実践的なワークフローの構築方法を詳しく解説します。
この記事で学ぶこと
- GitHub Actionsの基本概念
- ワークフローファイルの構文
- Node.js/Pythonプロジェクトのテスト自動化
- キャッシュによる高速化
- マトリックスビルド
- 本番環境へのデプロイ
- セキュリティベストプラクティス
基本概念
GitHub Actionsの構成要素
flowchart TB
subgraph Workflow["Workflow(ワークフロー)<br/>.github/workflows/ci.yml"]
subgraph TestJob["Job(ジョブ): test"]
Step1["Step 1: actions/checkout@v4"]
Step2["Step 2: actions/setup-node@v4"]
Step3["Step 3: npm install"]
Step4["Step 4: npm test"]
Step1 --> Step2 --> Step3 --> Step4
end
subgraph DeployJob["Job(ジョブ): deploy"]
DeploySteps["Deploy steps..."]
end
TestJob -->|depends on| DeployJob
end
| 用語 | 説明 |
|---|---|
| Workflow | YAMLで定義された自動化プロセス全体 |
| Job | ワークフロー内の実行単位。同じランナーで実行される |
| Step | ジョブ内の個々のタスク。アクションまたはシェルコマンド |
| Action | 再利用可能なタスク単位(actions/checkout等) |
| Runner | ワークフローを実行するサーバー |
最初のワークフロー
ファイルの配置
project/
└── .github/
└── workflows/
├── ci.yml # テスト・ビルド用
├── deploy.yml # デプロイ用
└── release.yml # リリース用
基本的なCIワークフロー
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
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: Run linter
run: npm run lint
- name: Run tests
run: npm test
- name: Build
run: npm run build
トリガー(on)の詳細
各種トリガー
on:
# プッシュ時
push:
branches:
- main
- 'release/**' # パターンマッチ
paths:
- 'src/**' # 特定パスの変更時のみ
- '!src/**/*.md' # マークダウンは除外
tags:
- 'v*' # タグ付け時
# プルリクエスト
pull_request:
branches: [main]
types: [opened, synchronize, reopened]
# 手動実行
workflow_dispatch:
inputs:
environment:
description: 'Deployment environment'
required: true
default: 'staging'
type: choice
options:
- staging
- production
# スケジュール実行(UTC)
schedule:
- cron: '0 0 * * 1' # 毎週月曜 00:00 UTC
# 他のワークフローからの呼び出し
workflow_call:
inputs:
config-path:
required: true
type: string
条件付き実行
jobs:
deploy:
# mainブランチへのプッシュ時のみ実行
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
runs-on: ubuntu-latest
steps:
# ...
notify:
# 前のジョブが失敗した場合のみ実行
if: failure()
needs: [test, deploy]
runs-on: ubuntu-latest
steps:
# ...
環境変数とシークレット
環境変数の設定
# ワークフローレベル
env:
NODE_ENV: production
CI: true
jobs:
build:
runs-on: ubuntu-latest
# ジョブレベル
env:
DATABASE_URL: postgres://localhost/test
steps:
- name: Build with environment
# ステップレベル
env:
API_KEY: ${{ secrets.API_KEY }}
run: |
echo "Node env: $NODE_ENV"
echo "Building..."
シークレットの使用
steps:
- name: Deploy to production
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: |
aws s3 sync ./dist s3://my-bucket
# GITHUB_TOKENは自動的に利用可能
- name: Create release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Environment Secrets
jobs:
deploy-prod:
runs-on: ubuntu-latest
environment:
name: production
url: https://myapp.com
steps:
# productionの環境変数・シークレットにアクセス可能
- name: Deploy
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: ./deploy.sh
キャッシュ戦略
npm キャッシュ
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm' # 自動キャッシュ
- run: npm ci
カスタムキャッシュ
steps:
- name: Cache node modules
id: cache-npm
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-
- name: Install dependencies
if: steps.cache-npm.outputs.cache-hit != 'true'
run: npm ci
# ビルド成果物のキャッシュ
- name: Cache build output
uses: actions/cache@v4
with:
path: |
.next/cache
dist
key: ${{ runner.os }}-build-${{ github.sha }}
restore-keys: |
${{ runner.os }}-build-
依存関係のキャッシュ(複数言語)
steps:
# Python
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
# Go
- uses: actions/setup-go@v5
with:
go-version: '1.22'
cache: true
# Rust
- uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
マトリックスビルド
複数バージョンでのテスト
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20, 22]
include:
- node-version: 20
is-primary: true
fail-fast: false # 1つ失敗しても他は継続
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
# プライマリバージョンでのみカバレッジレポート
- name: Upload coverage
if: matrix.is-primary
uses: codecov/codecov-action@v4
クロスプラットフォームテスト
jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node-version: [18, 20]
exclude:
# Windows + Node 18の組み合わせをスキップ
- os: windows-latest
node-version: 18
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
ジョブ間の依存関係
順次実行
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run lint
test:
needs: lint # lintが成功後に実行
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm test
build:
needs: [lint, test] # 複数ジョブに依存
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run build
アーティファクトの受け渡し
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
retention-days: 1
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Deploy
run: ./deploy.sh dist/
実践的なワークフロー例
Node.jsプロジェクトの完全なCI/CD
# .github/workflows/ci-cd.yml
name: CI/CD
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
NODE_VERSION: '20'
jobs:
# コード品質チェック
quality:
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
- name: Type check
run: npm run type-check
- name: Lint
run: npm run lint
- name: Format check
run: npm run format:check
# テスト
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- name: Run tests
env:
DATABASE_URL: postgres://test:test@localhost:5432/test
run: npm run test:coverage
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
# ビルド
build:
needs: [quality, 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
- uses: actions/upload-artifact@v4
with:
name: build
path: dist/
# ステージングデプロイ(PRマージ時)
deploy-staging:
needs: build
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.myapp.com
steps:
- uses: actions/download-artifact@v4
with:
name: build
path: dist/
- name: Deploy to staging
env:
DEPLOY_KEY: ${{ secrets.STAGING_DEPLOY_KEY }}
run: |
echo "Deploying to staging..."
# デプロイスクリプト
# 本番デプロイ(手動承認後)
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment:
name: production
url: https://myapp.com
steps:
- uses: actions/download-artifact@v4
with:
name: build
path: dist/
- name: Deploy to production
env:
DEPLOY_KEY: ${{ secrets.PRODUCTION_DEPLOY_KEY }}
run: |
echo "Deploying to production..."
# デプロイスクリプト
Docker イメージのビルドとプッシュ
name: Docker Build
on:
push:
tags: ['v*']
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- 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=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
再利用可能なワークフロー
呼び出し可能ワークフローの定義
# .github/workflows/reusable-test.yml
name: Reusable Test Workflow
on:
workflow_call:
inputs:
node-version:
required: false
type: string
default: '20'
secrets:
npm-token:
required: false
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: 'npm'
registry-url: 'https://registry.npmjs.org'
- run: npm ci
env:
NODE_AUTH_TOKEN: ${{ secrets.npm-token }}
- run: npm test
再利用可能ワークフローの呼び出し
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
call-test:
uses: ./.github/workflows/reusable-test.yml
with:
node-version: '20'
secrets:
npm-token: ${{ secrets.NPM_TOKEN }}
セキュリティベストプラクティス
依存関係のスキャン
name: Security
on:
push:
branches: [main]
schedule:
- cron: '0 0 * * 1' # 週1回
jobs:
dependency-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run npm audit
run: npm audit --audit-level=high
- name: Run Snyk scan
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
シークレットの安全な取り扱い
steps:
# 悪い例: シークレットをログに出力してしまう
- name: Bad example
run: echo ${{ secrets.API_KEY }} # ❌ ログに表示される可能性
# 良い例: 環境変数として渡す
- name: Good example
env:
API_KEY: ${{ secrets.API_KEY }}
run: ./deploy.sh # スクリプト内で$API_KEYを使用
最小権限の原則
# ワークフローレベルで最小限の権限を設定
permissions:
contents: read
packages: write
jobs:
build:
runs-on: ubuntu-latest
# ジョブレベルで上書きも可能
permissions:
contents: read
steps:
# ...
デバッグとトラブルシューティング
デバッグログの有効化
# リポジトリのSecretsに設定
# ACTIONS_STEP_DEBUG: true
# ACTIONS_RUNNER_DEBUG: true
steps:
- name: Debug info
run: |
echo "GitHub Context:"
echo '${{ toJson(github) }}'
echo "Runner Context:"
echo '${{ toJson(runner) }}'
手動でのジョブ再実行
on:
workflow_dispatch:
inputs:
debug_enabled:
description: 'Run with debug logging'
required: false
default: 'false'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Setup tmate session
if: inputs.debug_enabled == 'true'
uses: mxschmitt/action-tmate@v3
まとめ
GitHub Actionsを使ったCI/CDパイプラインのポイントをまとめます。
ワークフロー設計のベストプラクティス
- ジョブの分離: lint、test、build、deployを分離
- キャッシュの活用: npm/pip等のキャッシュで高速化
- マトリックスビルド: 複数環境での並列テスト
- 環境の分離: staging/productionを明確に分離
セキュリティ
- 最小権限の原則: 必要最低限のpermissionsを設定
- シークレット管理: Environment Secretsを活用
- 依存関係スキャン: 定期的な脆弱性チェック
- コードスキャン: CodeQLの導入
高速化のポイント
- キャッシュの適切な設定
- 並列実行の最大化
- 不要なステップの削除
- スリムなDockerイメージの使用
GitHub Actionsを活用することで、開発プロセスを大幅に効率化できます。