開発効率化

GitHub Actions layered custom images入門——Next.js CIを階層化する

GitHub Actions custom imagesのレイヤー化を題材に、Next.js + FirestoreのCI環境を共通基盤とアプリ固有環境に分ける手順を解説します。

2026年6月21日
GitHub ActionsCI/CDNext.jsFirestoreTypeScript
GitHub Actions layered custom images入門——Next.js CIを階層化する

はじめに

GitHub ActionsのCIで、こんな待ち時間が積み上がっていませんか?

状況問題
PRごとにNode.jsやブラウザを準備するyarn build前が長い
複数リポジトリで同じツールを入れる手順が重複する
アプリごとに追加ツールが違う共通imageが肥大化する

2026年6月18日、GitHubはGitHub-hosted larger runnersのcustom imagesについて、custom imageを別のcustom imageのベースにできるようになったと発表しました。あわせて、image snapshotを作るかどうかを条件で制御できるようになっています。

本記事では、架空の受注管理SaaS「OrderDock」を題材に、Next.js + Firestore + GitHub Actions + TypeScriptのCIを「共通基盤」と「アプリ固有環境」に分ける方法を解説します。

何が変わったのか

GitHub Docsでは、custom imagesはGitHub-hosted larger runnersで使う実行環境を事前に作成し、依存ツールや設定を先に入れておく仕組みとして説明されています。利用にはlarger runners、custom imagesの有効化、管理権限が必要です。

今回の更新でうれしいのは、画像を1枚に詰め込まず、次のように分けられる点です。

web-ci-baseにはNode.js、Yarn、共通の検証ツールを入れます。orderdock-nextjs-ciには、OrderDockだけで使うFirebase Emulatorやブラウザ検証を追加します。共通部分とアプリ固有部分を分けることで、CIを小さく保てます。

設計方針

OrderDockでは、CI環境を3層に分けます。GitHub-owned imageを土台に、週1回更新のweb-ci-baseを作り、その上にorderdock-nextjs-ciを重ねます。

GitHub Docsでは、custom imageから作った派生imageは、ベースimageの有効期限タイムラインを引き継ぐと説明されています。ベースを古いままにして派生imageだけ作り直しても、更新計画の問題は解決しません。

ハンズオン1: 共通ベースimageを作る

まず、Organization側でimage-generation用のlarger runnerを作成します。ここでは架空のrunner名を image-builder-linux-x64 とします。workflowでは、snapshotキーワードを使ってimage名を指定します。

name: Build web CI base image

on: workflow_dispatch

jobs:
  build-base:
    runs-on: image-builder-linux-x64
    snapshot:
      image-name: web-ci-base
      version: 1.*
    steps:
      - uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: "yarn"
      - run: node --version

実運用ではこれを週次scheduleで動かします。ここで入れるものは、複数のWebアプリで共通して使うものだけにします。Firebaseの本番資格情報、アプリ固有の.env、顧客ごとの設定ファイルはimageに含めません。

ハンズオン2: OrderDock専用imageを重ねる

次に、GitHubのrunner設定で web-ci-base をベースにしたimage-generation runnerを用意します。OrderDock専用imageでは、Firestore Emulatorを使う結合テストの前提を追加します。ただし、Google CloudのサービスアカウントキーやFirebase本番プロジェクトIDはimageに入れません。

name: Build OrderDock CI image

on: workflow_dispatch

jobs:
  build-orderdock:
    runs-on: image-builder-orderdock
    snapshot:
      if: ${{ github.ref == 'refs/heads/main' }}
      image-name: orderdock-nextjs-ci
      version: 1.*
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: "yarn"
      - run: yarn install --frozen-lockfile
      - run: yarn build

GitHub Docsでは、snapshotのmapping syntaxでifを使い、image snapshotを作る条件を制御できると説明されています。上の例では、main以外からimage versionを作らないようにしています。

ハンズオン3: PR用CIでcustom imageを使う

imageが生成され、通常実行用のlarger runnerにインストールされたら、PR用workflowのruns-onをそのrunner名に向けます。

name: PR Quality Gate

on:
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: orderdock-nextjs-runner
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v4
      - run: yarn install --frozen-lockfile
      - run: yarn build

custom imageを使ってもyarn installを完全に消すとは限りません。lockfileが変わったPRでは依存関係の検証が必要です。狙いは、OSパッケージや重い共通ツールの準備を毎回やらないことです。

Firestoreを使うテストは、本番DBへ接続せず、Emulatorかモックに寄せます。監査ログの保存処理はTypeScriptで薄く分けておくと、CIで検証しやすくなります。

import type { Firestore } from "firebase-admin/firestore";

type CiImageAudit = {
  imageName: string;
  imageVersion: string;
  sourceSha: string;
};

export async function recordCiImageAudit(
  db: Firestore,
  audit: CiImageAudit,
) {
  await db.collection("ciImageAudits").add({
    ...audit,
    createdAt: new Date(),
  });
}

image versionはGitHubの画面やjobログで確認し、リリース時に環境変数として渡す運用にします。確認できていないAPIを推測で呼び出すより、「どのimageを使う想定か」を監査ログに残すほうが安全です。

運用チェックリスト

custom imagesを導入すると、CIは速くなりますが、更新責任も増えます。OrderDockでは次のルールにします。

  1. ベースimageは週1回、派生imageはベース更新後に作る
  2. snapshotを作るworkflowはmainまたは管理者の手動実行に限定する
  3. image-generation runnerは専用runner groupに置く
  4. 本番secretsやサービスアカウントキーをimageに焼き込まない
  5. PR用CI、夜間結合テスト、デプロイworkflowでrunnerを分ける

特に3つ目は重要です。GitHub Docsも、production imageを生成するrunner groupを開発・テスト用リポジトリと共有しないことを推奨しています。

参考リンク

まとめ

GitHub Actions custom imagesのレイヤー化は、CI高速化の話でありながら、チームの責任分界を整理する話でもあります。

Next.js + Firestoreの業務アプリでは、共通のWeb CI基盤とアプリ固有の検証環境を分けることで、重いセットアップを減らしつつ、必要なツールだけを持つrunnerを作れます。まずは週次生成のベースimageを1つ作り、PR用CIで効果を測るところから始めるのが現実的です。