Flutter Development Efficiency: A Step-by-Step Guide to Automating Web Previews Using GitHub Actions and Firebase Hosting

This article is the entry for day 20 in the KINTO Technologies Advent Calendar 2024🎅🎄
Introduction
Hello, my name is Hand-Tomi and I am a mobile app (Flutter) developer at KINTO Technologies (KTC).
Recently, KTC has launched the “Flutter Team”, and now is actively developing different applications. I would like to introduce some techniques we’ve implemented and found to be particularly effective. This time, I will explain how to use GitHub Actions and Firebase Hosting to provide convenient web previews during code reviews. I hope this article is helpful to you.
🎯Goal
The goal of this article is to create a system where a debug web page link is automatically posted as a comment by adding the comment /preview
to a pull request.
When you click the link above, the Flutter project application will be displayed as shown below.
🔍Why Use Web Preview?
When reviewing code, you need to clone the source code, configure it, and build it to check how it operates, but this process takes time. On the other hand, if you set up Web Preview, you can easily and quickly check how it operates. From here on, I will introduce how to implement this system step by step.
Setting up Firebase
🌐Creating a Firebase Project
If you don't have a Firebase project yet, create a new project from the console.
The project name is "sample".
If you don't plan to use other features, you can disable them (though there's no harm in leaving them enabled).
Wait a while
The project creation is complete!
⚙️ Setting up Firebase CLI
I'm planning to set up Firebase Hosting for the Flutter project. Since you can easily set it up using the Firebase CLI, let's set it up in the Terminal.
1. Installing Firebase CLI
There are several ways to install the Firebase CLI, but on macOS with npm installed, you can easily install it using the following command:
npm install -g firebase-tools
For installation on other environments, please see here.
2. Log in to Firebase CLI
Run the following command to log in to Firebase on the CLI.
firebase login
🔧 Setting up Firebase Hosting
Now that we're ready, let's set up Firebase Hosting for the Flutter project.
1. Enabling webframeworks
To deploy a Flutter application to Firebase Hosting, you need to enable the experimental feature webframeworks
.
firebase experiments:enable webframeworks
2. Initializing Firebase Hosting
In the root directory of your Flutter project, run the following command to set up Firebase:
firebase init hosting
After running the above command, you will be asked the following questions:
# For the Firebase project, select the sample project you created earlier.
? Please select an option: Use an existing project
? Select a default Firebase project for this directory: sample-1234 (sample)
# It's fine to answer "Yes" here.
? Detected an existing Flutter Web codebase in the current directory, should we use this? Yes
# It's a question about region selection. I chose the default "us-central1 (Iowa)."
? In which region would you like to host server-side content, if applicable? us-central1 (Iowa)
# Since I plan to create it myself, I selected "No."
? Set up automatic builds and deploys with GitHub? No
i Writing configuration info to firebase.json...
i Writing project information to .firebaserc...
✔ Firebase initialization complete!
Once you answer the questions, a firebase.json
file will be generated.
:::message alert If your Flutter project does not include the Web platform, you may encounter an error. In that case, run the following command to add a Web platform:
flutter create . --platform web
:::
3. Deploy
Let's try deploying it.
firebase deploy
When you run the above command, the Hosting URL will be displayed as shown below.
...
✔ Deploy complete!
Project Console: https://console.firebase.google.com/project/sample-1234/overview
Hosting URL: https://sample-1234.web.app
If you open this URL, you can see that your Flutter project is displayed correctly.
Creating GitHub Actions
Next, let's create a YAML file that will be executed when you comment /preview
on a pull request.
🔑 Preparing your Firebase Service Account Key
To deploy to Firebase through GitHub Actions, you need a Firebase service account key. To easily obtain the key, use the following command:
firebase init hosting:github
After entering the above command, you will be asked the question below. Specify the repository containing the source code in the format user/repository
.
# Enter a GitHub repository. Write it as `user/repository`.
? For which GitHub repository would you like to set up a GitHub workflow? (format: user/repository) Hand-Tomi/sample
Firebase will then automatically set the service account key in the Secrets of the GitHub repository and provide you with the Secrets constant name as shown below (e.g. FIREBASE_SERVICE_ACCOUNT_SAMPLE_1234
). Save this constant name.
✔ Created service account github-action-1234 with Firebase Hosting admin permissions.
✔ Uploaded service account JSON to GitHub as secret FIREBASE_SERVICE_ACCOUNT_SAMPLE_1234.
i You can manage your secrets at https://github.com/Hand-Tomi/sample/settings/secrets.
Next, you will be asked the following question, but since you have everything you need, press Control+C
(or in the case of Windows, Ctrl+C
) to exit.
? Set up the workflow to run a build script before every deploy?
✍️ Create a YAML File for GitHub Actions
Now it's time to create a YAML file for GitHub Actions.
Create a YAML file in the .github/workflows
directory at the root of the Flutter project and add the following code:
name: Command Execute Deploy Web
on:
issue_comment:
types: [created]
jobs:
deploy-web:
if: ${{ github.event.issue.pull_request && github.event.comment.body == '/preview' }}
name: Deploy Web
runs-on: ubuntu-latest
concurrency:
group: ${{ github.workflow }}-${{ github.event.issue.number }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v4
with:
ref: refs/pull/${{ github.event.issue.number }}/head
fetch-depth: 0
- name: Set Up Flutter
uses: subosito/flutter-action@v2
with:
channel: 'stable'
- name: Install Dependencies
run: flutter pub get
- id: deploy-web
name: Deploy to Firebase Hosting
uses: FirebaseExtended/action-hosting-deploy@v0
with:
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_SAMPLE_1234 }}
expires: 7d
channelId: "issue_number_${{ github.event.issue.number }}"
env:
FIREBASE_CLI_EXPERIMENTS: webframeworks
- name: Comment on Success
if: success()
uses: peter-evans/create-or-update-comment@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
body: |
✅ Preview has been deployed.
- **Link**: ${{ steps.deploy-web.outputs.details_url }}
For firebaseServiceAccount
, specify the Secrets’constant name you created beforehand (e.g. FIREBASE_SERVICE_ACCOUNT_SAMPLE_1234
).
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_SAMPLE_1234 }}
After that, when you merge into the relevant repository and leave a comment /preview
in the pull request, Actions will be executed automatically, and GitHub Actions will comment a link.
💡Reference: Explanation of the YAML Code for GitHub Actions
The explanation for the YAML code above is as follows:
Execution Timing
on:
issue_comment:
types: [created]
When you use issue_comment
, a workflow is automatically executed when a comment is created. This comment applies not only to pull requests but also when a comment is made on an Issue.
In this article, I want to limit the scope to pull request comments, so I will include github.event.issue.pull_request
in if
of jobs
, ensuring that only pull requests are executed.
jobs:
deploy-web:
if: ${{ github.event.issue.pull_request && github.event.comment.body == '/preview' }}
Additionally, when using issue_comment
, you need to change the checkout location. issue_comment
does not have the latest commit information for the current pull request, so if you check it out as it is, it will check out to the latest commit on the default branch.
Therefore, you need to specify ref
in actions/checkout
as follows (Reference: https://github.com/actions/checkout/issues/331#issuecomment-1438220926).
- uses: actions/checkout@v4
with:
ref: refs/pull/${{ github.event.issue.number }}/head
fetch-depth: 0
Check Comment Message
Check whether the comment message is /preview
. Check github.event.comment.body
as shown below, and only if the message is /preview
, execute the process in the deploy-web job.
jobs:
deploy-web:
if: ${{ github.event.issue.pull_request && github.event.comment.body == '/preview' }}
Prevention of Concurrent Execution
If you leave /preview
as is and immediately comment /preview
again, concurrent execution may occur.
concurrency:
group: ${{ github.workflow }}-${{ github.event.issue.number }}
cancel-in-progress: true
In this case, GitHub Actions prevents concurrent execution through concurrency
.
The important thing is to place it under jobs
.
jobs:
deploy-web:
if: ${{ github.event.issue.pull_request && github.event.comment.body == '/preview' }}
name: Deploy Web
runs-on: ubuntu-latest
concurrency:
group: ${{ github.workflow }}-${{ github.event.issue.number }}
cancel-in-progress: true
If you do not place it under jobs
as shown above, concurrency
will be executed without checking github.event.comment.body == '/preview'
in if
, and if you leave a comment other than /preview
immediately after commenting /preview
, the Action will not be executed.
Deploy
The following steps are for deploying to Firebase Hosting.
- id: deploy-web
name: Deploy to Firebase Hosting
uses: FirebaseExtended/action-hosting-deploy@v0
with:
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_SAMPLE_1234 }}
expires: 7d
channelId: "issue_number_${{ github.event.issue.number }}"
env:
FIREBASE_CLI_EXPERIMENTS: webframeworks
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_SAMPLE_1234 }}
: This is the Firebase service account authentication key. Enter the one you previously obtained.expires: 7d
: This is the expiration date. If you set it like this, your preview site will expire after 7 days.channelId: "issue_number_${{ github.event.issue.number }}"
: This is the name of the Firebase Preview channel. If you specify achannelId
other thanlive
, it will be deployed to Firebase Preview and you can set the expiration date.FIREBASE_CLI_EXPERIMENTS: webframeworks
: This useswebframeworks
, an experimental feature of Firebase CLI. It is indispensable for Flutter Web.
Link Comments
A comment was left for the link using peter-evans/create-or-update-comment
. By using this, you can easily leave reactions and add or edit comments.
- name: Comment on Success
if: success()
uses: peter-evans/create-or-update-comment@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
body: |
✅ Preview has been deployed.
- **Link**: ${{ steps.deploy-web.outputs.details_url }}
if: success()
: Only if successful will this step be executed.token: ${{ secrets.GITHUB_TOKEN }}
: You need aGITHUB_TOKEN
to leave a comment. No additional setup is required.issue-number: ${{ github.event.issue.number }}
: This specifies which issue to comment on. When the workflow was executed viaissue_comment
, the issue number can be confirmed withgithub.event.issue.number
.steps.deploy-web.outputs.details_url
: This displays the URL obtained from the deployment step above.- If you want to include other information, see here.
Conclusion
The techniques introduced in this article will enable faster and easier verification of operation during code reviews than before. This will improve the development efficiency of the entire team, leading to the delivery of better products.
However, when introducing a web preview for debugging, it is essential to consider both security concerns and how to resolve OS-specific differences. Using the OS's functions extensively may result in the disadvantages outweighing the advantages.
However, in projects that don't heavily rely on OS functions, the advantages outweigh the disadvantages, so I encourage you to introduce it into your development environment.
Also, be sure to check out other articles written by the Flutter team members!
Thank you for reading this blog to the end.