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
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
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
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:
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
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:
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:
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,
Prima di tutto dobbiamo aggiungere una nuova azione alla catena di montaggio:
Indichiamo il compilatore utilizzato per costruire il progetto. Notare il contenitore docker installato in questa azione. Ad esempio, esiste un contenitore speciale per GCC:
Ora installiamo PVS-Studio e le utilità necessarie:
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:
Il fatto è che ora analizziamo non il risultato della fusione, ma l'HEAD del ramo da cui viene effettuata la richiesta di pull:
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:
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:
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:
Scorriamo verso il basso questa pagina e abilitiamo il salvataggio della cache per la raccolta delle richieste pull:
Ora andiamo alla scheda Ambiente, dove specifichiamo l'immagine per l'assembly e le variabili di ambiente necessarie:
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:
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:
È 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:
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
Se desideri condividere questo articolo con un pubblico di lingua inglese, utilizza il link di traduzione: Maxim Zvyagintsev.
Fonte: habr.com