У аналізатары PVS-Studio для моў З і C++ на Linux і macOS, пачынальна з версіі 7.04, з'явілася тэставая магчымасць праверыць спіс паказаных файлаў. З дапамогай новага рэжыму можна наладзіць аналізатар для праверкі коммітаў і pull request'аў. У гэтым артыкуле будзе расказана, як наладзіць праверку спісу змененых файлаў GitHub-праекта ў такіх папулярных CI (Continuous Integration) сістэмах, як Travis CI, Buddy і AppVeyor.
Рэжым праверкі спісу файлаў
У версіі PVS-Studio 7.04 для Linux і macOS з'явіўся рэжым праверкі спісу зыходных файлаў. Працуе гэта для праектаў, зборачная сістэма якіх дазваляе згенераваць файл.
Таксама рэжым праверкі спісу файлаў можна выкарыстоўваць разам з логам трасіроўкі strace запускаў кампілятара (pvs-studio-analyzer trace). Для гэтага вам трэба будзе спачатку правесці поўную зборку праекта і адсачыць яе, каб аналізатар сабраў поўную інфармацыю аб параметрах кампіляцыі ўсіх правяраных фалаў.
Аднак у такога варыянту ёсць істотны недахоп - трэба будзе альбо вырабляць поўную трасіроўку зборкі ўсяго праекта пры кожным запуску, што само па сабе супярэчыць ідэі хуткай праверкі комміта. Або, калі закэшаваць сам вынік трасіроўкі, наступныя запускі аналізатара могуць апынуцца няпоўнымі, калі пасля трасіроўкі памяняецца структура залежнасцяў зыходных файлаў (напрыклад, у адзін з зыходных файлаў будзе дададзены новы #include).
Таму мы не рэкамендуемы выкарыстоўваць рэжым праверкі спісу файлаў з логам трасіроўкі для праверкі коммітаў або pull request'аў. У выпадку, калі вы можаце рабіць пры праверцы комміта інкрыментальную зборку, разгледзьце магчымасць выкарыстоўваць рэжым
Спіс зыходных файлаў для аналізу захоўваецца ў тэкставы файл і перадаецца аналізатару з дапамогай параметру -S:
pvs-studio-analyzer analyze ... -f build/compile_commands.json -S check-list.txt
У гэтым файле паказваюцца адносныя ці абсалютныя шляхі да файлаў, прычым кожны новы файл павінен быць на новым радку. Дапушчальна паказваць не толькі імёны файлаў для аналізу, але і розны тэкст. Аналізатар убачыць, што гэта не файл, і праігнаруе радок. Гэта можа быць карысна для каментавання, калі файлы паказваюцца ўручную. Аднак часцяком спіс файлаў будзе згенераваны падчас аналізу ў CI, напрыклад, гэта могуць быць файлы з коміта ці pull request'а.
Цяпер з дапамогай гэтага рэжыму можна хутка правяраць новы код да траплення яго ў асноўную галіну распрацоўкі. Каб сістэма праверкі рэагавала на наяўнасць папярэджанняў аналізатара, ва ўтыліту plog-converter дададзены сцяг -indicate-warnings:
plog-converter ... --indicate-warnings ... -o /path/to/report.tasks ...
З гэтым сцягам канвэртар верне ненулявы код, калі ў справаздачы аналізатара прысутнічаюць папярэджанні. Па кодзе звароту можна заблакаваць прэкаміт хук, коміт ці pull request, а згенераваную справаздачу аналізатара вывесці на экран, расшарыць ці адправіць па пошце.
Заўвага. Пры першым запуску аналізу спісу файлаў будзе прааналізаваны ўвесь праект, т.я. аналізатару неабходна згенераваць файл залежнасцяў зыходных файлаў праекту ад загалоўкавых файлаў. Гэта асаблівасць аналізу C і C++ файлаў. У далейшым файл залежнасцяў можна закэшаваць і ён будзе абнаўляцца аналізатарам аўтаматычна. Перавага праверкі коммітаў пры выкарыстанні рэжыму праверкі спісу файлаў перад выкарыстаннем рэжыму інкрыментальнага аналізу складаецца ў тым, што трэба кэшаваць толькі гэты файл, а не аб'ектныя файлы.
Агульныя прынцыпы аналізу pull request'а
Аналіз усяго праекта займае дастаткова шмат часу, таму ёсць сэнс у тым, каб правяраць толькі некаторую яго частку. Праблема ў тым, што трэба аддзяліць новыя файлы ад астатніх файлаў праекту.
Разгледзім прыклад дрэва комітаў з двума галінамі:
Давайце ўявім, што коміт A1 змяшчае дастаткова вялікую колькасць кода, які ўжо праверылі. Крыху раней мы зрабілі галіну ад комміту. A1 і змянялі нейкія файлы.
Вы, вядома, заўважылі, што пасля A1 адбылося яшчэ два коміты, але гэта былі таксама зліцці іншых галінак, бо мы ж не каміцім у майстар. І вось надышоў час, калі выпраўленне гатовы. Таму з'явіўся pull request на зліццё B3 и A3.
Само сабой, можна было б праверыць увесь вынік іх зліцця, але гэта было б занадта доўга і неапраўдана, бо было зменена толькі некалькі файлаў. Таму больш эфектыўна прааналізаваць толькі змененыя.
Для гэтага атрымаем розніцу паміж галінкамі, знаходзячыся ў HEAD галінкі, з якой жадаем зліць у master:
git diff --name-only HEAD origin/$MERGE_BASE > .pvs-pr.list
$MERGE_BASE мы будзем падрабязна разглядаць пазней. Справа ў тым, што далёка не кожны CI сэрвіс дае неабходную інфармацыю пра базу для зліцця, таму кожны раз даводзіцца прыдумляць новыя спосабы атрымання гэтых дадзеных. Гэта будзе падрабязна распісана ніжэй у кожным з апісаных вэб-сэрвісаў.
Такім чынам, мы атрымалі розніцу паміж галінкамі, а дакладней - спіс імёнаў файлаў, якія былі зменены. Цяпер нам трэба аддаць файл .pvs-pr.list (мы ў яго перанакіравалі выснову вышэй) аналізатару:
pvs-studio-analyzer analyze -j8
-o PVS-Studio.log
-S .pvs-pr.list
Пасля аналізу нам трэба канвертаваць файл логаў (PVS-Studio.log) у зручны для ўспрымання фармат:
plog-converter -t errorfile PVS-Studio.log --cerr -w
Гэтая каманда выведзе спіс памылак у
Толькі вось нам трэба не толькі вывесці памылкі, але і паведаміць нашаму сэрвісу для зборкі і тэсціравання аб наяўнасці праблем. Для гэтага ў канвэртар быў дададзены сьцяг -W (-indicate-warnings). Пры наяўнасці хоць аднаго папярэджання аналізатара, код вяртання ўтыліты plog-converter зменіцца на 2, што, у сваю чаргу, паведаміць CI сэрвісу аб наяўнасці патэнцыйных памылак у файлах pull request'а.
Travis CI
Канфігурацыя выканана ў выглядзе файла .travis.yml. Для выгоды раю вынесці ўсё ў асобны bash-скрыпт з функцыямі, якія будуць выкліканыя з файла .travis.yml (bash імя_скрыпта.sh імя_функцыі).
Будзем дадаваць неабходны код у скрыпт на біць, такім чынам мы атрымаем большы функцыянал. У секцыі ўсталёўваць напішам наступнае:
install:
- bash .travis.sh travis_install
Калі ў вас былі якія-небудзь інструкцыі, то можаце перанесці іх у скрыпт, прыбраўшы злучкі.
Адкрыем файл .travis.sh і дадамо ўстаноўку аналізатара ў функцыю 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
}
Цяпер дадамо ў секцыю скрыпт запуск аналізу:
script:
- bash .travis.sh travis_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
}
Гэты код трэба запусціць пасля зборкі праекту, напрыклад, калі ў вас была зборка на CMake:
travis_script() {
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
cmake $CMAKE_ARGS CMakeLists.txt
make -j8
}
Атрымаецца так:
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
}
Мусіць, вы ўжо звярнулі ўвагу на паказаныя зменныя асяроддзі $TRAVIS_PULL_REQUEST и $TRAVIS_BRANCH. Travis CI аб'яўляе іх самастойна:
- $TRAVIS_PULL_REQUEST захоўвае нумар pull request'а або ілжывы, калі гэта звычайная галінка;
- $TRAVIS_REPO_SLUG захоўвае імя рэпазітара праекта.
Алгарытм працы гэтай функцыі:
Travis CI рэагуе на коды звароту, таму наяўнасць папярэджанняў пакажа сэрвісу пазначыць коміт як які змяшчае памылкі.
А зараз разгледзім падрабязней гэты радок кода:
git diff --name-only origin/HEAD > .pvs-pr.list
Справа ў тым, што Travis CI аўтаматычна робіць зліццё галінак падчас аналізу pull request:
Таму мы аналізуем A4, А не B3->A3. З-за гэтай асаблівасці нам трэба вылічваць розніцу з А3, якая як раз і з'яўляецца вяршыняй галінкі з паходжанне.
Засталася адна важная дэталь - кэшаванне залежнасцяў загалоўкавых файлаў ад кампіляваных адзінак трансляцыі (*.c, *.cc, *.cpp і г.д.). Гэтыя залежнасці аналізатар вылічае пры першым запуску ў рэжыме праверкі спісу файлаў і захоўвае затым у дырэкторыі .PVS-Studio. Travis CI дазваляе кэшаваць тэчкі, таму мы захаваем дадзеныя дырэкторыі .PVS-Studio/:
cache:
directories:
- .PVS-Studio/
Гэты код трэба дадаць у файл .travis.yml. Гэтая дырэкторыя захоўвае розныя дадзеныя, сабраныя пасля аналізу, якія істотна паскораць наступныя запускі аналізу спісу файлаў ці інкрыментальнага аналізу. Калі гэтага не зрабіць, то аналізатар фактычна кожны раз будзе аналізаваць усе файлы.
Прыяцель
Як і Travis CI,
У першую чаргу нам неабходна дадаць новае дзеянне ў лінію зборкі:
Укажам кампілятар, які выкарыстоўваўся для зборкі праекта. Звярніце ўвагу на docker кантэйнер, які ўсталяваны ў гэтым дзеянні. Напрыклад, для GCC ёсць спецыяльны кантэйнер:
Цяпер усталюем PVS-Studio і неабходныя ўтыліты:
Дадамо ў рэдактар наступныя радкі:
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
Цяпер пяройдзем на ўкладку Run (першы абразок) і ў адпаведнае поле рэдактара дадамо наступны код:
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
Калі вы чыталі раздзел, прысвечаны Travs-CI, то гэты код ужо вам знаёмы, аднак, зараз з'явіўся новы этап:
Справа ў тым, што зараз мы аналізуем не вынік зліцця, а HEAD галінкі, з якой робіцца pull request:
Таму мы знаходзімся ва ўмоўным коміце B3 і нам трэба атрымаць розніцу з 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
для вызначэння A3 скарыстаемся API GitHub:
https://api.github.com/repos/${USERNAME}/${REPO}/pulls/${PULL_REQUEST_ID}
Мы выкарыстоўвалі наступныя зменныя, якія дае Buddy:
- $BUDDY_EXECUTION_PULL_REQEUST_NO - Нумар pull request'а;
- $BUDDY_REPO_SLUG - Спалучэнне імя карыстальніка і рэпазітара (напрыклад max / test).
Цяпер захаваем змены, скарыстаўшыся кнопкай унізе, і ўлучым аналіз pull request:
У адрозненні ад Travis CI, нам няма неабходнасці ўказваць .pvs-studio для кэшавання, бо Buddy аўтаматычна кэшуе ўсе файлы для наступных запускаў. Таму засталося апошняе – захаваць лагін і пароль для PVS-Studio у Buddy. Пасля захавання змен мы патрапім зваротна ў Pipeline. Нам трэба перайсці да налады зменных і дадаць лагін і ключ для PVS-Studio:
Пасля гэтага з'яўленне новага pull request'а ці комміта будзе запускаць праверку. Калі коміт утрымоўвае памылкі, то Buddy пакажа на гэта на старонцы pull request'а.
AppVeyor
Настройка AppVeyor падобная на Buddy, бо ўсё адбываецца ў web інтэрфейсе і няма патрэбы дадаваць файл *.yml у рэпазітар праекту.
Пяройдзем на ўкладку Settings у аглядзе праекта:
Пракруцім гэтую старонку ўніз і ўключым захаванне кэша для зборкі pull request'аў:
Зараз пяройдзем на ўкладку Environment, дзе пакажам выяву для зборкі і неабходныя зменныя асяроддзі:
Калі вы прачыталі папярэднія раздзелы, ты вы добра знаёмыя з гэтымі двума зменнымі. PVS_KEY и PVS_USERNAME. Калі ж не, то нагадаю, што яны неабходны для праверкі ліцэнзіі аналізатара PVS-Studio. У далейшым мы сустрэнем іх ізноў у Bash скрыптах.
На гэтай жа старонцы ўнізе пакажам тэчку для кэшавання:
Калі мы гэтага не зробім, то будзем аналізаваць замест пары файлаў увесь праект, але выснова атрымаем па ўказаных файлах. Таму важна ўвесці правільную назву дырэкторыі.
Цяпер настаў час скрыпту для праверкі. Адкрыем укладку Tests і выберам Script:
У гэтую форму трэба ўставіць наступны код:
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
Звернем увагу на наступную частку кода:
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
Досыць спецыфічнае прысвойванне значэння каманды pwd зменнай, якая павінна захоўваць гэтае значэнне па змаўчанні, здаецца дзіўным на першы погляд, аднак, я зараз усё растлумачу.
Падчас налады аналізатара ў AppVeyor я сутыкнуўся з вельмі дзіўнымі паводзінамі аналізатара. З аднаго боку, усё працавала дакладна, але аналіз не запускаўся. Я патраціў нямала часу, каб заўважыць, што мы знаходзімся ў дырэкторыі /home/appveyor/projects/testcalc/, а аналізатар упэўнены ў тым, што мы знаходзімся ў /opt/appveyor/build-agent/. Тады я зразумеў, што зменная $PWD крыху хлусіць. Па гэтай прычыне ўручную абнавіў яе значэнне перад запускам аналізу.
А далей усё, як і раней:
Цяпер разгледзім наступны фрагмент:
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"`
У ім мы атрымліваем розніцу паміж галінкамі, над якімі аб'яўлены pull request. Для гэтага нам патрэбныя наступныя зменныя асяроддзі:
- $APPVEYOR_PULL_REQUEST_NUMBER - нумар pull request;
- $APPVEYOR_REPO_NAME - імя карыстальніка і рэпазітар праекта.
Заключэнне
Вядома, мы не разгледзелі ўсе з магчымых сэрвісаў бесперапыннай інтэграцыі, аднак, усе яны маюць вельмі падобную сябар з сябрам спецыфіку працы. За выключэннем кэшавання, кожны сэрвіс робіць свой «ровар», таму заўсёды ўсё па-рознаму.
Дзесьці, як у Travis-CI, пара радкоў кода і кэшаванне працуе бездакорна; дзесьці, як у AppVeyor, трэба проста паказаць тэчку ў наладах; але недзе трэба ствараць унікальныя ключы і спрабаваць пераканаць сістэму даць табе магчымасць перазапісаць закэшаваны фрагмент. Таму, калі вы жадаеце наладзіць аналіз pull request'аў на сэрвісе бесперапыннай інтэграцыі, які не быў разгледжаны вышэй, то спачатку пераканаецеся, што з кэшаваннем у вас не паўстане праблем.
Дзякуй за ўвагу. Калі нешта не атрымліваецца, то смела пішыце нам у
Калі хочаце падзяліцца гэтым артыкулам з англамоўнай аўдыторыяй, то прашу выкарыстаць спасылку на пераклад: Maxim Zvyagintsev.
Крыніца: habr.com