Static code analysis is a great and easy way to discover bugs, race conditions, code smells or to check whether code matches the coding conventions. I will motivate why it’s useful to use SonarQube for static analysis of Go code and show you how it’s done.
Go already ships with the tool vet which does static code analysis of Go code. To use vet just run
go vet source/directory/*.go. Vet will perform various checks like detecting shadowed variables, nil function comparison and many others (see
go doc cmd/vet for a full list) and list all violations. Vet is great because it performs many basic checks very fast.
Yet if we want to detect style mistakes, duplicated code or even check for security problems go vet is not enough. All these checks can be done by linters like Go Meta Linter or GolangCI-Lint. These are classic linters that perform static code analysis on go code and report their results in a standardized format. These linters can be integrated in editors like VisualStudio Code, Vim or GoLand. So, these linters perform in-depth analysis of Go code and can detect many kinds of violations. Depending on the linter configuration the linting can take a significant time in your build pipeline.
Static code analysis is also useful to calculate key metrics of your source code like lines of code or cyclomatic complexity. Combined with code coverage or unit test count these key metrics can provide a good first impression on the state of your source code. They can be displayed on a dashboard like atlasboard in your project for all to see.
But where do we get these key metrics from? Linters like Go Meta Linter can calculate most of the key metrics like cyclometric complexity. Other key metrics like code coverage of unit test count are calculated from the tests. That way we can calculate the key metrics during the continuous integration pipeline.
Are Linters not enough? Why SonarQube?
What if we want to know how the key metrics of our source code evolved over time? Have we been able to improve code coverage since the last release? How many lines of code do we add each sprint? Which components in our projects are the biggest?
That’s where SonarQube comes into play. SonarQube analyses source code using static code analysis, code coverage and unit tests over time. That way SonarQube is able to answer all the questions above. You see that version 1.0.0 had 1.562 lines of code with a coverage of 85%, whereas now you are at version 2.1.0 with 3.842 lines of code. If you look closer, you see that the package auth has grown the most. In SonarQube you see how your key metrics and your source code have evolved over time and over specific versions like shown below.
Let’s see how we can analyse our Go code using SonarQube.
SonarQube for Go
To analyse Go code with SonarQube you need a running SonarQube. You can run SonarQube locally with
docker run --name sonarqube -p 9000:9000 sonarqube. The Go plugin comes with SonarQube so no need to install it manually. Now you are ready to analyse your source code. For that we use the sonar-scanner tool provided by SonarQube, which needs a Java Runtime Environment. Next we need a configuration file for SonarQube named sonar-project.properties in the root of your project. The configuration file tells SonarQube which sources to analyse, the name and version of the project and where to find the test coverage. See an example sonar-project.properties below:
Before you can analyse your code with SonarQube you need to run the tests and record the code coverage (e.g. with
go test -short -coverprofile=bin/cov.out `go list./..|grep -v vendor/`). SonarQube will use the recorded code coverage.
Now you can run your first SonarQube analysis using sonar-scanner. Your code will be analysed and uploaded to SonarQube including the code coverage.
You find a sample project service_sonar with the full setup for SonarQube. In the sample project you can run the SonarQube analysis with make sonar. If you don’t have the sonar-scanner tool installed on your machine you can also use a dockerized version of the sonar scanner (see sample project). The dockerized version is a great fit for CI environments like GitlabCI.
All settings in the configuration file sonar-project.properties can also be provided using the command line. This makes sense especially for the SonarQube host and login. So in you CI pipeline you can run
The sample project also includes a configuration for Gitlab CI in .gitlab-ci.yml. The Gitlab CI pipeline performs a SonarQube analysis in the stage sonar. It expects the host and login for SonarQube in the environment variables
After running the SonarQube analysis you see the results in SonarQube:
We have learned how to statically analyse Go code using the build in tool vet, linters like Go Meta Lint or the SonarQube plugin for Go. All of these tools are capable of calculating key metrics of your source code.
SonarQube provides two benefits:
- First it can track changes of your key metrics over time and by that provide useful insights on how your code quality is evolving.
- Second SonarQube can analyse code in many different programming languages and so you can calculate overall key metrics.
So you should definitely give SonarQube for Go a try.
See my follow up Go for SonarCloud with Github Actions.