No analisador PVS-Studio para linguagens C e C++ no Linux e macOS, a partir da versão 7.04, apareceu uma opção de teste para verificar a lista de arquivos especificados. Usando o novo modo, você pode configurar o analisador para verificar commits e pull requests. Este artigo explicará como configurar a verificação da lista de arquivos alterados de um projeto GitHub em sistemas populares de CI (integração contínua) como Travis CI, Buddy e AppVeyor.
Modo de verificação da lista de arquivos
Na versão PVS-Studio 7.04 para Linux e macOS, apareceu um modo de verificação da lista de arquivos de origem. Isso funciona para projetos cujo sistema de compilação permite gerar um arquivo
Além disso, o modo de verificação da lista de arquivos pode ser usado junto com o log de rastreamento strace de inicializações do compilador (rastreamento pvs-studio-analyzer). Para fazer isso, você precisará primeiro realizar uma compilação completa do projeto e rastreá-lo para que o analisador colete informações completas sobre os parâmetros de compilação de todos os arquivos que estão sendo verificados.
No entanto, esta opção tem uma desvantagem significativa - você precisará executar um rastreamento completo de construção de todo o projeto toda vez que executá-lo, o que por si só contradiz a ideia de verificar rapidamente um commit. Ou, se você armazenar em cache o próprio resultado do rastreamento, as execuções subsequentes do analisador poderão ficar incompletas se a estrutura de dependência dos arquivos de origem for alterada após o rastreamento (por exemplo, um novo #include for adicionado a um dos arquivos de origem).
Portanto, não recomendamos usar o modo de verificação da lista de arquivos com o log de rastreamento para verificar confirmações ou solicitações pull. Caso você possa fazer uma compilação incremental ao verificar um commit, considere usar o modo
A lista de arquivos fonte para análise é salva em um arquivo de texto e passada ao analisador usando o parâmetro -S:
pvs-studio-analyzer analyze ... -f build/compile_commands.json -S check-list.txt
Este arquivo especifica caminhos relativos ou absolutos para arquivos, e cada novo arquivo deve estar em uma nova linha. É aceitável especificar não apenas nomes de arquivos para análise, mas também vários textos. O analisador verá que não se trata de um arquivo e ignorará a linha. Isto pode ser útil para comentários se os arquivos forem especificados manualmente. No entanto, muitas vezes uma lista de arquivos será gerada durante a análise no CI, por exemplo, podem ser arquivos de uma solicitação de confirmação ou pull.
Agora, usando este modo, você pode verificar rapidamente o novo código antes que ele chegue ao branch principal de desenvolvimento. Para garantir que o sistema de varredura responda aos avisos do analisador, o utilitário conversor plog bandeira adicionada --indicar-avisos:
plog-converter ... --indicate-warnings ... -o /path/to/report.tasks ...
Com este flag, o conversor retornará um código diferente de zero se houver avisos no relatório do analisador. Usando o código de retorno, você pode bloquear um gancho de pré-confirmação, confirmação ou solicitação pull, e o relatório do analisador gerado pode ser exibido, compartilhado ou enviado por email.
Observação. Quando você começa a analisar uma lista de arquivos, todo o projeto será analisado, porque o analisador precisa gerar um arquivo de dependências dos arquivos fonte do projeto nos arquivos de cabeçalho. Este é um recurso de análise de arquivos C e C++. No futuro, o arquivo de dependência poderá ser armazenado em cache e será atualizado automaticamente pelo analisador. A vantagem de verificar commits ao usar o modo de verificação de lista de arquivos em vez de usar o modo de análise incremental é que você só precisa armazenar em cache esse arquivo e não os arquivos de objeto.
Princípios gerais de análise de pull request
A análise de todo o projeto leva muito tempo, por isso faz sentido verificar apenas uma parte dele. O problema é que você precisa separar os novos arquivos do restante dos arquivos do projeto.
Vejamos um exemplo de árvore de commit com duas ramificações:
Vamos imaginar esse commit A1 contém uma quantidade bastante grande de código que já foi testado. Um pouco antes fizemos um branch do commit A1 e alterei alguns arquivos.
Você, é claro, percebeu que depois A1 ocorreram mais dois commits, mas também foram fusões de outras filiais, porque não nos comprometemos com dominar. E agora chegou a hora em que correcção preparar. É por isso que apareceu uma solicitação pull para a fusão B3 и A3.
É claro que seria possível verificar todo o resultado da sua fusão, mas isso seria muito demorado e injustificado, uma vez que apenas alguns arquivos foram alterados. Portanto, é mais eficiente analisar apenas os alterados.
Para isso, obtemos a diferença entre os ramos, estando no HEAD do ramo do qual queremos fazer o merge no master:
git diff --name-only HEAD origin/$MERGE_BASE > .pvs-pr.list
$MERGE_BASE veremos isso em detalhes mais tarde. O fato é que nem todo serviço de CI fornece as informações necessárias sobre o banco de dados para mesclagem, portanto, cada vez é necessário encontrar novas formas de obter esses dados. Isto será descrito em detalhes abaixo em cada um dos serviços web descritos.
Então, obtivemos a diferença entre as ramificações, ou melhor, uma lista de nomes de arquivos que foram alterados. Agora precisamos fornecer o arquivo .pvs-pr.list (redirecionamos a saída acima para ele) para o analisador:
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
-S .pvs-pr.list
Após a análise, precisamos converter o arquivo de log (PVS-Studio.log) em um formato de fácil leitura:
plog-converter -t errorfile PVS-Studio.log --cerr -w
Este comando listará os erros em
Só que agora precisamos não apenas exibir erros, mas também informar nosso serviço de montagem e teste sobre a presença de problemas. Para tanto, foi adicionado um sinalizador ao conversor -W (--indicar-avisos). Se houver pelo menos um aviso do analisador, o código de retorno do utilitário conversor plog mudará para 2, que por sua vez informará o serviço de CI sobre a presença de possíveis erros nos arquivos de pull request.
Travis C.I.
A configuração é feita como um arquivo .travis.yml. Por conveniência, aconselho você a colocar tudo em um script bash separado com funções que serão chamadas a partir do arquivo .travis.yml (bash nome_do_script.sh nome_da_função).
Adicionaremos o código necessário ao script em bater, assim obteremos mais funcionalidades. Na seção instalar vamos escrever o seguinte:
install:
- bash .travis.sh travis_install
Se você tiver alguma instrução, poderá transferi-la para o script, removendo os hífens.
Vamos abrir o arquivo .travis.sh e adicione a configuração do analisador à função 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
}
Agora vamos adicionar à seção escrita executar análise:
script:
- bash .travis.sh travis_script
E no 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
}
Este código precisa ser executado após a construção do projeto, por exemplo, se você tiver uma compilação no CMake:
travis_script() {
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
cmake $CMAKE_ARGS CMakeLists.txt
make -j8
}
Será assim:
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
}
Você provavelmente já percebeu essas variáveis de ambiente $TRAVIS_PULL_REQUEST и $TRAVIS_BRANCH. Travis CI os declara de forma independente:
- $TRAVIS_PULL_REQUEST armazena o número da solicitação pull ou falso, se este for um branch regular;
- $TRAVIS_REPO_SLUG armazena o nome do repositório do projeto.
O algoritmo para esta função:
O Travis CI responde aos códigos de retorno, portanto a presença de avisos dirá ao serviço para marcar o commit como contendo erros.
Agora vamos dar uma olhada mais de perto nesta linha de código:
git diff --name-only origin/HEAD > .pvs-pr.list
O fato é que o Travis CI mescla ramificações automaticamente enquanto analisa uma solicitação pull:
Portanto analisamos A4E não B3->A3. Devido a esse recurso, precisamos calcular a diferença com A3, que é precisamente o topo do ramo de origem.
Resta um detalhe importante - armazenar em cache as dependências dos arquivos de cabeçalho nas unidades de tradução compiladas (*.c, *.cc, *.cpp, etc.). O analisador calcula essas dependências quando é iniciado pela primeira vez no modo de verificação de lista de arquivos e depois os salva no diretório .PVS-Studio. Travis CI permite armazenar pastas em cache, então salvaremos os dados do diretório .PVS-Studio/:
cache:
directories:
- .PVS-Studio/
Este código precisa ser adicionado ao arquivo .travis.yml. Este diretório armazena vários dados coletados após a análise, o que irá acelerar significativamente as execuções subsequentes de análise de lista de arquivos ou análise incremental. Se isso não for feito, o analisador analisará todos os arquivos todas as vezes.
camarada
Como Travis CI,
Primeiramente, precisamos adicionar uma nova ação à linha de montagem:
Vamos indicar o compilador que foi utilizado para construir o projeto. Observe o contêiner docker instalado nesta ação. Por exemplo, existe um contêiner especial para GCC:
Agora vamos instalar o PVS-Studio e os utilitários necessários:
Vamos adicionar as seguintes linhas ao editor:
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
Agora vamos para a aba Executar (primeiro ícone) e adicionar o seguinte código ao campo do editor correspondente:
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
Se você leu a seção sobre Travs-CI, então este código já é familiar para você, porém, agora há uma nova etapa:
O fato é que agora analisamos não o resultado do merge, mas sim o HEAD do branch a partir do qual é feito o pull request:
Então estamos em um commit condicional B3 e precisamos obter a diferença de 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
Para determinar A3 Vamos usar a API GitHub:
https://api.github.com/repos/${USERNAME}/${REPO}/pulls/${PULL_REQUEST_ID}
Usamos as seguintes variáveis que o Buddy fornece:
- $BUDDY_EXECUTION_PULL_REQEUST_NO — número da solicitação pull;
- $BUDDY_REPO_SLUG — uma combinação de nome de usuário e repositório (por exemplo max/test).
Agora vamos salvar as alterações usando o botão abaixo e habilitar a análise do pull request:
Ao contrário do Travis CI, não precisamos especificar .pvs-studio para armazenamento em cache, já que o Buddy armazena automaticamente em cache todos os arquivos para lançamentos subsequentes. Portanto, a última coisa que resta é salvar o login e a senha do PVS-Studio no Buddy. Após salvar as alterações, seremos levados de volta ao Pipeline. Precisamos prosseguir com a configuração das variáveis e a adição de um login e chave para o PVS-Studio:
Depois disso, o aparecimento de uma nova solicitação pull ou commit acionará a revisão. Se um commit contiver erros, o Buddy indicará isso na página de pull request.
AppVeyor
A configuração do AppVeyor é semelhante à do Buddy, pois tudo acontece na interface web e não há necessidade de adicionar um arquivo *.yml ao repositório do projeto.
Vamos para a aba Configurações na visão geral do projeto:
Vamos rolar esta página para baixo e ativar o salvamento em cache para coletar solicitações pull:
Agora vamos para a aba Ambiente, onde especificamos a imagem para montagem e as variáveis de ambiente necessárias:
Se você leu as seções anteriores, está muito familiarizado com essas duas variáveis - PVS_KEY и PVS_USERNAME. Caso contrário, lembro que eles são necessários para verificar a licença do analisador PVS-Studio. Veremos eles novamente em scripts Bash no futuro.
Na mesma página abaixo indicamos a pasta para cache:
Se não fizermos isso, analisaremos o projeto inteiro em vez de alguns arquivos, mas obteremos a saída dos arquivos especificados. Portanto, é importante inserir o nome do diretório correto.
Agora é hora de testar o script. Abra a aba Testes e selecione Script:
Você precisa colar o seguinte código neste formulário:
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
Vamos prestar atenção na seguinte parte do código:
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
A atribuição bastante específica do valor do comando pwd a uma variável que deveria armazenar esse valor padrão parece estranha à primeira vista, porém vou explicar tudo agora.
Ao configurar o analisador no AppVeyor, encontrei um comportamento extremamente estranho do analisador. Por um lado, tudo funcionou bem, mas a análise não foi iniciada. Passei muito tempo percebendo que estamos no diretório /home/appveyor/projects/testcalc/ e o analisador tem certeza de que estamos em /opt/appveyor/build-agent/. Então percebi que a variável $PWD estava um pouco mentindo. Por esse motivo, atualizei manualmente o seu valor antes de iniciar a análise.
E então tudo fica como antes:
Considere agora o seguinte fragmento:
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"`
Nele obtemos a diferença entre os ramos sobre os quais o pull request é declarado. Para fazer isso, precisamos das seguintes variáveis de ambiente:
- $APPVEYOR_PULL_REQUEST_NUMBER — número da solicitação pull;
- $APPVEYOR_REPO_NAME – nome de usuário e repositório do projeto.
Conclusão
É claro que não consideramos todos os serviços de integração contínua possíveis; no entanto, todos eles têm especificidades operacionais extremamente semelhantes entre si. Com exceção do cache, cada serviço faz a sua “bicicleta”, então tudo é sempre diferente.
Em algum lugar, como no Travis-CI, algumas linhas de código e cache funcionam perfeitamente; em algum lugar, como no AppVeyor, basta especificar a pasta nas configurações; mas em algum lugar você precisa criar chaves exclusivas e tentar convencer o sistema a lhe dar a oportunidade de sobrescrever o fragmento em cache. Portanto, se você deseja configurar a análise de solicitações pull em um serviço de integração contínua que não foi discutido acima, primeiro certifique-se de que não terá problemas com o armazenamento em cache.
Obrigado pela sua atenção. Se algo não der certo, sinta-se à vontade para nos escrever em
Se você quiser compartilhar este artigo com um público que fala inglês, use o link de tradução: Maxim Zvyagintsev.
Fonte: habr.com