Enhancing Code Quality with PlatformIO, SonarCloud and GitHub Actions

Guest post

Leveraging SonarCloud in CI/CD environment for Effective Code Quality Improvement and Quality Assurance

Pascal Roobrouck
Pascal Roobrouck
Freelance Hardware & Firmware Engineer
Share:

This article continues where my previous blogpost, Setting up PlatformIO for CI/CD with Testing, Code Coverage and Versioning, left off. We will now add SonarCloud static code analysis to a PlatformIO project with a CI workflow.

Table of Contents

Introduction

Static code analysis involves testing your source code against a number of well-known and common mistakes by analyzing the source code without running it. PlatformIO already has this feature under Project Tasks > Advanced > Check which will, for example, launch CppCheck. But like many other tools, things are moving to the cloud, and a popular cloud-based provider of static code analysis is SonarCloud.io.

This company brings static code analysis to another level with several useful extra features:

  • Supporting many languages, so if your project has mixed C and C++, SonarCloud can check both.
  • Selecting which rules you want in your check: start from their defaults, then tweak to disable some checks or add extra ones.
  • Integrating code analysis and test coverage into one report.
  • Integrating into your CI workflow, so with every push of new code, you can have it automatically checked. The tagline here is “Clean as You Code” meaning that SonarCloud will show you not only the total project quality metrics but also the New Code metrics. This means that even on a legacy codebase, you can ensure that all changes meet the quality rules, resulting in continuous code quality improvement.

So let’s give it a try and see how well it works together with PlatformIO

SonarLint

A simple first step is to install the VSCode extension called SonarLint. This extension will provide the static code analysis features locally in your IDE, where SonarLint will add orange squiggles under suspicious code to warn you and recommend better options.

Note in the example below the squiggles on the line 276 and an explanation when you hover over it. All the yellow lines on the minimap (on the right) show potential problems with your code. While CppCheck would run on demand, SonarLint is running in the background, continuously checking your code as you write it.

SonarLint can pick up settings from a file called sonar-project.properties in the root of your project. Once you are using SonarCloud, it will also be possible to sync your settings stored in the cloud to SonarLint running locally in the IDE. This works for any project, including PlatformIO projects.

SonarCloud

A simple next step is to create an account on SonarCloud.io, then give that account access to some of your GitHub repositories, enabling cloud-based static code analysis. The default setting is Automatic Analysis which means SonarCloud will notice when you do a push or pull request on your main branch, then fetch the code from your repository and run its analysis. Again, this works for any project, including PlatformIO.

This mode has a few shortcomings:

  • The automatic mode does not handle code coverage.
  • In the automatic mode, SonarCloud polls the repository, whereas I prefer to “push” a trigger from a GitHub Actions workflow to SonarCloud. This way, the workflow remains in control of all actions and their sequences.

SonarCloud with Github Actions

Go to your SonarCloud project homepage, select “Administration | Analysis method” and disable the slider for Automatic Analysis. Then open your workflow file (.github/workflows/testbuildrelease.yml in the example) and add the following sections:

- name: install gcovr 5.0
  run: pip install gcovr==5.0

This step will install gcovr, which is needed to collect and transform the test coverage results into a format that SonarCloud understands. The following lines will install the SonarCloud agent:

 - name: Install sonar-scanner and build-wrapper
   uses: sonarsource/sonarcloud-github-c-cpp@v3

After your workflow is ready to do a build, add the following section:

- name: Generate Compilation DataBase
  run: pio run -t compiledb

This command will generate a special file called compile_commands.json, which is used as input for SonarCloud. This file describes which source files are being used for the build, and thus which files matter for calculating its resulting score.

After the unit testing step, add this section:

- name: Collect coverage into XML report
  run: gcovr --sonarqube > coverage.xml

This step will run gcovr and transform the coverage files into a proper format for SonarCloud.

After your application is built, add this section:

  - name: Run sonar-scanner
    env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
    run: |
     sonar-scanner \
        --define sonar.projectKey=Strooom_MuMo-v2-Node-SW \
        --define sonar.organization=strooom-1 \
        --define sonar.coverageReportPaths=coverage.xml

Of course you need to replace the projectKey and organization parameters with your own actual values. You can get them on your SonarCloud project homepage, just open Information to see the right keys.

Now, when the workflow is set up, make a commit and push it to the repository. This will trigger the workflow to run, allowing you to see the uploading of unit test coverage results as well as the source code being scanned.

On SonarCloud.io you can now see the results. The (short) commit hash makes it easy to link the analysis with the actual commit. Click on it to get a lot more detail:

Finally, when we merge our develop branch into main, the workflow will kick in again, but as SonarCloud detects the trigger is a Pull Request, it hooks itself into the checks before you merge. This is a great quality check before merging new code into your production main branch.

Will it make my code better?

Does SonarCloud help me to write better code? After my first analysis, I was facing almost 2000 issues in a codebase of 20K lines, so this was a bit disappointing.

It turned out that there were quite a bit of false positives:

  • Legitimate contents being flagged as outcommented code. For example, font files where the comments explained which character the bytes represents:
  • There were also a few rules to which I don’t agree because in an embedded environment you want to be careful with dynamic memory allocation. So much of the standard-library stuff needs to be used with care, so I deactivated these rules.

Still, I admit that SonarCloud did find quite a few areas for improvement:

  • Function parameters or class member functions that could be made “const”
  • Implicit casts with loss of precision: I checked all of them and, where appropriate, replaced them with explicit casts
  • Code duplication
  • Code with excessive cyclomatic complexity
  • SonarCloud flags all your comments with “TODO” as a problem, which helps me remember to address them
  • SonarCloud flags outcommented code as a problem, which pushed me to fix it and eliminate hanging technical debt

Finally, I also found a lot of problems in foreign code that I incorporated, including autogenerated code from ST’s CubeMX tooling. I don’t want to touch it, mainly because it will be overwritten if I ever regenerate it. Still, it provided me with valuable insights into the vulnerabilities of ST’s code, which helps me handle them better.

Then I included a QR-Code library from someone else, and it also had many warnings. In this case, it was my intention to eventually refactor it into something more robust and modern. Here, of course, all the advice from SonarCloud is very useful, as it points me to the parts needing refactoring.

As a whole, I found it a very positive experience and I intend to use SonarCloud on all of my future projects.

Unit Testing First

You will make many small code changes while cleaning and of course this can only be done with peace of mind if you have sufficient unit testing in place first, so get your unit testing working first, then take the next step with SonarCloud.

Pay it Forward

All this amazing functionality is free for open oource projects, and I don’t take that for granted, so thank you PlatformIO, Github and SonarCloud.

Stay in touch with us

Stay tuned to this blog or follow us on LinkedIn and Twitter @PlatformIO_Org to keep up to date with the latest news, articles and tips!

Pascal Roobrouck
Pascal Roobrouck
Share:

Have questions?

Join the discussion on our forum