Всем привет! Продолжим цикл историй про то, чем занимается команда Web Core в ДомКлик. В предыдущей статье мы рассказывали, как создаем дизайн-систему. А в этот раз хотелось бы поделиться историей разработки системы мониторинга качества frontend-проектов — Front Radar. У нас в компании много проектов, которые создаются большим количеством команд, и в связи с этим потребовалось проверять актуальность проектов и выявлять проблемные места, которые негативно влияют на клиентский опыт.
Цели, которые мы преследовали при реализации этого проекта:
оценивать актуальность проекта (своевременное обновление зависимостей);
проверка на уязвимости;
проверка легальности использования зависимостей (проверка лицензий);
сбор статистики по размерам проекта;
сбор статистики по таким параметрам, как Perfomance, SEO, Accessibility.
Front Radar представляет из себя REST-сервис, который содержит множество микросервисов и запускает их в зависимости от вызванного API и набора параметров. Набор этих микросервисов сегодня такой:
Dependencies license checker
Dependencies update checker
Dependencies safety checker
Bundle sizes checker
NPM sizes checker
Lighthouse
W3C Validator
Наш сервис будет запускаться для двух этапов проверок: как блокирующий Quality Gate в pipeline на этапе сборки, и второй этап — после успешного деплоя.
Первый этап проверок запускается через стандартный web-hook со таким набором параметров: идентификатор проекта, версия релиза, URL репозитория и объект данных с информацией о размере файлов, необходимых для функционирования веб-клиента (для сбора аналитики в Bundle sizes checker). Этого нам достаточно, чтобы стянуть проект из репозитория, установить зависимости и проанализировать проект каждым из микросервисов. Результаты анализа мы сохраняем в БД, где ключом будет являться ID проекта. Таким образом у нас сохраняется вся статистика, и от релиза к релизу мы можем оценивать состояние нашей кодовой базы.
Dependencies license checker
Проверяет, что в проектах мы используем библиотеки с лицензиями, разрешающими свободное использование в коммерческой деятельности. Под капотом сервиса мы используем библиотеку license-checker, предварительно установив зависимости для проекта, который стянули из репозитория. После проверки утилита нам отдает ответ в формате:
{
'resolve-url@0.2.1': {
licenses: 'MIT',
repository: 'https://github.com/lydell/resolve-url',
publisher: 'Simon Lydell',
path: '/Users/royroev/WebstormProjects/package-stats/temp/project-5/node_modules/resolve-url',
licenseFile: '/Users/royroev/WebstormProjects/package-stats/temp/project-5/node_modules/resolve-url/LICENSE'
},
'resolve@1.12.0': {
licenses: 'MIT',
repository: 'https://github.com/browserify/resolve',
publisher: 'James Halliday',
email: 'mail@substack.net',
url: 'http://substack.net',
path: '/Users/royroev/WebstormProjects/package-stats/temp/project-5/node_modules/resolve',
licenseFile: '/Users/royroev/WebstormProjects/package-stats/temp/project-5/node_modules/resolve/LICENSE'
},
...
}
Хочется отметить, что утилита собирает информацию не только по зависимостям первого уровня, которые указаны в package.json, но и обходит всё дерево зависимостей.
После того, как мы получили объект с информацией о лицензиях, мы фильтруем библиотеки, которые поставляются с ограниченной лицензией, и помечаем, что на них нужно обратить внимание. Также сохраняем в БД эти данные, чтобы отслеживать появляющиеся проблемы от версии к версии.
Dependencies update checker
Собирает статистику по обновлению зависимостей. Своевременное обновление используемых позволяет поддерживать проект в актуальном состоянии. Сообщества с каждым релизом улучшают свои библиотеки, уменьшают их вес, устраняют уязвимости и баги, которые могут навредить вашему проекту. Также этот модуль дает представление об обновлении компонентов UI Kit , так как команда Web Core каждый день улучшает свои продукты и хочет, чтобы другие команды их обновляли. Если кто-то этого не делает, мы должны выявить причину, мешающую обновиться, и устранить её.
Также в этом модуле мы проверяем, зафиксированы ли версии библиотек, а именно проверяем наличие lock-файлов. Это необходимо для того, чтобы при сборке проекта команда не столкнулась с такими проблемами в зависимостях, как наличие уязвимостей, изменение лицензионного соглашения, breaking changes и банальными багами. Подробнее ознакомиться с тем, зачем это нужно, можно в статье нашего сотрудника.
Dependencies safety checker
Проверяет зависимостей на уязвимости. Мы используем несколько проверок npm audit и обращаемся к базе уязвимостей Node Security Project. Ознакомиться с тем, как работает npm audit, можно в этой статье. Для нас этот сервис является блокирующим: если он находит в зависимостях библиотеку с уязвимостью, то мы вынуждены заблокировать публикацию проекта.
Bundle sizes checker
Собирает статистику по размеру собранных бандлов для веб-клиента. Для этого мы получаем в пришедшем web-hook объект данных, который содержит информацию о размере всех файлов в проекте, необходимых для его функционирования. Эти данные собираются в CI/CD с помощью рекурсивного обхода папки с собранным проектом. Пример объекта:
{
sumSize: 40355,
sumSizeZip: 10285,
files: [
{
name: 'main.js',
size: 11022,
sizeZip: 4033,
},
{
name: 'vendors.js',
size: 22142,
sizeZip: 8012,
},
...
]
}
Эти данные мы также сохраняем в БД для формирования статистики по проекту. Теперь мы можем проанализировать от релизу к релизу, как проект прибавляет или убавляет в весе, и если при текущем релизе проект стал весить значимо больше, то мы помечаем такую сборку флагом «требует внимания».
Второй этап проверок запускается непосредственно после деплоя проекта (для веб-клиентов это публикация проекта в production-среде, для npm-пакетов — публикация в npm registry). Как и на первом этапе, срабатывает web-hook, но уже с другим набором параметров: идентификатор проекта, массив адресов страниц проекта (для веб-клиента), название пакета в registry (для npm-пакета).
NPM sizes checker
Собирает статистику по размеру собранных npm-пакетов. В нашей компании есть свой приватный bundle registry, в котором мы публикуем свои библиотеки для дальнейшего переиспользования в других командах. И, конечно же, хотелось бы собирать статистику по размеру таких пакетов. В этом сервисе мы генерируем файл index.js (entryFile) с импортом нашей библиотеки, устанавливаем её командой npm i $package-name
и собираем с помощью webpack наш entryFile. После успешной сборки мы получаем JSON-объект stats, в котором содержатся данные о размере JS- и CSS-файлов. На основе исторических данных мы можем формировать статистику и выявлять положительную или отрицательную динамику.
Lighthouse
Есть несколько способов мониторить ваш веб-клиент с помощью Lighthouse:
С помощью PageSpeed API (настройка подробно описана в этой статье). Запускается по графику, не связано никак с вашим CI/CD, поэтому нет никакой привязки к релизу.
С помощью lighthouse-ci. Очень удобный инструмент с массой настроек. Можно запустить сценарий на Puppeteer, если ваша страница, к примеру, закрыта под авторизацию. Способ подойдет вам, если собираете проект одним из перечисленных инструментов для CI/CD: GitHub Actions, Travis CI, Circle CI, GitLab CI, Jenkins (на основе Ubuntu), Google Cloudbuild. Если у вас что-то иное, придется покопаться в чужом коде или перейти к пункту 3.
Написать свой сервис на основе сервера node.js и npm-библиотеки Lighthouse, со своими правилами, настройками и кастомными сценариями.
Мы выбрали 3 пункт, потому что нам хотелось более тонкой настройки сервиса и возможности запускать как при публикации новой версии проекта, так и по расписанию. После публикации веб-клиента в production-среде мы запускаем проверку массива пришедших в web-hook урлов с помощью Lighthouse. Сервис представляет из себя сервер node.js, на котором мы сначала запускаем chrome-launcher; если надо проанализировать веб-клиент, закрытый под авторизацию, то запускаем сценарии авторизации (наши автотесты), а затем запускаем проверку страниц с помощью библиотеки Lighthouse.
const chromeFlags = [
'--disable-gpu',
'--headless',
'--no-zygote',
'--no-sandbox',
'--disable-dev-shm-usage',
];
const config = {
"extends": "lighthouse:default",
"settings": {
"emulatedFormFactor": "mobile",
"useThrottling": true
}
};
const chrome = await chromeLauncher.launch({ chromeFlags });
const flags = {
port: chrome.port,
output: 'json',
'max-wait-for-load': 500000,
};
const result = await lighthouse(url, flags, config);
/*
result =
{
"performance-score": 83,
"accessibility-score": 98,
"best-practices-score": 100,
"seo-score": 100,
"pwa-score": 100,
"firstContentfulPaint": 2341,
"firstMeaningfulPaint": 2341,
"largestContentfulPaint": 3736,
"firstCPUIdle": 4951,
"interactive": 5101,
"speedIndex": 2341,
"estimatedInputLatency": 13,
"totalBlockingTime": 221,
"maxPotentialFID": 210,
"cumulativeLayoutShift": 0.009377760145399306,
"cumulativeLayoutShiftAllFrames": 0
}
*/
Мы знаем, что проверки Lighthouse могут отличаться в зависимости от разных условий выполнения проверок, поэтому мы запускаем 5 проверок для каждой страницы и вычисляем среднее значение по каждому показателю. После чего агрегируем и сохраняем данные в БД и пытаемся создать инфографику, понятную нашим командам.
Также с каждым релизом мы сохраняем отчет Lighthouse, в котором отображены результаты и рекомендации по улучшению сайта.
W3C checker
Представляет из себя NodeJS-приложение, которое использует библиотеку html-validator. Мы получаем в формате JSON данные об ошибках и предупреждениях, которые суммируем для отслеживания динамики от релиза к релизу, а также в формате HTML для отображения рекомендаций разработчикам.
Заключение
Все показатели влияют на клиентский опыт использования наших продуктов, поэтому на основе всех собранных метрик мы даем рекомендации проектам по улучшению кодовой базы и выявляем все отклонения от лучших практик в мире web-разработки. Команды стараются поправить отклонения в рамках технической квоты.
Мы не собираемся останавливаться на перечисленных проверках, в планах у нас подключения дополнительных систем анализа наших web-проектов — Favicon Checker, SEO Checker, Link Checker, CSS Validator, CSS Stats. Надеюсь, что статья была вам полезна и вы организуете у себя подобную систему мониторинга. Конечно же, жду вас в комментариях, всегда буду рад ответить на ваши вопросы и замечания.