使用PVS-Studio在Travis CI,Buddy和AppVeyor中分析提交和请求请求

图片11

从7.04版开始,针对Linux和macOS上的C和C ++语言的PVS-Studio分析仪提供了检查指定文件列表的测试功能。 使用新模式,您可以将分析器配置为检查提交和请求。 本文介绍如何在Travis CI,Buddy和AppVeyor等流行的CI(连续集成)系统中设置对来自GitHub项目的某些修改文件的检查。

检查文件列表的方式


PVS-Studio是一种工具,旨在检测用C,C ++,C#和Java编写的程序源代码中的错误和潜在漏洞。 在Windows,Linux和macOS上的64位系统上运行。

在适用于Linux和macOS的PVS-Studio 7.04版本中,现在提供了检查文件列表的模式。 它适用于项目,其构建系统允许生成compile_commands.json文件。 为了使分析器获得有关某些文件编译的信息,这是必需的。 如果您的构建系统不支持compile_commands.json文件的生成,则可以尝试使用Bear实用程序生成此类文件。

这种检查文件列表的方式也可以与strace生成的编译器运行跟踪日志一起使用(pvs-studio-analyzer跟踪)。 为此,首先需要完成完整的项目构建并进行跟踪,以便分析器收集有关所有已检查文件的编译参数的完整信息。

但是,此选项有一个很大的缺点-您必须在每次运行时都对整个项目执行完整的构建跟踪,这与快速提交检查的想法背道而驰。 或者,如果您缓存跟踪结果本身,则在跟踪源文件的结构依赖关系发生更改(例如,在一个源文件中添加了新的#include)后,后续的分析器运行可能会不完整。

因此,我们不建议您使用通过跟踪日志检查文件列表的模式来检查提交或请求请求。 如果在检查提交时可以执行增量构建,请考虑使用增量分析模式。

用于分析的源文件列表保存在文本文件中,并使用-S参数传递给分析器:

pvs-studio-analyzer analyze ... -f build/compile_commands.json -S check-list.txt 

该文件包含文件的相对路径和绝对路径,其中每个新文件都位于新行中。 您可以同时指定分析的文件名和不同的文本。 至于文本,分析器将注意到它不是文件名,并且将忽略该行。 如果手动指定文件,则对注释很有用。 但是,通常会在CI分析期间生成文件列表,例如,它可以是来自提交或拉取请求的文件。

现在,使用这种模式,您可以在新代码进入主开发分支之前快速对其进行检查。 在plog-converter实用程序中添加了--indicate-warnings标志,以便检查系统响应分析器警告的存在。

 plog-converter ... --indicate-warnings ... -o /path/to/report.tasks ... 

使用此标志,如果分析仪报告中有警告,则转换器将返回非零代码。 您可以阻止预提交挂钩,提交或拉取请求,并显示生成的分析器报告,共享或通过邮件发送。

注意事项 在文件列表的第一次分析期间,将检查整个项目,因为分析器必须从头文件生成与项目源文件相关的文件。 这是C和C ++文件分析的独特之处。 将来,此依赖文件可以被缓存,并且将由分析器自动更新。 与增量分析模式相比,使用检查文件列表的模式检查提交的优势在于您只需要缓存此文件,而不是对象文件。

拉取请求分析的一般原则


整个项目分析需要花费大量时间,因此仅检查其中一部分是有意义的。 问题是您需要将新文件与其余项目文件分开。

让我们看一下两分支提交树的示例:

图片5


想象一下, A1提交包含大量已经检查过的代码。 以前,我们从A1提交创建了一个分支,并更改了一些文件。

当然,您已经注意到,在A1之后发生了另外两个提交,并且另外两个分支合并了,因为我们没有在master中提交。 修补程序准备就绪的时刻到了。 这就是为什么我们收到合并B3A3的请求请求的原因。

我们可以检查它们合并的结果,但是它太长且不合理,因为仅修改了一些文件。 因此,仅分析已更改的内容更为有效。

为此,我们将收到分支之间的差异,该差异位于分支的HEAD中,我们希望将其合并到master中:

 git diff --name-only HEAD origin/$MERGE_BASE > .pvs-pr.list 

稍后我们将详细考虑$ MERGE_BASE 。 事实是,并非每个CI服务都在合并基础上提供所需的信息,因此每次我们都必须考虑获取此数据的新方法。 下面将针对所描述的每个Web服务对此进行详细考虑。

因此,我们得到了分支之间的区别,这是修改后的文件名的列表。 现在,我们需要将此.pvs-pr.list文件提供给分析器。 我们之前已将输出重定向到它。

 pvs-studio-analyzer analyze -j8 \ -o PVS-Studio.log \ -S .pvs-pr.list 

分析之后,我们需要将日志文件(PVS-Studio.log)转换为用户友好的格式:

 plog-converter -t errorfile PVS-Studio.log --cerr -w 

此命令将输出stderr中的错误列表(标准错误流)。

问题在于,我们不仅需要输出错误,而且还需要报告有关问题的构建和测试服务。 为此,在转换器中添加了-W (-- indicate-warnings )标志。 如果至少有一个分析器警告,则plog-converter实用程序的返回码将更改为2,这反过来将向CI服务报告有关请求请求文件中潜在错误的信息。

特拉维斯


.travis.yml文件的形式进行配置。 为了方便起见,我建议您在单独的bash脚本中隔离所有与PVS-Studio相关的命令,这些脚本具有将从文件.travis.ymlbash script_name.sh function_name )中调用的函数

通过扩展脚本,您将获得更多功能。 在安装部分中编写以下内容:

 install: - bash .travis.sh travis_install 

如果您有一些说明,则可以在除去连字符的情况下在脚本中移动它们。

打开.travis.sh文件,并在travis_install()函数中添加分析器安装:

 travis_install() { wget -q -O - https://files.viva64.com/etc/pubkey.txt \ | sudo apt-key add - sudo wget -O /etc/apt/sources.list.d/viva64.list \ https://files.viva64.com/etc/viva64.list sudo apt-get update -qq sudo apt-get install -qq pvs-studio } 

现在,让我们在脚本部分中运行分析器:

 script: - bash .travis.sh travis_script 

在bash脚本中:

 travis_script() { pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then git diff --name-only origin/HEAD > .pvs-pr.list pvs-studio-analyzer analyze -j8 \ -o PVS-Studio.log \ -S .pvs-pr.list \ --disableLicenseExpirationCheck else pvs-studio-analyzer analyze -j8 \ -o PVS-Studio.log \ --disableLicenseExpirationCheck fi plog-converter -t errorfile PVS-Studio.log --cerr -w } 

您必须在构建项目后运行此代码,例如,如果您在CMake上构建:

 travis_script() { CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}" cmake $CMAKE_ARGS CMakeLists.txt make -j8 } 

您将获得以下内容:

 travis_script() { CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}" cmake $CMAKE_ARGS CMakeLists.txt make -j8 pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then git diff --name-only origin/HEAD > .pvs-pr.list pvs-studio-analyzer analyze -j8 \ -o PVS-Studio.log \ -S .pvs-pr.list \ --disableLicenseExpirationCheck else pvs-studio-analyzer analyze -j8 \ -o PVS-Studio.log \ --disableLicenseExpirationCheck fi plog-converter -t errorfile PVS-Studio.log --cerr -w } 

您很可能已经注意到环境变量$ TRAVIS_PULL_REQUEST$ TRAVIS_BRANCH 。 Travis CI声明了它们本身:

  • $ TRAVIS_PULL_REQUEST存储拉取请求号,如果是常规分支则为false
  • $ TRAVIS_REPO_SLUG存储项目存储库的名称。

这是此函数的运算算法:

图片13

Travis CI会响应返回码,因此警告的出现将报告服务,以将提交标记为包含错误。

现在,让我们仔细看看以下代码行:
 git diff --name-only origin/HEAD > .pvs-pr.list 

事实是,Travis CI在分析拉取请求时会自动合并分支:

图片8

这就是为什么我们分析A4而不是B3-> A3的原因 。 由于这种特殊性,我们需要评估与A3的差异, A3是从原点开始分支的头。

还有一个重要的细节-从编译的翻译单元(* .c,* .cc,* .cpp等)缓存头文件的依赖项。 在首次运行期间,分析器会以检查文件列表的方式评估这些依赖关系,然后将其存储在.PVS-Studio目录中。 Travis CI允许缓存存储库,因此我们将数据保存在.PVS-Studio /目录中:

 cache: directories: - .PVS-Studio/ 

该代码必须添加到.travis.yml文件中:此目录存储分析后收集的各种数据。 此数据极大地加快了文件列表分析或增量分析的后续运行。 如果您不这样做,分析器将每次分析所有文件。

好友


与Travis CI一样, Buddy使您能够自动从GitHub构建和测试项目。 与Travis CI不同,它是在Web界面中配置的(提供bash支持),因此无需在项目中存储配置文件。

首先,我们需要在管道中添加新的步骤:

图片1

让我们指定用于构建项目的编译器。 请注意在此步骤中安装的Docker容器。 例如,GCC有一个特殊的容器:

图片6

现在,让我们安装PVS-Studio和必要的实用程序:

图片2

将以下行添加到编辑器:

 apt-get update && apt-get -y install wget gnupg jq 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 

让我们转到“运行”选项卡(第一个图标),然后将以下代码添加到适当的编辑器字段中:
 pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY if [ "$BUDDY_EXECUTION_PULL_REQUEST_NO" != '' ]; then PULL_REQUEST_ID="pulls/$BUDDY_EXECUTION_PULL_REQUEST_NO" MERGE_BASE=`wget -qO - \ https://api.github.com/repos/${BUDDY_REPO_SLUG}/${PULL_REQUEST_ID} \ | jq -r ".base.ref"` git diff --name-only HEAD origin/$MERGE_BASE > .pvs-pr.list pvs-studio-analyzer analyze -j8 \ -o PVS-Studio.log \ --disableLicenseExpirationCheck \ -S .pvs-pr.list else pvs-studio-analyzer analyze -j8 \ -o PVS-Studio.log \ --disableLicenseExpirationCheck fi plog-converter -t errorfile PVS-Studio.log --cerr -w 

如果您阅读有关Travs-CI的部分,则此代码是您熟悉的。 但是这里有一个新的步骤:

图片14

事实是,现在我们不分析合并的结果,而是分析经过检查的拉取请求的分支的HEAD:

图片10

因此,我们处于B3提交中,我们需要与A3取得区别:

 PULL_REQUEST_ID="pulls/$BUDDY_EXECUTION_PULL_REQUEST_NO" MERGE_BASE=`wget -qO - \ https://api.github.com/repos/${BUDDY_REPO_SLUG}/${PULL_REQUEST_ID} \ | jq -r ".base.ref"` git diff --name-only HEAD origin/$MERGE_BASE > .pvs-pr.list 

要定义A3,请使用GitHub API:

 https://api.github.com/repos/${USERNAME}/${REPO}/pulls/${PULL_REQUEST_ID} 

我们使用了Buddy为我们提供的以下变量:

  • $ BUDDY_EXECUTION_PULL_REQEUST_NO-拉取请求的编号;
  • $ BUDDY_REPO_SLUG-用户名和存储库的组合(例如,max / test)。

现在,使用下面的按钮保存更改,并启用拉取请求分析:

图片3

与Travis CI不同,我们不必为缓存指定.pvs-studio ,因为Buddy会自动缓存所有文件以供以后运行。 因此,剩下的最后一件事是在Buddy中保存PVS-Studio的登录名和密码。 保存更改后,我们将返回到管道中。 我们需要继续设置变量,并插入PVS-Studio的登录名和密钥:

图片4

此后,将从每个新的拉取请求或提交开始检查。 如果提交包含错误,Buddy会在拉取请求页面上指出该错误。

传送带


AppVeyor设置与Buddy类似,因为所有操作都在Web界面中发生,因此无需在项目存储库中添加* .yml文件。

让我们转到项目查看中的“设置”选项卡:

图片12

向下滚动此页面,并为拉取请求构建启用缓存保存:

图片18


现在,我们进入“环境”选项卡,在这里我们将为构建和所需的环境变量指定图像:

图片19

如果您已经阅读了前面的部分,则已经熟悉了这两个变量-PVS_KEYPVS_USERNAME 。 如果没有,请让我提醒您,检查PVS-Studio分析仪许可证需要它们。 将来,我们将在Bash脚本中再次与他们会面。

在底部的同一页面上,让我们指定缓存文件夹:

图片15

如果不这样做,我们将分析整个项目而不是几个文件,但是将接收指定文件的输出。 因此,输入存储库的正确名称很重要。

现在该检查脚本了。 打开“测试”选项卡,然后选择“脚本”:

图片20

以下代码应插入此表单中:

 sudo apt-get update && sudo apt-get -y install jq wget -q -O - https://files.viva64.com/etc/pubkey.txt \ | sudo apt-key add - sudo wget -O /etc/apt/sources.list.d/viva64.list \ https://files.viva64.com/etc/viva64.list sudo apt-get update && sudo apt-get -y install pvs-studio pvs-studio-analyzer credentials $PVS_USERNAME $PVS_KEY PWD=$(pwd -L) if [ "$APPVEYOR_PULL_REQUEST_NUMBER" != '' ]; then PULL_REQUEST_ID="pulls/$APPVEYOR_PULL_REQUEST_NUMBER" MERGE_BASE=`wget -qO - \ https://api.github.com/repos/${APPVEYOR_REPO_NAME}/${PULL_REQUEST_ID} \ | jq -r ".base.ref"` git diff --name-only HEAD origin/$MERGE_BASE > .pvs-pr.list pvs-studio-analyzer analyze -j8 \ -o PVS-Studio.log \ --disableLicenseExpirationCheck \ --dump-files --dump-log pvs-dump.log \ -S .pvs-pr.list else pvs-studio-analyzer analyze -j8 \ -o PVS-Studio.log \ --disableLicenseExpirationCheck fi plog-converter -t errorfile PVS-Studio.log --cerr -w 

让我们注意代码的以下部分:

 PWD=$(pwd -L) if [ "$APPVEYOR_PULL_REQUEST_NUMBER" != '' ]; then PULL_REQUEST_ID="pulls/$APPVEYOR_PULL_REQUEST_NUMBER" MERGE_BASE=`wget -qO - \ https://api.github.com/repos/${APPVEYOR_REPO_NAME}/${PULL_REQUEST_ID} \ | jq -r ".base.ref"` git diff --name-only HEAD origin/$MERGE_BASE > .pvs-pr.list pvs-studio-analyzer analyze -j8 \ -o PVS-Studio.log \ --disableLicenseExpirationCheck \ --dump-files --dump-log pvs-dump.log \ -S .pvs-pr.list else pvs-studio-analyzer analyze -j8 \ -o PVS-Studio.log \ --disableLicenseExpirationCheck fi 

这是pwd命令的输出值到变量的非常具体的分配,默认情况下必须存储该值。 起初看起来很奇怪,但是让我解释一下所有内容。

在AppVeyor中设置分析器时,我偶然发现了非常奇怪的分析器行为。 一方面,一切正常,但分析并未开始。 花了很多时间才注意到我们位于/ home / appveyor / projects / testcalc /目录中,而分析器确定我们位于/ opt / appveyor / build-agent /中。 那时我意识到$ PWD变量是欺骗性的。 因此,在运行分析之前,我手动更新了它的值。

进一步的操作顺序与以前相同:

图片16

现在看一下这个片段:

 PULL_REQUEST_ID="pulls/$APPVEYOR_PULL_REQUEST_NUMBER" MERGE_BASE=`wget -qO - \ https://api.github.com/repos/${APPVEYOR_REPO_NAME}/${PULL_REQUEST_ID} \ | jq -r ".base.ref"` 

在其中,我们得到与已检查的拉取请求相关的分支之间的差异。 为此,我们需要以下环境变量:

  • $ APPVEYOR_PULL_REQUEST_NUMBER-拉取请求号;
  • $ APPVEYOR_REPO_NAME-用户名和项目存储库。

结论


好吧,我们尚未考虑所有可能的持续集成服务,但是它们都有相似的操作细节。 但是对于缓存,每个服务都重新发明了自己的轮子,因此总是不同的。

在某些情况下(例如在Travis-CI中),它需要几行代码-缓存可以完美地工作。 在其他情况下(例如在AppVeyor中),只需在设置中指定目录。 但是有些服务需要创建特殊的密钥,并试图说服系统使您有机会重写缓存的片段。 因此,如果您想在持续集成服务上配置拉取请求分析(上面没有考虑),首先,请确保您不会在缓存方面遇到问题。

谢谢您的关注。 如果无法解决问题,您可以放心地写信给我们的支持部门 。 我们会给您一个提示和帮助。

Source: https://habr.com/ru/post/zh-CN470982/


All Articles