Securely deploy your progressive web app with Firebase and CircleCI

So you’ve taken quite some time to build your Progressive Web App (PWA) and it is now time to deploy it. Where to start?! I am going to share the simplest method I have come across to get this done. The key platforms that we shall use for this specific process are Firebase and CircleCI. Okay, let’s get started.

Setup Firebase

First, you need to sign up for Firebase. Once that is done, create a project on under the Hosting tab. I created one called ray-besiga. A modal will then popup with instructions to install the Firebase Tools on your command line.

$ npm install -g firebase-tools

Follow that up by navigating to the directory where your PWA is and run the following commands:

$ firebase login
$ firebase init

The first command signs you into the Google Firebase console, and the second one initiates your project. On your terminal, you should have the following output (comes with lots of fire emojis!!)

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

  /Users/username/Projects/fancy_new_project

Which Firebase CLI features do you want to setup for this folder?
Press Space to select features, then Enter to confirm your choices. (Press <space> to select)
❯◯ Database: Deploy Firebase Realtime Database Rules
 ◯ Firestore: Deploy rules and create indexes for Firestore
 ◯ Functions: Configure and deploy Cloud Functions
 ◯ Hosting: Configure and deploy Firebase Hosting sites
 ◯ Storage: Deploy Cloud Storage security rules

Scroll down to hosting and select it by pressing the space bar and then enter to confirm your choices. It then takes you to the project setup.

 === 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.

? Select a default Firebase project for this directory: (Use arrow keys)
  [don't setup a default project]
  Tribe Kampala (tribe-kampala)
❯  Ray Besiga (ray-besiga)
  [create a new project]

Next comes the Hosting Setup page. I went with the default setup below but we shall have to make changes for a number of reasons.

=== Hosting Setup

Your public directory is the folder (relative to your project directory) that
will contain Hosting assets to be uploaded with firebase deploy. If you
have a build process for your assets, use your build's output directory.

? What do you want to use as your public directory? public
? Configure as a single-page app (rewrite all urls to /index.html)? No
✔  Wrote public/404.html
✔  Wrote public/index.html

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

✔  Firebase initialization complete!

I am building my PWA using Jekyll as my static site generator. By default, Jekyll builds to _site while Firebase expects to deploy to the public folder. We shall make changes to this in the firebase.json file, which is created along with the .firebaserc file when we run the firebase init command. Edit the firebase.json file to look like this:

{
  "hosting": {
    "public": "_site",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ]
  }
}

With that, we are ready to deploy our site. Firebase provides the following command for us to make it happen.

$ firebase deploy

You should now be able to navigate to the URL shown in the terminal in your browser to see your newly deployed website. Thanks Firebase! However, I’m sure you also realize that the deployment is manual. A cooler way to do it is with the commit-to-deploy as done by Github. We are going to do this with CircleCI.

Setup CircleCI

Head on over to CircleCI , login with your Bitbucket or Github and add the project you wish to continuously deploy. At this point, you can create a circle.yml file or you can have one created for you. I prefer to make mine as CircleCI uses inference if you decide to build without making one. Mine is pretty basic and looks something like this:

# The Firebase tools want a v6.x version of Node but Circle CI
# defaults to v4.x. Set the latest LTS version.
machine:
  node:
    version: 6.10.3

# Need to install firebase-tools so that the deploy works
dependencies:
  pre:
    - npm install -g firebase-tools

# Need to tell Circle CI how to build the site
compile:
  override:
    - bundle exec jekyll build --verbose

# Circle CI expects some kind of testing to pass in order for the
# build to be successful and deploy. Since you don't have tests
# you can fake it by just returning true. http://gph.is/1MLPDWK
test:
  override:
    - "true"

# How you tell Circle to deploy to Firebase. The important thing to
# note here is the FIREBASE_TOKEN env variable. See below.
deployment:
  production:
    branch: master
    commands:
      - firebase use default
      - firebase deploy --token=$FIREBASE_TOKEN --non-interactive

Commit this file, push upstream to your remote, click build, and voila!

$ firebase use default

Error: Command requires authentication, please run firebase login

firebase use default returned exit code 1

Action failed: firebase use default

Oh wait, we’re not there yet. Looks like we are missing the token specified in the last line of the circle.yml file. This token is essential as it grants access to the firebase tool on the CI server. To fix this issue, we shall run the following command:

$ firebase login:ci

Once access if granted, you should a token similar to this in your terminal

Waiting for authentication...

✔  Success! Use this token to login on a CI server:

1/1QDE1Do0PhmhPG60pQi1fnXoSOjnRhfMQLzBzlJSxiaWUmacndrIee-GEhxvs41B

Example: firebase deploy --token "$FIREBASE_TOKEN"

Copy that token, head on over to CircleCI, under ‘Build Settings’ in the ‘Project Settings’, click the ‘Environment Variables’ link and click the ‘Add Variable’ button. Make sure the name of the variable is FIREBASE_TOKEN, the value being the token.

Now commit any changes you may have made in the repo, and watch the build. Success! Happy dance. Hope you enjoyed following along. Next time, we shall try a similar process with SemaphoreCI and Firebase.