
În analiza PVS-Studio pentru limbile C și C++ pe Linux și macOS, începând cu versiunea 7.04, a fost introdusă o funcționalitate experimentală care permite verificarea listei de fișiere specificate. Prin noul mod, analiza poate fi configurată pentru a verifica commit-urile și pull request-urile. În acest articol, vom discuta despre cum să configurăm verificarea listei de fișiere modificate pentru proiectele de pe GitHub în sisteme CI (Integrare Continuă) populare, cum ar fi Travis CI, Buddy și AppVeyor.
Modul de verificare a listei de fișiere
— este un instrument pentru identificarea erorilor și vulnerabilităților potențiale din codul sursă al programelor scrise în limbile C, C++, C# și Java. Funcționează pe sisteme de 64 de biți pe Windows, Linux și macOS.
În versiunea PVS-Studio 7.04 pentru Linux și macOS a fost introdus modul de verificare a listei de fișiere sursă. Acesta funcționează pentru proiecte ale căror sistem de construire poate genera un fișier . Acesta este necesar pentru ca analizatorul să extragă informații despre compilarea fișierelor specificate. Dacă sistemul tău de construire nu permite generarea fișierului compile_commands.json, poți încerca să generezi un astfel de fișier folosind utilitarul .
De asemenea, modul de verificare a listei de fișiere poate fi utilizat împreună cu jurnalul de urmărire strace al lansărilor compilatoarelor (pvs-studio-analyzer trace). Pentru aceasta, va trebui mai întâi să efectuezi o construcție completă a proiectului și să o urmărești, astfel încât analizatorul să colecteze informații complete despre parametrii de compilare pentru toate fișierele verificate.
Cu toate acestea, această opțiune are un dezavantaj semnificativ — va fi nevoie fie să efectuezi o urmărire completă a construcției întregului proiect la fiecare lansare, ceea ce contrazice ideea de verificare rapidă a commit-ului. Fie, dacă reții rezultatul urmării, lansările ulterioare ale analizatorului pot fi incomplete, dacă după urmărire se schimbă structura dependențelor fișierelor sursă (de exemplu, se adaugă un nou #include într-unul din fișierele sursă).
Prin urmare, nu recomandăm utilizarea modului de verificare a listei de fișiere cu jurnalul de urmărire pentru verificarea commit-urilor sau a pull request-urilor. Dacă poți efectua o construcție incrementală în timpul verificării commit-ului, ia în considerare utilizarea modului .
Lista fișierelor sursă de analizat este salvată într-un fișier text și transmisă analizatorului prin parametrul -S:
pvs-studio-analyzer analyze ... -f build/compile_commands.json -S check-list.txtÎn acest fișier se specifică căile relative sau absolute către fișiere, fiecare fișier nou trebuind să fie pe o linie nouă. Este permis să se specifice nu doar numele fișierelor pentru analiză, ci și diferite texte. Analizatorul va observa că acesta nu este un fișier și va ignora linia. Acest lucru poate fi util pentru comentarii, în cazul în care fișierele sunt specificate manual. Totuși, de multe ori lista fișierelor va fi generată în timpul analizei în CI, de exemplu, acestea pot fi fișiere dintr-un commit sau pull request.
Acum, cu ajutorul acestui mod, este posibil să verificați rapid noul cod înainte de a ajunge în ramura principală de dezvoltare. Pentru ca sistemul de verificare să reacționeze la avertizările analizatorului, în utilitarul plog-converter a fost adăugat un flag —indicate-warnings:
plog-converter ... --indicate-warnings ... -o /path/to/report.tasks ...Cu acest flag, convertorul va returna un cod diferit de zero dacă în raportul analizatorului există avertizări. Pe baza codului de returnare, se poate bloca hook-ul de precommit, commit-ul sau pull request-ul, iar raportul generat al analizatorului poate fi afișat pe ecran, partajat sau trimis prin email.
Notă. La prima rulare, analiza listei de fișiere va analiza întregul proiect, deoarece analizatorul trebuie să genereze fișierul de dependențe al fișierelor sursă ale proiectului față de fișierele header. Aceasta este o caracteristică a analizei fișierelor C și C++. Ulterior, fișierul de dependențe poate fi cached și va fi actualizat automat de analizator. Avantajul verificării commit-urilor în utilizarea modului de verificare a listei de fișiere față de utilizarea modului de analiză incrementală este că trebuie să se cacheze doar acest fișier, nu și fișierele obiect.
Principiile generale ale analizei pull request-ului
Analiza întregului proiect durează destul de mult timp, astfel că are sens să se verifice doar o anumită parte a acestuia. Problema este că trebuie să se separe fișierele noi de celelalte fișiere ale proiectului.
Să luăm un exemplu de arbore de commit-uri cu două ramuri:

Să presupunem că commit-ul A1 conține o cantitate destul de mare de cod care a fost deja verificat. Cu puțin timp înainte, am făcut o ramură din commit-ul A1 și am modificat anumite fișiere.
Desigur, ați observat că după A1 au avut loc încă două commit-uri, dar acestea au fost, de asemenea, fuziuni ale altor ramuri, deoarece nu ne angajăm în master. Și acum a venit vremea când hotfix este gata. De aceea a apărut un pull request pentru fuziune B3 și A3.
Desigur, ar fi fost posibil să verificăm întregul rezultat al fuziunii lor, dar ar fi fost prea lung și nejustificat, deoarece doar câteva fișiere au fost modificate. Prin urmare, este mai eficient să analizăm doar fișierele modificate.
Pentru aceasta, vom obține diferența dintre ramuri, aflându-ne în ramura HEAD din care dorim să fuzionăm în master:
git diff --name-only HEAD origin/$MERGE_BASE > .pvs-pr.list$MERGE_BASE vom analiza acest aspect în detaliu mai târziu. Problema este că nu toate serviciile CI oferă informațiile necesare despre baza pentru fuziune, astfel că, de fiecare dată, este nevoie să găsim noi metode de a obține aceste date. Acest lucru va fi descris în detaliu mai jos pentru fiecare dintre serviciile web descrise.
Așadar, am obținut diferența dintre ramuri, mai precis — lista numelui fișierelor care au fost modificate. Acum trebuie să oferim fișierul .pvs-pr.list (în care am redirecționat ieșirea mai sus) analizorului:
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
-S .pvs-pr.listDupă analiză, trebuie să convertim fișierul de loguri (PVS-Studio.log) într-un format mai ușor de înțeles:
plog-converter -t errorfile PVS-Studio.log --cerr -wAceastă comandă va genera o listă de erori în (fluxul standard de ieșire al mesajelor de eroare).
Totuși, trebuie să nu doar afișăm erorile, ci și să informăm serviciul nostru pentru compilare și testare despre existența problemelor. Pentru aceasta, în converter a fost adăugat un flag -W (—indicate-warnings). În cazul în care există cel puțin un avertisment al analizorului, codul de returnare al utilitarului plog-converter se va schimba la 2, ceea ce, la rândul său, va informa serviciul CI despre existența posibilelor erori în fișierele pull request-ului.
Travis CI
Configurația este realizată sub formă de fișier .travis.yml. Pentru comoditate, recomand să mutați totul într-un script bash separat cu funcții care vor fi apelate din fișierul .travis.yml (bash nume_script.sh nume_funcție).
Vom adăuga codul necesar în script în bash, astfel obținem o funcționalitate mai mare. În secțiunea install vom scrie următoarele:
install:
- bash .travis.sh travis_installDacă ați avut vreo instrucțiune, o puteți muta în script, eliminând liniile cu semne de minus.
Vom deschide fișierul .travis.sh și vom adăuga instalarea analizorului în funcția 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
}Acum să adăugăm în secțiunea script execuția analizei:
script:
- bash .travis.sh travis_scriptȘi în scriptul 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
}Acest cod trebuie rulat după compilarea proiectului, de exemplu, dacă ați avut o compilare cu CMake:
travis_script() {
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
cmake $CMAKE_ARGS CMakeLists.txt
make -j8
}Va trebui să arate astfel:
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
}Probabil ați observat deja variabilele de mediu menționate: $TRAVIS_PULL_REQUEST și $TRAVIS_BRANCH. Travis CI le declară de la sine:
- $TRAVIS_PULL_REQUEST păstrează numărul cererii de extragere sau false, dacă este o ramură obișnuită;
- $TRAVIS_REPO_SLUG păstrează numele depozitului proiectului.
Algoritmul de funcționare al acestei funcții:

Travis CI reacționează la codurile de returnare, astfel încât prezența avertismentelor îi va indica serviciului să marcheze comiterea ca având erori.
Acum să examinăm mai în detaliu această linie de cod:
git diff --name-only origin/HEAD > .pvs-pr.listProblema este că Travis CI realizează automat fuziunea ramurilor în timpul analizei cererii de extragere:

De aceea analizăm A4, nu B3->A3. Din această caracteristică trebuie să calculăm diferența cu A3, care este vârful ramurii din origin.
A mai rămas un detaliu important - caching-ul dependențelor fișierelor header din unitățile de compilare (*.c, *.cc, *.cpp etc.). Aceste dependențe sunt calculate de analyzer la prima rulare în modul de verificare a listei de fișiere și sunt apoi salvate în directorul .PVS-Studio. Travis CI permite caching-ul folderelor, așa că vom salva datele directorului .PVS-Studio/:
cache:
directories:
- .PVS-Studio/Acest cod trebuie adăugat în fișier .travis.yml. Această directoare conține diverse date, colectate în urma analizei, care accelerează semnificativ următoarele lansări ale analizei listei de fișiere sau analiza incrementală. Dacă acest lucru nu se face, analistul va analiza, de fapt, toate fișierele de fiecare dată.
Buddy
La fel ca Travis CI, oferă posibilitatea de a construi și testa automat proiecte care sunt stocate pe GitHub. Spre deosebire de Travis CI, acesta se configurează în interfața web (există suport pentru bash), așa că nu este necesar să păstrați fișierele de configurare în proiect.
În primul rând, trebuie să adăugăm o nouă acțiune în linia de construire:

Vom specifica compilatorul care a fost folosit pentru a construi proiectul. Rețineți containerul docker care este instalat în această acțiune. De exemplu, pentru GCC există un container special:

Acum vom instala PVS-Studio și utilitarele necesare:

Adăugați următoarele linii în 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-studioAcum trecem la tab-ul Run (prima pictogramă) și în câmpul corespunzător editorului adăugăm următorul cod:
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 -wDacă ați citit secțiunea dedicată Travs-CI, acest cod vă este deja familiar, însă acum a apărut o nouă etapă:

Ideea este că acum analizăm nu rezultatul fuziunii, ci HEAD-ul ramurii din care se face pull request:

Prin urmare, ne aflăm într-un commit condiționat B3 și trebuie să obținem diferența cu 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.listPentru identificarea A3 vom folosi API-ul GitHub:
https://api.github.com/repos/${USERNAME}/${REPO}/pulls/${PULL_REQUEST_ID}Am folosit următoarele variabile pe care le oferă Buddy:
- $BUDDY_EXECUTION_PULL_REQEUST_NO — numărul pull request-ului;
- $BUDDY_REPO_SLUG — combinația dintre numele utilizatorului și repository (de exemplu, max/test).
Acum vom salva modificările utilizând butonul de mai jos și vom activa analiza pull request-ului:

Spre deosebire de Travis CI, nu este necesar să indicăm .pvs-studio pentru cache, deoarece Buddy cache-uiește automat toate fișierele pentru execuțiile viitoare. Așadar, rămâne ultimul pas – să salvăm utilizatorul și parola pentru PVS-Studio în Buddy. După salvarea modificărilor, ne vom întoarce în Pipeline. Trebuie să mergem la configurarea variabilelor și să adăugăm utilizatorul și cheia pentru PVS-Studio:

După aceasta, apariția unui nou pull request sau commit va declanșa verificarea. Dacă commitul conține erori, Buddy va indica acest lucru pe pagina pull request-ului.
AppVeyor
Configurarea AppVeyor este similară cu cea de la Buddy, deoarece totul se desfășoară în interfața web și nu este necesar să adăugăm un fișier *.yml în repository-ul proiectului.
Să mergem la fila Settings în vizualizarea proiectului:

Derulăm această pagină în jos și activăm salvarea cache-ului pentru build-urile pull request-urilor:

Acum mergem la fila Environment, unde vom specifica imaginea pentru build și variabilele de mediu necesare:

Dacă ați citit secțiunile anterioare, sunteți familiarizat cu aceste două variabile – PVS_KEY și PVS_USERNAME. Dacă nu, îmi permit să reamintesc că acestea sunt necesare pentru verificarea licenței analizorului PVS-Studio. În continuare, ne vom întâlni din nou cu ele în scripturile Bash.
Pe aceeași pagină, în partea de jos, vom specifica folderul pentru cache:

Dacă nu facem acest lucru, vom analiza întreaga stațiune în loc de câteva fișiere, dar rezultatul va fi generat doar pentru fișierele specificate. Așadar, este important să introducem numele corect al directorului.
Acum a venit vremea scriptului pentru verificare. Să deschidem fila Tests și să selectăm Script:

În acest formular trebuie să inserați următorul cod:
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 -wSă ne concentrăm asupra următoarei părți a codului:
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
fiDeși la prima vedere actualmente, atribuirea unui parametru pentru comanda pwd unei variabile ar părea ciudată, permiteți-mi să explic.
În timpul configurării analistului în AppVeyor, am întâmpinat un comportament extrem de ciudat al acestuia. Pe de o parte, totul funcționa corect, dar analiza nu era inițiată. Am petrecut mult timp realizând că ne aflăm în directorul /home/appveyor/projects/testcalc/, în timp ce analistul era sigur că ne aflăm în /opt/appveyor/build-agent/. Atunci am realizat că variabila $PWD minte puțin. Din acest motiv, am actualizat manual valoarea acesteia înainte de a porni analiza.
Și apoi, totul este ca înainte:

Acum să analizăm următorul fragment:
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"`Aici obținem diferența între ramurile pentru care a fost declarat pull request. Pentru aceasta, avem nevoie de următoarele variabile de mediu:
- $APPVEYOR_PULL_REQUEST_NUMBER — numărul pull request-ului;
- $APPVEYOR_REPO_NAME — numele utilizatorului și depozitul proiectului.
Concluzie
Desigur, nu am acoperit toate serviciile posibile de integrare continuă, dar toate au o specificitate extrem de similară. Cu excepția caching-ului, fiecare serviciu creează propriul „bicicletă”, astfel că, întotdeauna, lucrurile funcționează diferit.
Undeva, ca în Travis-CI, câteva linii de cod și caching-ul funcționează perfect; altundeva, ca în AppVeyor, trebuie doar să specifici folderul în setări; dar în alte cazuri, trebuie să creezi chei unice și să încerci să convingi sistemul să-ți permită să rescrii fragmentul cache-uit. Așadar, dacă doriți să configurați analiza pull request-urilor pe un serviciu de integrare continuă care nu a fost analizat mai sus, asigurați-vă mai întâi că nu veți întâmpina probleme cu caching-ul.
Vă mulțumim pentru atenție. Dacă ceva nu funcționează, nu ezitați să ne scrieți la . Vă vom ajuta și vă vom oferi suport.
Dacă doriți să împărtășiți acest articol cu o audiență vorbitoare de engleză, vă rog să folosiți linkul către traducere: Maxim Zvyagintsev. .
Sursa: habr.com
