【2026年版】GitHub Actions CI/CD完全ガイド:自動テスト・ビルド・デプロイの実践設定

Tech Trends AI
- 8 minutes read - 1662 wordsはじめに:GitHub ActionsがCI/CDの標準になった理由
2026年、GitHub Actionsは世界で最も利用されているCI/CDプラットフォームの一つに成長しました。GitHubリポジトリとのシームレスな統合、豊富なマーケットプレイスアクション、そして柔軟なワークフロー定義により、個人開発者からエンタープライズまで幅広く採用されています。
本記事では、GitHub Actionsを使ったCI/CDパイプラインの構築方法を、基礎から実践レベルまで体系的に解説します。自動テスト、ビルド、デプロイの各フェーズについて、具体的なワークフロー設定を提示します。
GitHub Actionsの基本概念
コアコンポーネント
GitHub Actionsは以下のコアコンポーネントで構成されています。
| コンポーネント | 説明 | 例 |
|---|---|---|
| Workflow | CI/CDパイプライン全体の定義 | .github/workflows/ci.yml |
| Event | ワークフローを起動するトリガー | push, pull_request, schedule |
| Job | ワークフロー内の実行単位 | test, build, deploy |
| Step | ジョブ内の各処理ステップ | actions/checkout@v4 |
| Action | 再利用可能な処理ブロック | マーケットプレイスのアクション |
| Runner | ワークフローを実行するサーバー | ubuntu-latest, セルフホスト |
ワークフローファイルの基本構造
name: CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
permissions:
contents: read
pull-requests: write
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests
run: npm test
build:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: npm run build
自動テストの実践設定
Node.js / TypeScriptプロジェクトのテスト
name: Test
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run type check
run: npm run type-check
- name: Run unit tests
run: npm run test:unit -- --coverage
- name: Run integration tests
run: npm run test:integration
- name: Upload coverage report
if: matrix.node-version == 22
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage/
retention-days: 7
Pythonプロジェクトのテスト
name: Python Test
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- name: Setup Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-dev.txt
- name: Run ruff linter
run: ruff check .
- name: Run ruff formatter check
run: ruff format --check .
- name: Run mypy type check
run: mypy src/
- name: Run pytest with coverage
run: |
pytest tests/ \
--cov=src \
--cov-report=xml \
--cov-report=html \
-v
- name: Upload coverage to Codecov
if: matrix.python-version == '3.13'
uses: codecov/codecov-action@v4
with:
file: coverage.xml
token: ${{ secrets.CODECOV_TOKEN }}
データベースを使ったテスト
name: Integration Test with DB
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
POSTGRES_DB: testdb
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run database migrations
run: npm run db:migrate
env:
DATABASE_URL: postgresql://testuser:testpass@localhost:5432/testdb
- name: Run integration tests
run: npm run test:integration
env:
DATABASE_URL: postgresql://testuser:testpass@localhost:5432/testdb
REDIS_URL: redis://localhost:6379
ビルドとアーティファクト管理
Dockerイメージのビルドとプッシュ
name: Build and Push Docker Image
on:
push:
branches: [main]
tags: ['v*']
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub 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=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
platforms: linux/amd64,linux/arm64
静的サイトのビルド(Next.js)
name: Build Next.js
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
env:
NEXT_PUBLIC_API_URL: ${{ vars.NEXT_PUBLIC_API_URL }}
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: nextjs-build
path: .next/
retention-days: 3
デプロイの自動化
AWS ECSへのデプロイ
name: Deploy to AWS ECS
on:
push:
branches: [main]
concurrency:
group: deploy-production
cancel-in-progress: false
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/github-actions-deploy
aws-region: ap-northeast-1
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build, tag, and push image
id: build-image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: my-app
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
- name: Download task definition
run: |
aws ecs describe-task-definition \
--task-definition my-app-task \
--query taskDefinition \
> task-definition.json
- name: Update task definition
id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-definition.json
container-name: my-app
image: ${{ steps.build-image.outputs.image }}
- name: Deploy to ECS
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
service: my-app-service
cluster: my-cluster
wait-for-service-stability: true
Vercelへのデプロイ
name: Deploy to Vercel
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install Vercel CLI
run: npm install -g vercel
- name: Deploy to Preview (PR)
if: github.event_name == 'pull_request'
run: |
vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }}
vercel build --token=${{ secrets.VERCEL_TOKEN }}
vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }}
- name: Deploy to Production (main)
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: |
vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}
vercel deploy --prod --prebuilt --token=${{ secrets.VERCEL_TOKEN }}
高度なワークフロー設定
モノレポのパス別トリガー
name: Monorepo CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
frontend: ${{ steps.changes.outputs.frontend }}
backend: ${{ steps.changes.outputs.backend }}
infra: ${{ steps.changes.outputs.infra }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: changes
with:
filters: |
frontend:
- 'packages/frontend/**'
backend:
- 'packages/backend/**'
infra:
- 'infrastructure/**'
test-frontend:
needs: detect-changes
if: needs.detect-changes.outputs.frontend == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci --workspace=packages/frontend
- run: npm run test --workspace=packages/frontend
test-backend:
needs: detect-changes
if: needs.detect-changes.outputs.backend == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci --workspace=packages/backend
- run: npm run test --workspace=packages/backend
リリース自動化
name: Release
on:
push:
tags: ['v*']
permissions:
contents: write
packages: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Generate changelog
id: changelog
uses: orhun/git-cliff-action@v3
with:
config: cliff.toml
args: --latest --strip header
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
body: ${{ steps.changelog.outputs.content }}
draft: false
prerelease: ${{ contains(github.ref, 'rc') || contains(github.ref, 'beta') }}
generate_release_notes: false
スケジュール実行とキャッシュ戦略
name: Scheduled Tasks
on:
schedule:
# 毎日午前3時(JST)に実行
- cron: '0 18 * * *'
workflow_dispatch: # 手動実行も可能
jobs:
dependency-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Security audit
run: npm audit --audit-level=high
- name: Check outdated packages
run: npm outdated || true
- name: Notify on Slack
if: failure()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "セキュリティ監査で問題が検出されました: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
セキュリティのベストプラクティス
シークレット管理
GitHub Actionsでのシークレット管理は、セキュリティの最重要事項です。
| レベル | 用途 | 設定場所 |
|---|---|---|
| Repository secrets | リポジトリ固有のシークレット | Settings > Secrets |
| Environment secrets | 環境別のシークレット(staging/production) | Settings > Environments |
| Organization secrets | 組織全体で共有するシークレット | Organization > Settings |
OIDC認証によるクラウドアクセス
長期的なアクセスキーの代わりにOIDC(OpenID Connect)トークンを使用することで、セキュリティを大幅に向上できます。
jobs:
deploy:
permissions:
id-token: write # OIDC トークンの発行に必要
contents: read
steps:
# AWS - OIDC認証
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/github-actions
aws-region: ap-northeast-1
# GCP - OIDC認証
- uses: google-github-actions/auth@v2
with:
workload_identity_provider: 'projects/123456/locations/global/workloadIdentityPools/github/providers/github'
service_account: 'deploy@project.iam.gserviceaccount.com'
ワークフローの権限制御
# リポジトリレベルで最小権限をデフォルトに設定
permissions: read-all
jobs:
deploy:
# ジョブレベルで必要な権限のみ追加
permissions:
contents: read
packages: write
id-token: write
依存関係のピン留め
steps:
# SHA指定でアクションをピン留め(推奨)
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
# タグ指定(メジャーバージョンのみ)
- uses: actions/setup-node@v4
# 避けるべき:ブランチ指定
# - uses: actions/checkout@main # 危険
CI/CDパイプラインの主要プラットフォーム比較
| 機能 | GitHub Actions | GitLab CI | CircleCI | Jenkins |
|---|---|---|---|---|
| ホスティング | SaaS + セルフホスト | SaaS + セルフホスト | SaaS + セルフホスト | セルフホストのみ |
| 設定ファイル | YAML | YAML | YAML | Groovy (Jenkinsfile) |
| 無料枠 | 2,000分/月(Public無制限) | 400分/月 | 6,000分/月 | 無料(OSS) |
| マーケットプレイス | 20,000+アクション | テンプレートカタログ | Orbs | プラグイン1,800+ |
| コンテナサポート | Docker、Kubernetes | Docker、Kubernetes | Docker、Kubernetes | Docker、Kubernetes |
| セルフホストランナー | あり(無料) | あり | あり(有料) | 標準 |
| OIDC認証 | あり | あり | あり | プラグイン |
| マトリクスビルド | ネイティブ | ネイティブ | 制限あり | プラグイン |
| キャッシュ | 10GB/リポジトリ | 制限あり | あり | あり |
トラブルシューティング
よくある問題と対策
1. ワークフローが遅い
# 対策1: キャッシュを活用
- uses: actions/cache@v4
with:
path: |
~/.npm
node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
# 対策2: 不要なチェックアウトの最適化
- uses: actions/checkout@v4
with:
fetch-depth: 1 # 浅いクローン
sparse-checkout: |
src/
tests/
2. 同時実行の制御
# 同一ブランチの同時実行を防止
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true # 古い実行をキャンセル
3. タイムアウトの設定
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 30 # デフォルト360分から短縮
steps:
- name: Run tests
run: npm test
timeout-minutes: 15 # ステップ単位のタイムアウト
実践的なCI/CDパイプライン完全版
以下は、本番環境で使用できるCI/CDパイプラインの完全版です。
name: Production CI/CD
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
pull-requests: write
packages: write
id-token: write
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
NODE_VERSION: 22
REGISTRY: ghcr.io
jobs:
lint-and-typecheck:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run type-check
test:
runs-on: ubuntu-latest
timeout-minutes: 20
needs: lint-and-typecheck
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: testdb
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
- run: npm run db:migrate
env:
DATABASE_URL: postgresql://test:test@localhost:5432/testdb
- run: npm run test:coverage
env:
DATABASE_URL: postgresql://test:test@localhost:5432/testdb
build-and-push:
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
needs: test
runs-on: ubuntu-latest
timeout-minutes: 15
outputs:
image-tag: ${{ steps.meta.outputs.tags }}
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ github.repository }}
tags: |
type=sha
type=raw,value=latest
- uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
deploy:
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
needs: build-and-push
runs-on: ubuntu-latest
timeout-minutes: 15
environment: production
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ap-northeast-1
- name: Deploy to ECS
run: |
aws ecs update-service \
--cluster production \
--service my-app \
--force-new-deployment
- name: Wait for deployment
run: |
aws ecs wait services-stable \
--cluster production \
--services my-app
- name: Notify Slack
if: always()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "デプロイ ${{ job.status }}: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
まとめ
GitHub Actionsは、2026年現在のCI/CDパイプライン構築において最も柔軟で強力なプラットフォームの一つです。本記事で解説した主要なポイントを整理します。
| フェーズ | ベストプラクティス |
|---|---|
| テスト | マトリクスビルドで複数環境を並列テスト |
| ビルド | Docker Buildxでマルチプラットフォーム対応 |
| デプロイ | OIDC認証 + Environment保護ルール |
| セキュリティ | アクションのSHAピン留め、最小権限 |
| パフォーマンス | キャッシュ活用、同時実行制御 |
| 監視 | Slack通知、失敗時のアラート |
CI/CDパイプラインは一度構築して終わりではなく、継続的な改善が求められます。テスト実行時間の短縮、デプロイの安定性向上、セキュリティの強化を、チームの成長に合わせて進めていくことが重要です。