Edited at

【勉強会用資料】Cloud Functions for FirebaseとFirebase Hostingを使い、ベーシック認証付きステージング環境と本番環境を用意する

勉強会用に作成した、Cloud Functions for FirebaseとFirebase Hostingを使い、ベーシック認証付きステージング環境と本番環境を用意するまでのフローです。

以下の内容を学ぶことができます。


  • Git(GitHub)入門

  • Firebase Hosting と Cloud Functions for Firebase 入門


    • ほんのりTypeScript



始めるまえに、以下の設定が必要です。


  • GitHubアカウント

  • Googleアカウント

  • ローカル環境にNode.jsのインストール(version 8 以上)


GitHubのリポジトリを作成


  1. 「New repository」でリポジトリを新規作成

  2. 「Clone or download」でURLを取得しクローン

  3. 任意の場所でプロジェクト用ディレクトリを作成

  4. クローンしたディレクトリにターミナルで移動

$ mkdir demo-project

$ git clone <github-url>
$ cd demo-projecy


developブランチの作成

ターミナルでブランチを作成

$ git checkout -b develop


ベーシック認証の環境


Firebaseのプロジェクトを作成

コンソールで新規プロジェクト作成


firebaseの設定

$ firebase login

$ firebase init



  1. Functions: Configure and deploy Cloud Functionsを選択

  2. Firebase Consoleで作成したプロジェクトを選択

  3. せっかくなのでTypeScriptを選択

  4. Do you want to use TSLint to catch probable bugs and enforce style? -> n(TSLintよりも、ESLintが良いかと思うのでここはNoで)

  5. Do you want to install dependencies with npm now? -> [enter](npm installしてくれます)


開発環境の構築

リダイレクト用ホスティングディレクトリ作成

public/

└ placeholder.html

Webページの本体になるディレクトリを作成

nuxt generate等で出力する場合は、そのディレクトリ)

表示確認ようにダミーのindex.htmlを作成しておく

functions/

└ dist/
└ index.html

package.jsonpackage-lock.jsonを移動

functions/

├ package.json
└ package-lock.json

functions/

package.json
package-lock.json

npm scriptを書き換え

  "scripts": {

"build": "tsc --project functions",
"preserve": "npm run build && npm run copy-deps && npm run install-deps",
"serve": "npm run build && firebase serve",
"shell": "npm run build && firebase functions:shell",
"start": "npm run shell",
"init-deploy": "firebase deploy",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log",
"copy-deps": "cpx \"*{package.json,package-lock.json,yarn.lock}\" \"functions\" -C",
"install-deps": "cd functions && npm install && cd ../"
}

パッケージの追加

$ npm i express basic-auth-connect && npm i -D @types/express cpx

firebase.jsonの書き換え

{

"functions": {
"source": "functions",
"predeploy": "npm run preserve"
},
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"function": "stagingApp"
}
]
}
}

function/src/index.tsに以下を記述

import * as functions from 'firebase-functions';

import * as express from 'express';

// 型定義ファイルを作るのが面倒なので妥協。。。
const basicAuth = require('basic-auth-connect');

const user = 'user';
const pass = 'pass';

const server = express();
server.use(basicAuth(user, pass));
server.use(express.static('./dist'));

export const stagingApp = functions.https.onRequest(server);

function/src/index.tsをビルドしてローカルサーバー立ち上げ

$ npm run serve


機密情報を環境変数に変更

このままだとベーシック認証のアイパスが見えてしまうので環境変数に格納

$ cd functions

$ firebase functions:config:set basicauth.user="<yourUserName>" basicauth.pass="<yourPassword>"

設定した環境変数は以下で確認できる

$ firebase functions:config:get

ローカルサーバーでも使えるように、functions/.runtimeconfig.jsonを作成

$ firebase functions:config:get > .runtimeconfig.json

$ cd ../

.gitignore.runtimeconfig.jsonを追加してgit管理から外す(リモートにプッシュ禁止)

また、functions/配下にもnode_modulespackage.jsonが必要なため、以下のコマンドで複製しインストールしておく

(ローカルサーバー立ち上げ、デプロイ時にも実行されます)

$ npm run copy-deps && npm run install-deps

function/src/index.tsを修正

import * as functions from 'firebase-functions';

import * as express from 'express';

// 型定義ファイルを作るのが面倒なので妥協。。。
const basicAuth = require('basic-auth-connect');

// 環境変数
const user = functions.config().basicauth.user;
const pass = functions.config().basicauth.pass;

const server = express();
server.use(basicAuth(user, pass));
server.use(express.static('./dist'));

export const stagingApp = functions.https.onRequest(server);

ローカルサーバーで確認

$ npm run serve

publicディレクトリをアップするため、以下を実行

$ npm run init-deploy

次回以降は、publicディレクトリをアップしないので、以下でOK

$ npm run deploy

コミットしてGitHubにプッシュ

$ git add .

$ git commit -m "コミットメッセージ"
$ git push origin HEAD


本番環境

firebaseで本番環境も対応する場合は切り替えが必要なので対応

コンソールで新規プロジェクト作成

今回は2種類の本番環境を用意


  • 静的ホスティング


    • 静的なサイトであれば十分

    • Cloud Functions for Firebase を経由しないのでパフォーマンス的には少し有利?



  • Expressをそのまま使用


    • Node.jsが使える


      • よって、ExpressじゃなくてもOK






環境構築(静的ホスティング)

developブランチから新たにブランチを作成

$ git checkout -b static_hosting

作成した本番用プロジェクトのホスティング情報を.firebasercに追加

$ firebase target:apply hosting production <production-project-id>

$ firebase target:apply hosting staging <staging-project-id>

firebase.jsonに環境別のホスティング情報を追記

  {

"functions": {
"source": "functions",
"predeploy": "npm run preserve"
},
+ "hosting": [{
+ "target": "staging",
+ "public": "public",
+ "ignore": [
+ "firebase.json",
+ "**/.*",
+ "**/node_modules/**"
+ ],
+ "rewrites": [
+ {
+ "source": "**",
+ "function": "stagingApp"
+ }
+ ]
+ },
+ {
+ "target": "production",
+ "public": "functions/dist",
+ "ignore": [
+ "firebase.json",
+ "**/.*",
+ "**/node_modules/**"
+ ]
+ }]
}

package.jsonの書き換え

  "scripts": {

"build": "tsc --project functions",
"preserve": "npm run build && npm run copy-deps && npm run install-deps",
"serve": "npm run build && firebase serve",
"shell": "npm run build && firebase functions:shell",
"start": "npm run shell",
"deploy-init": "firebase deploy",
- "deploy": "firebase deploy --only functions",
+ "deploy-staging": "firebase deploy --only functions",
+ "deploy-production": "firebase deploy --only hosting:production",
"logs": "firebase functions:log",
"copy-deps": "cpx \"*{package.json,package-lock.json,yarn.lock}\" \"functions\" -C",
"install-deps": "cd functions && npm install && cd ../"
},

ステージングにデプロイ

$ npm run deploy-staging

本番にデプロイ

$ npm run deploy-production


環境構築(Express環境)

一度、developブランチに戻り、express_hostingブランチを新たに作成

$ git checkout develop

$ git checkout -b express_hosting

作成した本番用プロジェクトを指定し、エイリアスを設定

$ firebase use --add

$ What alias do you want to use for this project? (e.g. staging)

.firebasercにプロジェクトが追加される(今回はproductionというエイリアスで登録)

もともとのdefaultエイリアスはわかりやすい名前に変えてOK(以下はstagingに変更)

{

"projects": {
- "default": "<staging-project-id>",
+ "staging": "<staging-project-id>",
+ "production": "<production-project-id>"
}
}

firebase.jsonも変更

{

"functions": {
"source": "functions",
"predeploy": "npm run preserve"
},
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
- "function": "stagingApp"
+ "function": "app"
}
]
}
}

npm scriptにプロジェクトの切り替えタスクを追加

  "scripts": {

"build": "tsc --project functions",
"preserve": "npm run build && npm run copy-deps && npm run install-deps",
"serve": "npm run build && firebase serve",
"shell": "npm run build && firebase functions:shell",
"start": "npm run shell",
"init-deploy": "firebase deploy",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log",
"copy-deps": "cpx \"*{package.json,package-lock.json,yarn.lock}\" \"functions\" -C",
"install-deps": "cd functions && npm install && cd ../",
+ "env-staging": "firebase use staging",
+ "env-production": "firebase use production"
}

プロジェクト切り替えが可能になる

$ npm run env-xxx


本番環境でベーシック認証解除

環境変数をクローンし、問題がないか確認

$ firebase functions:config:clone --from <project-id>

$ firebase functions:config:get

以下のディレクトリとファイルを作成

functions/src

└ app
├ app.ts
├ index.ts
├ productionApp.ts
└ stagingApp.ts

functions/src/app/stagingApp.ts

import * as functions from 'firebase-functions';

import * as express from 'express';

// 型定義ファイルを作るのが面倒なので妥協。。。
const basicAuth = require('basic-auth-connect');

const user = functions.config().basicauth.user;
const pass = functions.config().basicauth.pass;

const server = express();
server.use(basicAuth(user, pass));
server.use(express.static('./dist'));

export const stagingApp = functions.https.onRequest(server);

functions/src/app/productionApp.ts

import * as functions from 'firebase-functions';

import * as express from 'express';

const server = express();
server.use(express.static('./dist'));

export const productionApp = functions.https.onRequest(server);

functions/src/app/app.ts

import { productionApp } from './productionApp';

import { stagingApp } from './stagingApp';

const isStaging = process.env.GCLOUD_PROJECT === '<staging-project-id>';
export const app = isStaging ? stagingApp : productionApp;

functions/src/app/index.ts

export { app } from './app';

functions/src/index.tsを書き換え

export { app } from './app';

本番環境用ローカルサーバーを立ち上げ

$ npm run env-production

$ npm run serve

テスト環境用ローカルサーバーを立ち上げ

$ npm run env-staging

$ npm run serve

本番環境にデプロイ

$ npm run env-production

$ npm run init-deploy

次回以降は、以下でOK

$ npm run env-production

$ npm run deploy

テスト環境にデプロイ

$ npm run env-production

$ npm run deploy

コミットしてGitHubにプッシュ

$ git add .

$ git commit -m "コミットメッセージ"
$ git push origin HEAD

最後にGitHubでプルリクエストを作り、developブランチにマージしてみましょう!