DEV Community

Cover image for Streamlining Coverage Reports in SonarCloud with an NX Monorepo
Daniel Sogl
Daniel Sogl

Posted on • Originally published at Medium

Streamlining Coverage Reports in SonarCloud with an NX Monorepo

After setting up your nx-workspace project, including your apps and libraries, you also want to use the power of nx-affected commands and the nx cloud. Perhaps you, like me, want also to define multiple code quality gates, including coverage reports for new and existing code in your repository. To handle that goal, I’m using SonarCube or SonarCloud for all of my professional projects.

Sonar Scan Example

SonarCloud makes it possible to auto-scan your repositories without setting up a CI pipeline. All you have to do is create a free SonarCloud account, connect your GitHub project, and commit and push the sonar config file. But with this approach, it is impossible to generate a coverage report for your repository. To handle this issue, Sonar provides a GitHub action to upload your generated coverage report as part of your defined quality gates.

nx project screenshot

Necessary Steps to Enhance Code Quality Reporting

To solve this problem, the following steps are necessary:

  1. run the test command on all nx-projects without using affected
  2. add a custom script to merge coverage reports into one file
  3. create a sonar-project.properties file and define the coverage report path
  4. upload the coverage report to SonarCloud using the official Sonar GitHub action.

Step 1: Running Tests Across All Projects

The first step is kind of simple. nx provides the run-many command to run a specified target on all nx-projects. In the past, I also had the best experience with Sonar using lcov reports.

npx nx run-many --all --target=test --parallel=3 --ci --code-coverage --coverageReporters=lcov
Enter fullscreen mode Exit fullscreen mode

Step 2: Merging Coverage Reports

The second step is also not that complicated. I wrote a simple JavaScript function to loop through the coverage folder and merge the project lcov files into one single file. The script is placed inside the tools/scripts folder.

const glob = require('glob');
const fs = require('fs');
const path = require('path');

const getLcovFiles = function (src) {
  return new Promise((resolve) => {
    glob(`${src}/**/lcov.info`, (error, result) => {
      if (error) resolve([]);
      resolve(result);
    });
  });
};

(async function () {
  const files = await getLcovFiles('coverage');
  const mergedReport = files.reduce((mergedReport, currFile) => (mergedReport += fs.readFileSync(currFile)), '');
  await fs.writeFile(path.resolve('./coverage/lcov.info'), mergedReport, (err) => {
    if (err) throw err;
    console.log('The file has been saved!');
  });
})();
Enter fullscreen mode Exit fullscreen mode

Step 3 & 4: Configuring and Uploading the Coverage Report

I added the merge command to my nx-cloud GitHubAction file. This will add the executed command to the nx-cloud report posted by nx in all pull requests.

report example

I also added the coverage report as an artifact to use it later with the Sonar GitHub action. You can take a look at my configuration here.

name: CI
on:
push:
branches:
- main
pull_request:
types:
- opened
- reopened
- synchronize
- ready_for_review
concurrency:
group: ${{ github.workflow }}-${{ github.event.number || github.ref }}
cancel-in-progress: true
jobs:
main:
name: Nx Cloud - Main Job
uses: nrwl/ci/.github/workflows/nx-cloud-main.yml@v0.10
with:
number-of-agents: 3
init-commands: |
npx nx-cloud start-ci-run --stop-agents-after="build" --agent-count=3
parallel-commands: |
npx nx-cloud record -- npx nx workspace-lint
npx nx-cloud record -- npx nx format:check
parallel-commands-on-agents: |
npx nx affected --target=lint --parallel=3
npx nx run-many --all --target=test --parallel=3 --ci --code-coverage --coverageReporters=lcov
npx nx affected --target=build --parallel=3
final-commands: |
npx nx-cloud record node ./tools/scripts/coverageMerger.js
artifacts-path: |
coverage/lcov.info
artifacts-name: coverage-report
sonar:
name: SonarCloud
needs: main
runs-on: ubuntu-latest
steps:
- name: download source
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: download coverage report
uses: actions/download-artifact@v3
with:
name: coverage-report
path: coverage
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
agents:
name: Nx Cloud - Agents
uses: nrwl/ci/.github/workflows/nx-cloud-agents.yml@v0.10
with:
number-of-agents: 3
view raw ci.yml hosted with ❤ by GitHub

Based on your Sonar configuration, your config file will differ from mine. The demo project’s related config file looks like this.

sonar.projectKey=danielsogl_nx-sonar-example
sonar.organization=danielsogl
sonar.host.url=https://sonarcloud.io

# This is the name and version displayed in the SonarCloud UI.
#sonar.projectName=nx-sonar-example
#sonar.projectVersion=1.0

# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
#sonar.sources=.

# Encoding of the source code. Default is default system encoding
sonar.sourceEncoding=UTF-8

sonar.test.inclusions=**/*.spec.ts
sonar.typescript.lcov.reportPaths=coverage/lcov.info
Enter fullscreen mode Exit fullscreen mode

After all CI steps are executed, the Sonar action will download the previously created coverage report artifact and upload it.

When I now create a new pull request, Sonar will not only check my code quality gates. Sonar now also checks my new checked-in code and its coverage.

scan result

I created a public demo repository where you can take a closer look at my solution: https://github.com/danielsogl/nx-sonar-example

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (0)