У сваёй працы мы актыўна выкарыстоўваем платформу SonarQube для падтрымання якасці кода на высокім узроўні. Пры інтэграцыі аднаго з праектаў, напісаным на VueJs+Typescript, узніклі праблемы. Таму хацеў бы расказаць падрабязней аб тым, як удалося іх вырашыць.
У дадзеным артыкуле прамова пайдзе, як пісаў вышэй, аб платформе SonarQube. Трохі тэорыі - што гэта такое наогул, для тых, хто чуе пра яе ўпершыню:
SonarQube (былы сонар) — платформа з адкрытым зыходным кодам для бесперапыннага аналізу (ангел. continuous inspection) і вымярэння якасці кода.
Падтрымлівае аналіз кода і пошук памылак паводле правіл стандартаў праграмавання MISRA C, MISRA C++, MITRE/CWE і CERT Secure Coding Standards. А таксама ўмее распазнаваць памылкі са спісаў OWASP Топ-10 і CWE/SANS Топ-25 памылак праграмавання.
Нягледзячы на тое, што платформа выкарыстоўвае розныя гатовыя прылады, SonarQube зводзіць вынікі да адзінай інфармацыйнай панэлі (ангел. dashboard), вядучы гісторыю прагонаў і дазваляючы тым самым убачыць агульную тэндэнцыю змены якасці праграмнага забеспячэння падчас распрацоўкі.
Больш падрабязна можна даведацца на
Падтрымліваецца вялікая колькасць моў праграмавання. Мяркуючы па інфармацыі са спасылкі вышэй - гэта больш за 25 моў. Для падтрымкі канкрэтнай мовы неабходна ўсталяваць адпаведную ўбудову. У community-версію ўваходзіць убудова для працы з Javascript (у тым ліку typesсript), хаця ў wiki напісана адваротнае. За Javascript адказвае плягін SonarJS, за Typescript SonarTS адпаведна.
Для адпраўкі інфармацыі аб пакрыцці выкарыстоўваецца афіцыйны кліент sonarqube-scanner, які, выкарыстоўваючы налады з конфіг-файла, адпраўляе гэтыя дадзеныя на сервер SonarQube для далейшай кансалідацыі і агрэгавання.
Для Javascript ёсць
Для разгортвання сервера SonarQube скарыстаемся докер-Compose.
sonar.yaml:
version: '1'
services:
simplesample-sonar:
image: sonarqube:lts
ports:
- 9001:9000
- 9092:9092
network_mode: bridge
запуск:
docker-compose -f sonar.yml up
Пасля гэтага SonarQube будзе даступны па адрасе –
Пакуль у ім няма праектаў, і гэта справядліва. Будзем выпраўляць дадзеную сітуацыю. За аснову я ўзяў афіцыйны праект-прыклад для VueJS+TS+Jest. Схілюем яго да сябе:
git clone https://github.com/vuejs/vue-test-utils-typescript-example.git
Спачатку нам трэба ўсталяваць кліент SonarQube, які называецца sonar-scanner, для НПМ ёсць абгортка:
yarn add sonarqube-scanner
І адразу ж дадамо каманду ў скрыпты для працы з ім.
package.json:
{
…
scripts: {
...
"sonar": "sonar-scanner"
...
},
…
}
Далей, для працы сканара, трэба задаць налады праекта ў спецыяльным файле. Пачнём з базавых.
sonar-project.properties:
sonar.host.url=http://localhost:9001
sonar.projectKey=test-project-vuejs-ts
sonar.projectName=Test Application (VueJS+TS)
sonar.sources=src
# sonar.tests=
sonar.test.inclusions=src/**/*tests*/**
sonar.sourceEncoding=UTF-8
- sonar.host.url - адрас сонар'а;
- sonar.projectKey - унікальны ідэнтыфікатар праекта на серверы сонар'а;
- sonar.projectName – яго найменне, яно можа быць зменена ў любы момант, бо ідэнтыфікацыя праекта робіцца па projectKey;
- sonar.sources – тэчка з зыходнікамі, звычайна гэта SRC, Але можа быць любым. Гэтая тэчка задаецца адносна рутовой тэчкі, якой з'яўляецца тэчка адкуль запушчаны сканер;
- sonar.tests - Параметр, які ідзе ў пары з папярэднім. Гэта тэчка, дзе знаходзяцца тэсты. У дадзеным праекце, няма такой тэчкі, а тэст знаходзіцца побач з тэстоўваным кампанентам у тэчцы 'тэст', таму мы яго пакуль праігнаруемы і скарыстаемся наступным параметрам;
- sonar.test.inclusions - шлях для тэстаў з выкарыстаннем маскі, можа быць некалькі элементаў пералічаных праз коску;
- sonar.sourceEncoding - кадоўка для зыходных файлаў.
Для першага запуску сканара ўсё гатова, акрамя асноўнага папярэдняга дзеяння: запуск самага тэставага рухавічка, для фармавання ім інфармацыі аб пакрыцці, якую і будзе ў наступстве выкарыстаць сканер.
Але для гэтага трэба наладзіць тэставы рухавічок на фармаванне дадзенай інфармацыі. У дадзеным праекце тэставы рухавічок - гэта ёсць. І яго налады знаходзяцца ў адпаведным раздзеле файла package.json.
Дадамо гэтыя наладкі:
"collectCoverage": true,
"collectCoverageFrom": [
"src/**/*",
"!src/main.ts",
"!src/App.vue",
"!src/**/*.d.*",
"!src/**/*__tests__*"
],
Гэта значыць задаем сам сцяг неабходнасці вылічэння пакрыцця і крыніца (разам з выключэннямі), на аснове якіх яно будзе фармавацца.
Цяпер запусцім тэст:
yarn test
Убачым наступнае:
Чыннік у тым, што ў самім кампаненце, як такога, кода няма. Выправім гэта.
HelloWorld.vue:
...
methods: {
calc(n) {
return n + 1;
}
},
mounted() {
this.msg1 = this.msg + this.calc(1);
},
...
Гэтага будзе дастаткова для разліку пакрыцця.
Пасля перазапуску цеста пераканаемся ў гэтым:
На экране мы павінны ўбачыць інфармацыю аб пакрыцці, а ў тэчцы праекта будзе створана тэчка ахоп з інфармацыяй аб пакрыцці тэстамі ва ўніверсальным фармаце LCOV (LTP GCOV extension).
Gcov - вольна распаўсюджваная ўтыліта для даследавання пакрыцця кода. Gcov генеруе дакладную колькасць выкананняў для кожнага аператара ў праграме і дазваляе дадаць анатацыі да зыходнага кода. Gcov пастаўляецца як стандартная ўтыліта ў складзе пакета GCC.
Lcov - графічны інтэрфейс для gcov. Ён збірае файлы gcov для некалькіх файлаў з зыходнікамі і стварае камплект HTML старонак з кодам і звесткамі аб пакрыцці. Таксама генеруюцца старонкі для спрашчэння навігацыі. Lcov падтрымлівае пакрыццё радкоў, функцый, галінаванняў.
Пасля выканання тэстаў інфармацыя аб пакрыцці будзе знаходзіцца ў coverage/lcov.info.
Нам трэба сказаць сонарАдкуль яе ўзяць. Таму дадамо наступныя радкі ў яго файл канфігурацыі. Але ёсць адзін момант: праекты могуць быць мультымоўныя, гэта значыць у тэчцы SRC знаходзяцца зыходнікі для некалькіх моў праграмавання і прыналежнасць да таго ці іншага, і ў сваю чаргу выкарыстанне той ці іншай убудовы, вызначаецца па ім пашырэнню. І інфармацыя аб пакрыцці можа захоўваецца ў розных месцах для розных моў праграмавання, таму для кожнага ЯП ёсць свой раздзел для наладкі гэтага. У нас праект выкарыстоўвае Машынапіс, таму нам неабходны падзел налад менавіта для яго:
sonar-project.properties:
sonar.typescript.coveragePlugin=lcov
sonar.typescript.lcov.reportPaths=coverage/lcov.info
Усё гатова да першага запуску сканара. Жадаю заўважыць, што праект у сонар'е ствараецца аўтаматычна пры першым запуску сканара для дадзенага праекту. У наступныя разы інфармацыя ўжо будзе акумулявацца, каб бачыць дынаміку змены параметраў праекту ў часе.
Такім чынам, скарыстаемся камандай, створанай раней у package.json:
yarn run sonar
Заўвага: можна таксама скарыстацца параметрам -X для больш дэталёвага лагіравання.
Калі запуск сканара быў упершыню, то спачатку запампуецца бінарнік самога сканара. Пасля гэтага ён запускаецца і пачынае сканаваць сервер сонарТа на прадмет усталяваных убудоў, вылічаючы тым самым падтрымоўваныя ЯП. Таксама загружаюцца іншыя розныя параметры для яго працы: quality profiles, active rules, metrics repository, server rules.
Заўвага: падрабязна на іх мы спыняцца не будзем у рамках дадзенага артыкула, але заўсёды можна звярнуцца ў афіцыйныя крыніцы.
Далей пачынаецца аналіз тэчкі SRC на прадмет наяўнасці зыходных файлаў для ўсіх (калі не зададзены відавочна нейкі пэўны) падтрымоўваных ЯП, з наступнай іх індэксацыяй.
Далей ідуць іншыя розныя аналізы, на якіх мы не завастраем увагу ў дадзеным артыкуле (напрыклад, такія як: лінтынг, вызначэнне дублявання кода і тд).
У самым канцы працы сканара адбываецца агрэгаванне ўсёй сабранай інфармацыі, архіваванне і адпраўка яе на сервер.
Пасля гэтага мы можам ужо паглядзець што атрымалася ў вэб-інтэрфейсе:
Як бачым, нешта атрымалася, і нават паказвае нейкае пакрыццё, але яно не адпавядае нашаму ёсць-справаздачы.
Давайце разбірацца. Паглядзім на праект больш дэталёва, клікнем па значэнні пакрыцця, і "правалімся" у дэталізаваную справаздачу па файлах:
Тут мы бачым акрамя асноўнага, доследнага файла HelloWorld.vue, прысутнічае і файл асноўны.ц, Які і псуе ўсю карціну пакрыцця. Але як жа так, мы яго выключалі з разліку пакрыцця. Так, усё правільна, але гэта было на ўзроўні ёсць, Але сканер яго праіндэксаваў, таму ён патрапіў у яго разлікі.
Давайце выправім гэта:
sonar-project.properties:
...
sonar.exclusions=src/main.ts
...
Жадаецца зрабіць удакладненне: апроч тых тэчак, якія зададзены ў дадзеным параметры, таксама дадаюцца ўсе тэчкі, пералічаныя ў параметры sonar.test.inclusions.
Пасля запуску сканара бачым ужо карэктную інфармацыю:
Разбярэм наступны момант - Quality profiles. Я казаў вышэй аб падтрымцы сонарТым некалькі ЯП адначасова. Вось гэта якраз мы і назіраем. Але мы ведаем, што праект у нас напісаны на TS, таму навошта напружваць сканер лішнімі маніпуляцыямі і праверкамі. Мова для аналізу зададзім праз даданне яшчэ аднаго параметра ў файл канфігурацыі. сонарТа:
sonar-project.properties:
...
sonar.language=ts
...
Зноў запусцім сканер і паглядзім вынік:
Пакрыццё знікла зусім.
Калі паглядзім у лог сканара, то можам убачыць наступны радок:
Гэта значыць, файлы нашага праекта проста не былі праіндэксаваныя.
Сітуацыя наступная: афіцыйна падтрымка VueJs ёсць у плагіне SonarJS, які адказвае за Javascript.
Але гэтай падтрымкі няма ў плягіне SonarTS для TS, пра што заведзены афіцыйны тыкет у баг-трэкеры сонарТа:
Вось некаторыя адказы аднаго з прадстаўнікоў з боку распрацоўшчыкаў SonarQube, які пацвярджае гэты факт.
Але ў нас жа ўсё працавала, запярэчыце Вы. Так, так і ёсць, давайце паспрабуем крыху "пахакерыць".
Калі ёсць падтрымка .vue-файлаў сонарТым, то давайце паспрабуем сказаць яму каб ён іх разглядаў як Машынапіс.
Дадамо параметр:
sonar-project.properties:
...
sonar.typescript.file.suffixes=.ts,.tsx,.vue
...
Запусцім сканер:
І, вуаля, усё вярнулася на кругі свая, і з адным профілем толькі для Машынапіс. Гэта значыць удалося вырашыць праблему ў падтрымцы VueJs+TS для SonarQube.
Паспрабуем пайсці далей і крыху палепшым інфармацыю аб пакрыцці.
Што ж мы зрабілі цяпер:
- дадалі ў праект сонар-сканер;
- настроілі ёсць для фарміравання інфармацыі аб пакрыцці;
- сканфігуравалі сонар-сканер;
- вырашылі праблему падтрымкі .vue-файлаў + Машынапіс.
Акрамя пакрыцця тэстамі ёсць іншыя цікавыя карысныя крытэры якасці кода, напрыклад, дубліраванне кода і колькасць радкоў (удзельнічае ў разліку каэфіцыентаў, звязаных са складанасцю кода) праекта.
У бягучай рэалізацыі плагіна для працы з TS (SonarTS) не будзе працаваць CPD (Copy Paste Detector) і падлік радкоў кода .vue-файлаў.
Для стварэння сінтэтычнай сітуацыі па дубляванні кода, проста задублюем файл кампанента з іншым імем, таксама дадамо ў код асноўны.ц функцыю-пустышку і задублюем яго з іншым імем. Каб праверыць дубліраванне як у .vue, Так і ў .ts -файлах.
main.ts:
...
function name(params:string): void {
console.log(params);
}
...
Для гэтага неабходна часова закаментаваць радок канфігурацыі:
sonar-project.properties:
...
sonar.exclusions=src/main.ts
...
Перазапусцім сканер разам з тэставаннем:
yarn test && yarn run sonar
У нас вядома ўпадзе пакрыццё, але зараз нам гэта не цікава.
У разрэзе дублявання радкоў кода ўбачым:
Для праверкі скарыстаемся CPD-утылітай - jscpd:
npx jscpd src
Для радкоў кода:
Магчыма, гэта вырашыцца ў будучых версіях убудоў. SonarJS(TS). Хачу заўважыць, што яны паступова пачынаюць зліваць гэтыя два плагіны ў адзін SonarJS, Што, думаю, правільна.
Цяпер хацелася разгледзець варыянт паляпшэння інфармацыі аб пакрыцці.
Пакуль мы бачым пакрыццё тэстамі ў працэнтных адносінах, па ўсім праекце, і па файлах у прыватнасці. Але ёсць магчымасць пашырыць гэты паказчык інфармацыяй аб колькасці блок-тэстаў па праекце, а таксама ў разрэзе файлаў.
Ёсць бібліятэка, якая ўмее ёсць-рэпарт канвертаваць у фармат для сонарТа:
generic test data -
Усталюем гэтую бібліятэку да сябе ў праект:
yarn add jest-sonar-reporter
І дадамо яго ў канфігурацыю ёсць:
package.json:
…
"testResultsProcessor": "jest-sonar-reporter"
…
Цяпер выканаем тэст:
yarn test
Пасля чаго ў корані праекту будзе створаны файл test-report.xml.
Задзейнічаем яго ў канфігурацыі сонарТа:
sonar-project.properties:
…
sonar.testExecutionReportPaths=test-report.xml
…
І перазапусцім сканер:
yarn run sonar
Паглядзім, што змянілася ў інтэрфейсе сонарТа:
І нічога не памянялася. Справа ў тым, што Sonar не разглядае файлы, апісаныя ў Jest-рэпарце, як файлы блок-тэстаў. Для таго, каб выправіць гэтую сітуацыю, задзейнічаем параметр канфігурацыі. сонар sonar.tests, у якім відавочна пакажам тэчкі з тэстамі (яна ў нас пакуль адна):
sonar-project.properties:
…
sonar.tests=src/components/__tests__
…
Перазапусцім сканер:
yarn run sonar
Паглядзім, што змянілася ў інтэрфейсе:
Цяпер мы ўбачылі колькасць нашых блок-тэстаў і, праваліўшыся па кліку ўнутр, можам паглядзець размеркаванне гэтага ліку па файлах праекту:
Заключэнне
Такім чынам, мы разгледзелі інструмент для бесперапыннага аналізу SonarQube. Паспяхова інтэгравалі ў яго праект, напісаны на VueJs+TS. Вырашылі некаторыя праблемы сумяшчальнасці. Павысілі інфарматыўнасць паказчыка аб пакрыцці тэстамі. У дадзеным артыкуле мы разгледзелі толькі адзін з крытэрыяў якасці кода (магчыма, адзін з асноўных), але SonarQube падтрымлівае і іншыя крытэры якасці, уключаючы тэсціраванне на бяспеку. Але не ўсе гэтыя магчымасці ў поўным аб'ёме даступныя ў супольнасць-версіі. Адна з цікавых і карысных магчымасцяў - гэта інтэграцыі SonarQube з рознымі сістэмамі кіравання рэпазітарамі кода, напрыклад, такія як GitLab і BitBucket. Каб не дапусціць merge pull(merge) requestТая ў асноўную галіну рэпазітара пры дэградацыі пакрыцця. Але гэта гісторыя ўжо зусім іншага артыкула.
PS: Усё, што апісана ў артыкуле ў выглядзе кода даступна ў
Толькі зарэгістраваныя карыстачы могуць удзельнічаць у апытанні.
Ці карыстаецеся Вы платформу SonarQube:
-
26,3%Так5
-
15,8%Няма3
-
15,8%Чуў пра дадзеную платформу і хачу выкарыстоўваць3
-
10,5%Чуў пра дадзеную платформу і не хачу выкарыстоўваць2
-
0,0%Выкарыстоўваю іншую платформу0
-
31,6%Упершыню чую пра яе6
Прагаласавалі 19 карыстальнікаў. Устрымаліся 3 карыстальніка.
Крыніца: habr.com