How to upload coverage reports to SonarCloud using an nx monorepo

Daniel Sogl
4 min readDec 3, 2022

--

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.

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.

Sadly, NX will generate a coverage report for each library or app separately, and the Sonar scanner is unable to combine them by itself into one single report. If you run your test with nx affected, nx will only generate coverage reports for affected projects. Unaffected projects will be missing from the generated coverage reports, and Sonar will give you a quality gate error.

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.

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

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!');
});
})();

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.

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.

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

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.

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

Follow me on Medium or Twitter to read more about Angular, Ionic and DevOps.

--

--

Daniel Sogl
Daniel Sogl

Written by Daniel Sogl

Software Engineer specializing in Angular, Ionic, & Capacitor. I build scalable apps, contribute to open source, and share knowledge via talks & articles.

Responses (2)