The comprehensive guide to GitHub Actions and Firebase Hosting
This blog post is also in podcast format. Checkout the Firebase Podcast!
Preview channels are a fantastic way to deploy versions of your site to a generated, expire-able, URL. For many of you, this immediately gets you thinking. “Hey, I’d love to deploy a preview channel whenever a pull request is created on GitHub” And that’s exactly what you can do with GitHub Actions, which is an awesome way to automate tasks in response to events in GitHub.
This guide is going to teach you how GitHub Actions and Firebase Hosting works from the ground up. In all it covers:
- Give you a crash course on how GitHub Actions work
- Dive into how the Firebase CLI sets up the Action with your Firebase project
- Dive deep into the internals of thee Firebase Hosting GitHub Action.
Let’s get started.
What are preview channels and GitHub Actions?
Preview channels are a way for you to deploy versions of your site to a generated URL that expires after a specific period of time. This is great for staging new blog posts, testing visual changes, and experimenting with new functionality. Basically, if you add changes to a website, you’ll likely want to deploy to a preview channel before deploying to your live channel.
This goes hand in hand with a service like GitHub. If you create a pull request, you’ll probably want a preview channel for that specific request. And when you send new commits to that pull-request you’ll want the preview channel up to date with the latest version tied to your latest commit. Now this might sound like you’ll have to do this manually. But GitHub has you covered with GitHub Actions.
GitHub Actions crash course
GitHub Actions is a service by GitHub, that allows you to automate tasks in response to events in GitHub. I want to clarify that you don’t need to know how GitHub Actions work to use them with Firebase Hosting. The Firebase CLI automates everything for you. But since this is a comprehensive guide, we gotta have a crash course on GitHub Actions.
GitHub has specific terminology, so I’m going to do my best to stick to that. There’s 6 terms we’re about to learn: Workflows, Events, Actions, Steps, Jobs, and Runners. Don’t worry there’s no quiz and it’s fairly easy to understand.
Workflows
Let’s start with Workflows. Workflows are automated procedures that live in your GitHub repository. This is the configuration for specifying how the automation works and it’s done with a YAML file.
name: Deploy to Firebase Hosting on PR
'on': pull_request
jobs:
build_and_preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: npm ci && npm run build
- uses: FirebaseExtended/action-hosting-deploy@v0
Events
Then you have Events. Workflows are event-driven. Events are activities like pull requests. Cron jobs are also a type of event, which is really cool. The YAML file has a directive called 'on'
where you specify the triggering events.
Actions
This leads us to Actions. Actions are standalone commands, like running npm install
or using the Firebase Hosting action to deploy to a preview channel. Actions are the smallest unit of a workflow.
Steps
Running an action is called a Step. It’s important to understand that an action is run as a single step, because actions are standalone commands. One step is to install dependencies from npm, the next is to run a build script, then the last step could be to deploy to Hosting.
Jobs & Runners
All of these steps create a Job. Jobs execute on a server called a Runner. For most cases the runner is hosted on GitHub. You can host your own, but I have no idea how that works, so this blog post only refers to using GitHub hosted runners. When configuring the workflow YAML file you’ll see a spot for Jobs.
That was a lot of vocabulary, but I thought it was important enough to cover. Let’s put it all together in a real use case. A workflow gets triggered by a pull request, that executes a job with a set of steps first installs dependencies from npm, then runs a build script, and lastly deploys to a preview channel. The entire process happened on a Runner.
Whew. That’s your crash course on GitHub Actions. Now that’s great for the whiteboard. But how do you set it up? How do you go from squares pointing to other squares to a real life workflow?
Setting up GitHub Actions with Firebase Hosting
Now you understand how GitHub actions work. Let’s dive into how to set it up with Firebase Hosting.
Like I said previously, you don’t need to know how GitHub Actions work in order to use them with Firebase Hosting. It’s okay if you just want it “to work”, and by work I mean setting up preview channels with pull requests. All you have to do is run:
# new sites
firebase init hosting# existing sites
firebase init hosting:github
The CLI will take you through a wizard and get everything set up for you. But again, this is the comprehensive guide, so let’s dive in.
First of all you’ll need a GitHub repository and a Firebase project. That sounds obvious, but it’s good to be clear about the prerequisites. I’m going to cover what the Firebase CLI does when you run firebase init hosting:github
.
Authorizing with GitHub
The first thing we need to do is authorize with GitHub. Now if you’re setting up deployments on GitHub manually, you’re already authorized, because well, you are logged in. For the Firebase CLI to set up the action for you, you’ll need to give it permissions for a small period of time. The Firebase CLI OAuth app asks to be authorized on your behalf so we can do two things: upload a secret to GitHub’s secret store and check to see if you own the repo you’re setting up a workflow with. That’s it.
Secure deployments with Service Accounts
To automate a preview channel deployment, you’ll need to give the GitHub Action permission to deploy on your behalf. The best way to grant this permission is with a Google Cloud Service Account. Remember, Firebase Projects are really just Google Cloud projects.
Service Accounts act like a user with specific scopes. In this case this Service account is scoped just for Firebase Hosting. The CLI creates a Service Account on your behalf and uploads its secret keys to GitHub’s secret store. GitHub’s secret store does not allow you or another user to read the value of the secret. If you go to the UI you can only update it. The GitHub Action will be able to read it.
Specifying a build script
Many modern websites follow a specific recipe: manage dependencies with npm, build code with TypeScript/Webpack/Rollup and then deploy the compiled assets.
If your site follows this recipe, you’ll need to make sure the workflow has a step where your dependencies are installed and your code is built. By default the Firebase CLI will add npm ci && npm run build
as the step, which surprisingly fits a lot of situations.
The npm ci
command downloads all of your dependencies and is optimized for continuous integration. The npm run build
command is a common location for build scripts. You’ll find it in many frameworks CLIs like the Angular CLI, Create React App, Preact CLI, Vue CLI, and many more. In general having your primary build command listed for npm run build
is a good idea.
The CLI wizard asks you what this script is and takes this input and keeps it around to write as a step in the workflow YAML file.
Deploying to live after a merge
The CLI wizard also asks if you want to deploy to your live channel after a merge to your main branch. This will set up a separate workflow YAML file that is triggered by an event and deploys the code in your main branch to your live channel. We’ll cover how these workflow YAML files are written next.
Creating the Workflow files
At this point we have all the information needed to set up the workflow YAML file. GitHub Actions require that workflow files live in a specific folder structure: .github/workflow
. The CLI looks to write a file in the workflow folder called firebase-hosting-pull-request.yml
. If you want to deploy to live on a merge to your main branch you’ll also have a file called firebase-hosting-merge.yml
.
These files represent your GitHub Workflows. They declare the jobs that run the steps that trigger the preview channel (or live channel) deployments. They run on a GitHub hosted runner.
Before you can do anything, you need to push these changes to GitHub. You can either push them to your main branch or as a pull request. GitHub will run all YAML files in the workflow
folder. To see the preview channel deployed, I recommend pushing as a pull request first.
How the Firebase Hosting GitHub Action works
Now all we’ve done at this point is configured workflows. Nothing has actually happened yet. Not until the event occurs. When you send that pull request you’ll finally see the workflow begin. GitHub will show you the job is running in the pull request UI, by showing you a “check”.
This “check” is created by the Firebase Hosting GitHub Action. The “check” shows you the status of the job. The steps are run in the workflow and then the action creates a comment in the pull request to update you with the result of the workflow. If all goes well you’ll see the generated preview channel URL (which by default will exist for 7 days) and the commit SHA associated with the URL.
Making more commits
The Firebase Hosting GitHub Action runs whenever a pull request is created, but also when code is pushed to a branch. If you keep adding commits to a pull request, the action will run for every commit.
The action will run the steps and then look for an existing comment in the pull request UI. If a comment exists, it doesn’t update the preview channel URL, because the URL does not change. The preview channel is created when the pull request is created and the URL structure is based on the Firebase site name, pull request number, branch name, and a random hash.
When more commits are added the new version is deployed to the preview channel but the URL does not change. To show that the latest commit has been built and deployed, the comment is updated to show the associated SHA with the channel URL.
Merging to main branch / deploying to live
Once everything looks good and the pull request gets merged, the merge workflow will run if configured. This workflow runs the Firebase Hosting GitHub Action but instead of being triggered by a pull request, it’s triggered by a push event, but only when it’s your specific main branch. The action will also deploy to the live channel.
Wrapping up
That was a lot of information, especially for something that you don’t need to know how it works to use it. I think GitHub Actions are really interesting and I love the chemistry between them and preview channels.