Análise de commits e pull requests no Travis CI, Buddy e AppVeyor usando o PVS-Studio

Análise de commits e pull requests no Travis CI, Buddy e AppVeyor com PVS-Studio
No analisador PVS-Studio para as linguagens C e C++ no Linux e macOS, a partir da versão 7.04, surgiu uma função para verificar uma lista de arquivos especificados. Com o novo modo, é possível configurar o analisador para verificar commits e pull requests. Neste artigo, vamos explicar como configurar a verificação da lista de arquivos alterados em um projeto do GitHub em sistemas CI (Integração Contínua) populares como Travis CI, Buddy e AppVeyor.

Modo de verificação da lista de arquivos

PVS-Studio — é uma ferramenta para detectar erros e vulnerabilidades potenciais no código fonte de programas escritos em C, C++, C# e Java. Funciona em sistemas de 64 bits no Windows, Linux e macOS.

Na versão PVS-Studio 7.04 para Linux e macOS, foi adicionado o modo de verificação da lista de arquivos fonte. Isso funciona para projetos cujos sistemas de construção permitem gerar um arquivo compile_commands.json. Ele é necessário para que o analisador extraia as informações de compilação dos arquivos especificados. Se o seu sistema de construção não suportar a geração do arquivo compile_commands.json, você pode tentar gerar esse arquivo usando a utilidade Bear.

O modo de verificação da lista de arquivos também pode ser usado junto com o log de rastreamento strace das execuções do compilador (pvs-studio-analyzer trace). Para isso, você deverá primeiro realizar uma construção completa do projeto e rastreá-la, para que o analisador colete todas as informações sobre os parâmetros de compilação de todos os arquivos verificados.

No entanto, essa abordagem tem uma desvantagem significativa — você terá que realizar um rastreamento completo da construção de todo o projeto a cada execução, o que contradiz a ideia de uma verificação rápida do commit. Ou, se você armazenar em cache o resultado do rastreamento, as execuções subsequentes do analisador podem ser incompletas se a estrutura das dependências dos arquivos fonte mudar (por exemplo, se um novo #include for adicionado a um dos arquivos fonte).

Portanto, não recomendamos usar o modo de verificação da lista de arquivos com o log de rastreamento para verificar commits ou pull requests. Caso você possa fazer uma construção incremental ao verificar o commit, considere usar o modo de análise incremental.

A lista de arquivos de origem para análise é salva em um arquivo de texto e é passada para o analisador por meio do parâmetro -S:

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

Neste arquivo, são indicados os caminhos relativos ou absolutos para os arquivos, sendo que cada novo arquivo deve estar em uma nova linha. É permitido indicar não apenas nomes de arquivos para análise, mas também texto diverso. O analisador verá que isso não é um arquivo e ignorará a linha. Isso pode ser útil para comentários, caso os arquivos sejam especificados manualmente. No entanto, muitas vezes a lista de arquivos será gerada durante a análise em CI, por exemplo, podem ser arquivos de um commit ou pull request.

Agora, com este modo, é possível verificar rapidamente o novo código antes de incorporá-lo ao ramo principal de desenvolvimento. Para que o sistema de verificação reaja à presença de advertências do analisador, na utilitário plog-converter um sinalizador foi adicionado --indicate-warnings:

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

Com este sinalizador, o conversor retornará um código diferente de zero se houver advertências no relatório do analisador. Com o código de retorno, é possível bloquear o hook pré-commit, o commit ou o pull request, e o relatório gerado pelo analisador pode ser exibido na tela, compartilhado ou enviado por e-mail.

Nota. Na primeira execução da análise da lista de arquivos, todo o projeto será analisado, pois o analisador precisa gerar o arquivo de dependências dos arquivos de origem do projeto em relação aos arquivos de cabeçalho. Essa é uma característica da análise de arquivos C e C++. Posteriormente, o arquivo de dependências pode ser armazenado em cache e será atualizado automaticamente pelo analisador. A vantagem de verificar commits ao usar o modo de verificação da lista de arquivos, em vez de usar o modo de análise incremental, é que apenas este arquivo precisa ser armazenado em cache e não os arquivos de objeto.

Princípios gerais da análise de pull request

A análise de todo o projeto leva bastante tempo, portanto, faz sentido verificar apenas uma parte dele. O problema é que é necessário separar os novos arquivos dos demais arquivos do projeto.

Consideremos um exemplo de árvore de commits com dois ramos:

Análise de commits e pull requests no Travis CI, Buddy e AppVeyor com PVS-Studio

Vamos imaginar que o commit A1 contém uma quantidade considerável de código que já foi verificado. Um pouco antes, criamos um ramo a partir do commit A1 e modificamos alguns arquivos.

Você certamente notou que depois A1 houve mais dois commits, mas também foram fusões de outros branches, já que não estamos fazendo commit em master. E agora chegou o momento em que hotfix está pronto. Por isso, surgiu um pull request para fusão B3 e A3.

Claro, poderíamos verificar todo o resultado das fusões, mas isso levaria muito tempo e seria desnecessário, já que apenas alguns arquivos foram alterados. Portanto, é mais eficiente analisar apenas os modificados.

Para isso, vamos obter a diferença entre os branches enquanto estamos no HEAD do branch do qual queremos fundir para master:

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

$MERGE_BASE vamos detalhar mais tarde. O fato é que nem todo serviço de CI fornece as informações necessárias sobre a base para fusão, então toda vez precisamos inventar novas formas de obter esses dados. Isso será descrito em detalhes abaixo em cada um dos serviços web mencionados.

Então, obtivemos a diferença entre os branches, ou mais especificamente — a lista dos nomes dos arquivos que foram alterados. Agora precisamos passar o arquivo .pvs-pr.list (para o qual redirecionamos a saída acima) ao analisador:

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

Após a análise, precisamos converter o arquivo de logs (PVS-Studio.log) em um formato mais legível:

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

Esse comando gerará uma lista de erros em stderr (o fluxo padrão de saída de mensagens de erro).

O problema é que precisamos não apenas exibir os erros, mas também informar nosso serviço de build e teste sobre a presença de problemas. Para isso, foi adicionado um flag ao conversor -W (--indicate-warnings). Se houver pelo menos um aviso do analisador, o código de retorno da ferramenta plog-converter mudará para 2, o que, por sua vez, informará ao serviço de CI sobre a presença de erros potenciais nos arquivos do pull request.

Travis CI

A configuração é realizada em um arquivo .travis.yml. Para conveniência, recomendo despejar tudo em um script bash separado com funções que serão chamadas a partir do arquivo .travis.yml (bash nome_script.sh nome_função).

Vamos adicionar o código necessário ao script em bash, assim teremos mais funcionalidade. Na seção install vamos escrever o seguinte:

install:
  - bash .travis.sh travis_install

Se você tinha alguma instrução, pode transferi-las para o script, removendo os hífens.

Vamos abrir o arquivo .travis.sh e adicionar a instalação do analisador na 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 script iniciar a 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
}

Esse código deve ser executado após a construção do projeto, por exemplo, se você fez a construção com CMake:

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

Vai ficar 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á notou as variáveis de ambiente mencionadas $TRAVIS_PULL_REQUEST e $TRAVIS_BRANCH. O Travis CI as declara automaticamente:

  • $TRAVIS_PULL_REQUEST armazena o número do pull request ou falso, se for um ramo normal;
  • $TRAVIS_REPO_SLUG armazena o nome do repositório do projeto.

O algoritmo de funcionamento desta função:

Análise de commits e pull requests no Travis CI, Buddy e AppVeyor com PVS-Studio
O Travis CI reage aos códigos de retorno, portanto, a presença de advertências indicará ao serviço que marque o commit como contendo erros.

Agora vamos examinar esta linha de código em detalhes:

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

Na verdade, o Travis CI automaticamente faz a mesclagem de ramos durante a análise do pull request:

Análise de commits e pull requests no Travis CI, Buddy e AppVeyor com PVS-Studio
Portanto, estamos analisando A4, e não B3->A3. Devido a essa particularidade, precisamos calcular a diferença com A3, que é exatamente o topo do ramo em origin.

Resta um detalhe importante — o cache das dependências dos arquivos de cabeçalho das unidades de tradução compiláveis (*.c, *.cc, *.cpp etc.). Essas dependências são calculadas pelo analisador na primeira execução no modo de verificação da lista de arquivos e depois são salvas no diretório .PVS-Studio. O Travis CI permite o cache de pastas, portanto, vamos armazenar os dados do diretório .PVS-Studio/:

cache:
  directories:
    - .PVS-Studio/

Este código deve ser adicionado ao arquivo .travis.yml. Este diretório armazena vários dados coletados após a análise, que aceleram significativamente as execuções subsequentes da análise da lista de arquivos ou da análise incremental. Se isso não for feito, o analisador irá efetivamente analisar todos os arquivos a cada vez.

Buddy

Assim como o Travis CI, Buddy oferece a possibilidade de compilação e teste automatizados de projetos armazenados no GitHub. Diferentemente do Travis CI, ele é configurado na interface da web (suporte a bash está disponível), portanto, não há necessidade de armazenar arquivos de configuração no projeto.

Primeiramente, precisamos adicionar uma nova ação à linha de compilação:

Análise de commits e pull requests no Travis CI, Buddy e AppVeyor com PVS-Studio
Especificaremos o compilador que foi usado para construir o projeto. Observe o contêiner docker que está configurado nesta ação. Por exemplo, para GCC, há um contêiner especial:

Análise de commits e pull requests no Travis CI, Buddy e AppVeyor com PVS-Studio
Agora vamos instalar o PVS-Studio e as utilidades necessárias:

Análise de commits e pull requests no Travis CI, Buddy e AppVeyor com PVS-Studio
Adicionaremos 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 Run (primeiro ícone) e no campo correspondente do editor, adicionaremos o seguinte código:

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 dedicada ao Travs-CI, este código já deve ser familiar, no entanto, agora há uma nova etapa:

Análise de commits e pull requests no Travis CI, Buddy e AppVeyor com PVS-Studio
A questão é que agora estamos analisando não o resultado da fusão, mas o HEAD do ramo de onde o pull request é criado:

Análise de commits e pull requests no Travis CI, Buddy e AppVeyor com PVS-Studio
Portanto, estamos em um commit condicional B3 e precisamos obter a diferença com 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 usaríamos a API GitHub:

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

Utilizamos as seguintes variáveis que o Buddy fornece:

  • $BUDDY_EXECUTION_PULL_REQEUST_NO — número do pull request;
  • $BUDDY_REPO_SLUG — combinação do nome de usuário e do repositório (por exemplo max/test).

Agora vamos salvar as alterações usando o botão abaixo e ativar a análise do pull request:

Análise de commits e pull requests no Travis CI, Buddy e AppVeyor com PVS-Studio
Diferente do Travis CI, não precisamos especificar .pvs-studio para cache, já que o Buddy automaticamente faz o cache de todos os arquivos para execuções posteriores. Portanto, resta apenas salvar o nome de usuário e a senha para o PVS-Studio no Buddy. Após salvar as alterações, retornaremos ao Pipeline. Precisamos ir para a configuração de variáveis e adicionar o nome de usuário e a chave para o PVS-Studio:

Análise de commits e pull requests no Travis CI, Buddy e AppVeyor com PVS-Studio
Após isso, a aparição de um novo pull request ou commit acionará a verificação. Se o commit contiver erros, o Buddy os indicará na página do pull request.

AppVeyor

A configuração do AppVeyor é semelhante à do Buddy, uma vez que tudo acontece na interface web e não é necessário adicionar um arquivo *.yml ao repositório do projeto.

Vamos para a aba Configurações na visão geral do projeto:

Análise de commits e pull requests no Travis CI, Buddy e AppVeyor com PVS-Studio
Role esta página para baixo e ative o salvamento do cache para a construção de pull requests:

Análise de commits e pull requests no Travis CI, Buddy e AppVeyor com PVS-Studio
Agora vamos para a aba Ambiente, onde indicaremos a imagem para a construção e as variáveis de ambiente necessárias:

Análise de commits e pull requests no Travis CI, Buddy e AppVeyor com PVS-Studio
Se você leu as seções anteriores, já deve estar familiarizado com essas duas variáveis — PVS_KEY e PVS_USERNAME. Se não, deixe-me lembrá-lo de que elas são necessárias para a verificação da licença do analisador PVS-Studio. Encontraremos essas variáveis novamente em scripts Bash a seguir.

Na mesma página, na parte inferior, vamos indicar a pasta para cache:

Análise de commits e pull requests no Travis CI, Buddy e AppVeyor com PVS-Studio
Se não fizermos isso, analisaremos todo o projeto em vez de apenas alguns arquivos, mas a saída será gerada com base nos arquivos especificados. Portanto, é importante inserir o nome correto do diretório.

Agora é hora de um script para a verificação. Vamos abrir a aba Testes e escolher Script:

Análise de commits e pull requests no Travis CI, Buddy e AppVeyor com PVS-Studio
Neste formulário, precisamos inserir o seguinte código:

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 dar atenção à próxima 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

Embora à primeira vista a atribuição do valor do comando pwd a uma variável possa parecer estranha, na verdade, isso faz sentido, e explicarei tudo agora.

Durante a configuração do analisador no AppVeyor, encontrei um comportamento muito peculiar do analisador. Por um lado, tudo estava funcionando corretamente, mas a análise não estava sendo iniciada. Passei bastante tempo tentando descobrir que estávamos no diretório /home/appveyor/projects/testcalc/, enquanto que o analisador acreditava que estávamos em /opt/appveyor/build-agent/. Foi então que percebi que a variável $PWD estava um pouco equivocada. Por isso, atualizei seu valor manualmente antes de iniciar a análise.

E então, tudo continuou como antes:

Análise de commits e pull requests no Travis CI, Buddy e AppVeyor com PVS-Studio
Agora vamos analisar o seguinte trecho:

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 nos quais o pull request foi declarado. Para isso, precisamos das seguintes variáveis de ambiente:

  • $APPVEYOR_PULL_REQUEST_NUMBER — o número do pull request;
  • $APPVEYOR_REPO_NAME — o nome de usuário e o repositório do projeto.

Conclusão

Claro que não cobrimos todos os possíveis serviços de integração contínua, mas todos eles possuem uma especificidade de funcionamento bastante semelhante. Exceto pelo cache, cada serviço cria sua própria 'bicicleta', então sempre é tudo diferente.

Em alguns casos, como no Travis-CI, algumas linhas de código fazem o cache funcionar perfeitamente; em outros, como no AppVeyor, você só precisa especificar a pasta nas configurações; mas em alguns lugares, você precisa criar chaves únicas e tentar convencer o sistema a permitir que você sobrescreva o fragmento em cache. Portanto, se você deseja configurar a análise de pull requests em um serviço de integração contínua que não foi mencionado acima, primeiro verifique se você não terá problemas com o cache.

Obrigado pela atenção. Se algo não estiver funcionando, não hesite em nos contatar pelo suporte. Nós estamos aqui para ajudar.

Análise de commits e pull requests no Travis CI, Buddy e AppVeyor com PVS-Studio

Se você deseja compartilhar este artigo com um público de língua inglesa, peço que use o link para a tradução: Maxim Zvyagintsev. Análise de commits e pull requests no Travis CI, Buddy e AppVeyor utilizando PVS-Studio.

Fonte: habr.com

Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster