第14章:CIでもApp Check(GitHub Actionsなど)🏗️🔒
CI(自動テスト)を回し始めると、App Check の“強制ON”が急に牙をむきます🧿💥 「ローカルは動くのに、CIだけ落ちる…😇」を、この章でスッキリ片付けます✌️
まず結論:CIでは“Debug Provider + デバッグトークン”で通す🧪🧿

CIのE2Eテストは 実機でも本番ブラウザでもないので、通常の reCAPTCHA 経由の App Check をそのまま通すのが難しい場面が出ます。 そこで公式が案内しているのが Debug Provider(= デバッグトークンで正規扱いにする) です。(Firebase)
この章でやること(ゴール)🎯✨
- CIの中で App Check 強制ONの環境でもテストが通るようにする🏃♂️💨
- デバッグトークンを コードに直書きしないで安全に運用する🔐
- ついでに「AI整形ボタン🤖」まで含めた 最低限のE2E を1本作る🧪
読む📖:CIで詰まる“あるある”と、公式の推奨ルート🧠
あるある①:強制ONにした瞬間、CIが全部 403/permission-denied 😇
App Check を “enforce(強制)” にすると、正規の証明がないリクエストが落ちやすくなります。 CIはまさに「証明がない側」になりがち。だから CI用のデバッグトークンが必要になります。(Firebase)
あるある②:トークンをフロントに埋めたら漏れそうで怖い😱
怖いです。なので Secrets(秘密情報)として管理し、ログに出さない・成果物として外に出さない、が基本です。 公式も「CI用のトークンは秘密情報として扱う」ルートを示しています。(Firebase)
手を動かす🛠️:CIでApp Checkを通す手順(王道コース)👑
ここは 3点セットです👇
- Firebase Console に「CI用デバッグトークン」を登録
- GitHub の Secrets に保存
- CIのビルド/テスト時だけ、そのトークンをフロントに渡して Debug Provider を有効化
1) CI用デバッグトークンを用意して Console に登録🧿✅
公式の Debug Provider の流れ(=デバッグトークンを登録して通す)に従います。(Firebase)
おすすめはこれ👇
- トークン名:
github-actions-e2e(みたいに用途が一目でわかる)📝 - 文字列:ランダム長め(例:32バイト以上)🔐
💡ポイント:デバッグトークンは「これを持ってるクライアントは“デバッグ中の正規”扱い」という通行証🎫 なので 漏れたら困る = Secret扱い必須 です😤
2) GitHub Secrets に入れる🔐(絶対にリポジトリへコミットしない)

GitHubのリポジトリ設定から、Actions用の Secrets を作ります。(GitHub Docs)
- 例:Secret名
APP_CHECK_DEBUG_TOKEN - 値:さっきのデバッグトークン
3) フロント側:CIだけ Debug Provider をONにする(重要:読み込み順)⚛️🧿

公式が強調してる大事ポイント👇
self.FIREBASE_APPCHECK_DEBUG_TOKENを App Check を読み込む前 にセットする(Firebase)
モダンなReact構成だと “import順” がややこしくなりがちなので、**安全なやり方(dynamic import)**でいきます✅
// src/lib/appCheck.ts
import { initializeApp } from "firebase/app";
const debugToken = import.meta.env.VITE_APP_CHECK_DEBUG_TOKEN;
// 先に Firebase App は作る
export const app = initializeApp({
// your firebaseConfig
});
export async function initAppCheck() {
// ✅ ここが超重要:App Check を import する前にセット
if (debugToken) {
(self as any).FIREBASE_APPCHECK_DEBUG_TOKEN = debugToken;
}
// ✅ dynamic import なら “読み込み前にセット” を守れる
const { initializeAppCheck, ReCaptchaV3Provider } = await import("firebase/app-check");
// CI中は Debug Provider が有効になる(debug token があるから)
// CIじゃない時は reCAPTCHA v3 で普通に運用、の形にできる
return initializeAppCheck(app, {
provider: new ReCaptchaV3Provider(import.meta.env.VITE_RECAPTCHA_SITE_KEY),
isTokenAutoRefreshEnabled: true,
});
}
🧠なぜCIでも reCAPTCHA provider を書いてるの? Debug Provider は「デバッグトークンが入ってるとき、SDKがデバッグ扱いにできる」仕組みなので、コードの形は保ったまま
4) CIワークフロー:Node 24 LTSでビルド→プレビュー→E2E🧪

2026年2月時点で Node 24 系が LTS で安定運用の対象になっています。(nodejs.org)
GitHub Actions では actions/setup-node がよく使われます。(GitHub)
ここでは E2E に Playwright を使う例にします(公式がCI手順を用意してて楽✨)。(playwright.dev)
プレビューサーバーは vite preview(デフォルト http://localhost:4173)がシンプルです。 (vitejs)
## .github/workflows/e2e.yml
name: e2e
on:
pull_request:
push:
branches: [ main ]
jobs:
e2e:
runs-on: ubuntu-latest
# forks の PR だと secrets が渡らないので、必要ならここで弾くのもアリ
# if: github.event.pull_request.head.repo.fork == false
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
- run: npm ci
# ✅ CIでだけDebug Tokenを渡す(buildに混ぜる)
- name: Build
run: npm run build
env:
VITE_APP_CHECK_DEBUG_TOKEN: ${{ secrets.APP_CHECK_DEBUG_TOKEN }}
# Playwright 推奨の入れ方(ブラウザ依存も入る)
- name: Install Playwright browsers
run: npx playwright install --with-deps
- name: Start preview server
run: npm run preview -- --host 127.0.0.1 --port 4173
env:
VITE_APP_CHECK_DEBUG_TOKEN: ${{ secrets.APP_CHECK_DEBUG_TOKEN }}
- name: Run E2E
run: npx playwright test
env:
BASE_URL: http://127.0.0.1:4173
🔥超重要:Debug Token は「build と preview の両方」で渡すほうが事故りにくいです(ビルド時にJSへ埋め込まれる系の構成が多いので)💡
5) Playwright側:App Checkが効いてる最低限の1本を作る🧪🧿🤖

「AI整形ボタン」を押して、結果が画面に出たらOK、みたいな 超スモークにします🧯 (毎PRで重たいAI呼び出しを連打すると財布が燃えるので、後で抑える仕組みも入れます🔥)
// tests/smoke.spec.ts
import { test, expect } from "@playwright/test";
test("App Checkありで、AI整形まで動く", async ({ page }) => {
const baseURL = process.env.BASE_URL!;
await page.goto(baseURL);
// 例:メモ入力→保存
await page.getByPlaceholder("メモを入力").fill("この文章を短くして!");
await page.getByRole("button", { name: "保存" }).click();
// 例:AI整形ボタン
await page.getByRole("button", { name: "AI整形" }).click();
// 例:結果が表示されるのを待つ
await expect(page.getByTestId("ai-result")).toBeVisible();
});
AI(Firebase AI Logic)をCIでどう扱う?🤖💸
おすすめ方針:PRでは“軽い確認”、重いのは夜間 or mainだけ🌙

Firebase AI Logic にはレート制限(デフォルト 100 RPM/user など)があり、運用では守りと制御がセットで語られます。(itnext.io) なのでCIは👇みたいにすると平和です🙂✨
- PR:AIのテストは ダミー応答に切り替える(環境変数で切替)🧩
- main:AIも含めたE2Eを回す✅
- nightly:さらに重いシナリオ(画像+要約など)を回す🌙
ミニ課題🎓🔥:「PRで動く最低限E2E」を1本完成させよう
- App Check 強制ONの状態でもCIが落ちないようにする🧿
- PRで
smoke.spec.tsが必ず走るようにする🏃♀️ - できたら「Debug Tokenなしだと失敗する」ネガティブテストも、手元で1回だけ確認👿(CIに入れるのは慎重に)
チェック✅:ここまでできたら合格🎉
- Debug Token を コードに直書きしてない(Secrets管理できてる)🔐(GitHub Docs)
- CIで
self.FIREBASE_APPCHECK_DEBUG_TOKENを App Check import より前にセットできてる🧠(Firebase) - E2Eが “App Checkあり” の状態で通る🧪
- (できれば)App Check メトリクスで CIアクセスが観測できる👀📈
落とし穴まとめ(ここだけ読んでも価値あるやつ)🕳️😇

- ✅ Secretsがfork PRに渡らない → forkからのPRではE2Eをスキップする設計が必要になりがち
- ✅ Debug Token を 成果物としてアップロードすると漏れる可能性(Artifacts注意⚠️)
- ✅ ログに環境変数を出す
echoは事故のもと💥 - ✅ “import順”が崩れると Debug Token が効かず、CIだけ落ちる(dynamic importが安全)(Firebase)
- ✅ AI呼び出しを毎PRで重く回すと、レート制限や費用で詰む(PRは軽量で)(itnext.io)
AIエージェントで、この章の作業を爆速化🚀🤖
Antigravity(Mission Control)で「章タスク」をそのままミッション化🧭
“計画→実装→Web調査→確認”をエージェントに流せる思想が紹介されています。(Google Codelabs) 例:ミッションに「e2e.ymlを作る」「debug token導線を安全に」などを並べる📋✨
Gemini CLIで「CI漏れ/危険ポイント」をレビューさせる🔎
Gemini CLI はターミナル上のオープンソースAIエージェントとして紹介されていて、リサーチやタスク整理にも向きます。(Google Cloud)
gemini "このリポジトリでApp Checkのデバッグトークンが漏れうる場所を探して。ログ出力、Artifacts、ビルド成果物の扱いもチェックして。"
次の第15章は「通らない時のUX🙂🧯」なので、この章で“CIでちゃんと通る”状態を作れてると一気に気持ちよく進められます😆🔥