Firebase CLIとGitHub Actionsで手動デプロイ環境を用意する
概要
- Firebase CLIを使ってデプロイコマンドを叩いていたがローカルからだった
- 簡単にできてこれまで何不自由なかったんですが、ローカル起点なので困ったことが2点
- フレームワークのバージョンアップ検証途中で別途修正依頼が入った場合、
node_modules
以下がアップデートされるので、一度ぶっ壊してbranchのcheckoutするのが手間 - 出張中、回線が弱いところでデプロイすると不安定なので欠損したり失敗したりするので困る
- フレームワークのバージョンアップ検証途中で別途修正依頼が入った場合、
- というわけで、GitHub Actionsでできないかと模索したら記事が色々でてきたので試してみてできた記事
前提
- Nuxt3を利用しています
- Firebase Hostingのみのデプロイです
- Cloud Functionsへもできたんですが、準備することが多いので一旦別記事で
- GitHubのプランはEnterprise
- 連携したリポジトリはもちろんprivate
- GitHub ActionsからFirebaseへデプロイする際に行う認証はOIDC tokenを利用しました
- 途中
gcloud
コマンドを利用するので参考にするならインストール必要です
手順
GitHub Environmentsの用意
こちらを参考にしてもらえれば(いつもありがとうございます!!)
ただ、僕が触ったタイミングだと、なんか他の記事含め設定できる内容やUIが変わっているようでした※2023/08/21時点
Settings -> Environments -> New environment -> 名前入力
- Deployment branches: 環境変数を利用するbranchを設定できます
- Environment secrets: 秘匿したい値を設定できます。設定すると、設定者でも参照できません。Authentication codeを入力して上書きするしかできません。GitHub Actionsのみ参照可能になります。
${{ secrets.XXX }}
- Environment variables: 機密性のない変数を設定します。
.env
の認識です。 GitHub Actions上から${{ vars.XXX }}
でアクセスできます
FirebaseにはAPI Keyとか設定値が7個ぐらいあって、GitHub Actionsで利用できるように Environment variables
を利用しました
サービスアカウントの用意
Firebaseへデプロイするためのサービスアカウントを用意します
発行したメールアドレスはメモしておく(あとで使う)
今回は aipa@service-acount.com
とかにしておきます
権限を設定する
今回は2つ
-
Firebase 管理者
※本当は利用するところだけ設定するのが良い サービス アカウント ユーザー
GitHub ActionsとGCPの連携
その昔(?)は、CI用にTokenを発行する方法があったらしいんですが、今は非推奨になっているとのことです(見たことも試したこともない)
GCPを利用する人なら説明もいらないと思いますが、よくあるService Accountのjsonを落としてきて、そいつを使って認証することが多いと思いますが、今回はOIDCというやつが目に入ったため、こいつを試してみることにしました
OIDCとは
OpenID Connectのことです。正直わからん(勉強中)
OAuthの拡張とふわっと理解していますが、よくないので学べたら別途記事でまとめます
手順
gcloud
コマンドを利用して設定していきます。インストールやログインの手順は省略しています
そしてこちらの記事を参考(ほぼコピペだけど)にしています
$ export PROJECT_ID=dev-aipa # 対象のGCPのProject ID
$ export POOL_NAME=github-actions # poolの名前。何でも良い
$ export PROVIDER_NAME=aipa-provider # providerの名前。何でも良い
$ export SA_EMAIL=aipa@service-acount.com # 作成したIAMユーザー
$ export GITHUB_REPO=aipacommander/firebase-deploy # GitHubのorgnization/repoの形式で渡す
# IAM Service Account Credentials API を有効にする
$ gcloud services enable iamcredentials.googleapis.com --project "${PROJECT_ID}"
$ gcloud iam workload-identity-pools create "${POOL_NAME}" --project="${PROJECT_ID}" --location="global" --display-name="GitHub Actions Pool"
$ export WORKLOAD_IDENTITY_POOL_ID=$(\
gcloud iam workload-identity-pools describe "${POOL_NAME}" \
--project="${PROJECT_ID}" --location="global" \
--format="value(name)" \
)
$ gcloud iam workload-identity-pools providers create-oidc "${PROVIDER_NAME}" --project="${PROJECT_ID}" --location="global" --workload-identity-pool="${POOL_NAME}" --display-name="use from GitHub Actions provider" --attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository,attribute.actor=assertion.actor,attribute.aud=assertion.aud" --issuer-uri="https://token.actions.githubusercontent.com"
$ gcloud iam service-accounts add-iam-policy-binding "${SA_EMAIL}" --project="${PROJECT_ID}" --role="roles/iam.workloadIdentityUser" --member="principalSet://iam.googleapis.com/${WORKLOAD_IDENTITY_POOL_ID}/attribute.repository/${GITHUB_REPO}"
GitHub Actionsのワークフローの用意
とりあえずこんな感じのやつを用意すればおkです
name: Firebase Deploy
on:
# 手動で実行できる設定
workflow_dispatch:
inputs:
environment:
default: development
options:
- development
- production
required: true
type: choice
jobs:
setup:
runs-on: ubuntu-latest
# トリガーするイベントに応じてinputをoutputに変換
steps:
- id: setup-from-inputs
if: github.event_name != 'repository_dispatch'
run: |
echo environment=${{ inputs.environment }} >> $GITHUB_OUTPUT
echo branch=${{ github.ref_name }} >> $GITHUB_OUTPUT
- id: setup-from-payload
if: github.event_name == 'repository_dispatch'
run: |
echo environment=${{ github.event.client_payload.environment }} >> $GITHUB_OUTPUT
echo branch=${{ github.event.client_payload.branch }} >> $GITHUB_OUTPUT
# ジョブのoutputとしてenvironmentとbranchを設定
outputs:
environment: ${{ steps.setup-from-inputs.outputs.environment || steps.setup-from-payload.outputs.environment }}
branch: ${{ steps.setup-from-inputs.outputs.branch || steps.setup-from-payload.outputs.branch }}
development:
needs: setup
if: needs.setup.outputs.environment == 'development'
runs-on: ubuntu-latest # 実行環境を指定
environment: aipa-env
permissions:
contents: read
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v3 # リポジトリをチェックアウトするアクションを使用
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v1
with:
workload_identity_provider: projects/12341234/locations/global/workloadIdentityPools/aipa-pool/providers/aipa-provider # 作成したProviderパス?を指定
service_account: aipa@service-acount.com # 作成したサービスアカウントを指定
access_token_lifetime: 600s
create_credentials_file: true
- name: Install Node.js
uses: actions/setup-node@v3 # Node.js 環境をセットアップ
- name: Install Firebase CLI
run: yarn global add firebase-tools # Firebase CLI をインストール
- name: Install dependencies
run: yarn install # 依存関係をインストール
- name: Build Nuxt
run: NITRO_PRESET=firebase yarn build
env:
NUXT_FIREBASE_API_KEY: ${{ vars.NUXT_FIREBASE_API_KEY }}
- name: Deploy to Firebase
run: firebase --project dev --config firebase.json deploy --only functions:server,hosting --force # Firebase ホスティングにデプロイ
# TODO: productionの設定も別途必要
# production:
# needs: setup
# if: needs.setup.outputs.environment == 'production'
# runs-on: ubuntu-latest # 実行環境を指定
# ...
注意点として、workload_identity_provider
の値が${WORKLOAD_IDENTITY_POOL_ID}//providers/${PROVIDER_NAME}
となります(これにハマった)
手動でworkflowを実行するための設定
下記ふたつの記事を参考にしました
# ...
on:
# 手動で実行できる設定
workflow_dispatch:
inputs:
environment:
default: development
options:
- development
- production
required: true
type: choice
jobs:
setup:
runs-on: ubuntu-latest
# トリガーするイベントに応じてinputをoutputに変換
steps:
- id: setup-from-inputs
if: github.event_name != 'repository_dispatch'
run: |
echo environment=${{ inputs.environment }} >> $GITHUB_OUTPUT
echo branch=${{ github.ref_name }} >> $GITHUB_OUTPUT
- id: setup-from-payload
if: github.event_name == 'repository_dispatch'
run: |
echo environment=${{ github.event.client_payload.environment }} >> $GITHUB_OUTPUT
echo branch=${{ github.event.client_payload.branch }} >> $GITHUB_OUTPUT
# ジョブのoutputとしてenvironmentとbranchを設定
outputs:
environment: ${{ steps.setup-from-inputs.outputs.environment || steps.setup-from-payload.outputs.environment }}
branch: ${{ steps.setup-from-inputs.outputs.branch || steps.setup-from-payload.outputs.branch }}
development:
needs: setup
if: needs.setup.outputs.environment == 'development'
runs-on: ubuntu-latest # 実行環境を指定
# ...
正直よくわかっていない(GitHub Actions難しい)
環境変数の渡し方
↑にも書きましたが、 ${{ vars.XXX }}
で渡します。ただ、ハマったんですが、GitHub Actionsで起動するインスタンス(?)にはEnvironmentsの設定はexportされていないため、- env:
の階層を用意して、渡してやる必要があります。めんどい
- name: Build Nuxt
run: NITRO_PRESET=firebase yarn build
env:
NUXT_FIREBASE_API_KEY: ${{ vars.NUXT_FIREBASE_API_KEY }}
# ...
ただ、わかっていないだけかもしれないので、間違いとか便利な方法あるよ〜とかあれば教えてほしい
default branchの切り替え
main
branchで作業していない場合(というのがほとんどだと思うけど)、Actionsでの手動実行ができないです。そのため、一時的にdefault branchをworkflowのyamlがcommitされているbranchに切り替えて動作確認する必要があります
GitHubでリポジトリのsettingsにて、下記UIから変更ください
動作確認
リポジトリのActionsから選択できるようになります
Actionsの左側からデプロイのワークフローを選択して、右側のRun workflow
にて、デプロイしたいbranchを選択して走らせればおk
テストのTips
npm install
とか途中含まれていると毎回走らせるのは時間がかかるので、ローカルでテストか、動作確認を簡略化したいなってなると思います
ローカルで試したい場合は、こういうコマンド(ツール?)がありました
ただ、僕の環境だとうまく動かなかった(理由もわからん)ので、GitHub Actions + ワークフローの簡易化でテストしました。
ビルドコマンドはローカルでうまくことはわかっていたので、 google-github-actions/auth
後に、うまくGCP環境へ命令が通るかをテストしたい場合、下記のようにしました
# ...
steps:
- name: Checkout repository
uses: actions/checkout@v3 # リポジトリをチェックアウトするアクションを使用
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v1
with:
workload_identity_provider: projects/12341234/locations/global/workloadIdentityPools/aipa-pool/providers/aipa-provider
service_account: aipa@service-acount.com
access_token_lifetime: 600s
create_credentials_file: true
- name: Install Node.js
uses: actions/setup-node@v3 # Node.js 環境をセットアップ
- name: Install Firebase CLI
run: yarn global add firebase-tools # Firebase CLI をインストール
- name: Install dependencies
run: firebase --project dev-aipa functions:list
それか、Firebaseじゃないなら、Cloud Storageを参照するなど簡単なコマンドで確認できるとあまり時間をかけず試すことができます
雑感
- 無事簡易に検証デプロイできるようになりました
- プロダクションへのデプロイ設定は今後
- Cloud Functionsへのデプロイ設定も今後
参考記事
いっぱいお世話になりました
Discussion