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

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

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

В данной статье речь пойдет, как писал выше, о платформе 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 с SonarQube
Пока в нем нет проектов и это справедливо. Будем исправлять данную ситуацию. За основу я взял официальный проект-пример для 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

Увидим следующее:

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

Причина в том, что в самом компоненте, как такового, кода нет. Исправим это.

HelloWorld.vue:

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

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

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

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

На экране мы должны увидеть информацию о покрытии, а в папке проекта будет создана папка 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.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

sonar-project.properties:

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

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

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

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

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

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

sonar-project.properties:

...
sonar.language=ts
...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

sonar-project.properties:

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

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

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

И, вуаля, все вернулось на круги своя, и с одним профилем только для 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

У нас конечно упадет покрытие, но сейчас нам это не интересно.

В разрезе дублирования строк кода увидим:

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

Для проверки воспользуемся CPD-утилитой – jscpd:

npx jscpd src

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

Для строк кода:

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

Возможно, это решится в будущих версиях плагинов 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’а:

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

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

sonar-project.properties:

…
sonar.tests=src/components/__tests__
…

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

yarn run sonar

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

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

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

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

Заключение

Итак, мы рассмотрели инструмент для непрерывного анализа SonarQube. Успешно интегрировали в него проект, написанный на VueJs+TS. Решили некоторые проблемы совместимости. Повысили информативность показателя о покрытии тестами. В данной статье мы рассмотрели лишь один из критериев качества кода (возможно, один из основных), но SonarQube поддерживает и другие критерии качества, включая тестирование на безопасность. Но не все эти возможности в полном объеме доступны в community-версии. Одна из интересных и полезных возможностей — это интеграции 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

Добавить комментарий