Firebase hosting上でSGしているサイトを再デプロイする方法

Tatsuya Asami
10 min readDec 31, 2022

--

概要

  • Firebase hostingにNext.jsのSG(スタティックジェネレーション)でビルドしたサイトをデプロイしているが、管理画面でデータを更新した際にCLIからfirebaseコマンドを叩く以外で簡単に再デプロイする方法がなさそうだった
  • 元々GitHub Actionsで自動デプロイする仕組みがあったのでそれを使えばデプロイはスムーズに出来そうだった
  • GitHub Actionsを実行するボタンを管理画面に置くのが一番簡単そうだったのでそうした

内容

Firebase hostingを使ってサイトを構築していた。hosting以外にもFirestore(NoSQL)、ストレージ、Authentication等開発に必要なものがほぼ全て完全無料のSparkプランで使えて嬉しい。Cloud functions(サーバーレスでコード実行するやつ)もBlazeプランにすれば使えるし、Blazeプランも個人で使うには無料枠で十分使えそうな感じ。
初期構築をしたときに自動デプロイするワークフローと、PR作成時にプレビュービルドをしてくれるワークフローを自動生成してくれたので、ちょっとだけ変えてそのまま使っていた。

デプロイ後の環境で管理画面からデータを編集して保存した後に再デプロイするボタンがFirebaseのコンソールにありそうでなかった。
Firebaseのコンソールに入れるのが自分だけ、データを編集する人は他にもいる状態なので、都度PCでterminalを開いてfirebaseコマンドを実行するのはやりたいことではなくて困った。

方法

正攻法はおそらくFirebase Hosting APIを使うことだろうが、結構面倒そうで、ちょっとした開発でそこまで頑張るのは違いそうだったので、なんとか邪道な方法を探したら、自動デプロイで使っているGitHub Actionsをクリックしたら実行するボタンを管理画面に設置する方法に行き着いた。

環境変数をローカルとGitHub secretに追加

元々用意していたfirebase関連の環境変数だけでなく、今回使用する値を.env.development.localと.env.production.localに追加。
ちなみに環境変数も型定義しておくと間違いがなくて安心。

// src/types/environment.d.ts
export {};

declare global {
namespace NodeJS {
interface ProcessEnv {
NEXT_PUBLIC_ENV_NAME: string | undefined;
NEXT_PUBLIC_FIREBASE_PROJECT: string | undefined;
NEXT_PUBLIC_RE_CAPTCHA_V3_PUBLIC_KEY: string | undefined;
NEXT_PUBLIC_GITHUB_ACTIONS_TOKEN: string | undefined;
NEXT_PUBLIC_GITHUB_OWNER: string | undefined;
NEXT_PUBLIC_GITHUB_REPO: string | undefined;
NEXT_PUBLIC_GITHUB_WORKFLOW_FILE: string | undefined;
NEXT_PUBLIC_GITHUB_BRANCH_NAME: string | undefined;
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN: string | undefined;
NEXT_PUBLIC_FIREBASE_PROJECT_ID: string | undefined;
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET: string | undefined;
NEXT_PUBLIC_FIREBASE_APP_ID: string | undefined;
}
}
}

GitHub Actions上で使うには開発用、本番用それぞれのenvファイルをbase64にしてGithub のsecretsのactionsに登録して、ワークフローで上書きするようにした。

GitHub Actionsのワークフロー

この後追加するデプロイボタンを押したときに実行できるようにトリガーに workflow_dispatch を追加した。先程secretsに登録したenvフィアルはecho “${{secrets.DOT_ENV_DEVELOPMENT}}” | base64 — decode > .env でデコードしている

# This file was auto-generated by the Firebase CLI
# https://github.com/firebase/firebase-tools

name: Deploy develop to Firebase Hosting on merge or webhook
"on":
push:
branches:
- develop
workflow_dispatch:

jobs:
build_and_deploy_to_develop:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- uses: actions/cache@v3
id: node_modules_cache_id
env:
cache-name: cache-node-modules
with:
path: "**/node_modules"
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
- uses: actions/cache@v3
with:
path: |
~/.npm
${{ github.workspace }}/.next/cache
# Generate a new cache whenever packages or source files change.
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}
# If source files changed but packages didn't, rebuild from a prior cache.
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-
- uses: google-github-actions/setup-gcloud@v0.2.0
with:
project_id: ${{ secrets.gcp_project }}
service_account_key: ${{ secrets.gcp_credentials }}
export_default_credentials: true
- uses: "google-github-actions/auth@v1"
with:
credentials_json: "${{ secrets.FIREBASE_SERVICE_ACCOUNT_DEVELOP }}"

- name: "Set up Cloud SDK"
uses: "google-github-actions/setup-gcloud@v1"

- name: Update .env file
run: |
echo "${{secrets.DOT_ENV_DEVELOPMENT}}" | base64 --decode > .env

- if: ${{ steps.node_modules_cache_id.outputs.cache-hit != 'true' }}
run: npm install
- run: npm run build
- uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: "${{ secrets.GITHUB_TOKEN }}"
firebaseServiceAccount: "${{ secrets.FIREBASE_SERVICE_DEVELOP }}"
channelId: live
projectId: develop

GitHub Actionsを実行するボタンのコード

こんな感じで先程のワークフローを実行する関数を作った

const useDeploy = () => {
const toast = useToast();
const postDeployWorkflow = useCallback(async () => {
const TOKEN = process.env.NEXT_PUBLIC_GITHUB_ACTIONS_TOKEN;
const OWNER = process.env.NEXT_PUBLIC_GITHUB_OWNER;
const REPO = process.env.NEXT_PUBLIC_GITHUB_REPO;
const WORKFLOW_FILE = process.env.NEXT_PUBLIC_GITHUB_WORKFLOW_FILE;
const BRANCH_NAME = process.env.NEXT_PUBLIC_GITHUB_BRANCH_NAME;
try {
// Octokit.js
// https://github.com/octokit/core.js#readme
const octokit = new Octokit({
auth: TOKEN,
});

await octokit.request(
`POST /repos/${OWNER}/${REPO}/actions/workflows/${WORKFLOW_FILE}/dispatches`,
{ ref: BRANCH_NAME }
);

toast({
title: "Success",
description: "デプロイ開始しました!",
isClosable: true,
status: "success",
});
} catch (error) {
if (error instanceof Error) {
console.error(error);
toast({
title: "Error",
description:
"デプロイの実行に失敗しました。問題が解決しない場合は管理者に問い合わせ下さい。",
isClosable: true,
status: "error",
});
return;
}
throw error;
}
}, [toast]);

return { postDeployWorkflow };
};

この関数を実行するボタンなどを好きなところに設置すれば、誰でも簡単に管理画面を編集した後の最新のFirestoreの値を使ってFirebase hostingが出来る。

デプロイするボタン

まとめ

シンプルに今ちゃんと動いているGitHub Actionsのワークフローを実行するボタンを作ることでハマることなく誰でもデプロイ出来るようになってよかった。

--

--

Tatsuya Asami
Tatsuya Asami

Written by Tatsuya Asami

Front end engineer. React, TypeScript, Three.js

No responses yet