Noh | エンジニア向け情報共有コミュニティ
Signup / Login

Firebase Hosting にデプロイ時にStaging用またはProduction用の環境変数を読み込む

firebase

投稿日: 2024/06/10

実務レベルでプロダクト開発を行おうとするとき、Production環境(本番環境)とほぼ同じ環境であるStaging環境も構築することになると思います。Production環境に変更を反映する前にStaging環境で動作確認するわけです。

FirebaseでStaging環境を作成するには、Production環境とは別のFirebaseプロジェクトとして作成します。そのときFirebase Hostingにデプロイする静的Webサイトのソースコードは共有し、処理をビルド時の環境変数で切り分けることになります。例えばViteでは、.env.staging.env.prouductionのようなファイルを用意して、Staging環境にデプロイするときは.env.stagingを読み取ってビルドし、Production環境にデプロイするときは.env.roductionを読み取ってビルドしなければなりません。

Firebase Hosting の Predeploy / Postdeploy 機能を使う方法

Firebase Hosting にはデプロイコマンドを実行したときにデプロイの前後に任意のコマンドを自動実行する Predeploy / Postdeploy 機能があるのでそれを使います。

https://firebase.google.com/docs/cli?hl=ja#hooks

前提としてFirebaseプロジェクトのディレクトリ構成は以下のようになっています。

. ├── Dockerfile ├── README.md ├── data ├── docker-compose.yml ├── firebase-debug.log ├── firebase.json ├── firestore-debug.log ├── firestore.indexes.json ├── firestore.rules ├── hostings └── web ├── README.md ├── astro.config.mjs ├── build_by_firebase_deploy.sh ├── dist ├── node_modules ├── package-lock.json ├── package.json ├── public ├── src ├── tsconfig.json └── vitest.config.ts ├── public ├── rules └── ui-debug.log

他の設定は以下のような感じで、Firebase Hostingのビルドファイルはhostings/web/distに生成されるようにしています。ファイルパスは自身の好みに従ってください。

firebase.json

{ ... "hosting": { "public": "hostings/web/dist", ... },

.firebaserc

{ "projects": { "default": "my-project-staging", "staging": "my-project-staging", "production": "my-project-production" } }

方法の説明をしていきます。
まず、ビルド用のコマンドをpackage.jsonに追加します。僕はフレームワークにAstroを使っていますが、他のフレームワークでも同じ方法で問題ないです。

package.json

"scripts": { ... - "build": "astro check && astro build" + "build:base": "astro check && astro build", + "build:staging": "npm run build:base -- --mode staging", + "build:production": "npm run build:base -- --mode production" },

modeオプションをつけてそれぞれの環境ごとに異なる環境変数を読み込むようにしています。

また、間違って実行しないように念のため元のbuildコマンドは削除しています。

次にビルド Predeploy 機能で実行するデプロイコマンドを書きます。ちょっと長くなるのでシェルスクリプトファイルに書きます。

hostings/web/build_by_firebase_deploy.sh

if [ "$GCLOUD_PROJECT" = "my-project-staging" ]; then npm run build:staging elif [ "$GCLOUD_PROJECT" = "my-project-production" ]; then npm run build:production else echo "Unknown project: $GCLOUD_PROJECT" exit 1 fi

デプロイする対象プロジェクトごとにビルドコマンドを切り分けています。
Firebase Hosting の Predeploy / Postdeploy 機能を使ってコマンドが実行されている最中は環境変数$GCLOUD_PROJECTにプロジェクトIDが入るようになっています。これを使って切り分けます。

最後にfirebase.jsonにコマンドを追記します。

firebase.json

{ ... "hosting": { "public": "hostings/web/dist", ... "predeploy": "cd hostings/web && rm -rf dist && ./build_by_firebase_deploy.sh", "postdeploy": "cd hostings/web && rm -rf dist" },

predeploy / postdeploy の部分に書くコマンドはfirebase.jsonがあるパスで実行される形式で書きます。ビルド前とデプロイ後にdistフォルダを消していますが、やらなくてもいいです。

これでfirebase deployコマンドを実行したときにデプロイ先のFirebaseプロジェクトごとに環境変数が読み込まれてビルドされるようになりました。

デプロイコマンドを打つ前に手動でビルドする方法(おすすめしない)

以下のようにスクリプトの設定をしているとします。

package.json

"scripts": { ... - "build": "astro check && astro build" + "build:base": "astro check && astro build", + "build:staging": "npm run build:base -- --mode staging", + "build:production": "npm run build:base -- --mode production" },

そしてStaging環境にデプロイしたいときは以下のコマンドを実行する。

cd hostings/web npm run build -- --mode staging firebase deploy --only hosting -P staging

そしてProduction環境にデプロイしたいときは以下のコマンドを実行する方法です。

cd hostings/web npm run build -- --mode production firebase deploy --only hosting -P production

この方法はおすすめしません。先に書いた Predeploy 機能を使うべきです。

理由は、複数コマンドを手動実行することでミスにつながりやすいからです。また、onlyオプションなしのfirebase deployを実行するとFirebase Hostingへもデプロイされるので、他のFirestoreなどのデプロイをする際にビルドし忘れるリスクもあります。

Predeploy 機能を使わずにGithub Actionsなどでデプロイする(おすすめしない)

Github Actionsなどで自動デプロイの環境を整えるのはいいと思います。ただ、その場合もPredeploy 機能を使うべきです。

Predeploy 機能を使わないと、間違えてローカル環境でfirebase deployを実行したときにビルドし忘れて、古い別環境のソースコードがデプロイされる事故が発生しやすくなります。

yosi

Noh を作ってるエンジニア。