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:
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:
Anyways, lets get on with it!
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.
npm installnpm test
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.
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
y
and 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.
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.json
or 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!
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 [PROJECT_ID]
.
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.
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.
npm installnpm testnpm buildfirebase 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.
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_DEPLOY_TOKEN]
.
firebase logout --token [FIREBASE_DEPLOY_TOKEN]
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 [PROJECT_ID]
and [FIREBASE_DEPLOY_TOKEN]
.
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.
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.
master
v*
(if you use semantic versioning, otherwise just *
)cloudbuild.deploy.json
_FIREBASE_DEPLOY_TOKEN
and 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!
Helpful Links