Analysis of merge requests in GitLab using PVS-Studio for C #

image1.png

Love GitLab and dislike bugs? Want to improve the quality of your source code? Then you’ve come to the right place. Today we will tell you how to set up the PVS-Studio C # analyzer to check merge requests. All unicorn mood and pleasant reading.

PVS-Studio Is a tool for identifying errors and potential vulnerabilities in the source code of programs written in C, C ++, C # and Java. Works in 64-bit systems on Windows, Linux and macOS. Can analyze code for 32-bit, 64-bit and embedded ARM platforms.

By the way, we have released PVS-Studio 7.08, in which we have done a lot interesting… For example:

  • C # analyzer for Linux and macOS;
  • plugin for Rider;
  • new mode for checking the list of files.

File list check mode

Previously, in order to check certain files, it was necessary to pass an .xml with a list of files to the parser. But since this is not very convenient, we added the ability to transfer .txt, which makes life very easy.

In order to check certain files, you must specify the flag –sourceFiles (-f) and transfer .txt with a list of files. It looks like this:

pvs-studio-dotnet -t path/to/solution.sln -f fileList.txt -o project.json

If you are interested in configuring check for commits or pull requests, you can also do it using this mode. The difference will be in getting a list of files to analyze and will depend on which systems you are using.

Merge request verification principle

The main point of the check is to ensure that the problems detected by the analyzer do not fall into master branch. We also do not want to analyze the entire project every time. Moreover, when merging branches, we have a list of changed files. Therefore, I suggest adding a check for the merge request.

This is what a merge request looks like before implementing a static analyzer:

image2.png

That is, all the errors that were in the branch changeswill go to the master branch. Since we would not want it, we add analysis, and now the diagram looks like this:

image3.png

We analyze changes2 and, if there are no errors, we accept the merge request, otherwise we reject it.

By the way, if you are interested in analysis of commits and pull requests for C / C ++, then you can read about it here

Gitlab

Gitlab Is an open source web-based DevOps lifecycle tool that provides a code repository management system for Git with its own wiki, bug tracking system, CI / CD pipeline and other features.

Before you start implementing the analysis of merge requests, you need to register and upload your project. If you do not know how to do this, then I suggest article my colleague.

Note… The environment setup method described below is one of the possible. The goal is to show the steps for setting up the environment required for analysis and launching the analyzer. Perhaps, in your case, it would be more optimal to separate the stages of preparing the environment (adding repositories, installing the analyzer) and analysis: for example, preparing Docker images with the necessary environment and using them, or some other method.

To better understand what is going to happen now, I suggest taking a look at the following diagram:

image4.png

The analyzer requires .NET Core SDK 3 to work, so before installing the analyzer, you need to add the Microsoft repositories, from which the dependencies necessary for the analyzer will be installed. Adding Microsoft repositories for various Linux distributions described in the relevant document

To install PVS-Studio through the package manager, you will also need to add PVS-Studio repositories. Adding repositories for different distributions is described in more detail in relevant section of the documentation

The analyzer requires a license key to operate. You can get a trial license at analyzer download page

Note… Please note that the described mode of operation (analysis of merge requests) requires an Enterprise license. Therefore, if you want to try this mode of operation, do not forget to indicate in the “Message” field that you need the Enterprise license.

If a merge request occurs, then we need to analyze only the list of changed files, otherwise we analyze all files. After the analysis, we need to convert the logs into the format we need.

Now, having before your eyes the algorithm of work, you can proceed to writing a script. To do this, you need to modify the file .gitlab-ci.yml or, if not, create. To create it, you need to click on the name of your project -> Set up CI / CD

image5.png

Now we are ready to write the script. Let’s first write the code that will install the analyzer and enter the license:

before_script:
  - apt-get update && apt-get -y install wget gnupg 

  - apt-get -y install git
  - wget https://packages.microsoft.com/config/debian/10/
packages-microsoft-prod.deb -O packages-microsoft-prod.deb
  - dpkg -i packages-microsoft-prod.deb
  - apt-get update
  - apt-get install apt-transport-https
  - apt-get update
  
  - wget -q -O - https://files.viva64.com/etc/pubkey.txt | apt-key add -
  - wget -O /etc/apt/sources.list.d/viva64.list
https://files.viva64.com/etc/viva64.list
  - apt-get update
  - apt-get -y install pvs-studio-dotnet

  - pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY
  - dotnet restore "$CI_PROJECT_DIR"/Test/Test.sln

Since installation and activation must occur before all other scripts, we use a special label before_script… I will explain this fragment a little.

Preparing to install the analyzer:

  - wget https://packages.microsoft.com/config/debian/10/
packages-microsoft-prod.deb -O packages-microsoft-prod.deb
  - dpkg -i packages-microsoft-prod.deb
  - apt-get update
  - apt-get install apt-transport-https
  - apt-get update

Adding PVS-Studio repositories and analyzer:

  - wget -q -O - https://files.viva64.com/etc/pubkey.txt | apt-key add -
  - wget -O /etc/apt/sources.list.d/viva64.list
https://files.viva64.com/etc/viva64.list
  - apt-get update
  - apt-get -y install pvs-studio-dotnet

License activation:

  - pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY

$ PVS_NAME – Username.

$ PVS_KEY – product key.

Recovering project dependencies where $ CI_PROJECT_DIR – full path to the project directory:

  - dotnet restore "$CI_PROJECT_DIR"/Path/To/Solution.sln

For correct analysis, the project must build successfully, and its dependencies must be restored (for example, the necessary NuGet packages must be loaded).

You can set environment variables containing license information by clicking on Setting, and after – on CI / CD

image6.png

In the window that opens, we find the item Variables, on the right we press the button Expand and add variables. The result should look like this:

image7.png

Now you can proceed to the analysis. First, let’s add a script for full analysis. Into the flag -t pass the path to the solution into the flag -o write the path to the file where the analysis results will be written. We are also interested in the return code. In this case, we are interested in the operation terminated when the return code contains information that warnings were issued during the analysis. This is what this snippet looks like:

job:
  script:
  - exit_code=0
  - pvs-studio-dotnet -t "$CI_PROJECT_DIR"/Test/Test.sln -o 
PVS-Studio.json || exit_code=$?
  - exit_code=$((($exit_code & 8)/8))
  - if [[ $exit_code == 1 ]]; then exit 1; else exit 0; fi

Return codes work like a bit mask. For example, if warnings were issued as a result of the analysis, then the return code will be 8. If the license expires within a month, the return code will be 4. If errors were found during the analysis, and the license expires within a month, the code return both values ​​will be written: add the numbers together and get the final return code – 8 + 4 = 12. Thus, by checking the corresponding bits, information about various states can be obtained during analysis. The return codes are described in more detail in the “pvs-studio-dotnet return codes (Linux / macOS)” section of the document.Checking Visual Studio / MSBuild / .NET Core projects from the command line using PVS-Studio“.

In this case, we are interested in all return codes, where 8 appears.

  - exit_code=$((($exit_code & 8)/8))

We’ll get 1 when the return code contains the bit of the number we’re interested in, otherwise we’ll get 0.

It’s time to add parsing to the merge request. Before doing this, let’s prepare a place for the script. We only need it to execute when a merge request occurs. It looks like this:

merge:
  script:
  only:
  - merge_requests

Let’s move on to the script itself. I ran into the fact that the virtual machine knows nothing about origin / master… Therefore, we help her a little:

  - git fetch origin

Now we get the difference between the branches and save the result in txt file:

  - git diff --name-only origin/master $CI_COMMIT_SHA > pvs-fl.txt

Where $ CI_COMMIT_SHA Is the hash of the last commit.

Next, we run the analysis of the list of files using the flag -f… We transfer the received earlier .txt file to it. Well, by analogy with the full analysis, we look at the return codes:

  - exit_code=0
  - pvs-studio-dotnet -t "$CI_PROJECT_DIR"/Test/Test.sln -f 
pvs-fl.txt -o PVS-Studio.json || exit_code=$?
  - exit_code=$((($exit_code & 8)/8))
  - if [[ $exit_code == 1 ]]; then exit 1; else exit 0; fi

The complete script for checking the merge request will look like this:

merge:
  script:
  - git fetch origin
  - git diff --name-only origin/master $CI_COMMIT_SHA > pvs-fl.txt
  - exit_code=0
  - pvs-studio-dotnet -t "$CI_PROJECT_DIR"/Test/Test.sln -f 
pvs-fl.txt -o PVS-Studio.json || exit_code=$?
  - exit_code=$((($exit_code & 8)/8))
  - if [[ $exit_code == 1 ]]; then exit 1; else exit 0; fi
  only:
  - merge_requests

It remains only to add the conversion of the log after all the scripts have run. Using the label after_script and utility plog-converter:

after_script:
  - plog-converter -t html -o eLog ./PVS-Studio.json

Utility plog-converter Is an open source project that is used to transform the analyzer error report into various forms, for example, HTML. A more detailed description of the utility is given in the subsection “Plog Converter Utility” the relevant section of the documentation

By the way, if you want to conveniently work with a .json report locally from the IDE, then I suggest our plugin for IDE Rider. Its use is described in more detail in relevant document

For convenience, here .gitlab-ci.yml entirely:

image: debian

before_script:
  - apt-get update && apt-get -y install wget gnupg 

  - apt-get -y install git
  - wget https://packages.microsoft.com/config/debian/10/
packages-microsoft-prod.deb -O packages-microsoft-prod.deb
  - dpkg -i packages-microsoft-prod.deb
  - apt-get update
  - apt-get install apt-transport-https
  - apt-get update
  
  - wget -q -O - https://files.viva64.com/etc/pubkey.txt | apt-key add -
  - wget -O /etc/apt/sources.list.d/viva64.list
https://files.viva64.com/etc/viva64.list
  - apt-get update
  - apt-get -y install pvs-studio-dotnet

  - pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY
  - dotnet restore "$CI_PROJECT_DIR"/Test/Test.sln

merge:
  script:
  - git fetch origin
  - git diff --name-only origin/master $CI_COMMIT_SHA > pvs-fl.txt
  - exit_code=0
  - pvs-studio-dotnet -t "$CI_PROJECT_DIR"/Test/Test.sln -f 
pvs-fl.txt -o PVS-Studio.json || exit_code=$?
  - exit_code=$((($exit_code & 8)/8))
  - if [[ $exit_code == 1 ]]; then exit 1; else exit 0; fi
  only:
  - merge_requests

job:
  script:
  - exit_code=0
  - pvs-studio-dotnet -t "$CI_PROJECT_DIR"/Test/Test.sln -o 
PVS-Studio.json || exit_code=$?
  - exit_code=$((($exit_code & 8)/8))
  - if [[ $exit_code == 1 ]]; then exit 1; else exit 0; fi
  
after_script:
  - plog-converter -t html -o eLog ./PVS-Studio.json

Once everything has been added to the file, click on Commit changes… In order to see that everything is correct, we go to CI / CD -> Pipelines -> Running… A virtual machine window will open, at the end of which should be the following:

image8.png

Saw Job succeeded – success, everything is fine. Now you can test what you have done.

Examples of work

For an example of work, let’s create a simple project (in master) in which there will be several files. After that, in another branch, we will change only one file and try to make a merge request.

Consider two cases: when the modified file contains an error and when not. First, an example with an error.

Let’s say there is a file in the master branch Program.cs, which contains no errors, and in another branch, the developer added erroneous code and wants to make a merge request. What kind of mistake he made is not so important, the main thing is that it exists. For example, I forgot the operator throw (Yes, so wrong):

void MyAwesomeMethod(String name)
{
  if (name == null)
    new ArgumentNullException(....);
  // do something
  ....
}

Let’s look at the result of analyzing an example with an error. Also, to ensure that only one file was parsed, I added the flag -r to the pvs-studio-dotnet startup line:

image9.png

We see that the analyzer found an error and did not allow the merging of branches.

Checking the example without error. We fix the code:

void MyAwesomeMethod(String name)
{
  if (name == null)
    throw new ArgumentNullException(....);
  // do something
  ....
}

Results of the merge request analysis:

image10.png

As we can see, no errors were found, and the execution of the task was successful, which we wanted to check.

Conclusion

Filtering out bad code before merging branches is very convenient and enjoyable. Therefore, if you are using CI / CD, try building in a static analyzer for testing. Moreover, this is done quite simply.

Thanks for your attention.

If you want to share this article with an English-speaking audience, please use the translation link: Nikolay Mironov. Analysis of merge requests in GitLab using PVS-Studio for C #.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *