A Continuous Integration and Deployment Setup with CircleCI and Coveralls

Avatar of Henry Eze
Henry Eze on

Continuous Integration (CI) and Continuous Deployment (CD) are crucial development practices, especially for teams. Every project is prone to error, regardless of the size. But when there is a CI/CD process set up with well-written tests, those errors are a lot easier to find and fix.

In this article, let’s go through how to check test coverage, set up a CI/CD process that uses CircleCI and Coveralls, and deploys a Vue application to Heroku. Even if that exact cocktail of tooling isn’t your cup of tea, the concepts we cover will still be helpful for whatever is included in your setup. For example, Vue can be swapped with a different JavaScript framework and the basic principles are still relevant.

Here’s a bit of terminology before we jump right in:

  • Continuous integration: This is a practice where developers commit code early and often, putting the code through various test and build processes prior to merge or deployment.
  • Continuous deployment: This is the practice of keeping software deployable to production at all times.
  • Test Coverage: This is a measure used to describe the degree to which software is tested. A program with high coverage means a majority of the code is put through testing.

To make the most of this tutorial, you should have the following:

  • CircleCI account: CircleCI is a CI/CD platform that we’ll use for automated deployment (which includes testing and building our application before deployment).
  • GitHub account: We’ll store the project and its tests in a repo.
  • Heroku account: Heroku is a platform used for deploying and scaling applications. We’ll use it for deployment and hosting.
  • Coveralls account: Coveralls is a platform used to record and show code coverage.
  • NYC: This is a package that we will use to check for code coverage.

A repo containing the example covered in this post is available on GitHub.

Let’s set things up

First, let’s install NYC in the project folder:

npm i nyc

Next, we need to edit the scripts in package.json to check the test coverage. If we are trying to check the coverage while running unit tests, we would need to edit the test script:

"scripts": {
  "test:unit": "nyc vue-cli-service test:unit",
},

This command assumes that we’re building the app with Vue, which includes a reference to cue-cli-service. The command will need to be changed to reflect the framework used on the project.

If we are trying to check the coverage separately, we need to add another line to the scripts:

"scripts": {
  "test:unit": "nyc vue-cli-service test:unit",
  "coverage": "nyc npm run test:unit"
},

Now we can check the coverage by with a terminal command:

npm run coverage

Next, we’ll install Coveralls which is responsible for reporting and showing the coverage:

npm i coveralls

Now we need to add Coveralls as another script in package.json. This script helps us save our test coverage report to Coveralls.

"scripts": {
  "test:unit": "nyc vue-cli-service test:unit",
  "coverage": "nyc npm run test:unit",
  "coveralls": "nyc report --reporter=text-lcov | coveralls"
},

Let’s go to our Heroku dashboard and register our app there. Heroku is what we’ll use to host it.

We’ll use CircleCI to automate our CI/CD process. Proceed to the CircleCI dashboard to set up our project.

We can navigate to our projects through the Projects tab in the CircleCI sidebar, where we should see the list of our projects in our GitHub organization. Click the “Set Up Project” button. That takes us to a new page where we’re asked if we want to use an existing config. We do indeed have our own configuration, so let’s select the “Use an existing config” option.

After that, we’re taken to the selected project’s pipeline. Great! We are done connecting our repository to CircleCI. Now, let’s add our environment variables to our CircleCI project.

To add variables, we need to navigate into the project settings.

The project settings has an Environment Variables tab in the sidebar. This is where we want to store our variables.

Variables needed for this tutorial are:

  • The Heroku app name: HEROKU_APP_NAME
  • Our Heroku API key: HEROKU_API_KEY
  • The Coveralls repository token: COVERALLS_REPO_TOKEN

The Heroku API key can be found in the account section of the Heroku dashboard.

The Coveralls repository token is on the repository’s Coveralls account. First, we need to add the repo to Coveralls, which we do by selecting the GitHub repository from the list of available repositories.

Now that we’ve added the repo to Coveralls. we can get the repository token by clicking on the repo.

Integrating CircleCI

We’ve already connected Circle CI to our GitHub repository. That means CircleCI will be informed whenever a change or action occurs in the GitHub repository. What we want to do now is run through the steps to inform CircleCI of the operations we want it to run after it detects change to the repo.

In the root folder of our project locally, let’s create a folder named .circleci and, in it, a file called config.yml. This is where all of CircleCI’s operations will be.

Here’s the code that goes in that file:

version: 2.1
orbs:
  node: circleci/[email protected] // node orb 
  heroku: circleci/[email protected] // heroku orb
  coveralls: coveralls/[email protected] // coveralls orb
workflows:
  heroku_deploy:
    jobs:
      - build
      - heroku/deploy-via-git: # Use the pre-configured job
        requires:
          - build
        filters:
          branches:
            only: master
jobs:
  build:
    docker:
      - image: circleci/node:10.16.0
    steps:
      - checkout
      - restore_cache:
        key: dependency-cache-{{ checksum "package.json" }}
      - run:
        name: install-npm-dependencies
        command: npm install
      - save_cache:
        key: dependency-cache-{{ checksum "package.json" }}
        paths:
          - ./node_modules
      - run: # run tests
        name: test
        command: npm run test:unit
      - run: # run code coverage report
        name: code-coverage
        command: npm run coveralls
      - run: # run build
        name: Build
        command: npm run build
      # - coveralls/upload

That’s a big chunk of code. Let’s break it down so we know what it’s doing.

Orbs

orbs:
  node: circleci/[email protected] // node orb 
  heroku: circleci/[email protected] // heroku orb
  coveralls: coveralls/[email protected] // coveralls orb

Orbs are open source packages used to simplify the integration of software and packages across projects. In our code, we indicate orbs we are using for the CI/CD process. We referenced the node orb because we are making use of JavaScript. We reference heroku because we are using a Heroku workflow for automated deployment. And, finally, we reference the coveralls orb because we plan to send the coverage results to Coveralls.

The Heroku and Coverall orbs are external orbs. So, if we run the app through testing now, those will trigger an error. To get rid of the error, we need to navigate to the “Organization Settings” page in the CircleCI account.

Then, let’s navigate to the Security tab and allow uncertified orbs:

Workflows

workflows:
  heroku_deploy:
    jobs:
      - build
      - heroku/deploy-via-git: # Use the pre-configured job  
          requires:
            - build
          filters:
            branches:
              only: master

A workflow is used to define a collection of jobs and run them in order. This section of the code is responsible for the automated hosting. It tells CircleCI to build the project, then deploy. requires signifies that the heroku/deploy-via-git job requires the build to be complete — that means it will wait for the build to complete before deployment.

Jobs

jobs:
  build:
    docker:
      - image: circleci/node:10.16.0
    steps:
      - checkout
      - restore_cache:
          key: dependency-cache-{{ checksum "package.json" }}
      - run:
          name: install-npm-dependencies
          command: npm install
      - save_cache:
          key: dependency-cache-{{ checksum "package.json" }}
          paths:
            - ./node_modules

A job is a collection of steps. In this section of the code, we restore the dependencies that were installed during the previous builds through the restore_cache job.

After that, we install the uncached dependencies, then save them so they don’t need to be re-installed during the next build.

Then we’re telling CircleCI to run the tests we wrote for the project and check the test coverage of the project. Note that caching dependencies make subsequent builds faster because we store the dependencies hence removing the need to install those dependencies during the next build.

Uploading our code coverage to coveralls

- run: # run tests
    name: test
    command: npm run test:unit
  - run: # run code coverage report
    name: code-coverage
    command: npm run coveralls
# - coveralls/upload

This is where the Coveralls magic happens because it’s where we are actually running our unit tests. Remember when we added the nyc command to the test:unit script in our package.json file? Thanks to that, unit tests now provide code coverage.

Unit tests also provide code coverage so we’ll those included in the coverage report. That’s why we’re calling that command here.

And last, the code runs the Coveralls script we added in package.json. This script sends our coverage report to coveralls.

You may have noticed that the coveralls/upload line is commented out. This was meant to be the finishing character of the process, but at the end became more of a blocker or a bug in developer terms. I commented it out as it may be another developer’s trump card.

Putting everything together

Behold our app, complete with continuous integration and deployment!

A screenshot of CircleCI showing 9 successful development, test, and build steps.
A successful build

Continuous integration and deployment helps in so many cases. A common example would be when the software is in a testing stage. In this stage, there are lots of commits happening for lots of corrections. The last thing I would want to do as a developer would be to manually run tests and manually deploy my application after every minor change made. Ughhh. I hate repetition!

I don’t know about you, but CI and CD are things I’ve been aware of for some time, but I always found ways to push them aside because they either sounded too hard or time-consuming. But now that you’ve seen how relatively little setup there is and the benefits that comes with them, hopefully you feel encouraged and ready to give them a shot on a project of your own.