0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

1. はじめに

以下の記事に記載されているReact勉強ロードマップの課題2のアプリのビルド・テスト・デプロイのCI/CDの作成を取り組んだ。

1-1. インプット

以下の教材の44.まで行った。

1-2. 知識習得

公式ドキュメントの参照

AIに質問するだけでなく、第1次情報である公式ドキュメントを読み解く力が大事といわれていたので挑戦してみた。
公式ドキュメントの中にクイックスタートがあったので、まずはそれを行い全体像をつかんだ。

クイックスタートを行ったことで大体の流れは確認できた。しかし、今回の学習はスピード感を重視する方が重要であり、AIに聞いてもわからないことや・裏側の仕組み・思想のような部分を確認するときにドキュメントを使うというやり方の方が良いと考え、この後はAIを用いた。

AIに質問

入力

「GHAを用いたCI/CDパイプラインの構築について教えて」

出力で学べた内容

GitHub Actions(GHA)を使ったCI/CDパイプラインとは、コードが変更されたときに自動でテスト・ビルド・デプロイを実行する仕組み

CI/CDとは

CI(Continuous Integration)
開発者がコードをPushしたら自動で

  • ソースコード取得
  • 依存関係インストール
  • テスト実行
  • ビルド実行

を行う仕組み。

例えば、

git push origin main

した瞬間に

npm install
npm run test
npm run build

が実行される。

もしテストが失敗したら、

❌ Build Failed

となり、問題を早期発見できる。

CD(Continuous Delivery / Deployment)
CIが成功したらさらに

ビルド
↓
本番環境へデプロイ

まで自動化する。

例えば

mainブランチへマージ
↓
テスト成功
↓
Firebase Hostingへデプロイ

である。

GitHub Actionsの基本構成

GitHubリポジトリに

.github/
└── workflows/
    └── ci.yml

を作成する。

例:

name: CI

on:
  push:
    branches:
      - main

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 22

      - run: npm ci

      - run: npm run test

      - run: npm run build

各ステップの意味

checkout

- uses: actions/checkout@v4

GitHub上のコードを取得

setup-node

- uses: actions/setup-node@v4

Node.js環境を作成

npm ci

- run: npm ci

依存関係をインストール
通常は

npm install

よりも

npm ci

が推奨される。

理由

  • package-lock.json通りにインストール
  • 高速
  • 再現性が高い

テスト実行
Vitestなら

- run: npm run test

package.json

{
  "scripts": {
    "test": "vitest run"
  }
}

ビルド実行
Viteなら

- run: npm run build

実行後

dist/

が生成される。

react/ViteアプリのCIの例

最近作成している学習記録アプリなら、

name: React CI

on:
  pull_request:
  push:
    branches:
      - main

jobs:
  test-and-build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 22

      - run: npm ci

      - run: npm run test

      - run: npm run build

Firebase Hostingへ自動デプロイ

Firebaseにデプロイする場合

name: Deploy

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 22

      - run: npm ci
      - run: npm run build

      - uses: FirebaseExtended/action-hosting-deploy@v0
        with:
          repoToken: ${{ secrets.GITHUB_TOKEN }}
          firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }}
          projectId: your-project-id

流れは

git push
↓
GitHub Actions起動
↓
npm ci
↓
npm run test
↓
npm run build
↓
Firebaseへデプロイ

補足

Dockerを使う場合

name: Docker CI

on:
  push:
    branches:
      - main

jobs:
  docker:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Build Image
        run: docker build -t app .

      - name: Test Container
        run: docker run app npm run test

実務でよくあるパイプライン

PR作成
↓
Lint
↓
Unit Test
↓
Build
↓
Docker Build
↓
E2E Test
↓
mainへマージ
↓
本番デプロイ

より具体的には

Push
 ↓
ESLint
 ↓
Vitest
 ↓
Vite Build
 ↓
Docker Build
 ↓
Docker Hub Push
 ↓
Cloud Run / ECS / Kubernetes Deploy

である。

学習手順

現在の学習状況(React、Vitest、Docker、Firebase)を踏まえると、次の順序が理解しやすい。

  1. GitHub ActionsでVitestを自動実行
  2. Buildを追加
  3. ESLintを追加
  4. Firebase Hostingへ自動デプロイ
  5. Dockerイメージのビルド
  6. Docker HubへのPush
  7. AWSやKubernetesへのデプロイ

まずは「PRを作ったらVitestが自動実行されるCI」を作るのがおすすめ。Reactアプリ開発では最も効果を実感しやすく、実務でもほぼ必須の構成。

1-3. 実装理解

AIに質問

入力

「今回は、buildとtestとdeployを行うパイプラインを作成する。具体的な手順を教えて」

出力で学べた内容

全体像

mainブランチへPush
 ↓
GitHub Actions起動
 ↓
npm ci
 ↓
npm run build
 ↓
npm run test
 ↓
Firebase Hostingへデプロイ

WIFを用いたキーレス認証

GHAからFirebaseへ接続するための認証をできるようにする。

Step1 受け入れ準備と「ロボット社員」の採用

Firebaseの裏側は「Google Cloud」というシステムで動いている。Step1では、GitHub ActionsがGoogle Cloud というオフィスビルに安全に入館し、仕事(デプロイ)ができるように準備する。

1. APIの有効化
必要な部屋の鍵を開ける

  • IAM Service Account Credentials API
    役割: 今回作成する「ロボット社員(サービスアカウント)」の身分証を一時的に発行するための機能

  • Security Token Service API
    役割: GitHubから「私はGitHubのアクションです」という証明書を持ってきた際に、それをGoogle Cloud内で使える「一時的な入場パス(トークン)」に交換する受付窓口の機能

  • Cloud Resource Manager API
    役割: Google Cloudのプロジェクト(ビル全体)の情報を読み取るための基本的な機能

2. デプロイ用サービスアカウントの作成
ロボット社員の採用
1.「IAMと管理」-> 「サービスアカウント」
2. 「+ サービス アカウントを作成」
3. 「サービス アカウント名」を入力
4. ロール(権限)に「Firebase Hosting 管理者」を付与
5. 「完了」

Step2 プールとプロバイダの作成

  • プールの作成(Pool)
    GitHub Actions専用の「外部からの専用エントランス(受付ロビー)」をGoogle Cloudのビルに増設する作業
gcloud iam workload-identity-pools create "github-actions-pool" \
  --project="YOUR_PROJECT_ID" \
  --location="global" \
  --display-name="GitHub Actions Pool"
  • プロバイダの作成 (Provider):
    そのエントランスに、「GitHubが発行した身分証(OIDCトークン)を持っているなら信用する」というマニュアルを持った受付担当者を配置する作業
gcloud iam workload-identity-pools providers create-oidc "github-provider" \
  --project="YOUR_PROJECT_ID" \
  --location="global" \
  --workload-identity-pool="github-actions-pool" \
  --display-name="GitHub Actions Provider" \
  --attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository" \
  --attribute-condition="assertion.repository == 'あなたのGitHubアカウント名/リポジトリ名'" \
  --issuer-uri="https://token.actions.githubusercontent.com"

💡 プールとプロバイダの関係性(おさらい)

  • プール (github-actions-pool):
    外部からのアクセスを管理するための「大きな箱(グループ)」です。最初はただの空箱。

  • プロバイダ (github-provider):
    前回のコマンドで作ったものです。プールの箱の中に設置する「専用の受付窓口」。

つまり、まずは「プール」という枠組みを作り、その中に「GitHub Actions専用の受付」や(必要であれば)「別の外部サービス専用の受付」を並べて管理していく、という階層構造になっている。

Step3 アカウントとリポジトリの紐づけ

「どのGitHubリポジトリ」が「どのサービスアカウント」になりすまして良いか(信用できるか)の許可を与える。

gcloud iam service-accounts add-iam-policy-binding "あなたのサービスアカウントのメールアドレス" \
  --project="YOUR_PROJECT_ID" \
  --role="roles/iam.workloadIdentityUser" \
  --member="principalSet://iam.googleapis.com/projects/YOUR_PROJECT_NUMBER/locations/global/workloadIdentityPools/github-actions-pool/attribute.repository/あなたのGitHubアカウント名/リポジトリ名"

補足

それぞれサービスなどの関係値が分かりずらかったので、図を作成した。

Gemini_Generated_Image_y5igpxy5igpxy5ig.png

Gemini_Generated_Image_o4zvl8o4zvl8o4zv.png

GitHub Secrets登録

先ほどまでのステップでGoogle Cloud 側の受け入れ準備が整ったので、Github ActionsがWIFを利用できるように、GitHubリポジトリに環境設定を登録する。
実のところ、WIFのプロバイダ情報やサービスアカウントのメールアドレスはパスワードのような機密情報ではないので、GitHubの**Repository secrets(シークレット)**ではなく、**Repository variables(変数)**に登録するのが良い。(シークレットに登録しても動作は同じ)

GitHubリポジトリ

Settings
 ↓
Secrets and variables
 ↓
Actions
 ↓
New repository variable

を選択。

名前

WIF_PROVIDER

SERVICE_ACCOUNT

FIREBASE_PROJECT_ID

projects/あなたのプロジェクト番号/locations/global/workloadIdentityPools/github-actions-pool/providers/github-provider

github-actions-deploy@YOUR_PROJECT_ID.iam.gserviceaccount.com

YOUR_PROJECT_ID

ワークフローの作成

pipeline.yml
name: Build, Test, and Deploy to Firebase

on:
  push:
    branches:
      - main

# 【重要】OIDCトークンを発行してWIFによるキーレス認証を行うために必須の設定
permissions:
  contents: read
  id-token: write

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout source code
        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 tests
        run: npm run test -- --run

      - name: Build application
        run: npm run build
        env:
            VITE_SUPABASE_URL: ${{ vars.VITE_SUPABASE_URL }}
            VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY }}

      # 1. サービスアカウントのJSONキーではなく、WIFを使ってGoogle Cloudの認証を通過する
      - name: Authenticate to Google Cloud
        id: auth
        uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: ${{ vars.WIF_PROVIDER }}
          service_account: ${{ vars.SERVICE_ACCOUNT }}

      # 2. 認証済みセッションを利用してFirebaseへデプロイを実行する
      - name: Deploy to Firebase Hosting
        run: npx firebase-tools deploy --only hosting --project ${{ vars.FIREBASE_PROJECT_ID }}

よくある失敗

テストが終わらない
VitestがWatchモードになっている

NG

run: npm run test

OK

run: npm run test -- --run

または

"test:ci": "vitest run""test:ci": "vitest run"

を作り

run: npm run test:ci

にする。

各部分の意味

workflow名

name: Build, Test, and Deploy to Firebase

GitHub Actions画面に表示される名前。

トリガー

on:
  push:
    branches:
      - main

mainブランチにPushされたら実行。
例えば、

git push origin main

で起動する。

job定義

jobs:
  deploy:

deploy はJob名。
任意の名前で構いません。

実行環境

runs-on: ubuntu-latest

GitHubが提供するLinux環境で実行する。
実際には新しい仮想マシンが起動される。

ソース取得

- name: Checkout source code
  uses: actions/checkout@v4

GitHub上のコードをRunnerへコピー。
これがないとソースコードが存在しません。

Node.jsセットアップ

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

Node.js 22をインストールする。

cache: npm

は依存関係のキャッシュ。
2回目以降が高速になる。

パッケージインストール

- name: Install dependencies
  run: npm ci

package-lock.json を利用して依存関係を復元する。
ローカルで言う

npm ci

と同じ。

テスト実行

- name: Run tests
  run: npm run test -- --run

VitestのWatchモードを無効にする。

ビルド

- name: Build application
  run: npm run build

Viteなら

vite build

が実行され、

dist/

が生成される。

また、環境変数の受け渡し

env:
    VITE_SUPABASE_URL: ${{ vars.VITE_SUPABASE_URL }}
    VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY }}

なぜデプロイ時ではなく「ビルド時」に渡すのか?
ReactやNext.jsなどのフロントエンドアプリは、npm run build を実行するタイミングで、環境変数の文字列を静的なファイル(JavaScriptコード)の中に直接埋め込む。そのため、デプロイ時(Firebaseへアップロードする時)ではなく、 ビルドする瞬間に環境変数が存在していること が不可欠です。

(GitHubにSupabaseの環境変数を登録した)

Google Cloud への認証とデプロイ

- name: Authenticate to Google Cloud
        id: auth
        uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: ${{ vars.WIF_PROVIDER }}
          service_account: ${{ vars.SERVICE_ACCOUNT }}

      - name: Deploy to Firebase Hosting
        run: npx firebase-tools deploy --only hosting --project ${{ vars.FIREBASE_PROJECT_ID }}

id: auth

id: auth

このステップに

auth

という識別子をつけている。
例えば後続で

${{ steps.auth.outputs.access_token }}

のように参照できる。

uses

uses: google-github-actions/auth@v2

GitHub Marketplaceにある
google-github-actions/auth
というActionを実行している。

役割は

GitHub
↓
Google Cloud認証

である。

with

with:

Actionへ渡す設定値。

workload_identity_provider

workload_identity_provider: ${{ vars.WIF_PROVIDER }}

GitHub Variablesに保存された値を渡す。

これは

GitHubのIDを信頼する設定

を表している。
イメージ

Google Cloud

Workload Identity Pool
  └ Provider
       └ GitHub Repository

このProviderを指定している。

service_account

service_account: ${{ vars.SERVICE_ACCOUNT }}

認証後、

GitHub Actions
↓
このService Accountとして振る舞う

ことになる。

認証後どうなる?
内部的には

gcloud auth login

のような状態。
以降の

firebase deploy
gcloud run deploy

などが実行可能になる。

Deploy to Firebase Hosting

run: npx firebase-tools deploy --only hosting --project ${{ vars.FIREBASE_PROJECT_ID }}

npx firebase-tools
はfirebaseCLIを実行している。
例えば、

firebase deploy

とほぼ同じ。

--only hosting

firebase deploy --only hosting

Hostingだけにデプロイする。
例えば

Hosting
Functions
Firestore Rules
Storage Rules

がある場合でも

Hostingのみ

更新する。

改善点

name: Build, Test, and Deploy to Firebase

on:
  push:
    branches:
      - main

# 1. 重複実行のキャンセル(Concurrency)
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

permissions:
  contents: read
  id-token: write

jobs:
  deploy:
    runs-on: ubuntu-latest
    # 2. タイムアウトの明示的設定
    timeout-minutes: 15
    # 3. GitHub Environmentsの指定
    environment: production

    steps:
      - name: Checkout source code
        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 tests
        run: npm run test -- --run

      - name: Build application
        run: npm run build
        env: # インデントを整理
          VITE_SUPABASE_URL: ${{ vars.VITE_SUPABASE_URL }}
          VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY }}

      - name: Authenticate to Google Cloud
        id: auth
        uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: ${{ vars.WIF_PROVIDER }}
          service_account: ${{ vars.SERVICE_ACCOUNT }}

      - name: Deploy to Firebase Hosting
        run: npx firebase-tools deploy --only hosting --project ${{ vars.FIREBASE_PROJECT_ID }}

1. concurrency による無駄なビルドの防止
デプロイ直後に軽微な修正(タイポなど)を見つけて、連続して main にPushすることがある。
cancel-in-progress: true を設定することで、「古いPushのデプロイ作業」を即座にキャンセルし、最新のPushだけを処理するようになる。これにより、GitHub Actionsの無料枠(利用時間)の節約と、古いバージョンが後から上書きデプロイされてしまう事故(競合)を防げる。

2. timeout-minutes: 15 の設定
デフォルトのGitHub Actionsは、何らかのエラー(無限ループや通信のハングアップなど)で処理が止まった場合、最大6時間(360分) ランナーが動き続けてしまう。
フロントエンドのビルドとデプロイであれば通常数分で終わるため、15分程度で強制終了(タイムアウト)させる設定を入れておくと、不測の事態でも安心。

3. environment: production の追加と保護
GitHub側の「Environments(環境)」機能と紐づけるための記述。
これを入れておくと、GitHubリポジトリの設定画面で「デプロイ前に特定のメンバーの承認(Approve)を必須にする」といった強力なアクセス制御が可能になる。現在登録しているSecretsやVariablesも、今後はこの「production環境専用の変数」として管理することで、より高いセキュリティを担保できる。

2. 振り返り

生成AIに聞きながらの勉強は大丈夫だと思う。質問の中に2026年現在のベストプラクティスという言葉を入れる。そしてまずは動くものを作る。アウトプットする。そこで経験値を積んで、「この書き方・文法結構出てくるな」っていうものを公式ドキュメントで仕様ちゃんとみとこみたいなフィードバック方式学習が良いと思った。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?