ReactプロジェクトをGitHubActionsを使ってFirebaseへ自動デプロイする手順

# はじめに

お疲れ様です、りつです。

前回、`firebase deploy`コマンドを実行してFirebaseへReactプロジェクトをデプロイすることができました。

https://qiita.com/ritsu21ctws/items/623512c2ad4462280e78

今回は、同じプロジェクトをGitHubActionsを用いてFirebaseへ自動デプロイできるようにしたので、その手順をまとめました。

# この記事で作成するワークフローについて

基本的には、以下のサイトの手順に沿ってワークフローを作成していきます。

https://github.com/marketplace/actions/deploy-to-firebase-hosting

今回作成されるワークフローによって以下のことが実現可能です。
とても便利ですね！

:::note
**PR（プルリクエスト）が作成されたとき**
- PR画面にコメントが追加され、そこにプレビューURLが表示される
- プレビューURLでは、そのPR内容を反映した場合の画面プレビューを確認することができる
:::

:::note
**PRが承認され、`main`ブランチに作業ブランチがマージされたとき**
- ホスティングURLに`main`ブランチの内容が自動でデプロイされる
:::

それでは、以下で実際の手順をご紹介します！

# 手順

:::note warn
**注意**
以下が完了している前提で進めておりますのでご注意ください。
- 既にReactのプロジェクトが存在する
- `firebase init`コマンドなどでFirebaseホスティングの初期設定が完了している

また、ターミナルの実行結果の一部は`{}`で囲んだ内容で置き換えております。
:::

## 1. `firebase init hosting:github`の実行

まずは、ターミナル上で、プロジェクトのルートディレクトリに移動します。
そして、以下のコマンドを実行します。

```terminal
$ firebase init hosting:github
```

## 2. GitHubの認証

すると以下のように表示されますので、`{ 認証用URL }`に記載されているURLにアクセスしてGitHubへログインします。
（`Ctrl` + URLクリックでリンク先をブラウザで開けます）

```terminal
$ firebase init hosting:github

     ######## #### ########  ######## ########     ###     ######  ########
     ##        ##  ##     ## ##       ##     ##  ##   ##  ##       ##
     ######    ##  ########  ######   ########  #########  ######  ######
     ##        ##  ##    ##  ##       ##     ## ##     ##       ## ##
     ##       #### ##     ## ######## ########  ##     ##  ######  ########

You're about to initialize a Firebase project in this directory:

  /home/ritsu/workspace/study-record

Before we get started, keep in mind:

  * You are initializing within an existing Firebase project directory


=== Project Setup

First, let's associate this project directory with a Firebase project.
You can create multiple project aliases by running firebase use --add,
but for now we'll just set up a default project.

i  Using project study-record-10e03 (study-record)

=== Hosting:github Setup

i  Detected a .git folder at /home/ritsu/workspace/study-record
i  Authorizing with GitHub to upload your service account to a GitHub repository's secrets store.

Visit this URL on this device to log in:
{ 認証用URL }

Waiting for authentication...
```

GitHubのログインが成功すると、ブラウザで以下のような画面が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3911933/4beae7db-7b5b-cc81-b520-bcf58239855f.png)


また、ログインが成功してしばらくすると、ターミナル上では以下のように表示されます。

```terminal
✔  Success! Logged into GitHub as { GItHubユーザー名 }
```

## 3. GitHubリポジトリの紐付け設定

ここからはいくつかの質問が続きますので回答していきます。

:::note warn
**注意**
今回は私の回答内容を例として記載していきますが、適宜ご自分の状況に合わせてご回答ください。
:::

まずは、どのGitHubリポジトリでワークフローを設定するか確認されます。

```terminal
? For which GitHub repository would you like to set up a GitHub workflow? (format: user/repository)
```

私の場合は以下のリポジトリに設定したかったので、`ritsu21ctws/study-record`と回答しました。

https://github.com/ritsu21ctws/study-record

回答すると以下のように表示されます。

```terminal
✔  Created service account { サービスアカウント名 } with Firebase Hosting admin permissions.
✔  Uploaded service account JSON to GitHub as secret { GitHubシークレットの名前 }.
i  You can manage your secrets at https://github.com/ritsu21ctws/study-record/settings/secrets.
```

簡単に書くと、以下のことをやってくれています。

- 自動でFirebaseのサービスアカウントを作成
- GitHubのシークレット（リポジトリ内で使用できる環境変数のようなもの）に、Firebaseとの接続用の鍵情報を自動で登録

ありがたいことに、本来はGCPの管理画面やGitHubリポジトリで操作が必要な処理を一瞬で実行してくれています。

## 4. ビルドコマンドの設定

以下の質問では、デプロイ時にビルドコマンドを実行する必要があるかどうかを聞かれています。

```terminal
? Set up the workflow to run a build script before every deploy?
```

今回はReactのプロジェクトのためビルドが必要になりますので、`Yes`で回答します。
続けて、具体的なビルドコマンドを聞かれます。

```
? What script should be run before every deploy? 
```

`npm ci && npm run build`と回答しました。
続けて、以下のような結果が返ってきます。

```terminal
✔  Created workflow file /home/ritsu/workspace/study-record/.github/workflows/firebase-hosting-pull-request.yml
```

:::note
**POINT!**
ここまでの回答内容を元に、以下のファイルが自動で作成されます。
`.github/workflows/firebase-hosting-pull-request.yml`
:::

## 5. マージ実行時の設定

続けて、プルリクエストがマージされた場合、ライブチャネル（本番サイト）に自動デプロイを行うか聞かれます。

```terminal
? Set up automatic deployment to your site's live channel when a PR is merged?
```

今回は`Yes`と回答しました。
続けて、どのブランチをライブチャネルに紐づけるか聞かれます。

```terminal
? What is the name of the GitHub branch associated with your site's live channel? 
```

今回は`main`と回答します。

すると、以下のように表示されます。

```terminal
✔  Created workflow file /home/ritsu/workspace/study-record/.github/workflows/firebase-hosting-merge.yml

i  Action required: Visit this URL to revoke authorization for the Firebase CLI GitHub OAuth App:
https://github.com/settings/connections/applications/89cf50f02ac6aaed3484
i  Action required: Push any new workflow file(s) to your repo

i  Writing configuration info to firebase.json...
i  Writing project information to .firebaserc...

✔  Firebase initialization complete!
```

:::note
**POINT!**
ここまでの回答内容を元に、以下のファイルが自動で作成されます。
`.github/workflows/firebase-hosting-merge.yml`
:::

これでFirebaseの設定は完了です。

## 6. ワークフローを`push`

上記手順で作成されたファイルを`main`ブランチに`push`（作業ブランチで作業していた場合は`merge`）しておきます。
続けて、作成されたワークフローを実際に使ってみます。

# 動作確認

設定されたワークフローが正しく動作するか確認していきます。
今回は、以下の学習記録アプリのボタン名を変更します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3911933/7aa7b333-d91b-bb33-2143-3bfedf219b0f.png)

## 1. ソースコードの修正

`feature/MVP5-3`ブランチを新たに作成し、ソースコードを修正しました。
以下はソースの差分です。

ボタン名の後ろに「ボタン」という文言を追加しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3911933/9d424a55-5dc0-9034-b014-6e208f347e2a.png)

上記の修正をコミット後、以下のコマンドを実行してリモートリポジトリに`push`しておきます。

```terminal
$ git push origin feature/MVP5-3
```

`feature/MVP5-3`ブランチから`main`ブランチへのプルリクエストを作成すると、以下のようにプルリクエスト画面のコメントにプレビュー用URLが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3911933/dfbf31a0-2588-1ec9-6b76-c4f5a018d826.png)

## 2. プレビュー画面の確認

試しにURLをクリックしてみます。
すると、画面が真っ白になっていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3911933/cff5e501-c1d4-72c9-9308-10f809b760f5.png)

コンソールには以下のエラーが出ています。

```console
Uncaught Error: supabaseUrl is required.
    at new jm (index-BFCwFFA1.js:45:69543)
    at Rm (index-BFCwFFA1.js:45:72592)
    at index-BFCwFFA1.js:45:72609
```

今回のReactのプロジェクトではDBにsupabaseを使用しており、supabaseへの接続情報を`.env`に定義していました。

`.env`はGit管理から外しているため、CIサーバーで`.env`の環境変数が読み取れずエラーになってしまったようです。

### `Uncaught Error: supabaseUrl is required.`エラーの解消方法

ここで結構はまってしまったのですが、以下に解決策をそのものずばり紹介してくれている記事がありました。

https://qiita.com/gantarooou_js/items/bc493bba82a8a6b1ab32

今回は上記の内容をふまえ、以下の手順にて修正を行いました。

1. GitHubリポジトリ > `Settings`タブ > サイドバーの`Secrets and Variables` > `Actions` > `New repository secret`を開く
2. 以下の2つのシークレットを入力
   - SUPABASE_URL
   - SUPABASE_ANON_KEY
3. `.github/workflows/firebase-hosting-merge.yml`を修正
   - 修正前
   ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3911933/cd88ee84-7844-7167-a94d-b37720bdd0da.png)
   - 修正後
     ```yml:.github/workflows/firebase-hosting-merge.yml
     # This file was auto-generated by the Firebase CLI
     # https://github.com/firebase/firebase-tools
     
     name: Deploy to Firebase Hosting on merge
     on:
       push:
         branches:
           - main
     jobs:
       build_and_deploy:
         runs-on: ubuntu-latest
         steps:
           - uses: actions/checkout@v4
           - run: npm ci && npm run build
             env:
               VITE_SUPABASE_URL: ${{secrets.SUPABASE_URL}}
               VITE_SUPABASE_ANON_KEY: ${{secrets.SUPABASE_ANON_KEY}}
           - uses: FirebaseExtended/action-hosting-deploy@v0
             with:
               repoToken: ${{ secrets.GITHUB_TOKEN }}
               firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_STUDY_RECORD_10E03 }}
               channelId: live
               projectId: study-record-10e03
     ```
4. `.github/workflows/firebase-hosting-pull-request.yml`を修正
   - 修正前
     ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3911933/901b151a-4976-70a5-5206-f2eef7ecfda8.png)
   - 修正後
     ```yml:.github/workflows/firebase-hosting-pull-request.yml
     # This file was auto-generated by the Firebase CLI
     # https://github.com/firebase/firebase-tools
     
     name: Deploy to Firebase Hosting on PR
     on: pull_request
     permissions:
       checks: write
       contents: read
       pull-requests: write
     jobs:
       build_and_preview:
         if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
         runs-on: ubuntu-latest
         steps:
           - uses: actions/checkout@v4
           - run: npm ci && npm run build
             env:
               VITE_SUPABASE_URL: ${{secrets.SUPABASE_URL}}
               VITE_SUPABASE_ANON_KEY: ${{secrets.SUPABASE_ANON_KEY}}
           - uses: FirebaseExtended/action-hosting-deploy@v0
             with:
               repoToken: ${{ secrets.GITHUB_TOKEN }}
               firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_STUDY_RECORD_10E03 }}
               projectId: study-record-10e03
     ```
5. ソースをコミット後、以下のコマンドでリモートリポジトリに修正を`push`
   ```terminal
   $ git push origin feature/MVP5-3
   ```

## 3. プレビューURLの再確認
さて、この状態で再度GitHubのプルリクエスト画面に戻り、プレビューURLをクリックしてみます。

:::note
**補足**
`push`でプルリクエストが更新されると、プレビューURLは同一のまま、プレビューサイトに修正内容を反映してくれるそうです。
:::

https://firebase.google.com/docs/hosting/github-integration?hl=ja

アプリの画面が開きました！ボタン名も変わっていますね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3911933/13117182-a6bb-0f10-336a-e42424dc78a0.png)


プレビューサイトは問題なさそうですので、プルリクエストをマージします。

## 4. ホスティングURLの確認

`main`ブランチに作業ブランチがマージされたことで、自動デプロイが実行されたはずです。

試しにホスティングURLからサイトを確認してみます。

https://study-record-10e03.web.app/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3911933/1f273d66-753e-2598-dd76-518a5fe39ad3.png)

こちらも無事表示することができました！！

# おわりに

GitHubActionsを用いて、以下を実行できるようになりました。

- プルリクエスト画面でプレビューURLを表示
- `main`ブランチへのマージ時にFirebaseへ自動でデプロイ

実際に触ってみて、非常に便利な機能だと感じましたね。

GitHubActionsでまだまだできることがたくさんあると思うので、今後も調べながら知識を身につけていきたいです！

# 参考

https://github.com/marketplace/actions/deploy-to-firebase-hosting

https://firebase.google.com/docs/hosting/github-integration?hl=ja

https://qiita.com/gantarooou_js/items/bc493bba82a8a6b1ab32

https://qiita.com/shin-tech/items/c11b8979b14a65b8192b