在Linux和macOS上的C和C++语言的PVS-Studio分析器中,从版本7.04开始,出现了一个测试选项来检查指定文件的列表。 使用新模式,您可以配置分析器来检查提交和拉取请求。 本文将告诉您如何在 Travis CI、Buddy 和 AppVeyor 等流行的 CI(持续集成)系统中设置检查 GitHub 项目的更改文件列表。
文件列表检查模式
在Linux和macOS版本的PVS-Studio 7.04中,出现了检查源文件列表的模式。 这适用于其构建系统允许您生成文件的项目
此外,文件列表检查模式可以与编译器启动的 strace 跟踪日志(pvs-studio-analyzer 跟踪)一起使用。 为此,您需要首先执行项目的完整构建并跟踪它,以便分析器收集有关正在检查的所有文件的编译参数的完整信息。
然而,这个选项有一个显着的缺点——你要么需要在每次运行时对整个项目执行完整的构建跟踪,这本身就与快速检查提交的想法相矛盾。 或者,如果您本身缓存跟踪结果,如果源文件的依赖关系结构在跟踪后发生更改(例如,将新的 #include 添加到源文件之一),则分析器的后续运行可能会不完整。
因此,我们不建议使用文件列表检查模式和跟踪日志来检查提交或拉取请求。 如果您可以在检查提交时进行增量构建,请考虑使用该模式
用于分析的源文件列表保存在文本文件中,并使用参数传递给分析器 -S:
pvs-studio-analyzer analyze ... -f build/compile_commands.json -S check-list.txt
该文件指定文件的相对或绝对路径,每个新文件必须换行。 不仅可以指定用于分析的文件名,还可以指定各种文本。 分析器将发现这不是一个文件,并将忽略该行。 如果手动指定文件,这对于注释非常有用。 然而,在 CI 中分析期间通常会生成文件列表,例如,这些文件可能是来自提交或拉取请求的文件。
现在,使用这种模式,您可以在新代码进入主开发分支之前快速检查它。 为了确保扫描系统响应分析仪警告,该实用程序 plog转换器 添加了标志 --指示警告:
plog-converter ... --indicate-warnings ... -o /path/to/report.tasks ...
使用此标志,如果分析器报告中存在警告,转换器将返回非零代码。 使用返回代码,您可以阻止预提交挂钩、提交或拉取请求,并且可以显示、共享或通过电子邮件发送生成的分析器报告。
笔记。 当您第一次开始分析文件列表时,将分析整个项目,因为分析器需要生成项目源文件对头文件的依赖关系文件。 这是分析 C 和 C++ 文件的一个功能。 将来,依赖文件可以被缓存,并由分析器自动更新。 与使用增量分析模式相比,使用文件列表检查模式检查提交的优点是您只需要缓存该文件而不是对象文件。
拉取请求分析的一般原则
分析整个项目需要花费大量时间,因此仅检查其中的某一部分是有意义的。 问题是您需要将新文件与其余项目文件分开。
让我们看一个具有两个分支的提交树的示例:
让我们想象一下提交 A1 包含相当大量已经经过测试的代码。 早些时候我们从提交中创建了一个分支 A1 并更改了一些文件。
你当然注意到了,之后 A1 又发生了两次提交,但这些也是其他分支的合并,因为我们不承诺 主。 现在是时候了 修补程序 准备好。 这就是合并请求出现的原因 B3 и A3.
当然,可以检查合并的整个结果,但这太耗时且不合理,因为只更改了少数文件。 因此,仅分析发生变化的部分会更有效。
为此,我们获得分支之间的差异,位于我们想要合并到 master 的分支的 HEAD 中:
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
该命令将列出以下错误
现在我们不仅需要显示错误,还需要通知我们的组装和测试服务存在问题。 为此,转换器中添加了一个标志 -W (--指示警告)。 如果至少有一个分析器警告,则实用程序返回代码 plog转换器 将更改为 2,这反过来将通知 CI 服务拉取请求文件中是否存在潜在错误。
特拉维斯CI
配置被做成一个文件 .travis.yml。 为了方便起见,我建议您将所有内容放入单独的 bash 脚本中,其中包含将从文件中调用的函数 .travis.yml (bash 脚本名称.sh 函数名称).
我们将在脚本中添加必要的代码 打坏,这样我们将获得更多的功能。 在部分 安装 让我们写下以下内容:
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 存储项目存储库的名称。
该函数的算法:
Travis CI 响应返回代码,因此警告的存在将告诉服务将提交标记为包含错误。
现在让我们仔细看看这行代码:
git diff --name-only origin/HEAD > .pvs-pr.list
事实上,Travis CI 在分析拉取请求时会自动合并分支:
因此我们分析 A4而且不 B3->A3。 由于这个特性,我们需要计算差异 A3,这正是来自的分支的顶部 起源.
剩下一个重要的细节 - 缓存头文件对已编译翻译单元(*.c、*.cc、*.cpp 等)的依赖关系。 分析器在首次启动时以检查文件列表的方式计算这些依赖关系,然后将它们保存在.PVS-Studio 目录中。 Travis CI 允许缓存文件夹,因此我们将保存目录数据 .PVS-Studio/:
cache:
directories:
- .PVS-Studio/
这段代码需要添加到文件中 .travis.yml。 该目录存储分析后收集的各种数据,这将显着加快后续文件列表分析或增量分析的运行速度。 如果不这样做,那么分析器实际上每次都会分析所有文件。
伙伴
像 Travis CI 一样,
首先,我们需要在流水线中添加一个新的动作:
让我们指出用于构建项目的编译器。 请注意在此操作中安装的 docker 容器。 例如,GCC有一个特殊的容器:
现在让我们安装 PVS-Studio 和必要的实用程序:
让我们将以下几行添加到编辑器中:
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 的部分,那么您已经熟悉这段代码,但是,现在有一个新阶段:
事实是,现在我们分析的不是合并的结果,而是发出拉取请求的分支的 HEAD:
所以我们处于有条件提交中 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)。
现在,让我们使用下面的按钮保存更改并启用拉取请求的分析:
与 Travis CI 不同,我们不需要指定 .pvs-studio 用于缓存,因为 Buddy 会自动缓存所有文件以供后续启动。 因此,最后剩下的事情就是在 Buddy 中保存 PVS-Studio 的登录名和密码。 保存更改后,我们将返回到 Pipeline。 我们需要继续设置变量并添加 PVS-Studio 的登录名和密钥:
此后,新的拉取请求或提交的出现将触发审核。 如果提交包含错误,Buddy 将在拉取请求页面上指出这一点。
应用程序载体
设置 AppVeyor 与 Buddy 类似,因为一切都发生在 Web 界面中,无需将 *.yml 文件添加到项目存储库中。
让我们转到项目概述中的“设置”选项卡:
让我们向下滚动此页面并启用缓存保存以收集拉取请求:
现在让我们转到“环境”选项卡,在其中指定用于组装的图像和必要的环境变量:
如果您已阅读前面的部分,那么您对这两个变量非常熟悉 - PVS_KEY и PVS_用户名。 如果没有,我提醒您,他们需要验证PVS-Studio分析仪的许可证。 将来我们会在 Bash 脚本中再次看到它们。
在下面的同一页面上,我们指出了用于缓存的文件夹:
如果我们不这样做,我们将分析整个项目而不是几个文件,但我们将从指定文件中获取输出。 因此,输入正确的目录名称很重要。
现在是测试脚本的时候了。 打开测试选项卡并选择脚本:
您需要将以下代码粘贴到此表单中:
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 变量有点撒谎。 因此,我在开始分析之前手动更新了它的值。
然后一切都像以前一样:
现在考虑以下片段:
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 中,您只需要在设置中指定文件夹即可; 但是您需要在某个地方创建唯一的密钥并尝试说服系统给您机会覆盖缓存的片段。 因此,如果您想在上面未讨论的持续集成服务上设置拉取请求分析,那么首先确保您不会遇到缓存问题。
感谢您的关注。 如果出现问题,请随时写信给我们:
如果您想与英语读者分享这篇文章,请使用翻译链接:Maxim Zvyagintsev。
来源: habr.com