
Nell'analizzatore PVS-Studio per i linguaggi C e C++ su Linux e macOS, a partire dalla versione 7.04, è stata introdotta la funzione di verifica dell'elenco dei file specificati. Con la nuova modalità, è possibile configurare l'analizzatore per controllare i commit e le pull request. In questo articolo, spiegheremo come impostare il controllo dell'elenco dei file modificati nel progetto GitHub in popolari sistemi CI (Continuous Integration) come Travis CI, Buddy e AppVeyor.
Modalità di controllo dell'elenco dei file
è uno strumento per identificare errori e vulnerabilità potenziali nel codice sorgente di programmi scritti nei linguaggi C, C++, C# e Java. Funziona su sistemi a 64 bit su Windows, Linux e macOS.
Nella versione 7.04 di PVS-Studio per Linux e macOS è stata introdotta la modalità di controllo dell'elenco dei file sorgenti. Funziona per i progetti la cui sistema di build consente di generare un file . Serve a permettere all'analizzatore di estrarre informazioni sulla compilazione dei file specificati. Se il tuo sistema di build non supporta la generazione del file compile_commands.json, puoi provare a generare tale file utilizzando l'utility .
Il modo di controllo della lista dei file può essere utilizzato insieme al log di tracciamento degli avvii del compilatore strace (pvs-studio-analyzer trace). Per farlo, dovrai prima eseguire una build completa del progetto e tracciarla, affinché l'analizzatore raccolga tutte le informazioni sui parametri di compilazione di tutti i file controllati.
Tuttavia, questa opzione presenta un notevole svantaggio: sarà necessario eseguire una tracciatura completa della build dell'intero progetto ad ogni avvio, il che contraddice l'idea di un controllo rapido del commit. Oppure, se si memorizza nella cache il risultato stesso della tracciatura, le esecuzioni successive dell'analizzatore potrebbero risultare incomplete se la struttura delle dipendenze dei file sorgente cambia dopo la tracciatura (ad esempio, se in uno dei file sorgente viene aggiunto un nuovo #include).
Pertanto, non raccomandiamo di utilizzare il modo di controllo della lista dei file con il log di tracciamento per la verifica dei commit o delle pull request. Se puoi effettuare una build incrementale durante il controllo del commit, considera l'opzione di utilizzare il modo .
L'elenco dei file sorgente da analizzare viene salvato in un file di testo e passato all'analizzatore tramite il parametro -S:
pvs-studio-analyzer analyze ... -f build/compile_commands.json -S check-list.txtIn questo file vengono indicati i percorsi relativi o assoluti ai file, con ogni nuovo file su una nuova riga. È possibile specificare non solo i nomi dei file da analizzare, ma anche diverso testo. L'analizzatore vedrà che non si tratta di un file e ignorerà la riga. Questo può essere utile per commenti, nel caso in cui i file vengano specificati manualmente. Tuttavia, spesso l'elenco dei file sarà generato durante l'analisi nella CI, ad esempio, possono essere file da un commit o una pull request.
Ora, con questa modalità, è possibile controllare rapidamente il nuovo codice prima che venga integrato nel ramo principale di sviluppo. Perché il sistema di verifica reagisca alla presenza di avvisi dell'analizzatore, nella utility plog-converter è stato aggiunto il flag —indicate-warnings:
plog-converter ... --indicate-warnings ... -o /path/to/report.tasks ...Con questo flag, il convertitore restituirà un codice non nullo se nel rapporto dell'analizzatore sono presenti avvisi. In base al codice di ritorno, è possibile bloccare l'hook pre-commit, il commit o la pull request e visualizzare, condividere o inviare via email il rapporto generato dall'analizzatore.
Nota. Al primo avvio dell'analisi, verrà analizzato l'intero progetto, poiché l'analizzatore deve generare un file di dipendenze dei file sorgente del progetto dai file header. Questa è una caratteristica dell'analisi dei file C e C++. In seguito, il file di dipendenze può essere memorizzato nella cache e sarà aggiornato automaticamente dall'analizzatore. Il vantaggio della verifica dei commit utilizzando la modalità di controllo dell'elenco dei file, rispetto all'uso della modalità di analisi incrementale, è che è necessario memorizzare nella cache solo questo file e non i file oggetto.
Principi generali dell'analisi della pull request
Analizzare l'intero progetto richiede molto tempo, quindi ha senso controllare solo una sua parte. Il problema è separare i nuovi file dagli altri file del progetto.
Consideriamo un esempio di albero dei commit con due rami:

Immaginiamo che il commit A1 contenga una quantità considerevole di codice che è già stato revisionato. Poco prima abbiamo creato un ramo dal commit A1 e modificato alcuni file.
Ovviamente avrete notato che dopo A1 ci sono stati altri due commit, ma sono stati anche fusioni di altri rami, dato che non stiamo effettuando commit in master. È giunto il momento in cui hotfix è pronto. Perciò è stato creato un pull request per unire B3 e A3.
Naturalmente, si potrebbe controllare l'intero risultato della fusione, ma richiederebbe troppo tempo e sarebbe ingiustificato, dato che sono stati modificati solo alcuni file. Pertanto, è più efficace analizzare solo quelli cambiati.
Per fare ciò, otteniamo la differenza tra i rami, trovandoci nel ramo HEAD da cui vogliamo unire nel master:
git diff --name-only HEAD origin/$MERGE_BASE > .pvs-pr.list$MERGE_BASE lo esamineremo con attenzione più avanti. Sta di fatto che non tutti i servizi CI forniscono le informazioni necessarie sulla base per la fusione, quindi ogni volta è necessario inventare nuovi modi per ottenere questi dati. Questo sarà descritto in dettaglio di seguito in ciascuno dei servizi web descritti.
Quindi, abbiamo ottenuto la differenza tra i rami, ovvero un elenco dei nomi dei file che sono stati modificati. Adesso dobbiamo passare il file .pvs-pr.list (dove abbiamo reindirizzato l'output sopra) all'analizzatore:
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
-S .pvs-pr.listDopo l'analisi, dobbiamo convertire il file di log (PVS-Studio.log) in un formato più leggibile:
plog-converter -t errorfile PVS-Studio.log --cerr -wQuesto comando genererà un elenco di errori in (stream standard di output dei messaggi di errore).
Ma abbiamo bisogno non solo di visualizzare gli errori, ma anche di comunicare al nostro servizio di build e test la presenza di problemi. A tal fine, è stato aggiunto un flag al convertitore -W (—indicate-warnings). In caso di almeno un avviso dell'analizzatore, il codice di ritorno dell'utilità plog-converter cambierà in 2, il che, a sua volta, comunicherà al servizio CI la presenza di potenziali errori nei file della pull request.
Travis CI
La configurazione è realizzata sotto forma di file .travis.yml. Per comodità, consiglio di estrarre tutto in uno script bash separato con funzioni che verranno chiamate dal file .travis.yml (bash nome_script.sh nome_funzione).
Aggiungeremo il codice necessario allo script su bash, in questo modo otterremo una funzionalità maggiore. Nella sezione install scriveremo il seguente:
install:
- bash .travis.sh travis_installSe hai avuto istruzioni, puoi trasferirle nello script rimuovendo i trattini.
Apriremo il file .travis.sh e aggiungeremo l'installazione dell'analizzatore nella 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 script lanciare l'analisi:
script:
- bash .travis.sh travis_scriptE 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 la costruzione del progetto, per esempio, se hai avuto una costruzione 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 le variabili d'ambiente indicate. $TRAVIS_PULL_REQUEST e $TRAVIS_BRANCH. Travis CI le autodichiara:
- $TRAVIS_PULL_REQUEST memorizza il numero della pull request oppure false, se si tratta di un branch normale;
- $TRAVIS_REPO_SLUG memorizza il nome del repository del progetto.
Il funzionamento di questa funzione è il seguente:

Travis CI reagisce ai codici di ritorno, quindi la presenza di avvisi indicherà al servizio di contrassegnare il commit come contenente errori.
Adesso consideriamo in dettaglio questa riga di codice:
git diff --name-only origin/HEAD > .pvs-pr.listInfatti, Travis CI esegue automaticamente il merge dei branch durante l'analisi della pull request:

Pertanto, analizziamo A4, e non B3->A3. A causa di questa particolarità dobbiamo calcolare la differenza con A3, che è proprio la cima del branch di origin.
C'è un'ultima importante questione: la memorizzazione nella cache delle dipendenze dei file di intestazione delle unità di traduzione compilate (*.c, *.cc, *.cpp, ecc.). Queste dipendenze sono calcolate dall'analizzatore al primo avvio in modalità verifica dell'elenco dei file e vengono poi salvate nella directory .PVS-Studio. Travis CI 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 conserva i vari dati raccolti dopo l'analisi, che accelerano notevolmente le analisi successive dell'elenco dei file o dell'analisi incrementale. Se non lo si fa, l'analizzatore decoderà effettivamente ogni volta tutti i file.
Buddy
Come Travis CI, offre la possibilità di compilarli e testarli automaticamente per i progetti memorizzati su GitHub. A differenza di Travis CI, si configura tramite l'interfaccia web (c'è supporto per bash), quindi non è necessario mantenere file di configurazione nel progetto.
In primo luogo, dobbiamo aggiungere una nuova azione alla pipeline di build:

Indichiamo il compilatore utilizzato per costruire il progetto. Fate attenzione al contenitore docker installato in questa operazione. Ad esempio, per GCC esiste un contenitore specifico:

Ora installeremo PVS-Studio e le utilità necessarie:

Aggiungiamo nel nostro editor le seguenti righe:
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-studioOra passiamo alla scheda Esegui (prima icona) e nell'apposito campo dell'editor aggiungiamo il seguente codice:
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 -wSe avete letto la sezione dedicata a Travs-CI, questo codice vi sarà già familiare, ma ora è emerso un nuovo stadio:

Il fatto è che ora analizziamo non il risultato della fusione, ma l'HEAD del ramo da cui viene effettuata la pull request:

Pertanto, siamo in un commit condizionale B3 e dobbiamo ottenere la differenza con 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.listPer determinare A3 utilizzeremo l'API di 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 pull request;
- $BUDDY_REPO_SLUG — combinazione del nome utente e del repository (ad esempio max/test).
Ora salveremo le modifiche utilizzando il pulsante qui sotto e attiveremo l'analisi della pull request:

A differenza di Travis CI, non abbiamo bisogno di specificare .pvs-studio per la memorizzazione nella cache, poiché Buddy memorizza automaticamente tutti i file per le esecuzioni successive. Quindi resta solo l'ultimo — salvare il login e la password per PVS-Studio in Buddy. Dopo aver salvato le modifiche, torneremo nel Pipeline. Dobbiamo andare alla configurazione delle variabili e aggiungere il login e la chiave per PVS-Studio:

Dopo di ciò, la comparsa di una nuova pull request o di un commit attiverà il controllo. Se il commit contiene errori, Buddy lo segnalerà nella pagina della pull request.
AppVeyor
La configurazione di AppVeyor è simile a quella di Buddy, poiché avviene tutto tramite l'interfaccia web e non è necessario aggiungere un file *.yml nel repository del progetto.
Passiamo alla scheda Impostazioni nel riepilogo del progetto:

Scorriamo verso il basso in questa pagina e abilitiamo il salvataggio della cache per le build delle pull request:

Ora passiamo alla scheda Ambiente, dove specificheremo l'immagine per la build e le necessarie variabili d'ambiente:

Se hai letto le sezioni precedenti, dovresti essere già familiare con queste due variabili — PVS_KEY e PVS_USERNAME. Se non lo sei, ricorda che sono necessarie per la verifica della licenza dell'analizzatore PVS-Studio. In seguito le incontreremo di nuovo negli script Bash.
Nella stessa pagina, in fondo, specificheremo la cartella per la cache:

Se non lo faremo, analizzeremo anziché un paio di file l'intero progetto, ma l'output sarà relativo ai file specificati. Pertanto, è importante inserire il nome corretto della directory.
Ora è il momento dello script per il controllo. Apriamo la scheda Test e selezioniamo Script:

In questo modulo devi incollare il seguente codice:
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 -wGuardiamo la seguente parte del 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
fiL'assegnazione del valore del comando pwd a una variabile, che dovrebbe conservarne il valore per impostazione predefinita, sembra strana a prima vista, ma ora spiegherò tutto.
Durante la configurazione dell'analizzatore in AppVeyor, mi sono imbattuto in un comportamento estremamente strano dell'analizzatore. Da un lato, tutto funzionava correttamente, ma l'analisi non si avviava. Ho speso molto tempo a notare che ci trovavamo nella directory /home/appveyor/projects/testcalc/, mentre l'analizzatore era convinto che fossimo in /opt/appveyor/build-agent/. Allora ho capito che la variabile $PWD sta dicendo un po' di bugie. Per questo motivo, ho aggiornato manualmente il suo valore prima di avviare l'analisi.
E poi il tutto, come prima:

Ora consideriamo 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 per cui è stato dichiarato il pull request. A tal fine, abbiamo bisogno delle seguenti variabili d'ambiente:
- $APPVEYOR_PULL_REQUEST_NUMBER — numero del pull request;
- $APPVEYOR_REPO_NAME — nome utente e repository del progetto.
Conclusione
Certamente, non abbiamo esaminato tutti i possibili servizi di integrazione continua, ma hanno tutte caratteristiche di funzionamento molto simili. A parte la cache, ogni servizio costruisce il proprio "veicolo", quindi ci sono sempre differenze.
In alcuni casi, come in Travis-CI, basta un paio di righe di codice e la cache funziona perfettamente; in altri, come in AppVeyor, è sufficiente indicare una cartella nelle impostazioni; ma in alcuni è necessario creare chiavi uniche e cercare di convincere il sistema a permetterti di sovrascrivere un frammento memorizzato. Pertanto, se desideri configurare l'analisi delle pull request in un servizio di integrazione continua non menzionato sopra, assicurati prima che non ci siano problemi con la cache.
Grazie per l'attenzione. Se hai bisogno di aiuto, non esitare a scriverci a . Ti daremo indicazioni e supporto.
Se desideri condividere questo articolo con un pubblico anglofono, ti prego di utilizzare il link alla traduzione: Maxim Zvyagintsev. .
Fonte: habr.com
