Deploying static files to Firebase hosting is pretty easy with the Firebase CLI, but setting up reliable automatic deploys can save some headaches as your team or project grows. Without CI/CD (continuous integration/continuous delivery, another term for automated deployment), time can be wasted testing features that you forgot to deploy, or fixing failing tests that made it to master because no one ran tests on the branch before it was merged.
I didn’t want to worry about that stuff on my Create-React-App project that uses Firebase, so I used Google Cloud Build to:
- Test every branch in my repository so I can see that sweet sweet green dot on GitHub when all tests pass
- Deploy to the Beta project on every merge to master
- Deploy to the Production project on every new release tag
Why I chose Google Cloud Build
There are lots of services that will do automated deployment for you. Cloud Build seemed like the obvious choice to me because I was already using Firebase for authentication and data. The other options seemed to be expensive unless your project is open-source. Since my project is just a personal one, $69/month for Travis-CI is too much.
You’ll need a web project checked in to version control, with test and build steps. Google Cloud Build integrates easily with GitHub, Bitbucket, and of course Googles Cloud Source Repository. My project uses Create-React-App and GitHub. You’ll need a Firebase or GCP project already created. I have two for my project, beta and production, but the steps are mostly the same regardless of the number of projects — you’ll just need to decide how you want to setup the triggers.
To reiterate, in order to follow along you’ll need the following:
- a web project that builds static files for deployment (my project uses Create-React-App)
- a version control repo hosted on GitHub, Bitbucket, or Googles Cloud Source Repository
- at least one Firebase project that you deploy to manually (guide here)
- Google Cloud SDK installed (download here)
Anyways, lets get on with it!
Automatically testing each branch and pull request
Creating configuration files for testing
First we are going to create the testing build steps. Google Cloud Build uses a build configuration file (either yaml or json, I prefer json) with a list of a build steps. These build steps will run on every branch you push to GitHub. A few of the supported build steps are npm, yarn, git, and docker. Click here for a complete list.
Most web projects that are built with Node use the same standard commands. For example, these are the commands you would run to test most web apps locally.
We want to translate those commands into build steps. Create a file
cloudbuild.json and add the following build steps:
Pretty simple. If any of your steps need environment variables, additional arguments, or other custom stuff, Googles documentation on build config files can be found here.
Ensuring the test config file is working
Before we let GitHub trigger our tests, we are going to make sure our config file is working by triggering the test process from the command line. You’ll need the Google Cloud SDK installed and you want to be logged in. The install steps are here and directions for logging in are here. Once it’s installed, run the following command, replacing
[PROJECT_ID]. You can run
gcloud projects list to find your project ID.
gcloud builds submit --config=cloudbuild.json . --project=[PROJECT_ID]
If you have not yet enabled Cloud Build for your project, you’ll be asked if you want to enable it. Hit
yand press enter to do so. I had to do this twice for some reason.
This will run your build process in the cloud and show the output in your local command line. Go to https://console.cloud.google.com/cloud-build/builds to see the build history and all that.
Enabling the GitHub integration
If you are using GitHub for version control, you can add the GitHub integration and it will run your tests to determine if you see a green dot or a red dot on each branch and pull request.
Make sure you named your test config file
cloudbuild.yaml because that is what the integration will look for. Commit and push this file too.
Then all we have to do to enable the GitHub integration is add it from the GitHub marketplace found here. We now have green and red dots!
Automated build and deploy
Creating a custom Firebase CLI build step
In our last build config file, all of our build steps only used
npm. Since that is one of the supported build steps, it was easy to use. Deploying to Firebase requires the Firebase CLI, and sadly that is not supported, which means we need to create a custom build step.
A custom build step is a docker image that is stored in the cloud workspace specific to your project. This will benefit us because it means the build process will not need to download and install the Firebase CLI on every build, as it will reuse it.
To create the container image in your projects cloud workspace, create a folder called
firebase-build-step and add the following files. Don’t worry about replacing
$PROJECT_ID here, Cloud Build will do that for you.
Next, run the following command, replacing
gcloud builds submit --config=./firebase-build-step/cloudbuild.yaml ./firebase-build-step/ --project=[PROJECT_ID]
Okay so we just created a docker image in our projects cloud workspace that contains an installation of the
firebase-tools npm package. Now we can use that package in our build steps without installing it every time.
If you are doing this for multiple projects (I was because I wanted beta and production projects), now is a good time to go back and change the project id in the command and run it again.
After you are done with this step, you could delete the
firebase-build-step folder and it’s contents if you wanted to. Since the docker container has been created in your project, they won’t be used for the rest of this guide, BUT I don’t recommend it because they are nice to have when you add another project or periodically when you want to update the version of firebase-tools.
Creating configuration files for building and deploying
Now we want to create a config file for building and deploying. Like with the test steps, we start with the commands you would run locally.
firebase deploy --project [PROJECT_ID] --only hosting
The build steps would look like this:
I called this file
cloudbuild.deploy.json. The last build step here uses the custom build step we created.
$PROJECT_ID is substituted automatically by Cloud Build.
$_FIREBASE_DEPLOY_TOKEN on the other hand is a custom substitution variable, as evidenced by the underscore at the beginning. We have to pass this token in as an argument if we are triggering the build from the command line.
We don’t need this token when deploying from our local environment because Firebase CLI is logged in to your Google account. But when Cloud Build is deploying, it won’t be logged in. The
firebase login command we use locally can’t be used as a build step because it opens your browser to allow you to log in, and Cloud Build doesn’t understand that.
How to get a Firebase deploy token
To get a Firebase deploy token, have the Firebase CLI installed locally and run
firebase login:ci. Log in. Grant access to the CLI. Save the token somewhere. Definitely do not commit it.
If you ever need to revoke access from a token, use the following command, replacing
firebase logout --token [
Ensuring the build config file is working
We are going to test the build config file before we commit it, by triggering the build process from the command line. Run the following command, replacing
gcloud builds submit --config=cloudbuild.deploy.json . --project=[PROJECT_ID] --substitutions=_FIREBASE_DEPLOY_TOKEN="[FIREBASE_DEPLOY_TOKEN]"
Visit https://console.cloud.google.com/cloud-build/builds again to see the build history. If everything is running smoothly at this point we can move on to creating triggers.
Triggering builds from code changes
We’ve accomplished a lot so far, but the whole point of all of this was to have your deploys run automatically! Lets set that up now. Go to https://console.cloud.google.com/cloud-build/triggers and create a trigger. Steps 1, 2, and 3 should be pretty obvious. Stop when you are at trigger settings.
- If you want to deploy this project on every change to master, change “branch (regex)” to
- If you want to deploy this project whenever you tag a new release, change “Trigger type” to “Tag” and change “tag (regex)” to
v*(if you use semantic versioning, otherwise just
- Change “Build configuration” to “cloudbuild.yaml”
- Change “cloudbuild.yaml location” to
- Add a substitution variable with the name
_FIREBASE_DEPLOY_TOKENand paste the token from before into the value.
After you hit save your builds will happen automatically 😄. If you have another project, follow the same steps with different trigger settings.
So we automated the test process and integrated it with GitHub. We created our own build step so we could use the Firebase CLI in our build steps. Last, we created our build steps for building and deploying, and set it up to run whenever we push to master or tag a release. This should be a big upgrade for any web app that was doing manual deploys. Let me know if you get stuck on any step, or if you are struggling because your project is a little different.
This was my first Medium article and I’m definitely open to any feedback or conversation, thanks for reading!