Static Code Analysis and GitLab CI

Few days ago I gave a talk on KDE Akademy about running static code analyzers in the KDE instance of GitLab. There I explained what static code analyzers are good for and why you want to use them. Then I showcased my progress implementing a GitLab CI jobs to automatically run clazy and clang-tidy on merge requests as well as individual git pushes to make it as easy for KDE developers as possible to add static analyzers to their projects.

Based on some feedback from the KDE sysadmins as well as the talk audience I have revamped the pipeline and the helper scripts, so let’s see how the pipeline works now and how you can add it to your project.

How It Works

The pipeline on GitLab CI can have multiple stages. Jobs in the same stage are executed in parallel and artifacts from the first stage are made available to jobs in the next stage. The default stage is the build stage where the project is, well, built to see if the code actually compiles. The entire “build” directory from this stage is than carried over to the static analysis job in the test stage. Thanks to that we no longer have to compile the project twice, once in the build job and once in the static analysis job.

Why do we need to make sure the project is built before applying static analysis? Because the static analyzers need the code to compile, so we need to make sure that all the generated include files (MOC files, UI header files, DBus adaptors etc.) exist - and so far I’ve been unable to find a better way to have those files generated than actually building the project.

There are two types of static analysis job: one for merge requests and one for regular pushes into the master branch and for manually triggered pipelines. The merge-request= job detects which C++ files have been changed in the branch and will only run the static analyzers for those jobs. This saves a lot of CPU time and makes the static analysis jobs to finish much faster than if we had to analyze the entire project every time.

The second job is used when someone just pushes directly into the master branch (the branch trigger can be customized, I’ll get to that later). The job only executes the static analyzers on files that have changed since the last push - again, no need to recheck the entire project over and over again. The only case when the static analyzers are ran for the entire project codebase is when the job is launched manually from the GitLab UI.

The job will run clazy and clang-tidy on each file, collect the output of the tools and convert them to codequality report which is then displayed in the GitLab merge request UI.

How To Enable It:

Create .gitlab-ci.yml file in the root of your git repository. Add the following content to it:


Replace <PRODUCT> with applications, frameworks or extragear based on what “product” your project belongs to. If your project is not part of any of those group, you will need to experiment a bit more, or refer to the documentation on the wiki here [TODO]

Those includes will give you access to all the necessary job templates. The templates themselves do not create any jobs, you still need to define the jobs:

  extends: .static-analysis-linux-merge-request

  extends: .static-analysis-linux-commit

This creates two new jobs that inherit from the job templates. This is all you need to do. Once you commit and push, you should already see a new pipeline appear in GitLab. I recommend that you go and manually trigger a new pipeline to make sure you get an initial run with full check - it will probably throw a lot of warnings that you will want to fix, otherwise your future merge requests might fail on issues that are unrelated to the change itself.


The above is just a quick-start guideline. Go to the community wiki here to read in-depth documentation on how to configure and customize the static analysis jobs.

If you run into any issues with the integration, please let me know via email or try to catch me on IRC.

Happy analyzing! :)