github actions でfirebaseに自動デプロイするための設定


サービス開発のときは、環境構築と同時にデプロイ自動化まで行うのが俺的セオリーです。
というわけで、firebaseを使ったアプリケーションのデプロイを自動化する方法を紹介します。

想定QA

Q. なんで環境構築と同時にデプロイ自動化までするの?面倒くない?

A. 下のような理由

  • 実際に使うインフラを知っておいたほうが、開発環境をどう構築したらいいかがわかる。開発環境は本番環境にできる限り近づけて作るべきなので、本番環境を先に知っといたほうがいい。
  • 開発終わってから、デプロイの仕方わからーんってなって、構成見直すといった手戻りを防げる。
  • どうせいつかやるから最初やっても同じ
  • 開発のときのテンションが上がる

Q. Github Actions癖強くない?CircleCIとかで良くない?餅は餅屋理論

A. Githubで完結するのは強い。Github Actionsのデプロイ設定が面倒くさいのは一時の苦痛だが、デプロイのためにCircleCI等使うサービス1つ増やすのは一生の苦痛。

目的

githubにpushしたら自動でデプロイされるように、github actionsを設定します。
nuxtアプリ(SPAモード)を題材に書きますが、ビルドコマンド等をよしなに変更したら、他の環境でも使えるはずです。

前提

firebaseのhosting, firestoreのrules/index, GCP Cloud Functionsの3つをデプロイします。

手順

  1. ci用にサービスアカウントを作成
  2. サービスアカウントの認証ファイルをbase64で変換
  3. github secretsにSA keyの登録
  4. [optional] firebaseの各種認証情報もついでに登録
  5. github actionsの設定
    1. firebase hostingへのデプロイ
    2. firebase firestoreのデプロイ
    3. functionsのデプロイ

ci用にサービスアカウントを作成

https://console.cloud.google.com/iam-admin/serviceaccounts よりci用のサービスアカウントを作成します。


「サービスアカウントを作成」ボタンをおします。


IDを適当に設定し、作成を押します。


「Firebase Develop 管理者」の権限を付与します。
後述するCloudFunctionsにもデプロイする場合は、「Cloud Functions開発者」「サービス アカウント ユーザー」のロールも付与します。

次の画面の「ユーザーにこのサービス アカウントへのアクセス権を付与」は設定せずに、そのまま作成します。


一覧画面に戻ったら、詳細メニューから鍵を作成のボタンを押し、jsonファイルをダウンロードします。

サービスアカウントの認証ファイルをbase64で変換

jsonファイルをbase64で文字列に変換します。
macだとこんな感じで変換できます。
gcp-sa-key.jsonは先程ダウンロードしたファイルです。

> cd ~/Downloads
> cat gcp-sa-key.json | base64
NANKARANDOMPPOIMOZIRETSUGADETEKURU4LJ5HFROPO2pSTNgt1HhKU6ZIo1n2t
1YY5uiNoIziuZI5QwYrd0szqBu6vGQJ6UuZPqZfEP6gmKX8FFXUXldj0yKcEx+7C
DWgMir3m/Tz4PoXQBVx7tqG3NdMoM3gCm4k++HHWHRP/VX3Xf+UB8UQEl+VsSOgk
PP96iGr2pW3WAo7IJK/qB0nQ
# このままクリップボードにコピー(mac限定)
> cat gcp-sa-key.json | base64 | pbcopy

github secretsにSA keyの登録

githubリポジトリに行き、settingsの中のsecretsから環境変数を設定します。
url的にはhttps://github.com/{your_account}/{your_repo_name}/settings/secretsでアクセスできます。

[optional] firebaseの各種認証情報もついでに登録

本筋と外れますが、nuxtとfirebaseをつなぐ認証情報をここで色々設定しておきます。
最終的にはこんな感じになります。

github actionsの設定

ここからが本題です。
今回用いるプロジェクト構成図は以下のようになっています。
(関係するファイルのみ抜き出しています。)

root
├── firestore // firestoreのrulesやindexesをまとめたディレクトリ
│   ├── firebase.json // firestoreの設定
│   ├── firestore.rules // firestoreの読み書きrules
│   ├── firestore.indexes.json // firestoreのindexの設定
│    ・・・
├── front // フロントのコードをまとめたディレクトリ nuxt create-app の成果物。
│   ├── firebase.json // hostingの設定が書かれている
│    ・・・
└── functions
    ├── func1
    └── func2

firebase hostingへのデプロイ

説明はyml内に書きます。

deploy.yml
name: Deploy

# masterにpushするたびに、github actionsを走らせる。
on:
  push:
    branches: [ master ]

# jobを複数定義できる。
jobs:

  # frontのデプロイ
  front:
    runs-on: ubuntu-latest
    # デフォルトで走らせるディレクトリを指定
    defaults:
      run:
        working-directory: ./front
    steps:
    - uses: actions/checkout@v2

    # node & yarn を入れる
    - name: setup node
      uses: actions/setup-node@v1
      with:
        node-version: '12.x'
    - name: install
      run: yarn
      env:
        NODE_ENV: development

    # yarn generateコマンドでbuildする。
    # nuxtに注入する環境変数を明示的に指定する。
    - name: build
      run: yarn generate
      env:
        APP_ENV: production
        NUXT_ENV_APIKEY: ${{ secrets.NUXT_ENV_APIKEY }}
        NUXT_ENV_AUTH_DOMAIN: ${{ secrets.NUXT_ENV_AUTH_DOMAIN }}
        NUXT_ENV_DATABASE_URL: ${{ secrets.NUXT_ENV_DATABASE_URL }}
        NUXT_ENV_PROJECT_ID: ${{ secrets.NUXT_ENV_PROJECT_ID }}
        NUXT_ENV_STORAGE_BUCKET: ${{ secrets.NUXT_ENV_STORAGE_BUCKET }}
        NUXT_ENV_MESSAGING_SENDER_ID: ${{ secrets.NUXT_ENV_MESSAGING_SENDER_ID }}
        NUXT_ENV_APP_ID: ${{ secrets.NUXT_ENV_APP_ID }}

    # build結果をfirebase hosgint にデプロイ
    # 先ほど取得したSAのkeyを環境変数として渡す。
    # PROJECT_ID は firebaseのプロジェクトID(uniqueな方)
    - name: Deploy Firebase Hosting
      uses: w9jds/firebase-action@master
      with:
        args: deploy --only hosting
      env:
        PROJECT_ID: your-pj-id
        GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
        PROJECT_PATH: ./front

hostingだけしたい人はここまでで大丈夫です。

firebase firestoreのデプロイ

先ほどのdeploy.ymlに追記します。

deploy.yml
name: Deploy

# masterにpushするたびに、github actionsを走らせる。
on:
  push:
    branches: [ master ]

# jobを複数定義できる。
jobs:
  front:
    # ((省略))

  firestore:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2

    # front同様、SAのkeyを環境変数として渡す。
    # firebaseのdeployコマンドのoptionが異なる。
    - name: Deploy Firestore rules and index
      uses: w9jds/firebase-action@master
      with:
        args: deploy --only firestore
      env:
        PROJECT_ID: your-pj-id
        GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
        PROJECT_PATH: ./firestore

functionsのデプロイ

注意: この章ではfirebaseのfunctionsではなくGCPのCloud Functionsへのデプロイの仕方を紹介します。(pythonを使いたかったため)

最後にfunctionsへのデプロイの仕方です。
functionsは別jobに並列化することで実行時間を減らすことができます。
functionのdeployにわたすoptionは公式ドキュメントから確認できます。

参考
- https://github.com/GoogleCloudPlatform/github-actions
- https://cloud.google.com/functions/docs/deploying/filesystem?hl=ja

deploy.yml
name: Deploy

# masterにpushするたびに、github actionsを走らせる。
on:
  push:
    branches: [ master ]

# jobを複数定義できる。
jobs:
  front:
    # ((省略))

  firestore:
    # ((省略))

  functions_func1:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout
      uses: actions/checkout@v2
    - uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
      with:
        version: '290.0.1'
        service_account_key: ${{ secrets.GCP_SA_KEY }}
        project_id: your-pj-id
    - name: Deploy Functions
      run: |
        gcloud functions deploy a --allow-unauthenticated --source ./functions/func1 \
        --runtime python37 --trigger-http --region=asia-northeast1 \
        --set-env-vars ENV=production

  functions_func2:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout
      uses: actions/checkout@v2
    - uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
      with:
        version: '290.0.1'
        service_account_key: ${{ secrets.GCP_SA_KEY }}
        project_id: your-pj-id
    - name: Deploy Functions
      run: |
        gcloud functions deploy a --allow-unauthenticated --source ./functions/func2 \
        --runtime python37 --trigger-http --region=asia-northeast1 \
        --set-env-vars ENV=production

まとめ

みなさんも快適な自動デプロイライフを!