Integracja projektu VueJS+TS z SonarQube

W naszej pracy aktywnie korzystamy z platformy SonarQube do utrzymywania wysokiej jakości kodu. Podczas integracji jednego z projektów, napisanego w VueJs+Typescript, pojawiły się problemy. Dlatego chciałbym szczegółowo opowiedzieć, jak udało się je rozwiązać.

Integracja projektu VueJS+TS z SonarQube

W tym artykule mowa będzie, jak wspomniałem wcześniej, o platformie SonarQube. Trochę teorii — czym właściwie jest, dla tych, którzy słyszą o niej po raz pierwszy:

SonarQube (wcześniej Sonar) — to platforma z otwartym źródłem do ciągłej analizy (ang. continuous inspection) i pomiaru jakości kodu.
Obsługuje analizę kodu i wyszukiwanie błędów zgodnie z zasadami standardów programowania MISRA C, MISRA C++, MITRE/CWE i CERT Secure Coding Standards. A także potrafi rozpoznawać błędy z list OWASP Top-10 i CWE/SANS Top-25 błędów programowania.
Mimo że platforma korzysta z różnych gotowych narzędzi, SonarQube sprowadza wyniki do jednego pulpitu informacyjnego (ang. dashboard), prowadząc historię biegów, umożliwiając tym samym zobaczenie ogólnej tendencji zmiany jakości oprogramowania w trakcie rozwijania.

Więcej szczegółów można znaleźć na oficjalnej stronie

Obsługiwanych jest wiele języków programowania. Zgodnie z informacjami z powyższego linku — jest to ponad 25 języków. Aby wspierać konkretny język, należy zainstalować odpowiednią wtyczkę. W wersji community zawarta jest wtyczka do pracy z Javascript (w tym typescript), chociaż w wiki napisano coś innego. Za Javascript odpowiada wtyczka SonarJS, za Typescript SonarTS odpowiednio.

Aby wysłać informacje o pokryciu, używany jest oficjalny klient sonarqube-scanner, który, używając ustawień z config-pliku, wysyła te dane na serwer SonarQube do dalszej konsolidacji i agregacji.

Dla Javascript jest nbm-obudowa. Zatem zaczynamy krok po kroku wprowadzenie SonarQube do Vue-projekt wykorzystujący Typescript.

Do uruchomienia serwera SonarQube skorzystamy z docker-compose.

sonar.yaml:

version: '1'
    services:
        simplesample-sonar:
            image: sonarqube:lts
            ports:
                - 9001:9000
                - 9092:9092
            network_mode: bridge

Uruchomienie:

docker-compose -f sonar.yml up

Po tym SonarQube będzie dostępny pod adresem – http://localhost:9001 .

Integracja projektu VueJS+TS z SonarQube
Na razie nie ma w nim projektów i to jest zrozumiałe. Będziemy poprawiać tę sytuację. Jako podstawę wziąłem oficjalny projekt-przykład dla VueJS+TS+Jest. Sklonujemy go do siebie:

git clone https://github.com/vuejs/vue-test-utils-typescript-example.git

Najpierw musimy zainstalować klienta SonarQube, który nazywa się sonar-scanner, dla npm jest obudowa:

yarn add sonarqube-scanner

I od razu dodamy komendę do scripts do pracy z nim.

package.json:

{
 … 
   skrypty: {
      ...
      "sonar": "sonar-scanner"
      ...
   },
 …
}

Następnie, aby skaner działał, należy ustawić konfiguracje projektu w specjalnym pliku. Zacznijmy od podstawowych.

sonar-project.properties:

sonar.host.url=http://localhost:9001

sonar.projectKey=test-project-vuejs-ts
sonar.projectName=Testowa aplikacja (VueJS+TS)

sonar.sources=src
# sonar.tests=
sonar.test.inclusions=src/**/*tests*/*
sonar.sourceEncoding=UTF-8

  • sonar.host.url – adres Sonar’a;
  • sonar.projectKey – unikalny identyfikator projektu na serwerze Sonar’a;
  • sonar.projectName – jego nazwa, którą można zmienić w dowolnym momencie, ponieważ identyfikacja projektu odbywa się za pomocą projectKey;
  • sonar.sources – folder z kodem źródłowym, zazwyczaj jest to src, ale może być dowolnym. Folder ten określa się w odniesieniu do folderu głównego, którym jest folder, z którego uruchomiono skaner;
  • sonar.tests – parametr, który idzie w parze z poprzednim. Jest to folder, w którym znajdują się testy. W tym projekcie nie ma takiego folderu, a test znajduje się obok testowanego komponentu w folderze 'test‘, dlatego na razie go zignorujemy i skorzystamy z następnego parametru;
  • sonar.test.inclusions – ścieżka dla testów używających maski, może być kilka elementów wymienionych przez przecinek;
  • sonar.sourceEncoding – kodowanie dla plików źródłowych.

Na pierwsze uruchomienie skanera wszystko jest gotowe, oprócz głównego działania wstępnego: uruchomienia samego silnika testowego, aby utworzył informacje o pokryciu, które później wykorzysta skaner.

Ale aby to zrobić, trzeba skonfigurować silnik testowy do generowania tych informacji. W tym projekcie silnik testowy to Jest. A jego konfiguracje znajdują się w odpowiedniej sekcji pliku package.json.

Dodajmy te ustawienia:

"collectCoverage": true,
"collectCoverageFrom": [
      "src/**",
      "!src/main.ts",
      "!src/App.vue",
      "!src/**/*.d.*",
      "!src/**/*__tests__*"
],

To znaczy, wskazujemy flagę konieczności obliczania pokrycia oraz źródło (razem z wyjątkami), na podstawie których będzie ono formułowane.

Teraz uruchommy test:

yarn test

Zobaczymy następujące:

Integracja projektu VueJS+TS z SonarQube

Przyczyną jest to, że w samym komponencie tak naprawdę nie ma kodu. Naprawmy to.

HelloWorld.vue:

...
metody: {
    calc(n) {
      return n + 1;
    }
  },
mounted() {
  this.msg1 = this.msg + this.calc(1);
},
...

Tego będzie wystarczająco do obliczenia pokrycia.

Po ponownym uruchomieniu testu upewnijmy się o tym:

Integracja projektu VueJS+TS z SonarQube

Na ekranie powinniśmy zobaczyć informacje o pokryciu, a w folderze projektu zostanie utworzony folder coverage z informacjami o pokryciu testami w uniwersalnym formacie LCOV (rozszerzenie LTP GCOV).

Gcov — ogólnodostępne narzędzie do badania pokrycia kodu. Gcov generuje dokładną liczbę wykonania dla każdego operatora w programie i pozwala dodać adnotacje do kodu źródłowego. Gcov jest dostarczany jako standardowe narzędzie w pakiecie GCC.
Lcov — interfejs graficzny dla gcov. Zbiera pliki gcov dla wielu plików źródłowych i tworzy zestaw stron HTML z kodem oraz informacjami o pokryciu. Generowane są również strony ułatwiające nawigację. Lcov obsługuje pokrycie linii, funkcji oraz gałęzi.

Po wykonaniu testów informacje o pokryciu będą znajdować się w coverage/lcov.info.
Musimy powiedzieć Sonarskąd pobrać. Dlatego dodamy następujące linie do pliku konfiguracyjnego. Ale jest jeden aspekt: projekty mogą być wielojęzyczne, co oznacza, że w folderze src znajdują się źródła dla wielu języków programowania, a przynależność do danego z nich oraz użycie odpowiedniej wtyczki zależy od jej rozszerzenia. Informacje o pokryciu mogą być przechowywane w różnych miejscach dla różnych języków programowania, dlatego dla każdego języka programowania istnieje własna sekcja konfiguracji. Nasz projekt używa Typescript, dlatego potrzebujemy sekcji ustawień właśnie dla niego:

sonar-project.properties:

sonar.typescript.coveragePlugin=lcov
sonar.typescript.lcov.reportPaths=coverage/lcov.info

Wszystko jest gotowe do pierwszego uruchomienia skanera. Chcę zauważyć, że projekt w Sonartworzy się automatycznie przy pierwszym uruchomieniu skanera dla danego projektu. Przy kolejnych uruchomieniach informacje będą już gromadzone, aby móc obserwować dynamikę zmian parametrów projektu w czasie.

Zatem skorzystamy z polecenia, stworzonego wcześniej w package.json:

yarn run sonar 

Uwaga: można również skorzystać z parametru -X do bardziej szczegółowego logowania.

Jeśli skanera uruchamiano po raz pierwszy, najpierw zostanie pobrany binarny plik samego skanera. Następnie uruchomi się on i zacznie skanować serwer Sonarpod kątem zainstalowanych wtyczek, obliczając tym samym obsługiwane języki programowania. Równocześnie ładowane są inne różne parametry dla jego pracy: profile jakości, aktywne zasady, repozytorium metryk, zasady serwera.

Integracja projektu VueJS+TS z SonarQube

Integracja projektu VueJS+TS z SonarQube

Uwaga: o nich szczegółowo się nie zatrzymamy w ramach tego artykułu, ale zawsze można sięgnać do oficjalnych źródeł.

Następnie zaczyna się analiza folderu src w kwestii dostępności plików źródłowych dla wszystkich (o ile nie określono wyraźnie jakiegoś konkretnego) wspieranych Języków Programowania, a następnie ich indeksacji.

Integracja projektu VueJS+TS z SonarQube

Następnie stają się dostępne różne inne analizy, na których nie skupiamy się w tym artykule (na przykład: linting, określanie duplikacji kodu itp.).

Na samym końcu pracy skanera następuje agregacja wszystkich zebranych informacji, archiwizacja i wysyłka ich na serwer.

Po tym możemy już zobaczyć, co wyszło w interfejsie webowym:

Integracja projektu VueJS+TS z SonarQube

Jak widać, coś się udało, i nawet pokazuje jakieś pokrycie, ale nie odpowiada ono naszemu Jest-raportowi.

Przyjrzyjmy się bliżej. Zobaczmy projekt w bardziej szczegółowy sposób, kliknijmy na wartość pokrycia i "zanużmy się" w szczegółowy raport dotyczący plików:

Integracja projektu VueJS+TS z SonarQube

Tutaj widzimy oprócz głównego, badanej pliku HelloWorld.vue, obecny jest także plik main.ts, który psuje całą koncepcję pokrycia. Ale jak to możliwe, skoro go wykluczyliśmy z obliczeń pokrycia. Tak, to prawda, lecz miało to miejsce na poziomie Jest, ale skaner go zaindeksował, więc trafił do jego kalkulacji.

Naprawmy to:

sonar-project.properties:

...
sonar.exclusions=src/main.ts
...

Chcę dokonać doprecyzowania: oprócz tych folderów, które zostały określone w tym parametrze, dodawane są także wszystkie foldery wymienione w parametrze sonar.test.inclusions.

Po uruchomieniu skanera widzimy już poprawne informacje:

Integracja projektu VueJS+TS z SonarQube

Integracja projektu VueJS+TS z SonarQube

Omówmy następny aspekt – Profile jakości. Mówiłem wcześniej o wsparciu Sonarwielu Języków Programowania jednocześnie. To właśnie w tej sytuacji obserwujemy. Ale wiemy, że nasz projekt jest napisany w TS, więc po co obciążać skaner zbędnymi manipulacjami i kontrolami. Język analizy wskaźmy poprzez dodanie jeszcze jednego parametru do pliku konfiguracyjnego Sonar:

sonar-project.properties:

...
sonar.language=ts
...

Ponownie uruchommy skaner i zobaczmy wynik:

Integracja projektu VueJS+TS z SonarQube

Pokrycie całkowicie zniknęło.

Jeśli spojrzymy w log skanera, możemy zobaczyć następującą linię:

Integracja projektu VueJS+TS z SonarQube

To znaczy, że pliki naszego projektu po prostu nie zostały zaindeksowane.

Sytuacja wygląda następująco: wsparcie VueJs jest dostępne w wtyczce, SonarJSktóra odpowiada za Javascript.

Integracja projektu VueJS+TS z SonarQube

Ale tego wsparcia nie ma w wtyczce, SonarTS do TSo czym został założony oficjalny ticket w systemie śledzenia błędów. Sonar:

  1. https://jira.sonarsource.com/browse/MMF-1441
  2. https://github.com/SonarSource/SonarJS/issues/1281

Oto niektóre odpowiedzi jednego z przedstawicieli zespołu programistów SonarQube, potwierdzające ten fakt.

Integracja projektu VueJS+TS z SonarQube

Integracja projektu VueJS+TS z SonarQube

Ale przecież wszystko działało, można by zapytać. Tak, to prawda, spróbujmy trochę "hackerować".
Jeśli jest wsparcie dla plików .vue .vue-plików Sonarjeśli tak, spróbujmy powiedzieć mu, żeby traktował je jako Typescript.

Dodajmy parametr:

sonar-project.properties:

...
sonar.typescript.file.suffixes=.ts,.tsx,.vue
...

Uruchomimy skaner:

Integracja projektu VueJS+TS z SonarQube

I voilà, wszystko wróciło do normy, z jednym profilem tylko dla Typescript. To znaczy udało się rozwiązać problem z obsługą VueJs+TS do SonarQube.

Spróbujemy pójść dalej i nieco poprawić informacje o pokryciu.

Co zrobiliśmy do tej pory:

  • dodaliśmy do projektu Sonar-skaner;
  • skonfigurowaliśmy Jest w celu generowania informacji o pokryciu;
  • skonfigurowaliśmy Sonar-skaner;
  • rozwiązaliśmy problem z obsługą .vue-plików + Typescript.

Oprócz pokrycia testami są inne interesujące kryteria jakości kodu, takie jak duplikacja kodu i liczba linii (bierze udział w obliczeniu współczynników związanych ze złożonością kodu) projektu.

W bieżącej wersji wtyczki do pracy z TS (SonarTS) nie będzie działać CPD (Copy Paste Detector) i zliczanie linii kodu .vue-plików.

Aby stworzyć syntetyczną sytuację z duplikacją kodu, po prostu skopiujemy plik komponentu z inną nazwą, dodamy również do kodu main.ts pustą funkcję i skopiujemy ją z inną nazwą. Aby sprawdzić duplikację jak w .vue, jak i w .ts -plikach.

main.ts:

...
function name(params:string): void {
  console.log(params);
}
...

W tym celu należy tymczasowo zakomentować linię konfiguracyjną:

sonar-project.properties:

...
sonar.exclusions=src/main.ts
...

Uruchomimy skaner wraz z testowaniem:

yarn test && yarn run sonar

Nasze pokrycie oczywiście spadnie, ale teraz nas to nie interesuje.

W odniesieniu do duplikacji linii kodu zobaczymy:

Integracja projektu VueJS+TS z SonarQube

Do sprawdzenia skorzystamy z CPD-narzędzia - jscpd:

npx jscpd src

Integracja projektu VueJS+TS z SonarQube

Dla linii kodu:

Integracja projektu VueJS+TS z SonarQube

Możliwe, że to zostanie rozwiązane w przyszłych wersjach wtyczek SonarJS(TS). Chcę zauważyć, że stopniowo zaczynają łączyć te dwie wtyczki w jedną SonarJS, co uważam za słuszne.

Teraz chcielibyśmy rozważyć opcję poprawy informacji o pokryciu.

Na razie widzimy pokrycie testami w stosunku procentowym, w całym projekcie oraz w poszczególnych plikach. Ale istnieje możliwość rozszerzenia tego wskaźnika informacjami o liczbie unit-testów w projekcie, a także w odniesieniu do plików.

Jest biblioteka, która potrafi Jest-raport przekonwertować na format dla Sonar:
generic test datahttps://docs.sonarqube.org/display/SONAR/Generic+Test+Data.

Zainstalujemy tę bibliotekę w naszym projekcie:

yarn add jest-sonar-reporter

I dodamy ją do konfiguracji Jest:

package.json:

…
"testResultsProcessor": "jest-sonar-reporter"
…

Teraz wykonamy test:

yarn test

Po czym w katalogu głównym projektu zostanie utworzony plik test-report.xml.

Użyjemy go w konfiguracji Sonar:

sonar-project.properties:

…
sonar.testExecutionReportPaths=test-report.xml
…

I uruchomimy ponownie skaner:

yarn run sonar

Sprawdźmy, co zmieniło się w interfejsie Sonar:

Integracja projektu VueJS+TS z SonarQube

I nic się nie zmieniło. Problem polega na tym, że Sonar nie traktuje plików opisanych w raporcie Jest jako pliki unit-testów. Aby to naprawić, wykorzystamy parametr konfiguracyjny Sonar sonar.tests, w którym wyraźnie wskażemy foldery z testami (na razie mamy tylko jeden):

sonar-project.properties:

…
sonar.tests=src/components/__tests__
…

Uruchomimy ponownie skaner:

yarn run sonar

Zobaczmy, co zmieniło się w interfejsie:

Integracja projektu VueJS+TS z SonarQube

Teraz widzimy liczbę naszych unit-testów i po kliknięciu możemy zobaczyć rozkład tej liczby w plikach projektu:

Integracja projektu VueJS+TS z SonarQube

Podsumowanie

Zbadaliśmy narzędzie do ciągłej analizy SonarQube. Pomyślnie zintegrowaliśmy je z projektem napisanym w VueJs+TS. Rozwiązaliśmy kilka problemów z kompatybilnością. Zwiększyliśmy informacyjność wskaźnika dotyczącego pokrycia testami. W tym artykule omówiliśmy tylko jeden z kryteriów jakości kodu (prawdopodobnie jeden z najważniejszych), ale SonarQube obsługuje również inne kryteria jakości, w tym testowanie bezpieczeństwa. Jednak nie wszystkie te możliwości są dostępne w pełnym zakresie w wersji community. Jedną z interesujących i przydatnych możliwości jest integracja SonarQube z różnymi systemami zarządzania repozytoriami kodu, takimi jak GitLab czy BitBucket. Aby zapobiec merge pull(merge) requestw głównym gałęzi repozytorium przy degradacji pokrycia. Ale to już inna historia do zupełnie innego artykułu.

PS: Wszystko, co opisano w artykule w postaci kodu, jest dostępne w moim forku.

Tylko zarejestrowani użytkownicy mogą brać udział w ankiecie. Zaloguj się, proszę.

Czy używasz platformy SonarQube:

  • 26,3%Tak5

  • 15,8%Nie3

  • 15,8%Słyszałem o tej platformie i chcę z niej korzystać3

  • 10,5%Słyszałem o tej platformie i nie chcę z niej korzystać2

  • 0,0%Używam innej platformy0

  • 31,6%Po raz pierwszy o niej słyszę6

Głosowało 19 użytkowników. 3 użytkowników wstrzymało się od głosu.

Ź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