,

Deploying a React app to Firebase Hosting using Azure Pipelines with Azure Repos


Wait. What?

You’re probably thinking – “WHY.” It’s simple, really.

  • Historically, I’ve been a Microsoft stack developer. C#, SQL, Azure… the whole 9 yards. I’m very comfortable with the Microsoft stack. I already store my repos in AzureDevops.
  • I wanted to ramp up a project quickly to learn React.
  • A friend told me, “Dude, you have to go with Firebase for React”

So, here we are.

Continuous Integration/Continuous Deployment Goals

I have some straightforward goals to achieve:

  • Have a development and production branch (DEV and MASTER, respectively)
  • Make changes on DEV branch. Push should build and deploy to my development instance
  • Merge DEV into MASTER. Push should build and deploy to my production instance
  • If possible, I want only one pipeline to handle both (yeah, yeah, this probably won’t scale)
  • Don’t build or deploy feature branches

Assumptions and Prerequisites

This article assumes a lot of things. I assume that you:

  • Have a React app that was created with create-react-app or at least meets Firebase’s deploy requirements
  • Have a Firebase account and some code you’re going to deploy
  • Have two Firebase projects – one for your development build and one for your production build.
  • Have installed and can use the Firebase CLI
  • Have an Azure DevOps account and can get code into your repo there
  • Know how to use git and repos and how to push your code to remotes
  • Aren’t afraid to try some of this out if you don’t have any of the above

Steps

I followed Victor Bruce-Crabbe’s “How to deploy a react-app to different firebase hosting environments(dev and prod)” to set up my project for dev and production builds. Once I had that in place, I used Microsoft’s Build, test, and deploy Javascript and Node.js apps document to figure out the rest.

The entire setup hinges on defining environment set ups in your code, then creating an Azure Pipelines yaml file that is added to your repo.

Code Prep

Make sure all your changes are pushed/stashed/whatever. You should start with no pending changes.

Create and Use Environment Variables

You can follow the article here for more in-depth instructions. I’m just going to hit the main points here.

Install env-cmd

Install env-cmd in your project. The env-cmd package lets you group variables into an environment file (.env) and pass them to your script.

npm install env-cmd
Separate Your REACT_APP Variables

In your project root, create two files that contain variables for the respective environments. You’re going to put the web-app configuration values into these files and check them in. Yes, check them in. Victor’s instructions say otherwise, but the values you put in here are in your Firebase Console > Project Overview > Project Settings and would have been pasted into your HTML anyway. Just don’t put other secrets here (like other API keys or secrets). I’m creating another article for that later…

The files you need to create at the root of your project are:

  • .env.development
  • .env.production

In both files I added a REACT_APP_ENV_ID variable, set to the appropriate value (PRD or DEV). So, in .env.development, I it looks like:

REACT_APP_ENV_ID=DEV

In my file where I initialize the Firebase client, I did the following:

import app from 'firebase/app';

const prodConfig = {
    apiKey: process.env.REACT_APP_PROD_API_KEY,
    authDomain: process.env.REACT_APP_PROD_AUTH_DOMAIN,
    databaseURL: process.env.REACT_APP_PROD_DATABASE_URL,
    projectId: process.env.REACT_APP_PROD_PROJECT_ID,
    storageBucket: process.env.REACT_APP_PROD_STORAGE_BUCKET,
    messagingSenderId: process.env.REACT_APP_PROD_MESSAGING_SENDER_ID,
    appId: process.env.REACT_APP_PROD_APP_ID,
    measurementId: process.env.REACT_APP_PROD_MEASUREMENT_ID,
};
   
const devConfig = {
    apiKey: process.env.REACT_APP_DEV_API_KEY,
    authDomain: process.env.REACT_APP_DEV_AUTH_DOMAIN,
    databaseURL: process.env.REACT_APP_DEV_DATABASE_URL,
    projectId: process.env.REACT_APP_DEV_PROJECT_ID,
    storageBucket: process.env.REACT_APP_DEV_STORAGE_BUCKET,
    messagingSenderId: process.env.REACT_APP_DEV_MESSAGING_SENDER_ID,
    appId: process.env.REACT_APP_DEV_APP_ID,
    measurementId: process.env.REACT_APP_DEV_MEASUREMENT_ID,
};
   
const config = process.env.REACT_APP_ENV_ID === 'PRD' ? prodConfig : devConfig;

class Firebase {
    constructor() {
        app.initializeApp(config);
        /* moar code */
    }
}

export default Firebase;
Update package.json

In your root package.json file, in the scripts node, add build scripts for build:development and build:production. Mine look like this:

  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "build:development": "env-cmd -f .env.development react-scripts build",
    "build:production": "env-cmd -f .env.production react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
Save and Test

Save and test to make sure that everything builds and works correctly.

npm startThis command should start your code in debug mode locally
npm build:developmentThis command should build optimized code using your dev configuration
npm build:productionThis command should build optimized code using your prod configuration

Check that running these commands does what you expect. Check in and push.

Firebase Prep

Make sure you’ve initialized firebase in your project directory.

Follow the Firebase CLI reference to get a refresh token. Your pipeline needs this to deploy.

.firebaserc

Check your .firebaserc file. It should have your production and development environments in it. You can manually edit or use the CLI to both firebase use development and firebase use production

In my case, my development project is the default.

{
  "projects": {
    "default": "<<development-project-name>>",
    "production": " <<production-project-name>> ",
    "development": " <<development-project-name>> "
  }
}

From the Firebase CLI reference:

In general, you should check your .firebaserc file into source control to allow your team to share project aliases. However, for open source projects or starter templates, you should generally not check in your .firebaserc file.https://firebase.google.com/docs/cli#project_aliases

Azure DevOps Pipeline

  1. Create a new Pipeline.
  2. Select your repo location.
  3. Select your repo.
  4. Select Node.js as the pipeline.
    You’re provided with default yaml.
  5. Click the Variables button.
    The New Variable flyout opens.
  6. Click New Variable.
  7. Use the name FIREBASE_TOKEN and paste your refresh token into the Value field.
  8. Check the Keep this value secret checkbox.
  9. Click OK.
  10. Replace the default yaml with the code below.
    • If your branches aren’t named master or dev, make changes under trigger.
# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript

trigger:
- master
- dev

pool:
  vmImage: 'ubuntu-latest'

variables:
    ${{ if eq(variables['Build.SourceBranchName'], 'master') }}: # master
      buildToken: 'production'
    ${{ if eq(variables['Build.SourceBranchName'], 'dev') }}: # dev
      buildToken: 'development'

steps:
- task: NodeTool@0
  inputs:
    versionSpec: '12.x'
  displayName: 'Install Node.js'

- script: |
    npm install -g firebase-tools
  displayName: 'Install firebase tools'

- script: |
    npm install env-cmd --save
  displayName: 'Install env-cmd'

- script: |
    npm install
    cd functions && npm install
    cd ..
    npm run build:$(buildToken)
  displayName: 'npm install and build $(buildToken)'

- script: |
    firebase deploy -P $(buildToken) --token "$(FIREBASE_TOKEN)"
  displayName: 'Deploy to firebase $(buildToken)'

NOTE: This script assumes that you have functions. If you don’t have functions, remove the following two lines in the third script block

    cd functions && npm install
    cd ..

The Moment of Truth

Click the Save and Run button.

Here’s where things get a little fuzzy, because I actually did this a while ago. If I remember correctly, the wizard checks the file into the root of your repo. Hopefully, you get to choose which repo so it goes into the dev branch.

If the Save and Run process pushes your dev branch successfully, then you’re almost there!

Pull your dev branch, since you added an azure-pipelines.yml file. Make a small change and push; your dev branch should build and be pushed to Firebase development hosting.

Assuming everything tests well, merge your dev branch into your master branch, then push and your production branch should build and be pushed to your Firebase production hosting.

Keith Menor

Hey – that’s me! Since 1997, I’ve done a lot of technical things in my life – from technical writing, to network administration, and then on to application and web development. Hopefully you’ll find some useful information here!

Categories

Archives

Tags

There’s no content to show here yet.