Analiza commitów i pull requestów w Travis CI, Buddy i AppVeyor za pomocą PVS-Studio

Analiza commitów i pull requestów w Travis CI, Buddy i AppVeyor z pomocą PVS-Studio
W analizatorze PVS-Studio dla języków C i C++ na systemach Linux i macOS, począwszy od wersji 7.04, pojawiła się testowa funkcjonalność umożliwiająca sprawdzenie listy wskazanych plików. Nowy tryb pozwala skonfigurować analizator do sprawdzania commitów i pull requestów. W tym artykule omówimy, jak skonfigurować sprawdzanie listy zmodyfikowanych plików w projekcie GitHub w popularnych systemach CI (Continuous Integration), takich jak Travis CI, Buddy i AppVeyor.

Tryb sprawdzania listy plików

PVS-Studio — to narzędzie do wykrywania błędów i potencjalnych luk w kodzie źródłowym programów napisanych w językach C, C++, C# i Java. Działa w systemach 64-bitowych na Windows, Linux i macOS.

W wersji PVS-Studio 7.04 dla Linux i macOS wprowadzono tryb sprawdzania listy plików źródłowych. Działa to dla projektów, których system budowania pozwala na wygenerowanie pliku compile_commands.json. Jest on potrzebny, aby analizator mógł wydobyć informacje o kompilacji wskazanych plików. Jeśli Twój system budowania nie obsługuje generacji pliku compile_commands.json, możesz spróbować wygenerować taki plik za pomocą narzędzia Bear.

Ponadto tryb sprawdzania listy plików można wykorzystywać wraz z logiem śledzenia strace uruchomień kompilatora (pvs-studio-analyzer trace). W tym celu musisz najpierw przeprowadzić pełną kompilację projektu i śledzić ją, aby analizator zebrał pełne informacje o parametrach kompilacji wszystkich sprawdzanych plików.

Jednak taki wariant ma istotną wadę — będziesz musiał albo przeprowadzać pełne śledzenie kompilacji całego projektu przy każdym uruchomieniu, co przeczy idei szybkiego sprawdzania commitów. Albo, jeśli zakodujesz sam wynik śledzenia, kolejne uruchomienia analizatora mogą być niekompletne, jeśli po śledzeniu zmieni się struktura zależności plików źródłowych (na przykład, jeśli do jednego z plików źródłowych zostanie dodany nowy #include).

Dlatego nie zalecamy używania trybu sprawdzania listy plików z logiem śledzenia do sprawdzania commitów lub pull requestów. Jeśli możesz wykonywać inkrementalną kompilację podczas sprawdzania commitów, rozważ możliwość użycia trybu analizy inkrementalnej.

Lista plików źródłowych do analizy jest zapisywana w pliku tekstowym i przekazywana analizatorowi za pomocą parametru -S:

pvs-studio-analyzer analyze ... -f build/compile_commands.json -S check-list.txt

W tym pliku podaje się względne lub absolutne ścieżki do plików, przy czym każdy nowy plik powinien znajdować się w nowej linii. Dopuszcza się podawanie nie tylko nazw plików do analizy, ale także różnego tekstu. Analizator zauważy, że to nie jest plik, i zignoruje ten wiersz. Może to być przydatne do komentowania, gdy pliki są podawane ręcznie. Często jednak lista plików jest generowana podczas analizy w CI, na przykład mogą to być pliki z commitów lub pull requestów.

Teraz dzięki temu trybowi można szybko sprawdzić nowy kod przed jego dodaniem do głównej gałęzi rozwoju. Aby system sprawdzania reagował na obecność ostrzeżeń analizatora, do narzędzia plog-converter dodano flagę —indicate-warnings:

plog-converter ... --indicate-warnings ... -o /path/to/report.tasks ...

Z tym flagą konwerter zwróci niezerowy kod, jeśli w raporcie analizatora znajdują się ostrzeżenia. Na podstawie kodu zwrotnego można zablokować hook pre-commit, commit lub pull request, a wygenerowany raport analizatora wyświetlić na ekranie, udostępnić lub wysłać mailem.

Uwaga. Przy pierwszym uruchomieniu analizy listy plików cały projekt zostanie poddany analizie, ponieważ analizator musi wygenerować plik zależności pomiędzy plikami źródłowymi projektu a plikami nagłówkowymi. To cecha analizy plików C i C++. Później plik zależności można zbuforować i zostanie on automatycznie aktualizowany przez analizator. Zaletą sprawdzania commitów podczas używania trybu sprawdzania listy plików w porównaniu do użycia trybu inkrementalnej analizy jest to, że trzeba zbuforować tylko ten plik, a nie pliki obiektowe.

Ogólne zasady analizy pull requestów

Analiza całego projektu zajmuje dość dużo czasu, dlatego warto sprawdzać tylko pewną jego część. Problemem jest to, że trzeba oddzielić nowe pliki od pozostałych plików projektu.

Rozważmy przykład drzewa commitów z dwiema gałęziami:

Analiza commitów i pull requestów w Travis CI, Buddy i AppVeyor z pomocą PVS-Studio

Wyobraźmy sobie, że commit A1 zawiera dość dużą ilość kodu, który został już sprawdzony. Nieco wcześniej utworzyliśmy gałąź z commita A1 i edytowaliśmy niektóre pliki.

Oczywiście zauważyliście, że po A1 nastąpiły jeszcze dwa commity, ale były to także połączenia innych gałęzi, przecież nie commitujemy do master. I nadeszła pora, kiedy hotfixa jest gotowe. Dlatego pojawił się pull request na połączenie B3 i A3.

Oczywiście, można by sprawdzić cały wynik ich połączenia, ale byłoby to zbyt czasochłonne i nieuzasadnione, ponieważ zmieniono tylko kilka plików. Dlatego efektywniej jest przeanalizować tylko zmienione.

Aby to zrobić, uzyskamy różnicę między gałęziami, będąc w HEAD gałęzi, z której chcemy zmergować do master:

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

$MERGE_BASE będziemy szczegółowo omawiać później. Chodzi o to, że nie każdy system CI dostarcza niezbędnych informacji o bazie do łączenia, dlatego za każdym razem trzeba wymyślać nowe sposoby uzyskiwania tych danych. Zostanie to szczegółowo opisane poniżej w każdym z opisanych serwisów internetowych.

Tak więc uzyskaliśmy różnicę między gałęziami, a dokładniej — listę nazw plików, które zostały zmienione. Teraz musimy przekazać plik .pvs-pr.list (do którego przekierowaliśmy wyjście powyżej) analizatorowi:

pvs-studio-analyzer analyze -j8 
                            -o PVS-Studio.log 
                            -S .pvs-pr.list

Po analizie musimy przekonwertować plik dziennika (PVS-Studio.log) na wygodny format do przyswojenia:

plog-converter -t errorfile PVS-Studio.log --cerr -w

To polecenie wyświetli listę błędów w stderr (standardowy strumień wyjściowy komunikatów o błędach).

Tylko że potrzebujemy nie tylko wyświetlić błędy, ale również poinformować nasz serwis do budowy i testowania o występowaniu problemów. W tym celu do konwertera dodano flagę -W (—indicate-warnings). W przypadku wystąpienia chociaż jednego ostrzeżenia z analizatora, kod wyjścia narzędzia plog-converter zmieni się na 2, co z kolei powiadomi serwis CI o możliwościach błędów w plikach pull request’a.

Travis CI

Konfiguracja została wykonana w postaci pliku .travis.yml. W celu ułatwienia zaleca się umieszczenie wszystkiego w oddzielnym skrypcie bash z funkcjami, które będą wywoływane z pliku .travis.yml (bash nazwa_skryptu.sh nazwa_funkcji).

Będziemy dodawać niezbędny kod do skryptu w bash, w ten sposób uzyskamy większą funkcjonalność. W sekcji install napiszemy następujące:

install:
  - bash .travis.sh travis_install

Jeśli masz jakiekolwiek instrukcje, możesz je przenieść do skryptu, usuwając myślniki.

Otworzymy plik .travis.sh i dodamy instalację analizatora do funkcji 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 
}

Teraz dodajmy do sekcji script uruchamiania analizy:

script:
  - bash .travis.sh travis_script

I w skrypcie 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
}

Ten kod należy uruchomić po budowie projektu, na przykład, jeśli używasz CMake:

travis_script() {
  CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=On ${CMAKE_ARGS}"
  cmake $CMAKE_ARGS CMakeLists.txt
  make -j8
}

Będzie to wyglądać tak:

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
}

Pewnie już zwróciłeś uwagę na podane zmienne środowiskowe: $TRAVIS_PULL_REQUEST i $TRAVIS_BRANCH. Travis CI definiuje je automatycznie:

  • $TRAVIS_PULL_REQUEST przechowuje numer pull requesta lub false, jeśli to normalna gałąź;
  • $TRAVIS_REPO_SLUG przechowuje nazwę repozytorium projektu.

Algorytm pracy tej funkcji:

Analiza commitów i pull requestów w Travis CI, Buddy i AppVeyor z pomocą PVS-Studio
Travis CI reaguje na kody zakończenia, dlatego obecność ostrzeżeń poinformuje serwis, żeby oznaczył commit jako zawierający błędy.

A teraz przyjrzyjmy się bliżej temu wierszowi kodu:

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

Faktem jest, że Travis CI automatycznie łączy gałęzie podczas analizy pull requestów:

Analiza commitów i pull requestów w Travis CI, Buddy i AppVeyor z pomocą PVS-Studio
Dlatego analizujemy A4, a nie B3->A3. Z powodu tej cechy musimy obliczyć różnicę z A3, która jest dokładnie szczytem gałęzi z origin.

Pozostał jedna ważna kwestia — cache'owanie zależności plików nagłówkowych od kompilowanych jednostek translacyjnych (*.c, *.cc, *.cpp itd.). Te zależności analizer oblicza podczas pierwszego uruchomienia w trybie sprawdzania listy plików i następnie zapisuje w katalogu .PVS-Studio. Travis CI pozwala na cache'owanie katalogów, więc zachowamy dane z katalogu .PVS-Studio/:

cache:
  directories:
    - .PVS-Studio/

Ten kod należy dodać do pliku .travis.yml. Ten katalog przechowuje różne dane zebrane po analizie, które znacznie przyspieszają kolejne uruchomienia analizy listy plików lub analizy inkrementalnej. Jeśli tego nie zrobisz, to analizator praktycznie za każdym razem będzie analizować wszystkie pliki.

Buddy

Podobnie jak Travis CI, Buddy umożliwia automatyczną kompilację i testowanie projektów przechowywanych na GitHubie. W przeciwieństwie do Travis CI, jest konfigurowany w interfejsie webowym (wsparcie dla bash jest dostępne), więc nie ma potrzeby przechowywania plików konfiguracyjnych w projekcie.

Przede wszystkim musimy dodać nowe działanie do linii kompilacji:

Analiza commitów i pull requestów w Travis CI, Buddy i AppVeyor z pomocą PVS-Studio
Określimy kompilator, który był używany do kompilacji projektu. Zwróć uwagę na kontener docker, który jest zainstalowany w tym działaniu. Na przykład, dla GCC istnieje specjalny kontener:

Analiza commitów i pull requestów w Travis CI, Buddy i AppVeyor z pomocą PVS-Studio
Teraz zainstalujemy PVS-Studio i potrzebne narzędzia:

Analiza commitów i pull requestów w Travis CI, Buddy i AppVeyor z pomocą PVS-Studio
Dodaj do edytora następujące linie:

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

Teraz przejdź do zakładki Run (pierwsza ikona) i w odpowiednim polu edytora dodaj następujący 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 -w

Jeśli czytałeś sekcję poświęconą Travs-CI, to ten kod jest ci już znany, jednak teraz pojawił się nowy etap:

Analiza commitów i pull requestów w Travis CI, Buddy i AppVeyor z pomocą PVS-Studio
Chodzi o to, że teraz analizujemy nie wynik złączenia, ale HEAD gałęzi, z której powstaje pull request:

Analiza commitów i pull requestów w Travis CI, Buddy i AppVeyor z pomocą PVS-Studio
Dlatego znajdujemy się w warunkowym commicie B3 i musimy uzyskać różnicę z 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

Aby określić A3 zastosujemy API GitHub:

https://api.github.com/repos/${USERNAME}/${REPO}/pulls/${PULL_REQUEST_ID}

Użyliśmy następujących zmiennych, które dostarcza Buddy:

  • $BUDDY_EXECUTION_PULL_REQUEST_NO — numer pull request’a;
  • $BUDDY_REPO_SLUG — kombinacja nazwy użytkownika i repozytorium (na przykład max/test).

Teraz zapiszemy zmiany, korzystając z przycisku na dole, i włączymy analizę pull request:

Analiza commitów i pull requestów w Travis CI, Buddy i AppVeyor z pomocą PVS-Studio
W przeciwieństwie do Travis CI, nie musimy podawać .pvs-studio do cache'owania, ponieważ Buddy automatycznie cache'uje wszystkie pliki do kolejnych uruchomień. Pozostało nam tylko to — zapisać login i hasło do PVS-Studio w Buddy. Po zapisaniu zmian wrócimy do Pipeline. Musimy przejść do ustawienia zmiennych i dodać login oraz klucz dla PVS-Studio:

Analiza commitów i pull requestów w Travis CI, Buddy i AppVeyor z pomocą PVS-Studio
Po tym, pojawienie się nowego pull request'a lub commita uruchomi kontrolę. Jeśli commit zawiera błędy, Buddy wskaże na to na stronie pull request'a.

AppVeyor

Konfiguracja AppVeyor jest podobna do Buddy, ponieważ wszystko odbywa się w interfejsie webowym i nie ma potrzeby dodawania pliku *.yml do repozytorium projektu.

Przejdźmy do zakładki Ustawienia w przeglądzie projektu:

Analiza commitów i pull requestów w Travis CI, Buddy i AppVeyor z pomocą PVS-Studio
Przewińmy tę stronę w dół i włączmy zapisywanie cache'a dla budowy pull request'ów:

Analiza commitów i pull requestów w Travis CI, Buddy i AppVeyor z pomocą PVS-Studio
Teraz przejdźmy do zakładki Środowisko, gdzie wskaźmy obraz dla budowy oraz niezbędne zmienne środowiskowe:

Analiza commitów i pull requestów w Travis CI, Buddy i AppVeyor z pomocą PVS-Studio
Jeśli przeczytałeś poprzednie rozdziały, powinieneś być dobrze zaznajomiony z tymi dwiema zmiennymi — PVS_KEY i PVS_USERNAME. Jeśli nie, przypomnę, że są one niezbędne do weryfikacji licencji analityka PVS-Studio. W przyszłości spotkamy je znowu w skryptach Bash.

Na tej samej stronie na dole wskaźmy folder do cachowania:

Analiza commitów i pull requestów w Travis CI, Buddy i AppVeyor z pomocą PVS-Studio
Jeśli tego nie zrobimy, będziemy analizować nie tylko kilka plików, ale cały projekt, natomiast wyniki uzyskamy dla wskazanych plików. Dlatego ważne jest, aby wprowadzić poprawną nazwę katalogu.

Teraz nadszedł czas na skrypt do weryfikacji. Otwórzmy zakładkę Testy i wybierzmy Skrypt:

Analiza commitów i pull requestów w Travis CI, Buddy i AppVeyor z pomocą PVS-Studio
Do tego formularza należy wkleić następujący 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 -w

Zwróćmy uwagę na następną część kodu:

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

Przypisanie wartości polecenia pwd do zmiennej, która powinna przechowywać tę wartość domyślnie, wydaje się dziwne na pierwszy rzut oka, ale zaraz wszystko wyjaśnię.

Podczas konfigurowania analizatora w AppVeyor napotkałem na niezwykle dziwne zachowanie analizatora. Z jednej strony wszystko działało poprawnie, ale analiza nie była uruchamiana. Spędziłem sporo czasu, aby zauważyć, że znajdujemy się w katalogu /home/appveyor/projects/testcalc/, podczas gdy analizator był przekonany, że jesteśmy w /opt/appveyor/build-agent/. Wtedy zrozumiałem, że zmienna $PWD nieco oszukuje. Dlatego ręcznie zaktualizowałem jej wartość przed rozpoczęciem analizy.

A potem wszystko jak dawniej:

Analiza commitów i pull requestów w Travis CI, Buddy i AppVeyor z pomocą PVS-Studio
Teraz rozważmy następujący 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"`

W nim uzyskujemy różnicę między gałęziami, na których zgłoszono pull request. Potrzebujemy do tego następujących zmiennych środowiskowych:

  • $APPVEYOR_PULL_REQUEST_NUMBER — numer pull request;
  • $APPVEYOR_REPO_NAME — nazwa użytkownika i repozytorium projektu.

Podsumowanie

Oczywiście nie omówiliśmy wszystkich możliwych usług ciągłej integracji, jednak wszystkie mają bardzo podobną specyfikę działania. Z wyjątkiem buforowania, każda usługa buduje swój własny „rower”, dlatego zawsze jest wszystko inaczej.

Gdzieś, jak w Travis-CI, wystarczą dwie linijki kodu, a buforowanie działa bezbłędnie; gdzie indziej, jak w AppVeyor, wystarczy po prostu wskazać folder w ustawieniach; ale gdzie indziej trzeba stworzyć unikalne klucze i spróbować przekonać system, by dał ci możliwość nadpisania zakumulowanego fragmentu. Dlatego, jeśli chcesz skonfigurować analizę pull requestów na usłudze ciągłej integracji, która nie została omówiona powyżej, upewnij się najpierw, że nie będziesz miał problemów z buforowaniem.

Dziękuję za uwagę. Jeśli coś nie działa, śmiało pisz do nas na wsparcie. Pomożemy.

Analiza commitów i pull requestów w Travis CI, Buddy i AppVeyor z pomocą PVS-Studio

Jeśli chcesz podzielić się tym artykułem z anglojęzyczną publicznością, proszę użyj linku do tłumaczenia: Maxim Zvyagintsev. Analiza commitów i pull requestów w Travis CI, Buddy i AppVeyor z użyciem PVS-Studio.

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster