Building an Angular app and deploying it to Firebase using Bitbucket Pipelines


This is a guest post from Joaquin Cid.


In the following tutorial, we'll learn how to setup a CI/CD pipeline using Bitbucket Pipelines and configure automatic build, run tests (both unit and e2e) and deploy an Angular app to Firebase Hosting.

Why is CI/CD important?

How much time do you spend on building, running tests and validating them, and finally deploying to the web server? How many times have you missed or forgotten how to run a specific step? A CI/CD pipeline will run all these tasks automatically for you. 

CI/CD (continuous integration / continuous delivery) is the process by which you can set up automatic builds of your app, run all tests and deploy to each different environment you might have. 

Once your CI/CD pipeline is setup, you won't need to spend any more time building, running tests and deploying to the different environments. You can focus on developing your app and increase the delivery of new features to your customers.

Bitbucket Pipelines

Bitbucket Pipelines is an integrated system that allows you to configure all your CI/CD tasks in a pipeline. It also lets you define triggers to start your build automatically. For instance you can configure your pipeline to run after changes are pushed on specific branches, or tags. This is very powerful, and allows you to set up an end to end automatic process that takes your code changes to different environments without manual interaction required.

Bitbucket Pipelines are simple to use, and has a free plan including 50 minutes of pipeline run time, which can be more than enough if you have a small app or require deployments only once a day.

Firebase Hosting

This is a product from Google's Firebase suite that offers static hosting for free, and CLI tools that you can easily integrate with CI/CD pipelines.


Let’s get started with our example. We'll create a new Angular app, connect it with a Bitbucket repo, configure the pipeline and run it to get it deployed to Firebase Hosting. 

1. Create Angular app

To create an Angular app, you'll need to have installed:

  • Node.js
  • @angular/cli (npm install -g @angular/cli)

Now, lets run the command to create the app:

ng new application ci-cd-app

“`

? Would you like to add Angular routing? Yes

? Which stylesheet format would you like to use? SCSS 

“`

cd ci-cd-app/

To start our app we run:

ng serve

We have our Angular app up and running!

To run Unit tests we need to run:

ng test

And for e2e tests:

ng e2e

You'll need to have chrome v75 to run e2e tests locally.

2. Create Bitbucket repository

Go to bitbucket.org, sign in with your account (or create a new one if you don't have one) and create a repository. 

For this tutorial, I'll create a public git repository, with all default settings. You can change your configuration as required.

Now that we have our repo, lets connect it with our local Angular app repository. For that we'll set the remote origin with the newly created Bitbucket repo. (You can see the commands in the initial page of your repo)

git remote add origin https://{your-username}@bitbucket.org/{your-username}/ci-cd-app.git
git push -u origin master

Great, now your code is on Bitbucket!

3. Set up Firebase Hosting

First, we'll need to create a Firebase project. Go to Firebase, sign in with your account and create a new project.

Now, that we have our Firebase project created, let's go back to our terminal to connect our app to Firebase project.

First, let's install Firebase CLI

npm install -g firebase-tools

Next let's login with Firebase account:

firebase login

This step will open a prompt on a browser to grant access to the CLI to run commands with your account. Once access is granted you can run `firebase list` to list all your projects.

Now, let's build the app:

ng build --prod

This will generate the app in the folder dist/ci-cd-app. Next we need to configure Firebase Hosting, and set the app folder we want to upload, to do that we'll run:

firebase init

Select Hosting and then select a default Firebase project for this directory: ci-cd-app (ci-cd-app)

What do you want to use as your public directory? dist/ci-cd-app

File dist/ci-cd-app/index.html already exists. Overwrite? (y/N) N
“`

This command will create two files .firebaserc and firebase.json (which hold the configuration we previously set), commit and push those to bitbucket.

Finally let's deploy our app, to do that we'll run

firebase deploy

You can check the site at https://{your-firebase-proj-id}.firebaseapp.com/

Setup CI/CD on your Angular & Firebase app

So far, we have created an Angular app, built it locally, ran tests and deployed it to Firebase Hosting. Now we can go ahead and setup the pipeline to run this process in the cloud and to be triggered on every push to the repo.

First we'll start with adding the npm scripts that we'll run in the pipeline. In the package.json file include the following:

{
 ...
 "scripts": {
   ...
   "build:prod": "ng build --prod",
   "test:ci": "ng test --no-watch --no-progress --browsers ChromeHeadlessCI",
   "e2e:ci": "ng e2e --protractor-config=e2e/protractor-ci.conf.js"
 },
 ...
}

Since the tests need a different configuration to run in the pipeline context, we created specific scripts for that.

Note that we are defining a ChromeHeadlessCI to run the unit tests and protractor-ci.conf.js as the config for e2e tests.

In the file karma.conf.js we'll add the custom launcher with flag –no-sandbox to run a headless chrome in the pipeline context:

module.exports = function (config) {
 config.set({
   ...   
   customLaunchers: {
     ChromeHeadlessCI: {
       base: 'ChromeHeadless',
       flags: ['--no-sandbox']
     }
   }
 });
};

Then we'll create a e2e/protractor-ci.conf.js file. Like before, we are setting args to run headless and no sandbox:

const config = require('./protractor.conf').config;

config.capabilities = {
 browserName: 'chrome',
 chromeOptions: {
   args: ['--headless', '--no-sandbox']
 }
};

exports.config = config;

Next, we need to go to Bitbucket's repo site in settings -> pipelines settings to enable the pipelines.

Then, we have to generate a Firebase token and add it as a repository variable.

To do that we'll run firebase login:ci in the app terminal and copy the token generated

Then in the settings page go to Repository Variables and create a variable with name FIREBASE_TOKEN_CI and paste the token as the value.

You'll need to have chrome v75 to run e2e tests locally.

Next, we are going to create a bitbucket-pipelines.yml file.

image: node:10.15.3

pipelines:
 default:
   - step:
       name: Install, Unit tests, e2e tests, Build
       caches:
         - node
       deployment: test
       script:
         - npm install
         - >
           wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \
             sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' && \
             apt-get update && \
             apt-get install -y google-chrome-stable xvfb procps       
         - npm run test:ci
         - npm run e2e:ci
         - npm run build:prod
         - pipe: atlassian/firebase-deploy:0.3.0
           variables:
             FIREBASE_TOKEN: '$FIREBASE_TOKEN_CI'

This file holds the configuration of our pipeline, and what commands it will run. To keep it simple, I created a default configuration, that will run on every push we make to the repository, and it will run the following scripts

  • npm install
  • wget … (this command installs chrome in the pipeline container, because we need it to run the tests)
  • npm run test:ci
  • npm run e2e:ci
  • npm run build:prod
  • pipe: firebase deploy (with the Firebase token defined previously)

This configuration is set as `test` and you can look at the pipelines run for each environment in the deployments page.

When you push all these changes you should see the pipeline running and you can watch the progress and details:

Conclusion

Excellent! You have configured your pipeline to build, test and deploy your Angular app to Firebase. We discussed all the necessary steps to create the app, repo, Firebase project, and how to configure the pipeline to run all the necessary steps. 

Now you can go ahead and extend this configuration for other environments you might have (staging, prod). For those usecases, you can take a look at the docs to understand how to define triggers on specific branches or tags.


Author bio: Joaquin is a freelance full-stack developer at Toptal with 12+ years of experience. A passionate developer, he has worked for companies like WebMD and Getty Images. He specializes in web application development and also has experience on cloud technologies. He also writes technical content on Medium and occasionally gives talks. He lives in Rosario, Argentina and is huge fan of Newell’s Old Boys soccer team.


Love sharing your technical expertise? Learn more about the Bitbucket writing program.