Analisi di commit e richieste pull in Travis CI, Buddy e AppVeyor utilizzando PVS-Studio

Analisi di commit e pull request in Travis CI, Buddy e AppVeyor utilizzando PVS-Studio
Nell'analizzatore PVS-Studio per i linguaggi C e C++ su Linux e macOS, a partire dalla versione 7.04, è apparsa un'opzione di test per verificare l'elenco dei file specificati. Utilizzando la nuova modalità, è possibile configurare l'analizzatore per verificare i commit e le richieste pull. Questo articolo ti spiegherà come impostare il controllo dell'elenco dei file modificati di un progetto GitHub in sistemi CI (Continuous Integration) popolari come Travis CI, Buddy e AppVeyor.

Modalità di controllo dell'elenco dei file

Studio PVS è uno strumento per identificare errori e potenziali vulnerabilità nel codice sorgente di programmi scritti in C, C++, C# e Java. Funziona su sistemi a 64 bit su Windows, Linux e macOS.

Nella versione PVS-Studio 7.04 per Linux e macOS è apparsa una modalità per controllare l'elenco dei file sorgente. Funziona per progetti il ​​cui sistema di compilazione consente di generare un file compile_commands.json. È necessario affinché l'analizzatore estragga informazioni sulla compilazione dei file specificati. Se il tuo sistema di compilazione non supporta la generazione del file compile_commands.json, puoi provare a generare tale file utilizzando l'utilità Orso.

Inoltre, la modalità di controllo dell'elenco dei file può essere utilizzata insieme al registro di traccia strace dei lanci del compilatore (traccia pvs-studio-analyzer). Per fare ciò, dovrai prima eseguire una compilazione completa del progetto e monitorarlo in modo che l'analizzatore raccolga informazioni complete sui parametri di compilazione di tutti i file da controllare.

Tuttavia, questa opzione presenta uno svantaggio significativo: dovrai eseguire una traccia di compilazione completa dell'intero progetto ogni volta che lo esegui, il che di per sé contraddice l'idea di controllare rapidamente un commit. Oppure, se si memorizza nella cache il risultato della traccia, le esecuzioni successive dell'analizzatore potrebbero essere incomplete se la struttura delle dipendenze dei file di origine cambia dopo la traccia (ad esempio, un nuovo #include viene aggiunto a uno dei file di origine).

Pertanto, non consigliamo di utilizzare la modalità di controllo dell'elenco di file con il log di traccia per controllare i commit o le richieste pull. Nel caso in cui sia possibile eseguire una compilazione incrementale durante il controllo di un commit, considerare l'utilizzo della modalità mode analisi incrementale.

L'elenco dei file sorgente per l'analisi viene salvato in un file di testo e passato all'analizzatore utilizzando il parametro -S:

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

Questo file specifica i percorsi relativi o assoluti dei file e ogni nuovo file deve trovarsi su una nuova riga. È accettabile specificare non solo i nomi dei file da analizzare, ma anche vari testi. L'analizzatore vedrà che questo non è un file e ignorerà la riga. Questo può essere utile per commentare se i file vengono specificati manualmente. Tuttavia, spesso durante l'analisi in CI viene generato un elenco di file, ad esempio, potrebbero trattarsi di file da una richiesta di commit o pull.

Ora, utilizzando questa modalità, puoi controllare rapidamente il nuovo codice prima che entri nel ramo di sviluppo principale. Per garantire che il sistema di scansione risponda agli avvisi dell'analizzatore, l'utility convertitore di plog aggiunta la bandiera --indica-avvertimenti:

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

Con questo flag il convertitore restituirà un codice diverso da zero se sono presenti avvisi nel report dell'analizzatore. Utilizzando il codice di ritorno, è possibile bloccare un hook precommit, un commit o una richiesta pull e il report dell'analizzatore generato può essere visualizzato, condiviso o inviato tramite e-mail.

Nota. Quando inizi ad analizzare un elenco di file per la prima volta, verrà analizzato l'intero progetto, perché l'analizzatore deve generare un file di dipendenze dei file sorgente del progetto sui file di intestazione. Questa è una funzionalità di analisi dei file C e C++. In futuro, il file delle dipendenze potrà essere memorizzato nella cache e verrà aggiornato automaticamente dall'analizzatore. Il vantaggio di controllare i commit quando si utilizza la modalità di controllo dell'elenco di file rispetto all'utilizzo della modalità di analisi incrementale è che è necessario memorizzare nella cache solo quel file e non i file oggetto.

Principi generali dell'analisi delle pull request

L'analisi dell'intero progetto richiede molto tempo, quindi ha senso controllarne solo una certa parte. Il problema è che devi separare i nuovi file dal resto dei file di progetto.

Diamo un'occhiata ad un esempio di albero dei commit con due rami:

Analisi di commit e pull request in Travis CI, Buddy e AppVeyor utilizzando PVS-Studio

Immaginiamo che si impegni A1 contiene una quantità abbastanza grande di codice che è già stato testato. Poco prima abbiamo creato un ramo dal commit A1 e modificato alcuni file.

Ovviamente te ne sei accorto dopo A1 si sono verificati altri due commit, ma anche queste erano fusioni di altri rami, perché non ci impegniamo a farlo Mastercard. E ora è giunto il momento in cui hotfix pronto. Ecco perché è apparsa una richiesta pull per la fusione B3 и A3.

Naturalmente sarebbe possibile verificare l'intero risultato della loro fusione, ma ciò sarebbe troppo dispendioso in termini di tempo e ingiustificato, poiché sono stati modificati solo pochi file. Pertanto, è più efficiente analizzare solo quelli modificati.

Per fare ciò, otteniamo la differenza tra i rami, trovandoci nell'HEAD del ramo da cui vogliamo confluire nel master:

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

$MERGE_BASE lo vedremo in dettaglio più avanti. Il fatto è che non tutti i servizi CI forniscono le informazioni necessarie sul database per la fusione, quindi ogni volta è necessario trovare nuovi modi per ottenere questi dati. Ciò sarà descritto in dettaglio di seguito in ciascuno dei servizi web descritti.

Quindi, abbiamo ottenuto la differenza tra i rami, o meglio, un elenco di nomi di file che sono stati modificati. Ora dobbiamo fornire il file .pvs-pr.list (abbiamo reindirizzato l'output sopra ad esso) all'analizzatore:

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

Dopo l'analisi, dobbiamo convertire il file di registro (PVS-Studio.log) in un formato di facile lettura:

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

Questo comando elencherà gli errori in stderr (output del messaggio di errore standard).

Solo ora dobbiamo non solo visualizzare gli errori, ma anche informare il nostro servizio di assemblaggio e test della presenza di problemi. A questo scopo è stato aggiunto un flag al convertitore -W (--indica-avvertimenti). Se è presente almeno un avviso dell'analizzatore, il codice restituito dall'utilità convertitore di plog cambierà in 2, che a sua volta informerà il servizio CI della presenza di potenziali errori nei file di richiesta pull.

Travis CI

La configurazione viene eseguita come file .travis.yml. Per comodità ti consiglio di inserire tutto in uno script bash separato con funzioni che verranno richiamate dal file .travis.yml (bash nome_script.sh nome_funzione).

Aggiungeremo il codice necessario allo script in bash, in questo modo otterremo più funzionalità. Nella sezione install scriviamo quanto segue:

install:
  - bash .travis.sh travis_install

Se hai delle istruzioni, puoi trasferirle nello script, rimuovendo i trattini.

Apriamo il file .travis.sh e aggiungere l'impostazione dell'analizzatore alla funzione 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 
}

Ora aggiungiamo alla sezione copione eseguire l'analisi:

script:
  - bash .travis.sh travis_script

E nello 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
}

Questo codice deve essere eseguito dopo aver creato il progetto, ad esempio, se hai creato una build su CMake:

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

Risulterà così:

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
}

Probabilmente hai già notato queste variabili d'ambiente $TRAVIS_PULL_REQUEST и $TRAVIS_BRANCH. Travis CI li dichiara indipendentemente:

  • $TRAVIS_PULL_REQUEST memorizza il numero della richiesta pull o falso, se si tratta di un ramo regolare;
  • $TRAVIS_REPO_SLUG memorizza il nome del repository del progetto.

L'algoritmo per questa funzione:

Analisi di commit e pull request in Travis CI, Buddy e AppVeyor utilizzando PVS-Studio
Travis CI risponde ai codici restituiti, quindi la presenza di avvisi dirà al servizio di contrassegnare il commit come contenente errori.

Ora diamo un'occhiata più da vicino a questa riga di codice:

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

Il fatto è che Travis CI unisce automaticamente i rami mentre analizza una richiesta pull:

Analisi di commit e pull request in Travis CI, Buddy e AppVeyor utilizzando PVS-Studio
Pertanto analizziamo A4E non B3->A3. A causa di questa funzionalità, dobbiamo calcolare la differenza con A3, che è proprio la sommità del ramo da origine.

Rimane un dettaglio importante: memorizzare nella cache le dipendenze dei file di intestazione sulle unità di traduzione compilate (*.c, *.cc, *.cpp, ecc.). L'analizzatore calcola queste dipendenze quando viene avviato per la prima volta nella modalità di controllo di un elenco di file e quindi li salva nella directory .PVS-Studio. Travis CI ti consente di memorizzare nella cache le cartelle, quindi salveremo i dati della directory .PVS-Studio/:

cache:
  directories:
    - .PVS-Studio/

Questo codice deve essere aggiunto al file .travis.yml. Questa directory memorizza vari dati raccolti dopo l'analisi, il che velocizzerà notevolmente le successive esecuzioni dell'analisi dell'elenco di file o dell'analisi incrementale. Se ciò non viene fatto, l'analizzatore analizzerà effettivamente tutti i file ogni volta.

compagno

Come Travis CI, compagno offre la possibilità di creare e testare automaticamente progetti archiviati su GitHub. A differenza di Travis CI, è configurato nell'interfaccia web (è disponibile il supporto bash), quindi non è necessario archiviare file di configurazione nel progetto.

Prima di tutto dobbiamo aggiungere una nuova azione alla catena di montaggio:

Analisi di commit e pull request in Travis CI, Buddy e AppVeyor utilizzando PVS-Studio
Indichiamo il compilatore utilizzato per costruire il progetto. Notare il contenitore docker installato in questa azione. Ad esempio, esiste un contenitore speciale per GCC:

Analisi di commit e pull request in Travis CI, Buddy e AppVeyor utilizzando PVS-Studio
Ora installiamo PVS-Studio e le utilità necessarie:

Analisi di commit e pull request in Travis CI, Buddy e AppVeyor utilizzando PVS-Studio
Aggiungiamo le seguenti righe all'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

Andiamo ora nella scheda Esegui (prima icona) e aggiungiamo il seguente codice nel campo corrispondente dell'editor:

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 leggi la sezione su Travs-CI, questo codice ti è già familiare, tuttavia ora c'è una nuova fase:

Analisi di commit e pull request in Travis CI, Buddy e AppVeyor utilizzando PVS-Studio
Il fatto è che ora analizziamo non il risultato della fusione, ma l'HEAD del ramo da cui viene effettuata la richiesta di pull:

Analisi di commit e pull request in Travis CI, Buddy e AppVeyor utilizzando PVS-Studio
Quindi siamo in un commit condizionale B3 e dobbiamo capire la differenza 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

Determinare A3 Usiamo l'API GitHub:

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

Abbiamo utilizzato le seguenti variabili fornite da Buddy:

  • $BUDDY_EXECUTION_PULL_REQEUST_NO — numero della richiesta di pull;
  • $BUDDY_REPO_SLUG — una combinazione di nome utente e repository (ad esempio max/test).

Ora salviamo le modifiche utilizzando il pulsante in basso e abilitiamo l'analisi della richiesta pull:

Analisi di commit e pull request in Travis CI, Buddy e AppVeyor utilizzando PVS-Studio
A differenza di Travis CI, non abbiamo bisogno di specificare .pvs-studio per la memorizzazione nella cache, poiché Buddy memorizza automaticamente nella cache tutti i file per i lanci successivi. Pertanto, l'ultima cosa rimasta è salvare il login e la password per PVS-Studio in Buddy. Dopo aver salvato le modifiche, verremo riportati alla Pipeline. Dobbiamo passare alla configurazione delle variabili e all'aggiunta di un login e di una chiave per PVS-Studio:

Analisi di commit e pull request in Travis CI, Buddy e AppVeyor utilizzando PVS-Studio
Successivamente, la comparsa di una nuova richiesta pull o commit attiverà la revisione. Se un commit contiene errori, Buddy lo indicherà nella pagina della richiesta pull.

AppVeyor

La configurazione di AppVeyor è simile a Buddy, poiché tutto avviene nell'interfaccia web e non è necessario aggiungere un file *.yml al repository del progetto.

Andiamo alla scheda Impostazioni nella panoramica del progetto:

Analisi di commit e pull request in Travis CI, Buddy e AppVeyor utilizzando PVS-Studio
Scorriamo verso il basso questa pagina e abilitiamo il salvataggio della cache per la raccolta delle richieste pull:

Analisi di commit e pull request in Travis CI, Buddy e AppVeyor utilizzando PVS-Studio
Ora andiamo alla scheda Ambiente, dove specifichiamo l'immagine per l'assembly e le variabili di ambiente necessarie:

Analisi di commit e pull request in Travis CI, Buddy e AppVeyor utilizzando PVS-Studio
Se hai letto le sezioni precedenti, hai molta familiarità con queste due variabili − PVS_KEY и PVS_NOMEUTENTE. In caso contrario, ti ricordo che sono necessari per verificare la licenza dell'analizzatore PVS-Studio. Li vedremo di nuovo negli script Bash in futuro.

Nella stessa pagina in basso indichiamo la cartella per il caching:

Analisi di commit e pull request in Travis CI, Buddy e AppVeyor utilizzando PVS-Studio
Se non lo facciamo, analizzeremo l'intero progetto invece di un paio di file, ma otterremo l'output dai file specificati. Pertanto, è importante inserire il nome della directory corretto.

Ora è il momento di testare lo script. Apri la scheda Test e seleziona Script:

Analisi di commit e pull request in Travis CI, Buddy e AppVeyor utilizzando PVS-Studio
È necessario incollare il seguente codice in questo modulo:

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

Prestiamo attenzione alla seguente parte di codice:

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

L'assegnazione piuttosto specifica del valore del comando pwd ad una variabile che dovrebbe memorizzare questo valore di default sembra strana a prima vista, tuttavia ora vi spiego tutto.

Durante la configurazione dell'analizzatore in AppVeyor, ho riscontrato un comportamento estremamente strano dell'analizzatore. Da un lato, tutto ha funzionato correttamente, ma l'analisi non è stata avviata. Ho passato molto tempo a notare che siamo nella directory /home/appveyor/projects/testcalc/ e l'analizzatore è sicuro che siamo in /opt/appveyor/build-agent/. Poi mi sono reso conto che la variabile $PWD menteva un po'. Per questo motivo ho aggiornato manualmente il suo valore prima di iniziare l'analisi.

E poi tutto è come prima:

Analisi di commit e pull request in Travis CI, Buddy e AppVeyor utilizzando PVS-Studio
Consideriamo ora il seguente frammento:

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"`

In esso otteniamo la differenza tra i rami su cui viene dichiarata la richiesta pull. Per fare ciò abbiamo bisogno delle seguenti variabili d'ambiente:

  • $APPVEYOR_PULL_REQUEST_NUMBER: numero della richiesta pull;
  • $APPVEYOR_REPO_NAME - nome utente e repository del progetto.

conclusione

Naturalmente non abbiamo considerato tutti i possibili servizi di integrazione continua, ma hanno tutti caratteristiche operative estremamente simili tra loro. Ad eccezione del caching, ogni servizio realizza la propria “bicicletta”, quindi tutto è sempre diverso.

Da qualche parte, come in Travis-CI, un paio di righe di codice e la memorizzazione nella cache funzionano perfettamente; da qualche parte, come in AppVeyor, devi solo specificare la cartella nelle impostazioni; ma da qualche parte devi creare chiavi univoche e cercare di convincere il sistema a darti l'opportunità di sovrascrivere il frammento memorizzato nella cache. Pertanto, se desideri impostare l'analisi delle richieste pull su un servizio di integrazione continua che non è stato discusso in precedenza, assicurati innanzitutto di non avere problemi con la memorizzazione nella cache.

Grazie per l'attenzione. Se qualcosa non funziona, sentiti libero di scriverci a sostegno. Ti consiglieremo e aiuteremo.

Analisi di commit e pull request in Travis CI, Buddy e AppVeyor utilizzando PVS-Studio

Se desideri condividere questo articolo con un pubblico di lingua inglese, utilizza il link di traduzione: Maxim Zvyagintsev. Analisi di commit e pull request in Travis CI, Buddy e AppVeyor utilizzando PVS-Studio.

Fonte: habr.com

Aggiungi un commento