📝

CI/CD 構築:フロントエンド自動デプロイ

に公開

実現したこと

手動で行っていた以下のフローを、main ブランチに push したらフロントが自動で本番反映されるように構築した。

コード修正
  ↓ 手動
npm run build
  ↓ 手動
AWS S3 コンソール画面より build ファイルをアップロード
  ↓ 手動
AWS CloudFront のコンソール画面よりキャッシュ削除

📚 CI/CD とは

名称 役割
CI Continuous Integration コード変更のたび自動でチェック(typecheck / lint / build / test)
CD Continuous Delivery / Deployment CI が通ったら自動で本番反映

Step 1:AWS リソースの準備

Step 1-1 全体像の把握

# リソース 役割
OIDC Provider 「GitHub の発行する身分証を信頼するよ」と AWS に教える設定
IAM Role + Trust Policy Trust Policy:IAM Role を GitHub Actions に貸す条件(特定リポジトリの main ブランチからの AssumeRole は許可)
権限ポリシー このロールに「何ができるか」を付与(S3 書き込み・CloudFront キャッシュ削除)
- AssumeRole GitHub Actions が実際に IAM Role の権限を一時的に取得する

Step 1-2 OIDCプロバイダーの作成

OIDCについては、こちらの記事を参照

https://zenn.dev/fusic/articles/48620c1c798e

IDプロバイダー作成画面

固定値

  • プロバイダの URL: https://token.actions.githubusercontent.com
  • 対象者 (Audience): sts.amazonaws.com

Step 1-3 IAMロールの作成

  • IAM コンソールから「ロールを作成」を選び、信頼されたエンティティタイプとして **「ウェブアイデンティティ」**を選択する。

入力項目

項目
アイデンティティプロバイダー token.actions.githubusercontent.com
Audience sts.amazonaws.com
GitHub organization your-org
GitHub repository(オプション) your-repo
GitHub branch(オプション) main

Step 1-4 権限の追加(スキップ)

  • 「何も選択せず」 に Next をクリック。
    • 理由: 権限ポリシーは別途独立して作って、後からアタッチするため。

Step 1-5 Trust Policyの修正

ロール作成時に AWS が自動生成する Trust Policy
には、ウィザードの仕様で重複や冗長な記述が含まれる。最終的に以下の形に整える。

Before(自動生成)

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Principal": {
        "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
      },
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": ["sts.amazonaws.com"]
        },
        "StringLike": {
          "token.actions.githubusercontent.com:sub": [
            "repo:your-org/your-repo:ref:refs/heads/main",
            "repo:your-org/your-repo:ref:refs/heads/main"
          ]
        }
      }
    }
  ]
}

After(最終形)

 {
   "Version": "2012-10-17",
   "Statement": [
     {
       "Effect": "Allow",
       "Principal": {
         "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
       },
       "Action": "sts:AssumeRoleWithWebIdentity",
       "Condition": {
         "StringEquals": {
           "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
           "token.actions.githubusercontent.com:sub": "repo:your-org/your-repo:ref:refs/heads/main"
         }
       }
     }
   ]
 }

修正内容

# 修正 理由
1 sub の重複削除 同じ値が 2 回入っていても効果は同じ。冗長性排除
2 StringLikeStringEquals sub の値にワイルドカードなし → 完全一致演算子で最小権限の原則を徹底
3 audsub を同じ StringEquals ブロックに統合 同じ演算子はまとめて可読性向上

Step 1-6 権限ポリシー作成(S3 + CloudFront)

次に「ポリシーの作成」から、このロールに付与する権限(何ができるか)をJSONで定義する。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "S3SyncBucketLevel",
      "Effect": "Allow",
      "Action": ["s3:ListBucket"],
      "Resource": "arn:aws:s3:::your-bucket-name"
    },
    {
      "Sid": "S3SyncObjectLevel",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject"
      ],
      "Resource": "arn:aws:s3:::your-bucket-name/*"
    },
    {
      "Sid": "CloudFrontInvalidation",
      "Effect": "Allow",
      "Action": ["cloudfront:CreateInvalidation"],
      "Resource": "arn:aws:cloudfront::123456789012:distribution/EXXXXXXXXXXXXX"
    }
  ]
}

Step 1-7 作成したポリシーをRoleにアタッチ

作成した権限ポリシーを、Step 1-3 で作った IAM Role にアタッチする。

  1. IAM → Roles → 該当 Role を開く
  2. Permissions タブ → Add permissionsAttach policies
  3. ポリシー名で検索 → チェック → Add permissions

Step 2:GitHub Actions の設定

Step 2-1 GitHub Actions について

GitHub Actions を理解する - GitHub Docs

┌──────────────────────────────────────┐
│         Workflow(ワークフロー)         │
│   = 1 つの yaml ファイル                │
│   = 「何をきっかけに何をやるか」のまとまり    │
│                                      │
│  ┌──────────────────────────────┐    │
│  │       Job(ジョブ)            │    │
│  │  = 1 台の Runner で動く処理単位 │    │
│  │  = 並列実行可(複数 Job)        │    │
│  │                              │    │
│  │  ┌─────────────────────┐     │    │
│  │  │  Step(ステップ)    │     │    │
│  │  │  = 1 コマンド         │     │    │
│  │  │  = 直列実行           │     │    │
│  │  └─────────────────────┘     │    │
│  │  ┌─────────────────────┐     │    │
│  │  │  Step                │     │    │
│  │  └─────────────────────┘     │    │
│  └──────────────────────────────┘    │
└──────────────────────────────────────┘
階層 役割
Workflow yaml ファイル 1 つ deploy.yml
Job 実行単位(VM 1 台分) build-and-deploy
Step コマンド or アクション 1 個 npm ci, npm run build
Runner 実際にコマンドを動かすマシン ubuntu-latest(GitHub 提供)

Step 2-2 Workflow の分離方針

個人開発ですが、あえてチーム開発を想定した構成にする。

.github/workflows/
├── ci.yml          ← PR 作成・更新時に発火(typecheck / lint / build)
└── deploy.yml      ← main への push 時に発火(build + deploy)

開発フロー

  1. feature ブランチを切る (git checkout -b feature/xxx)
  2. 開発・commit・push (git push origin feature/xxx)
  3. GitHub で Pull Request 作成
  4. ci.yml が自動実行(typecheck / lint / build)
  5. レビュー(セルフレビューでも OK)
  6. main へ Merge
  7. deploy.yml が自動実行(本番デプロイ)
  8. 本番反映 🎉

Step 2-3 事前準備:GitHub Variables 登録

Workflow内で使用する変数を GitHub の Settings > Secrets and variables > Actions から登録する

Step 2-4 ci.yml の作成

.github/workflows/ci.yml

name: CI

on:
  pull_request:
    branches: [main]

jobs:
  ci:
    runs-on: ubuntu-latest

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

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: "npm"

      - name: Install dependencies
        run: npm ci

      - name: Type check
        run: npm run typecheck

      - name: Lint
        run: npm run lint

      - name: Build
        run: npm run build
        env:
          NEXT_PUBLIC_API_BASE_URL: ${{ vars.NEXT_PUBLIC_API_BASE_URL }}

Step 2-5 deploy.yml の作成

.github/workflows/deploy.yml

name: Deploy Frontend

on:
  push:
    branches: [main]
    paths:
      - "src/**"
      - "public/**"
      - "package*.json"
      - "next.config.*"
      - "tsconfig.json"
      - ".github/workflows/deploy.yml"
  workflow_dispatch:

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest

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

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: "npm"

      - name: Install dependencies
        run: npm ci

      - name: Type check
        run: npm run typecheck

      - name: Lint
        run: npm run lint

      - name: Build
        run: npm run build
        env:
          NEXT_PUBLIC_API_BASE_URL: ${{ vars.NEXT_PUBLIC_API_BASE_URL }}

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ vars.AWS_ROLE_ARN }}
          aws-region: ${{ vars.AWS_REGION }}

      - name: Deploy to S3
        run: aws s3 sync ./out s3://${{ vars.S3_BUCKET }} --delete

      - name: Invalidate CloudFront
        run: |
          aws cloudfront create-invalidation \
            --distribution-id ${{ vars.CLOUDFRONT_DISTRIBUTION_ID }} \
            --paths "/*"

Step 2-6 動作確認と Node.js バージョンの警告対応

実行時に以下のような警告が出た

Node.js 20 actions are deprecated. The following actions are running on Node.js 20...

  • 強制移行: 2026/6/2
  • Node 20 削除: 2026/9/16

これは、GitHub Actions のランナー上で Action 自体が動く Node.js のバージョン(≠ ビルドで使う Node 22)が、Node 20 → Node 24 に移行するという予告。

Action 状況
actions/checkout@v5 ✅ Node 24 対応、警告消えた
actions/setup-node@v5 ✅ Node 24 対応、警告消えた
aws-actions/configure-aws-credentials@v5 ❌ まだ Node 20、警告残った

aws-actions/configure-aws-credentials などは、メジャーバージョンを上げても内部のランタイム宣言が Node 20 のままである場合がある。

🛠 解決策:環境変数で Node 24 を強制する

両 workflow の jobs.X: 直下に環境変数を追加して対応する。

jobs:
  deploy:
    runs-on: ubuntu-latest
    env:
      FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" # ← 追加
    steps:
      # ... 以降の処理

Step 2-7 ブランチ保護ルールの設計

  1. ブランチを切る
  2. 変更を commit
  3. PR 作成 → ci.yml が走る
  4. PR をオープンのまま、GitHubリポジトリの Settings > Branches から保護ルール(Branch protection rules)を設定
  5. Require status checks to pass before merging を有効にし、ステータスチェック候補から ci を登録
  6. PR を merge
  7. deploy.yml が main で発火 → 本番反映

本番反映確認

最後に、実際にデプロイされたURLにアクセスして反映を確認する

Discussion