Аналіз комітаў і pull request'аў у Travis CI, Buddy і AppVeyor з дапамогай PVS-Studio

Аналіз комітаў і pull request'аў у Travis CI, Buddy і AppVeyor з дапамогай PVS-Studio
У аналізатары PVS-Studio для моў З і C++ на Linux і macOS, пачынальна з версіі 7.04, з'явілася тэставая магчымасць праверыць спіс паказаных файлаў. З дапамогай новага рэжыму можна наладзіць аналізатар для праверкі коммітаў і pull request'аў. У гэтым артыкуле будзе расказана, як наладзіць праверку спісу змененых файлаў GitHub-праекта ў такіх папулярных CI (Continuous Integration) сістэмах, як Travis CI, Buddy і AppVeyor.

Рэжым праверкі спісу файлаў

PVS-студыя - Гэта прылада для выяўлення памылак і патэнцыйных уразлівасцяў у зыходным кодзе праграм, напісаных на мовах З, C++, C# і Java. Працуе ў 64-бітных сістэмах на Windows, Linux і macOS.

У версіі PVS-Studio 7.04 для Linux і macOS з'явіўся рэжым праверкі спісу зыходных файлаў. Працуе гэта для праектаў, зборачная сістэма якіх дазваляе згенераваць файл. compile_commands.json. Ён патрэбен для таго, каб аналізатар дастаў інфармацыю аб кампіляцыі паказаных файлаў. Калі ваша зборачная сістэма не падтрымлівае генерацыю файла compile_commands.json, вы можаце паспрабаваць згенераваць такі файл з дапамогай утыліты мядзведзь.

Таксама рэжым праверкі спісу файлаў можна выкарыстоўваць разам з логам трасіроўкі 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'а

Аналіз усяго праекта займае дастаткова шмат часу, таму ёсць сэнс у тым, каб правяраць толькі некаторую яго частку. Праблема ў тым, што трэба аддзяліць новыя файлы ад астатніх файлаў праекту.

Разгледзім прыклад дрэва комітаў з двума галінамі:

Аналіз комітаў і pull request'аў у Travis CI, Buddy і AppVeyor з дапамогай PVS-Studio

Давайце ўявім, што коміт 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 захоўвае імя рэпазітара праекта.

Алгарытм працы гэтай функцыі:

Аналіз комітаў і pull request'аў у Travis CI, Buddy і AppVeyor з дапамогай PVS-Studio
Travis CI рэагуе на коды звароту, таму наяўнасць папярэджанняў пакажа сэрвісу пазначыць коміт як які змяшчае памылкі.

А зараз разгледзім падрабязней гэты радок кода:

git diff --name-only origin/HEAD > .pvs-pr.list

Справа ў тым, што Travis CI аўтаматычна робіць зліццё галінак падчас аналізу pull request:

Аналіз комітаў і pull request'аў у Travis CI, Buddy і AppVeyor з дапамогай PVS-Studio
Таму мы аналізуем A4, А не B3->A3. З-за гэтай асаблівасці нам трэба вылічваць розніцу з А3, якая як раз і з'яўляецца вяршыняй галінкі з паходжанне.

Засталася адна важная дэталь - кэшаванне залежнасцяў загалоўкавых файлаў ад кампіляваных адзінак трансляцыі (*.c, *.cc, *.cpp і г.д.). Гэтыя залежнасці аналізатар вылічае пры першым запуску ў рэжыме праверкі спісу файлаў і захоўвае затым у дырэкторыі .PVS-Studio. Travis CI дазваляе кэшаваць тэчкі, таму мы захаваем дадзеныя дырэкторыі .PVS-Studio/:

cache:
  directories:
    - .PVS-Studio/

Гэты код трэба дадаць у файл .travis.yml. Гэтая дырэкторыя захоўвае розныя дадзеныя, сабраныя пасля аналізу, якія істотна паскораць наступныя запускі аналізу спісу файлаў ці інкрыментальнага аналізу. Калі гэтага не зрабіць, то аналізатар фактычна кожны раз будзе аналізаваць усе файлы.

Прыяцель

Як і Travis CI, Прыяцель дае магчымасць аўтаматызаванай зборкі і тэсціравання праектаў, якія захоўваюцца на GitHub. У адрозненне ад Travis CI, ён наладжваецца ў вэб інтэрфейсе (падтрымка bash маецца), таму няма неабходнасці захоўваць файлы канфігурацыі ў праекце.

У першую чаргу нам неабходна дадаць новае дзеянне ў лінію зборкі:

Аналіз комітаў і pull request'аў у Travis CI, Buddy і AppVeyor з дапамогай PVS-Studio
Укажам кампілятар, які выкарыстоўваўся для зборкі праекта. Звярніце ўвагу на docker кантэйнер, які ўсталяваны ў гэтым дзеянні. Напрыклад, для GCC ёсць спецыяльны кантэйнер:

Аналіз комітаў і pull request'аў у Travis CI, Buddy і AppVeyor з дапамогай PVS-Studio
Цяпер усталюем PVS-Studio і неабходныя ўтыліты:

Аналіз комітаў і pull request'аў у Travis CI, Buddy і AppVeyor з дапамогай 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, то гэты код ужо вам знаёмы, аднак, зараз з'явіўся новы этап:

Аналіз комітаў і pull request'аў у Travis CI, Buddy і AppVeyor з дапамогай PVS-Studio
Справа ў тым, што зараз мы аналізуем не вынік зліцця, а HEAD галінкі, з якой робіцца pull request:

Аналіз комітаў і pull request'аў у Travis CI, Buddy і AppVeyor з дапамогай PVS-Studio
Таму мы знаходзімся ва ўмоўным коміце 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:

Аналіз комітаў і pull request'аў у Travis CI, Buddy і AppVeyor з дапамогай PVS-Studio
У адрозненні ад Travis CI, нам няма неабходнасці ўказваць .pvs-studio для кэшавання, бо Buddy аўтаматычна кэшуе ўсе файлы для наступных запускаў. Таму засталося апошняе – захаваць лагін і пароль для PVS-Studio у Buddy. Пасля захавання змен мы патрапім зваротна ў Pipeline. Нам трэба перайсці да налады зменных і дадаць лагін і ключ для PVS-Studio:

Аналіз комітаў і pull request'аў у Travis CI, Buddy і AppVeyor з дапамогай PVS-Studio
Пасля гэтага з'яўленне новага pull request'а ці комміта будзе запускаць праверку. Калі коміт утрымоўвае памылкі, то Buddy пакажа на гэта на старонцы pull request'а.

AppVeyor

Настройка AppVeyor падобная на Buddy, бо ўсё адбываецца ў web інтэрфейсе і няма патрэбы дадаваць файл *.yml у рэпазітар праекту.

Пяройдзем на ўкладку Settings у аглядзе праекта:

Аналіз комітаў і pull request'аў у Travis CI, Buddy і AppVeyor з дапамогай PVS-Studio
Пракруцім гэтую старонку ўніз і ўключым захаванне кэша для зборкі pull request'аў:

Аналіз комітаў і pull request'аў у Travis CI, Buddy і AppVeyor з дапамогай PVS-Studio
Зараз пяройдзем на ўкладку Environment, дзе пакажам выяву для зборкі і неабходныя зменныя асяроддзі:

Аналіз комітаў і pull request'аў у Travis CI, Buddy і AppVeyor з дапамогай PVS-Studio
Калі вы прачыталі папярэднія раздзелы, ты вы добра знаёмыя з гэтымі двума зменнымі. PVS_KEY и PVS_USERNAME. Калі ж не, то нагадаю, што яны неабходны для праверкі ліцэнзіі аналізатара PVS-Studio. У далейшым мы сустрэнем іх ізноў у Bash скрыптах.

На гэтай жа старонцы ўнізе пакажам тэчку для кэшавання:

Аналіз комітаў і pull request'аў у Travis CI, Buddy і AppVeyor з дапамогай PVS-Studio
Калі мы гэтага не зробім, то будзем аналізаваць замест пары файлаў увесь праект, але выснова атрымаем па ўказаных файлах. Таму важна ўвесці правільную назву дырэкторыі.

Цяпер настаў час скрыпту для праверкі. Адкрыем укладку Tests і выберам Script:

Аналіз комітаў і pull request'аў у Travis CI, Buddy і AppVeyor з дапамогай PVS-Studio
У гэтую форму трэба ўставіць наступны код:

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'аў у Travis CI, Buddy і AppVeyor з дапамогай PVS-Studio
Цяпер разгледзім наступны фрагмент:

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'аў на сэрвісе бесперапыннай інтэграцыі, які не быў разгледжаны вышэй, то спачатку пераканаецеся, што з кэшаваннем у вас не паўстане праблем.

Дзякуй за ўвагу. Калі нешта не атрымліваецца, то смела пішыце нам у падтрымку. Мы падкажам і дапаможам.

Аналіз комітаў і pull request'аў у Travis CI, Buddy і AppVeyor з дапамогай PVS-Studio

Калі хочаце падзяліцца гэтым артыкулам з англамоўнай аўдыторыяй, то прашу выкарыстаць спасылку на пераклад: Maxim Zvyagintsev. Analysis of commits and pull requests in Travis CI, Buddy and AppVeyor using PVS-Studio.

Крыніца: habr.com

Дадаць каментар