Deploying multiple Firebase Environments with CodeShip

04 October 2018By Rich @Black Sand Solutions
  • Firebase
  • CI
  • Codeship
  • Development
Photo by SpaceX on Unsplash

Deploying multiple Firebase Environments with CodeShip

Deploying multiple Firebase Environments with CodeShip

An application that I am developing for a client has reached the stage where it makes sense to seperate the development and production environments. I've configured deployment to multiple envirnoments before using CodeShip, but not for a project that is serverless. It turned out be pretty straightforward and just required the use of a simple bash script.

The result is a single codebase that is built and deployed to two different Firbaese environements, each having it's own hosting, database, authentication and storage. Additionally the production build is optimized for production.

The steps below detail how to configure this. But before we look at that, let's show how a single project is built and deployed using a custom script in CodeShip.

Building And Deploying A Single Environment

Starting Point

It's assumed that there is already an existing Firebase Project. Let's call it my-app.

Configure CodeShip

First, let's setup Codeship to deploy the code from the master branch to our Firebase Project. To do this we create a new pipeline in CodeShip and configure it to run each time code is pushed to the master branch.

We'll use a custom script to handle the deployment:

# SET VARIABLES
echo building ${PROJECT_ID}
######################################
#install node version (defaults to 0.10 otherwise)
nvm install 8.9
#######################################
#install dependencies
#######################################
npm install
#######################################
#build angular app
#######################################
npm install -g @angular/cli@6.0.0
ng build
#######################################
#deploy to firebase hosting
#######################################
npm install -g firebase-tools
firebase deploy --only hosting --debug

NOTE: we don't need to set the project ID when deploying to firebase, because it will be read from the .firebaserc file.

And add an Environment Variable to hold the Project Id - in this case my-app.

We now have Codeship configured to build and deploy our app each time code is pushed to the master branch.

Deploying Multiple Environments

Now we want the ability to deploy to either a development or production environment.

Create New Firebase Project

We already have a project called my-app and this will become the production project. Another project is created for the development project. We'll call that my-app-dev.

Configure Branches

The single repository is split into two branches:

  • master - the default branch, this will contain the production ready code.
  • develop - contains the current development work in progress.

When the development work is considered production ready, the develop branch will be merged back into the master branch.

FirebaseRC

The .firebaserc is updated to support two projects, with the default being the development project.

{
  "projects": {
    "default": "my-app-dev",
    "production": "my-app"
  }
}

Create Firebase Environments Folder

Each environment will have it's own config. We'll place these in a folder called firebase-environments. Create a sub folder called production and create a file called firebase.config.ts. Copy the existing firebase config used by the app into this file.

E.g.

import * as firebase from 'firebase';
export default firebase.initializeApp({
    apiKey: "AIzaS9Ipo09uTPf9sHdDsWRFCuU4EFAq03RKm8",
    authDomain: "my-app.firebaseapp.com",
    databaseURL: "https://my-app.firebaseio.com",
    projectId: "my-app",
    storageBucket: "my-app.appspot.com",
    messagingSenderId: "533454532037"
  });

Now create another folder called develop and create a file called firebase.config.ts. Copy the firebase config for the development project here.

E.g.

import * as firebase from 'firebase';
export default firebase.initializeApp({
    apiKey: "AIzaS9Ipo09uTPf9sHdDsWRFCuU4EFAq03RKm8",
    authDomain: "my-app-dev.firebaseapp.com",
    databaseURL: "https://my-app-dev.firebaseio.com",
    projectId: "my-app-dev",
    storageBucket: "my-app-dev.appspot.com",
    messagingSenderId: "533454532037"
  });

Each project now has it's own configuration.

Create Development Project ENV Variable

We already have an Environment variable called PROJECT_ID. Add another and call it PROJECT_ID_DEV. We will use this in the development pipeline.

Create Development Pipeline

We already have a deployment pipeline for the master branch. Now we create a new deployment pipeline and configure it to run when code is pushed to the develop branch. This has a custom script that is almost identical to the production pipepline.

We just need to:

  • replace PROJECT_ID with PROJECT_ID_DEV
# SET VARIABLES
echo building ${PROJECT_ID_DEV}
######################################
#install node version (defaults to 0.10 otherwise)
nvm install 8.9
#######################################
#install dependencies
#######################################
npm install
#######################################
#build angular app
#######################################
npm install -g @angular/cli@6.0.0
ng build
#######################################
#deploy to firebase hosting
#######################################
npm install -g firebase-tools
firebase deploy --only hosting --debug

Update the Production Pipeline

We need to make a few small changes to the production pipeline. Before we call the firebase deploy command, we set the firebase project. By default the development project will be used (see .firebaserc above). Instead we tell it to deploy the production project. firebase use production

E.g.

...
  lines truncated for brevity
...
#######################################
#deploy to firebase hosting
#######################################
npm install -g firebase-tools
firebase use production
firebase deploy --only hosting --debug --project ${PROJECT_ID_DEV}

So far, so good. Updating either branch will trigger tthe deployment of the a project to the appropriate location. But there is one step remaining.

Automate The Switching of Environment

We need to ensure that correct config files are used. This is accomplished with a simple bash file.

At the root of the project create a file called configure-firebase.sh. And paste in the following content.

#!/usr/bin/env bash

# This file ensures that the correct firebase config is used for the environment being built
# If production environment we copy over the production config, else we leave it as

if [ "$FIREBASE_ENV" == "production" ];
then
  echo "Switching to Firebase Production environment"
  yes | cp -rf "./firebase-environments/production/firebase.config.ts" ./src/config
else
  echo "Switching to Firebase Development environment"
  yes | cp -rf "./firebase-environments/develop/firebase.config.ts" ./src/config
fi

To each pipeline add another Custom Script section before the existing deployment script.

And paste in the following:

Development Pipeline

echo set up FIREBASE environment
export FIREBASE_ENV="develop"
chmod +x ./configure-firebase.sh
./configure-firebase.sh
### Production Pipeline
echo set up FIREBASE environment
export FIREBASE_ENV="production"
chmod +x ./configure-firebase.sh
./configure-firebase.sh

This will run the bash script configure-firebase.sh before the build and ensure the correct firebase.config.ts file is used.

We use chmod on the file before calling it, to ensure that it is marked as executable, otherwise the deployment will fail.

ET, VOILA!

You now have CodeShip configured to deploy development code to a development environment and production code to a production environment. Simples.

All Posts