
U PVS-Studio analizeru za jezike C i C++ na Linuxu i macOS-u, od verzije 7.04, uvedena je testna mogućnost provjere popisa navedenih fajlova. Novim režimom možete konfigurirati analizer za provjeru commit-a i pull request-ova. U ovom članku bit će objašnjeno kako postaviti provjeru popisa izmijenjenih fajlova GitHub projekta u popularnim CI (Continuous Integration) sustavima kao što su Travis CI, Buddy i AppVeyor.
Režim provjere popisa datoteka
je alat za otkrivanje grešaka i potencijalnih sigurnosnih ranjivosti u izvornoj šifri programa napisanih na jezicima C, C++, C# i Java. Radi na 64-bitnim sistemima na Windowsu, Linuxu i macOS-u.
U verziji PVS-Studio 7.04 za Linux i macOS uveden je režim provjere popisa izvornih fajlova. Ovaj režim radi za projekte čiji sustav gradnje omogućava generiranje fajla . Potreban je da bi analizer izvukao informacije o kompilaciji navedenih fajlova. Ako vaš sustav gradnje ne podržava generiranje fajla compile_commands.json, možete pokušati generirati takav fajl pomoću alata .
Također, režim provjere popisa fajlova može se koristiti zajedno s logom traganja strace pokretanja kompajlera (pvs-studio-analyzer trace). Za to prvo trebate provesti potpunu gradnju projekta i praćenje, kako bi analizer prikupio potpune informacije o parametrima kompilacije svih provjerenih fajlova.
Međutim, takva opcija ima značajan nedostatak — trebati ćete ili provoditi potpuno praćenje gradnje cijelog projekta pri svakom pokretanju, što je samo po sebi u suprotnosti s idejom brze provjere commit-a. Ili, ako se rezultati praćenja keširaju, sljedeća pokretanja analizeru mogu biti nepotpuna ako se nakon praćenja promijeni struktura zavisnosti izvornih fajlova (npr. ako se u jedan od izvornih fajlova doda novi #include).
Stoga ne preporučujemo korištenje režima provjere popisa fajlova s logom praćenja za provjeru commit-a ili pull request-ova. Ako možete napraviti inkrementalnu gradnju prilikom provjere commit-a, razmotrite mogućnost korištenja režima .
Popis izvornih fajlova za analizu pohranjuje se u tekstualnu datoteku i prosljeđuje analizeru putem parametra -S:
pvs-studio-analyzer analyze ... -f build/compile_commands.json -S check-list.txtU ovoj datoteci se navode relativne ili apsolutne putanje do datoteka, pri čemu svaka nova datoteka mora biti na novom redu. Dozvoljeno je navoditi ne samo imena datoteka za analizu, već i razni tekst. Analizator će vidjeti da to nije datoteka i ignorirati red. To može biti korisno za komentiranje, ako se datoteke navode ručno. Ipak, često će lista datoteka biti generisana tokom analize u CI, na primjer, to mogu biti datoteke iz commita ili pull requesta.
Sada je uz pomoć ovog moda moguće brzo provjeravati novi kod prije nego što uđe u glavnu razvojnu granu. Da bi sistem provjere reagovao na prisutnost upozorenja analizatora, u alatku plog-converter dodana je zastavica —indicate-warnings:
plog-converter ... --indicate-warnings ... -o /path/to/report.tasks ...S ovom oznakom, konverter će vratiti nenulti kod ako u izvještaju analizatora postoje upozorenja. Prema kodu povrata može se blokirati pre-commit hook, commit ili pull request, a generisani izvještaj analizatora ispisati na ekran, dijeliti ili slati e-poštom.
Napomena. Prilikom prvog pokretanja analize liste datoteka, biće analiziran ceo projekat, jer analizator mora generisati datoteku zavisnosti izvornih datoteka projekta od zaglavnih datoteka. To je osobina analize C i C++ datoteka. U budućnosti se datoteka zavisnosti može keširati i automatski će je ažurirati analizator. Prednost provjere commit-a prilikom korišćenja moda provjere liste datoteka u odnosu na korišćenje moda inkrementalne analize je u tome što se mora keširati samo ova datoteka, a ne objekti.
Opća načela analize pull request-a
Analiza celog projekta traje dovoljno dugo, stoga ima smisla provjeravati samo njegov deo. Problem je što treba odvojiti nove datoteke od ostalih datoteka projekta.
Razmotrimo primer stabla commit-a sa dve grane:

Pretpostavimo da commit A1 sadrži dovoljno veliku količinu koda koji je već provjeren. Malo ranije smo uradili granu od commita A1 i menjali neke datoteke.
Naravno, primijetili ste da su se nakon A1 desila još dva commita, ali to su takođe bila spajanja drugih grana, jer ne radimo commit u master. I tako je došlo vreme kada hotfix-a je spreman. Stoga je otvoren pull request za spajanje B3 i A3.
Naravno, mogli bismo provjeriti cijeli rezultat njihove kombinacije, ali bi to bilo predugo i neopravdano, s obzirom na to da je promijenjeno samo nekoliko datoteka. Stoga je učinkovitije analizirati samo promijenjene.
Za to ćemo dobiti razliku između grana, nalazeći se u HEAD grani iz koje želimo objediniti u master:
git diff --name-only HEAD origin/$MERGE_BASE > .pvs-pr.list$MERGE_BASE o tome ćemo detaljno govoriti kasnije. Naime, nije svaki CI servis u stanju pružiti potrebne informacije o osnovici za spajanje, pa svako malo moramo izmišljati nove načine za dobivanje tih podataka. To će biti detaljno opisano u nastavku za svaki od opisani web-servisa.
Dakle, dobili smo razliku između grana, točnije — popis imena datoteka koje su promijenjene. Sada trebamo predati datoteku .pvs-pr.list (u koju smo preusmjerili izlaz iznad) analizeru:
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
-S .pvs-pr.listNakon analize, trebamo konvertirati log datoteku (PVS-Studio.log) u prikladniji format:
plog-converter -t errorfile PVS-Studio.log --cerr -wOva naredba će ispisati popis grešaka u (standardni tok izlaza poruka o greškama).
Samo što trebamo ne samo prikazati greške, već i obavijestiti naš servis za izgradnju i testiranje o prisutnosti problema. Za to je konverteru dodat oznaku -W (—indicate-warnings). U slučaju da postoji barem jedno upozorenje analize, povratna vrijednost alata plog-converter će se promijeniti na 2, što će, zauzvrat, obavijestiti CI servis o mogućim greškama u datotekama pull request-a.
Travis CI
Konfiguracija je rađena u obliku datoteke .travis.yml. Za praktičnost preporučujem da sve izdvojite u odvojeni bash skript s funkcijama koje će biti pozvane iz datoteke .travis.yml (bash ime_skripta.sh ime_funkcije).
Dodavat ćemo potrebni kod u skript na bash, tako ćemo dobiti veću funkcionalnost. U odjeljku install napisat ćemo sljedeće:
install:
- bash .travis.sh travis_installAko ste imali neke upute, možete ih prenijeti u skript, uklonivši crte.
Otvorit ćemo datoteku .travis.sh i dodati instalaciju analizatora u funkciju 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
}Sada dodajmo u odjeljak script pokretanje analize:
script:
- bash .travis.sh travis_scriptI u bash skripti:
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
}Ovaj kod treba pokrenuti nakon izgradnje projekta, na primjer, ako ste imali izgradnju na CMake:
travis_script() {
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
cmake $CMAKE_ARGS CMakeLists.txt
make -j8
}Ispisat će se ovako:
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
}Vjerojatno ste već primijetili navedene varijable okruženja $TRAVIS_PULL_REQUEST i $TRAVIS_BRANCH. Travis CI ih automatski proglašava:
- $TRAVIS_PULL_REQUEST čuva broj pull request-a ili false, ako je to obična grana;
- $TRAVIS_REPO_SLUG čuva ime repozitorija projekta.
Algoritam ove funkcije je:

Travis CI reagira na povratne kodove, tako da će prisutnost upozorenja označiti commit kao sadržavajući greške.
A sada hajde da detaljnije razmotrimo ovu liniju koda:
git diff --name-only origin/HEAD > .pvs-pr.listNaime, Travis CI automatski spaja grane tokom analize pull request-a:

Zato analiziramo A4, a ne B3->A3. Zbog ove karakteristike trebamo izračunati razliku s A3, koja je upravo vrh grane iz origin.
Preostala je jedna važna stvar - keširanje zavisnosti zaglavlja od kompiliranih jedinica prevodnog programa (*.c, *.cc, *.cpp itd.). Ove zavisnosti analizer izračunava prilikom prvog pokretanja u režimu provjere liste fajlova i zatim ih čuva u direktoriju .PVS-Studio. Travis CI omogućava keširanje direktorija, tako da ćemo sačuvati podatke iz direktorija .PVS-Studio/:
cache:
directories:
- .PVS-Studio/Ovaj kod trebate dodati u datoteku .travis.yml. Ova direktorij pohranjuje različite podatke prikupljene tijekom analize, što značajno ubrzava buduće pokrete analize popisa datoteka ili inkrementalne analize. Ako to ne učinite, analizator će zapravo svaki put analizirati sve datoteke.
Buddy
Poput Travis CI, nudi mogućnost automatizirane izgradnje i testiranja projekata pohranjenih na GitHub-u. Za razliku od Travisa CI, konfigurira se putem web sučelja (podrška za bash je dostupna), tako da nije potrebno pohranjivati datoteke konfiguracije u projektu.
Prvo, potrebno je dodati novu akciju u liniju izgradnje:

Odredit ćemo prevodilac koji se koristi za izgradnju projekta. Obratite pažnju na docker kontejner koji je postavljen u ovoj akciji. Na primjer, za GCC postoji poseban kontejner:

Sada ćemo instalirati PVS-Studio i potrebne alate:

Dodajte sljedeće retke u uređivač:
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-studioSada ćemo preći na karticu Run (prva ikona) i u odgovarajuće polje uređivača dodati sljedeći kod:
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 -wAko ste čitali odjeljak posvećen Travs-CI, ovaj kod vam je već poznat, međutim, sada je nastao novi korak:

Naime, sada analiziramo ne rezultat spajanja, već HEAD grane iz koje se radi pull request:

Stoga se nalazimo u uvjetnom commitu B3 i moramo dobiti razliku s 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.listZa određivanje A3 iskorištavamo GitHub API:
https://api.github.com/repos/${USERNAME}/${REPO}/pulls/${PULL_REQUEST_ID}Koristili smo sljedeće varijable koje nudi Buddy:
- $BUDDY_EXECUTION_PULL_REQEUST_NO — broj pull request-a;
- $BUDDY_REPO_SLUG — kombinacija imena korisnika i repozitorija (npr. max/test).
Sada ćemo spremiti promjene koristeći gumb dolje i uključiti analizu pull request-a:

Za razliku od Travisa CI, ne moramo navoditi .pvs-studio za keširanje, jer Buddy automatski kešira sve datoteke za sljedeće pokretanje. Preostaje nam samo da sačuvamo korisničko ime i lozinku za PVS-Studio u Buddy-u. Nakon spremanja promjena, vratit ćemo se u Pipeline. Moramo preći na postavke varijabli i dodati korisničko ime i ključ za PVS-Studio:

Nakon toga, pojava novog pull request-a ili commita pokrenut će provjeru. Ako commit sadrži pogreške, Buddy će to naznačiti na stranici pull request-a.
AppVeyor
Postavka AppVeyor-a slična je Buddy-u, jer sve odvija u web sučelju i nije potrebno dodavati *.yml datoteku u repozitorij projekta.
Pređimo na karticu Postavke u pregledu projekta:

Pomaknimo ovu stranicu prema dolje i uključimo spremanje keša za izgradnju pull request-a:

Sada ćemo preći na karticu Okruženje, gdje ćemo navesti sliku za izgradnju i potrebne varijable okruženja:

Ako ste pročitali prethodne odjeljke, dobro ste upoznati s ovim dvjema varijablama — PVS_KEY i PVS_USERNAME. Ako niste, podsjetit ću da su one potrebne za provjeru licence analizatora PVS-Studio. U budućnosti ćemo ih ponovno sresti u Bash skriptama.
Na istoj stranici dolje navest ćemo mapu za keširanje:

Ako to ne učinimo, analizirat ćemo cijeli projekt umjesto nekoliko datoteka, no izlaz ćemo dobiti prema navedenim datotekama. Stoga je važno unijeti ispravno ime direktorija.
Sada je došlo vrijeme za skriptu za provjeru. Otvorimo karticu Testovi i odaberimo Skriptu:

U ovu formu trebate umetnuti sljedeći kod:
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 -wObratite pažnju na sljedeći dio koda:
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
fiNa prvi pogled, dodjeljivanje vrijednosti komande pwd varijabli koja bi trebala pohraniti tu vrijednost po defaultu, izgleda čudno, ali sve ću objasniti.
Tijekom postavljanja analizatora u AppVeyoru, naišao sam na vrlo neobično ponašanje analizatora. S jedne strane, sve je radilo ispravno, ali analiza se nije pokretala. Potrošio sam dosta vremena primijetivši da se nalazimo u direktoriju /home/appveyor/projects/testcalc/, dok analizator misli da smo u /opt/appveyor/build-agent/. Tada sam shvatio da varijabla $PWD malo laže. Zbog toga sam ručno ažurirao njezinu vrijednost prije pokretanja analize.
I dalje je sve kao i prije:

Sada razmotrimo sljedeći dio:
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"`U njemu dobivamo razliku između grana na kojima je izjavljen pull request. Za to su nam potrebne sljedeće varijable okruženja:
- $APPVEYOR_PULL_REQUEST_NUMBER — broj pull requesta;
- $APPVEYOR_REPO_NAME — ime korisnika i repozitorij projekta.
Zaključak
Naravno, nismo razmotrili sve moguće servise kontinuirane integracije, ali svi oni imaju veoma slične specifičnosti rada. Osim keširanja, svaki servis pravi svoj "bicikl", zato je sve uvijek drugačije.
Negdje, kao u Travis-CI, par redaka koda i keširanje radi besprijekorno; negdje, kao u AppVeyoru, treba jednostavno odabrati mapu u postavkama; ali negdje treba stvarati jedinstvene ključeve i pokušavati uvjeriti sustav da ti omogući prepisivanje keširanog fragmenta. Zato, ako želite postaviti analizu pull requestova na servisu kontinuirane integracije koji nije obrađen gore, prvo se uvjerite da nećete imati problema s keširanjem.
Hvala na pažnji. Ako nešto ne ide, slobodno nam pišite na . Pomoći ćemo vam.
Ako želite podijeliti ovaj članak s engleskim čitateljima, molimo vas da upotrijebite poveznicu na prijevod: Maxim Zvyagintsev. .
Izvor: habr.com
