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 have 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 detecting 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 of interesting things . For instance:



  • 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 it is not very convenient, we added the ability to transfer .txt, which greatly simplifies life.



In order to check specific files, you must specify the --sourceFiles ( -f ) flag and pass .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 for analysis and will depend on which systems you are using.



Merge request verification principle



The main point of the check is that problems detected by the analyzer do not end up in the master branch during the merge . Also, we do not want to analyze the whole 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 errors that were in the changes branch will go to the master branch. Since we would not like this, 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 analyzing 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 tracker, CI / CD pipeline, and more.



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 an article by my colleague.



Note... The environment setting method described below is one of the possible. The goal is to show the steps for setting up the environment required for analysis and running 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 the .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 is described in the corresponding 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 the corresponding section of the documentation .



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



Note... Please note that the described operating mode (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 .gitlab-ci.yml file or, if not, create it. 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 before_script tag . 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.



Restoring project dependencies, where $ CI_PROJECT_DIR is the 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 then on CI / CD .



image6.png


In the window that opens, we find the Variables item , on the right, click on the Expand button 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. The flag -t transfer path to solution, a flag -o write location of the file in which are recorded the results of analysis. We are also interested in the return code. In this case, we are interested in terminating the work 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, then the return code will be 4. If errors were found during the analysis, and the license expires within a month, in 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 (Linux / macOS) return codes" 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 the merge request parsing. 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 let's get the difference between the branches and save the result to a 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 -f flag . 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. We use the after_script label and the plog-converter utility :



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


The plog-converter utility is an open source project that is used to convert the analyzer error report into various forms, such as HTML. A more detailed description of the utility is given in the "Plog Converter utility" subsection of the corresponding section of the documentation .



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



For convenience, here's the entire .gitlab-ci.yml :



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, go to CI / CD -> Pipelines -> Running . A virtual machine window will open, at the end of which should be the following:



image8.png


We 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 Program.cs file in the master branch , which does not contain errors, and in another branch, the developer has 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 throw operator (yes, they are 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 -r flag 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. Fixing 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.



Thank you for 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 # .



All Articles