
I PVS-Studio-analyzeren for C- og C++-sprog på Linux og macOS, siden version 7.04, er der blevet tilføjet en testmulighed for at kontrollere listen over angivne filer. Med den nye tilstand kan analysatoren konfigureres til at kontrollere commits og pull requests. I denne artikel vil vi forklare, hvordan man konfigurerer kontrollen af den ændrede fil-liste i et GitHub-projekt i populære CI-systemer som Travis CI, Buddy og AppVeyor.
Filkontrol-tilstand
— er et værktøj til at identificere fejl og potentielle sårbarheder i kildekoden til programmer skrevet i C, C++, C# og Java. Det fungerer på 64-bit-systemer på Windows, Linux og macOS.
I PVS-Studio version 7.04 for Linux og macOS er der blevet tilføjet en tilstand til at kontrollere listen over kildefiler. Dette virker for projekter, hvis buildsystem muliggør generering af en fil . Den er nødvendig for, at analysatoren kan udtrække information om kompileringen af de angivne filer. Hvis dit buildsystem ikke understøtter genereringen af compile_commands.json-filen, kan du prøve at generere en sådan fil ved hjælp af værktøjet .
Den tilstand til kontrol af filerne kan også anvendes sammen med strace-loggen fra kompilatorens eksekveringer (pvs-studio-analyzer trace). For at gøre dette skal du først udføre en fuld bygning af projektet og spore det, så analysatoren kan samle fulde oplysninger om kompilationsparametrene for alle kontrollerede filer.
Men denne mulighed har en betydelig ulempe — man skal enten udføre en fuld sporing af hele projektbygningen ved hver kørselsgang, hvilket i sig selv modsiger ideen om hurtig kontrol af en commit, eller hvis man cacher resultatet af sporet, kan efterfølgende kørsel af analysatoren vise sig at være ufuldstændig, hvis strukturen af afhængighederne til kildefilerne ændres (f.eks. hvis der tilføjes et nyt #include i en af kildefilerne).
Derfor anbefaler vi ikke at bruge tilstanden til kontrol af filerne med sporingsloggen til kontrollen af commits eller pull requests. I tilfælde af at du kan lave en inkrementel bygning under kontrol af en commit, overvej muligheden for at bruge tilstanden .
Listen over kildefiler til analyse gemmes i en tekstfil og videregives til analysatoren med parametret -S:
pvs-studio-analyzer analyze ... -f build/compile_commands.json -S check-list.txtI denne fil angives relative eller absolutte stier til filer, hvor hver ny fil skal være på en ny linje. Det er tilladt ikke kun at angive filnavne til analyse, men også forskellig tekst. Analyseværktøjet vil se, at dette ikke er en fil og ignorere linjen. Dette kan være nyttigt til kommentering, hvis filer angives manuelt. Oftest vil filerne dog blive genereret under analysen i CI, f.eks. kan det være filer fra et commit eller en pull request.
Nu kan denne tilstand hurtigt kontrollere ny kode, før den når hovedudviklingsgrenen. For at systemet reagerer på tilstedeværelsen af advarsler fra analyseværktøjet, skal værktøjet plog-converter en flag er tilføjet —indicate-warnings:
plog-converter ... --indicate-warnings ... -o /path/to/report.tasks ...Med dette flag vil konverteren returnere en ikke-nul kodet, hvis der er advarsler i analyseværket. Denne returkode kan blive blokeret i pre-commit hook, commit eller pull request, og den genererede rapport kan vises på skærmen, deles eller sendes via e-mail.
Bemærk: Ved første gang analysen af filerne køres, vil hele projektet blive analyseret, da analyseværktøjet skal generere en afhængighedsfil mellem projektets kildefiler og headerfiler. Dette er en funktion ved analysen af C og C++ filer. På senere tidspunkter kan afhængighedsfilen caches, og den vil blive opdateret automatisk af analysator. Fordelen ved at kontrollere commits ved hjælp af tilstanden med filliste før brug af inkrementel analyse er, at der kun skal caches denne fil, ikke objektfilerne.
Generelle principper for analyse af pull request
Analysen af hele projektet tager en betydelig tid, så det giver mening kun at kontrollere en del af det. Problemet ligger i at adskille nye filer fra de øvrige filer i projektet.
Lad os overveje et eksempel på en commit-tree med to grene:

Lad os forestille os, at commit A1 indeholder en tilstrækkelig stor mængde kode, som allerede er blevet kontrolleret. Tidligere lavede vi en gren fra commit A1 og ændrede nogle filer.
Du har selvfølgelig bemærket, at efter A1 der er sket yderligere to commits, men disse var også sammenlægninger af andre grene, da vi ikke committer til master. Og nu er tiden kommet, hvor hotfix er klar. Derfor er der oprettet en pull request til sammenlægning B3 og A3.
Selvfølgelig kunne man have tjekket hele resultatet af deres sammensmeltning, men det ville have været for langvarigt og uretfærdigt, da kun få filer var ændret. Derfor er det mere effektivt at analysere kun de ændrede.
For dette vil vi få forskellen mellem brancherne, mens vi befinder os i HEAD-branch, som vi vil flette ind i master:
git diff --name-only HEAD origin/$MERGE_BASE > .pvs-pr.list$MERGE_BASE vi vil se nærmere på senere. Sagen er, at langt fra alle CI-tjenester giver de nødvendige oplysninger om basen til sammensmeltning, så hver gang må man finde nye metoder til at få disse data. Dette vil blive beskrevet detaljeret nedenfor i hver af de beskrevne webtjenester.
Så vi har fået forskellen mellem brancherne, mere præcist - listen over filnavne, der er blevet ændret. Nu skal vi give filen .pvs-pr.list (vi har omdirigeret output ovenfor) til analysatoren:
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
-S .pvs-pr.listEfter analysen skal vi konvertere logfilen (PVS-Studio.log) til et mere overskueligt format:
plog-converter -t errorfile PVS-Studio.log --cerr -wDenne kommando vil udskrive en liste over fejl i (standard outputstrøm for fejlmeddelelser).
Det, vi har brug for, er ikke kun at udskrive fejlene, men også at informere vores service til bygning og test om tilstedeværelsen af problemer. Derfor blev der tilføjet en flag til konverteren -W (—indicate-warnings). Hvis der er blot én advarsel fra analysatoren, vil returværdien for værktøjet plog-converter ændres til 2, hvilket igen vil informere CI-tjenesten om tilstedeværelsen af potentielle fejl i filerne i pull request’en.
Travis CI
Konfigurationen udføres i form af en fil .travis.yml. For nemheds skyld anbefaler jeg at placere alt i et separat bash-script med funktioner, der kaldes fra filen .travis.yml (bash script_navn.sh funktions_navn).
Vi vil tilføje den nødvendige kode til scriptet på bash, således at vi får større funktionalitet. I sektionen installation vil vi skrive følgende:
install:
- bash .travis.sh travis_installHvis du har haft nogen instruktioner, kan du overføre dem til scriptet og fjerne bindestregerne.
Lad os åbne filen .travis.sh og tilføje installationen af analysatoren til funktionen 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
}Lad os nu tilføje til sektionen script kør analyse:
script:
- bash .travis.sh travis_scriptOg i bash-scriptet:
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
}Denne kode skal køres efter projektet er bygget, for eksempel hvis du har udført en byggeproces med CMake:
travis_script() {
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
cmake $CMAKE_ARGS CMakeLists.txt
make -j8
}Det vil se sådan ud:
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
}Du har nok allerede bemærket de nævnte miljøvariabler $TRAVIS_PULL_REQUEST og $TRAVIS_BRANCH. Travis CI erklærer dem selv:
- $TRAVIS_PULL_REQUEST opbevarer nummeret på pull requesten eller false, hvis det er en almindelig gren;
- $TRAVIS_REPO_SLUG opbevarer navnet på projektets repository.
Algoritmen for denne funktion:

Travis CI reagerer på returkoder, så tilstedeværelsen af advarsler vil indikere for tjenesten at markere committet som indeholdende fejl.
Lad os nu se nærmere på denne kode:
git diff --name-only origin/HEAD > .pvs-pr.listTravis CI udfører automatisk merge af grene under analysen af pull requests:

Derfor analyserer vi A4, ikke B3->A3. På grund af denne egenskab skal vi beregne forskellen med A3, som netop er toppen af grenen fra origin.
Der er en vigtig detalje tilbage — cache af headerfiler fra de kildemoduler, der bliver kompileret (*.c, *.cc, *.cpp osv.). Disse afhængigheder beregner analysatoren ved første kørsel i filkontroltilstand og gemmer derefter i kataloget .PVS-Studio. Travis CI tillader caching af mapper, så vi vil gemme data fra kataloget .PVS-Studio/:
cache:
directories:
- .PVS-Studio/Denne kode skal tilføjes til filen .travis.yml. Denne mappe indeholder forskellige data indsamlet efter analysen, som væsentligt vil accelerere efterfølgende analyser af filerne eller inkrementelle analyser. Hvis dette ikke gøres, vil analysatoren faktisk skulle analysere alle filer hver gang.
Buddy
Ligesom Travis CI, giver mulighed for automatiseret bygning og test af projekter, der er gemt på GitHub. I modsætning til Travis CI konfigureres det i webgrænsefladen (der er understøttelse af bash), så det er ikke nødvendigt at gemme konfigurationsfiler i projektet.
Først skal vi tilføje en ny handling til byggeprocessen:

Vi angiver den compiler, der blev brugt til at bygge projektet. Bemærk docker-containeren, der er installeret i denne handling. For eksempel er der en speciel container til GCC:

Nu installerer vi PVS-Studio og nødvendige værktøjer:

Tilføj følgende linjer til redaktøren:
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-studioLad os nu gå til fanen Kør (det første ikon) og tilføje følgende kode i det relevante felt i redaktøren:
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 -wHvis du har læst afsnittet om Travs-CI, er denne kode allerede bekendt for dig, men nu er der et nyt trin:

Kernen er, at vi nu analyserer ikke resultatet af sammensmeltningen, men HEAD fra den gren, hvorfra pull requesten laves:

Derfor befinder vi os i en betinget commit B3 og vi skal finde forskellen med 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.listTil definition A3 vi bruger GitHub API:
https://api.github.com/repos/${USERNAME}/${REPO}/pulls/${PULL_REQUEST_ID}Vi har brugt følgende variabler, som Buddy stiller til rådighed:
- $BUDDY_EXECUTION_PULL_REQEUST_NO — nummeret på pull requesten;
- $BUDDY_REPO_SLUG — kombinationen af brugernavn og repository (f.eks. max/test).
Lad os gemme ændringerne ved at bruge knappen nedenfor og aktivere analysen af pull request:

I modsætning til Travis CI har vi ikke behov for at angive .pvs-studio til caching, da Buddy automatisk cacher alle filer til efterfølgende kørsel. Derfor er det sidste trin at gemme login og adgangskode til PVS-Studio i Buddy. Efter gemning af ændringerne vil vi vende tilbage til Pipeline. Vi skal gå til indstillingerne for variabler og tilføje login og nøgle til PVS-Studio:

Efter dette vil fremkomsten af en ny pull request eller commit starte tjekket. Hvis commit indeholder fejl, vil Buddy vise det på siden for pull request.
AppVeyor
Opsætningen af AppVeyor ligner Buddy, da alt foregår i webgrænsefladen og der ikke er behov for at tilføje en *.yml-fil til projektets repository.
Lad os gå til fanen Indstillinger i projektoversigten:

Lad os rulle ned på denne side og aktivere cachelagring for bygninger af pull requests:

Nu skal vi gå til fanen Miljø, hvor vi angiver billedet til bygning og de nødvendige miljøvariabler:

Hvis du har læst de tidligere sektioner, er du godt bekendt med disse to variabler — PVS_KEY og PVS_USERNAME. Hvis ikke, så lad mig minde om, at de er nødvendige for validering af licensen for PVS-Studio analysatoren. På et senere tidspunkt vil vi møde dem igen i Bash scripts.
På denne side nederst skal vi angive mappen til caching:

Hvis vi ikke gør det, vil vi analysere hele projektet i stedet for et par filer, men outputtet vil kun være baseret på de angivne filer. Derfor er det vigtigt at indtaste det korrekte navn på mappen.
Nu er det tid til scriptet til tjekket. Lad os åbne fanen Tests og vælge Script:

I denne formular skal du indsætte følgende kode:
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 -wLad os se nærmere på den næste del af koden:
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
fiDet kan virke mærkeligt ved første øjekast med dette specifikke tildeling af output fra kommandolinjen pwd til en variabel, der skal gemme denne værdi som standard, men jeg vil nu forklare det.
Da jeg opsatte analysatoren i AppVeyor, oplevede jeg en yderst mærkelig adfærd fra analysatoren. På den ene side fungerede alt korrekt, men analysen startede ikke. Jeg brugte en del tid på at bemærke, at vi befandt os i mappen /home/appveyor/projects/testcalc/, mens analysatoren troede, at vi var i /opt/appveyor/build-agent/. Så indså jeg, at variablen $PWD lyver lidt. Af denne grund opdaterede jeg manuelt dens værdi før jeg startede analysen.
Og så videre, som før:

Lad os nu se på følgende 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"`Her får vi forskellen mellem de grene, der er erklæret i pull requesten. For dette har vi brug for følgende miljøvariabler:
- $APPVEYOR_PULL_REQUEST_NUMBER — nummeret på pull requesten;
- $APPVEYOR_REPO_NAME — brugernavnet og projektets repository.
Konklusion
Selvfølgelig har vi ikke dækket alle de mulige kontinuerlige integrationsservices, men de har alle en meget lignende arbejdsgang. Med undtagelse af caching, laver hver service sin egen "cykel", så derfor er der altid forskelle.
Nogle steder, som i Travis-CI, kræver et par linjer kode og caching fungerer perfekt; andre steder, som i AppVeyor, skal man blot angive mappen i indstillingerne; men nogle steder skal man oprette unikke nøgler og forsøge at overbevise systemet om at lade dig overskrive den cachede del. Derfor, hvis du ønsker at opsætte analyse af pull requests på en kontinuerlig integrationsservice, der ikke er blevet beskrevet ovenfor, skal du først sikre dig, at du ikke får problemer med caching.
Tak for opmærksomheden. Hvis der er noget, der ikke fungere, så tøv ikke med at skrive til os på . Vi vil rådgive og hjælpe.
Hvis du ønsker at dele denne artikel med et engelsktalende publikum, bedes du bruge linket til oversættelsen: Maxim Zvyagintsev. .
Kilde: habr.com
