Как стать автором
Обновить
0
Ingram Micro Cloud
Дистрибьюторская платформа для облачных сервисов

Интеграция проекта VueJS+TS с SonarQube

Время на прочтение9 мин
Количество просмотров6.6K

В своей работе мы активно используем платформу SonarQube для поддержания качества кода на высоком уровне. При интеграции одного из проектов, написанном на VueJs+Typescript, возникли проблемы. Поэтому хотел бы рассказать подробней о том, как удалось их решить.



В данной статье речь пойдет, как писал выше, о платформе SonarQube. Немного теории — что это такое вообще, для тех, кто слышит о ней впервые:


SonarQube (бывший Sonar) — платформа с открытым исходным кодом для непрерывного анализа (англ. 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, который, используя настройки из config-файла, отправляет эти данные на сервер SonarQube для дальнейшей консолидации и агрегирования.


Для Javascript есть npm-обертка. Итак, начинаем пошаговое внедрение SonarQube в Vue-проект, использующий Typescript.


Для развертывания сервера SonarQube воспользуемся docker-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 будет доступен по адресу – http://localhost:9001 .



Пока в нем нет проектов и это справедливо. Будем исправлять данную ситуацию. За основу я взял официальный проект-пример для VueJS+TS+Jest. Склонируем его к себе:


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

Сначала нам нужно установить клиент SonarQube, который называется sonar-scanner, для npm есть обертка:


yarn add sonarqube-scanner

И сразу же добавим команду в scripts для работы с ним.


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’а;
  • sonar.projectKey – уникальный идентификатор проекта на сервере Sonar’а;
  • sonar.projectName – его наименование, оно может быть изменено в любой момент, так как идентификация проекта производится по projectKey;
  • sonar.sources – папка с исходниками, обычно это src, но может быть любым. Эта папка задается относительно рутовой папки, которой является папка откуда запущен сканер;
  • sonar.tests – параметр, который идет в паре с предыдущим. Это папка, где находятся тесты. В данном проекте, нет такой папки, а тест находится рядом с тестируемым компонентом в папке 'test', поэтому мы его пока проигнорируем и воспользуемся следующим параметром;
  • sonar.test.inclusions – путь для тестов с использованием маски, может быть несколько элементов перечисленных через запятую;
  • sonar.sourceEncoding – кодировка для исходных файлов.

Для первого запуска сканера все готово, кроме основного предшествующего действия: запуск самого тестового движка, для формирования им информации о покрытии, которую и будет в последствии использовать сканер.


Но для этого нужно настроить тестовый движок на формирование данной информации. В данном проекте тестовый движок — это Jest. И его настройки находятся в соответствующем разделе файла 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);
},
...

Этого будет достаточно для расчета покрытия.


После перезапуска теста убедимся в этом:



На экране мы должны увидеть информацию о покрытии, а в папке проекта будет создана папка coverage с информацией о покрытии тестами в универсальном формате LCOV (LTP GCOV extension).


Gcov — свободно распространяемая утилита для исследования покрытия кода. Gcov генерирует точное количество исполнений для каждого оператора в программе и позволяет добавить аннотации к исходному коду. Gcov поставляется как стандартная утилита в составе пакета GCC.
Lcov — графический интерфейс для gcov. Он собирает файлы gcov для нескольких файлов с исходниками и создает комплект HTML страниц с кодом и сведениями о покрытии. Также генерируются страницы для упрощения навигации. Lcov поддерживает покрытие строк, функций, ветвлений.

После выполнения тестов информация о покрытии будет находится в coverage/lcov.info.
Нам надо сказать Sonar’у откуда ее взять. Поэтому добавим следующие строчки в его файл конфигурации. Но есть один момент: проекты могут быть мультиязычные, то есть в папке src находятся исходники для нескольких языков программирования и принадлежность к тому или иному, и в свою очередь использование того или иного плагина, определяется по его расширению. И информация о покрытии может хранится в разных местах для разных языков программирования, поэтому для каждого ЯП есть свой раздел для настройки этого. У нас проект использует Typescript, поэтому нам необходим раздел настроек именно для него:


sonar-project.properties:


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

Все готово к первому запуску сканера. Хочу заметить, что проект в Sonar’е создается автоматически при первом запуске сканера для данного проекта. В последующие разы информация уже будет аккумулироваться, чтобы видеть динамику изменения параметров проекта во времени.


Итак, воспользуемся командой, созданной ранее в package.json:


yarn run sonar 

Примечание: можно также воспользоваться параметром -X для более детального логирования.


Если запуск сканера был впервые, то сначала скачается бинарник самого сканера. После этого он запускается и начинает сканировать сервер Sonar’а на предмет установленных плагинов, вычисляя тем самым поддерживаемые ЯП. Также загружаются другие различные параметры для его работы: quality profiles, active rules, metrics repository, server rules.




Примечание: подробно на них мы останавливаться не будем в рамках данной статьи, но всегда можно обратиться в официальные источники.


Далее начинается анализ папки src на предмет наличия исходных файлов для всех (если не задан явно какой-то конкретный) поддерживаемых ЯП, с последующей их индексацией.



Далее идут другие различные анализы, на которых мы не заостряем внимание в данной статье (например, такие как: линтинг, определение дублирования кода и тд).


В самом конце работы сканера происходит агрегирование всей собранной информации, архивирование и отправка ее на сервер.


После этого мы можем уже посмотреть что получилось в вэб-интерфейсе:



Как видим, что-то получилось, и даже показывает какое-то покрытие, но оно не соответствует нашему Jest-отчету.


Давайте разбираться. Посмотрим на проект более детально, кликнем по значению покрытия, и "провалимся" в детализированный отчет по файлам:



Здесь мы видим помимо основного, исследуемого файла HelloWorld.vue, присутствует и файл main.ts, который и портит всю картину покрытия. Но как же так, мы его исключали из расчета покрытия. Да, все правильно, но это было на уровне Jest, но сканер его проиндексировал, поэтому он попал в его расчеты.


Давайте исправим это:


sonar-project.properties:


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

Хочется сделать уточнение: помимо тех папок, которые заданы в данном параметре, также добавляются все папки, перечисленные в параметре sonar.test.inclusions.


После запуска сканера видим уже корректную информацию:




Разберем следующий момент – Quality profiles. Я говорил выше о поддержке Sonar’ом несколько ЯП одновременно. Вот это как раз мы и наблюдаем. Но мы знаем, что проект у нас написан на TS, поэтому зачем напрягать сканер лишними манипуляциями и проверками. Язык для анализа зададим через добавление еще одного параметра в файл конфигурации Sonar’а:


sonar-project.properties:


...
sonar.language=ts
...

Снова запустим сканер и посмотрим результат:



Покрытие пропало вовсе.


Если посмотрим в лог сканера, то можем увидеть следующую строчку:



То есть файлы нашего проекта просто не были проиндексированы.


Ситуация следующая: официально поддержка VueJs есть в плагине SonarJS, который отвечает за Javascript.



Но этой поддержки нет в плагине SonarTS для TS, о чем заведен официальный тикет в баг-трекере Sonar’а:


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

Вот некоторые ответы одного из представителей со стороны разработчиков SonarQube, подтверждающий этот факт.




Но у нас же все работало, возразите Вы. Да, так и есть, давайте попробуем немного “похакерить”.
Если есть поддержка .vue-файлов Sonar’ом, то давайте попробуем сказать ему чтобы он их рассматривал как Typescript.


Добавим параметр:


sonar-project.properties:


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

Запустим сканер:



И, вуаля, все вернулось на круги своя, и с одним профилем только для Typescript. То есть удалось решить проблему в поддержке VueJs+TS для SonarQube.


Попробуем пойти дальше и немного улучшим информацию о покрытии.


Что же мы сделали на данный момент:


  • добавили в проект Sonar-сканер;
  • настроили Jest для формирования информации о покрытии;
  • сконфигурировали Sonar-сканер;
  • решили проблему поддержки .vue-файлов + Typescript.

Кроме покрытия тестами есть другие интересные полезные критерии качества кода, например, дублирование кода и количество строк (участвует в расчете коэффициентов, связанных со сложностью кода) проекта.


В текущей реализации плагина для работы с TS (SonarTS) не будет работать CPD (Copy Paste Detector) и подсчет строк кода .vue-файлов.


Для создания синтетической ситуации по дублированию кода, просто задублируем файл компонента с другим именем, также добавим в код main.ts функцию-пустышку и задублируем его с другим именем. Чтобы проверить дублирование как в .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, что, думаю, правильно.


Теперь хотелось рассмотреть вариант улучшения информации о покрытии.


Пока мы видим покрытие тестами в процентном отношении, по всему проекту, и по файлам в частности. Но есть возможность расширить этот показатель информацией о количестве unit-тестов по проекту, а также в разрезе файлов.


Есть библиотека, которая умеет Jest-репорт конвертировать в формат для Sonar’а:
generic test datahttps://docs.sonarqube.org/display/SONAR/Generic+Test+Data.


Установим эту библиотеку к себе в проект:


yarn add jest-sonar-reporter

И добавим его в конфигурацию Jest:


package.json:


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

Теперь выполним тест:


yarn test

После чего в корне проекта будет создан файл test-report.xml.


Задействуем его в конфигурации Sonar’а:


sonar-project.properties:


…
sonar.testExecutionReportPaths=test-report.xml
…

И перезапустим сканер:


yarn run sonar

Посмотрим, что поменялось в интерфейсе Sonar’а:



И ничего не поменялось. Дело в том, что Sonar не рассматривает файлы, описанные в Jest-репорте, как файлы unit-тестов. Для того, чтобы исправить эту ситуацию задействуем параметр конфигурации Sonar sonar.tests, в котором явно укажем папки с тестами (она у нас пока одна):


sonar-project.properties:


…
sonar.tests=src/components/__tests__
…

Перезапустим сканер:


yarn run sonar

Посмотрим, что поменялось в интерфейсе:



Теперь мы увидели количество наших unit-тестов и, провалившись по клику внутрь, можем посмотреть распределение этого числа по файлам проекта:



Заключение


Итак, мы рассмотрели инструмент для непрерывного анализа SonarQube. Успешно интегрировали в него проект, написанный на VueJs+TS. Решили некоторые проблемы совместимости. Повысили информативность показателя о покрытии тестами. В данной статье мы рассмотрели лишь один из критериев качества кода (возможно, один из основных), но SonarQube поддерживает и другие критерии качества, включая тестирование на безопасность. Но не все эти возможности в полном объеме доступны в community-версии. Одна из интересных и полезных возможностей — это интеграции SonarQube с различными системами управления репозиториями кода, например, такие как GitLab и BitBucket. Чтобы не допустить merge pull(merge) request’а в основную ветку репозитория при деградации покрытия. Но это история уже совершенно другой статьи.


PS: Все, что описано в статье в виде кода доступно в моем форке.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Используете ли Вы платформу SonarQube:
32.14% Да9
10.71% Нет3
21.43% Слышал о данной платформе и хочу использовать6
10.71% Слышал о данной платформе и не хочу использовать3
0% Использую другую платформу0
25% Впервые слышу о ней7
Проголосовали 28 пользователей. Воздержались 5 пользователей.
Теги:
Хабы:
+5
Комментарии5

Публикации

Информация

Сайт
www.ingrammicrocloud.ru
Дата регистрации
Численность
201–500 человек
Местоположение
Россия

Истории