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

Análise de commits e pull requests em Travis CI, Buddy e AppVeyor usando PVS-Studio
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

Estúdio PVS é uma ferramenta para identificar erros e possíveis vulnerabilidades 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, 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 compile_commands.json. É necessário que o analisador extraia informações sobre a compilação dos arquivos especificados. Se o seu sistema de compilação não suporta a geração do arquivo compile_commands.json, você pode tentar gerar esse arquivo usando o utilitário Tenha.

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 análise incremental.

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:

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

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 stderr (saída de mensagem de erro padrão).

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:

Análise de commits e pull requests em Travis CI, Buddy e AppVeyor usando PVS-Studio
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:

Análise de commits e pull requests em Travis CI, Buddy e AppVeyor usando PVS-Studio
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, camarada fornece a capacidade de criar e testar automaticamente projetos armazenados no GitHub. Ao contrário do Travis CI, ele é configurado na interface web (suporte 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 montagem:

Análise de commits e pull requests em Travis CI, Buddy e AppVeyor usando PVS-Studio
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:

Análise de commits e pull requests em Travis CI, Buddy e AppVeyor usando PVS-Studio
Agora vamos instalar o PVS-Studio e os utilitários necessários:

Análise de commits e pull requests em Travis CI, Buddy e AppVeyor usando PVS-Studio
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:

Análise de commits e pull requests em Travis CI, Buddy e AppVeyor usando PVS-Studio
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:

Análise de commits e pull requests em Travis CI, Buddy e AppVeyor usando PVS-Studio
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:

Análise de commits e pull requests em Travis CI, Buddy e AppVeyor usando PVS-Studio
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:

Análise de commits e pull requests em Travis CI, Buddy e AppVeyor usando 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:

Análise de commits e pull requests em Travis CI, Buddy e AppVeyor usando PVS-Studio
Vamos rolar esta página para baixo e ativar o salvamento em cache para coletar solicitações pull:

Análise de commits e pull requests em Travis CI, Buddy e AppVeyor usando PVS-Studio
Agora vamos para a aba Ambiente, onde especificamos a imagem para montagem e as variáveis ​​de ambiente necessárias:

Análise de commits e pull requests em Travis CI, Buddy e AppVeyor usando PVS-Studio
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:

Análise de commits e pull requests em Travis CI, Buddy e AppVeyor usando PVS-Studio
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:

Análise de commits e pull requests em Travis CI, Buddy e AppVeyor usando PVS-Studio
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:

Análise de commits e pull requests em Travis CI, Buddy e AppVeyor usando PVS-Studio
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 apoiar. Nós aconselharemos e ajudaremos.

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

Se você quiser compartilhar este artigo com um público que fala inglês, use o link de tradução: Maxim Zvyagintsev. Análise de commits e pull requests em Travis CI, Buddy e AppVeyor usando PVS-Studio.

Fonte: habr.com

Adicionar um comentário