Code Quality и статический анализ

Подключаем встроенный анализ качества кода и понимаем, как он попадает в Merge Request.

Code Quality — встроенная в GitLab фича, которая прогоняет анализаторы кода и показывает в MR появившиеся и исправленные замечания.

Зачем автоматическое качество

Ревьюеру тяжело держать в голове все правила стиля, цикломатическую сложность и дублирование. Автоматический анализатор делает это последовательно и беспристрастно, а GitLab показывает разницу: какие замечания добавил именно этот MR, а какие исправил. Это смещает фокус ревью с придирок к стилю на смысл изменений.

За этим стоит важная мысль о разделении труда между машиной и человеком. Машина блестяще ловит формальное: длинные функции, дубли, нарушения стиля, подозрительные конструкции — то, что описывается правилами. Человек незаменим там, где нужен контекст: правильно ли выбрана абстракция, понятно ли имя, нет ли логической ошибки в бизнес-правиле. Когда анализатор берёт на себя формальную часть, ревью перестаёт вырождаться в споры о пробелах и переносится на содержание. Команды, которые этого не сделали, часто тонут в «бикешеддинге» — бесконечных придирках к мелочам, потому что мелочи проще заметить, чем разобраться в сути изменения.

Подключение через шаблон

GitLab поставляет готовый шаблон, который достаточно включить:

include:
  - template: Code-Quality.gitlab-ci.yml

# джоба code_quality придёт из шаблона и запустится в стадии test

Шаблон добавляет джобу code_quality, которая под капотом запускает движок анализа (на базе Code Climate) и выдаёт отчёт в стандартном формате codequality.

Стоит понимать, что прячется за одной строкой include. Шаблон — это кусок готового YAML, который GitLab подмешивает в ваш пайплайн на этапе сборки конфигурации. Внутри он описывает джобу, которая запускает контейнер с движком Code Climate; движок сам определяет языки в репозитории и подбирает подходящие плагины-анализаторы. Удобство в том, что вам не надо знать, какой линтер для какого языка нужен. Платой становится непрозрачность: джоба тянет дополнительный образ и под капотом обычно нуждается в Docker (часто через dind), а на ограниченных раннерах это не всегда заводится сразу. Поэтому на практике многие команды для своего основного языка предпочитают второй путь — отдать отчёт от собственного линтера.

Свой анализатор

Можно и не использовать шаблон, а отдать отчёт качества от своего линтера, приведённый к нужному формату:

lint:
  stage: test
  image: node:20
  script:
    - npm ci
    - npx eslint . -f gitlab -o gl-code-quality-report.json
  artifacts:
    reports:
      codequality: gl-code-quality-report.json

Ключ reports: codequality заставляет GitLab показать замечания в diff Merge Request: рядом со строками появятся иконки с найденными проблемами.

Ключевая деталь — флаг -f gitlab (форматтер). ESLint по умолчанию печатает отчёт в своём виде, который GitLab не понимает. Форматтер gitlab приводит вывод к требуемому JSON-формату codequality: списку объектов с описанием проблемы, путём к файлу, номером строки и отпечатком (fingerprint). Многие популярные линтеры (ESLint, RuboCop, golangci-lint и другие) либо имеют такой форматтер из коробки, либо умеют отдавать формат Code Climate, совместимый с GitLab. Этот путь прозрачнее шаблона: вы точно знаете, какой линтер и с какими правилами запускается, и контролируете его конфигурацией в репозитории.

Про fingerprint стоит сказать отдельно, потому что от него зависит корректность дельты. Каждое замечание получает стабильный отпечаток — хэш, который не меняется, если проблема та же, даже когда строки сдвинулись из-за правок выше по файлу. Благодаря этому GitLab понимает, что замечание «то же самое», а не «исчезло и появилось новое», и не показывает ложных «исправлений» при простом сдвиге кода.

Что это меняет в процессе

В виджете MR появляется блок «Code Quality» с числом ухудшений и улучшений. Команда может договориться не сливать MR, ухудшающий метрики, превращая качество в измеримый критерий, а не вкусовщину.

Здесь важно не перегнуть палку. Жёсткий гейт «ни одного нового замечания» звучит дисциплинированно, но на легаси-базе с тысячами накопленных проблем он парализует работу: любой невинный MR краснеет из-за чужого долга. Зрелые команды настраивают политику по принципу «не ухудшать»: блокируют только новые замечания, внесённые именно этим MR, а старый долг разгребают отдельными задачами. Так качество растёт постепенно и не превращается в стену, об которую разбивается каждая поставка.

Как работает под капотом

Джоба анализа формирует JSON-отчёт в формате codequality (список замечаний с файлом, строкой и описанием). GitLab сравнивает отчёт текущей ветки с отчётом целевой ветки и вычисляет дельту — что добавилось, что ушло. Эту дельту он рендерит в виджете MR и в самом diff. Полный список замечаний при этом не блокирует, если вы сами не настроите гейт.

Сравнение работает по тем самым fingerprint-ам. GitLab берёт множество отпечатков из отчёта целевой ветки и множество из текущей и считает разницу множеств: отпечатки, которых не было в целевой, — это новые замечания (ухудшение); отпечатки, что были в целевой, но исчезли, — исправленные (улучшение). Поэтому виджет показывает не сухой счётчик «всего проблем», а именно вклад данного MR. И именно поэтому стабильность fingerprint так важна: сломанный отпечаток превратил бы каждый сдвиг строк в фантомную дельту.

Сравнение с GitHub Actions

Как и с тестами, разница в степени «встроенности». В GitHub Actions нет родного виджета Code Quality в pull request: линтеры запускают через сторонние actions, а замечания выводят либо в лог, либо как аннотации к строкам через специальный вывод, либо подключают внешние платформы вроде SonarCloud или того же Code Climate отдельным сервисом. GitLab же держит весь цикл — анализ, отчёт, дельту, отображение в diff — в одной платформе, без внешних интеграций и токенов. Минус прежний: гибкость и каталог готовых проверок у экосистемы Actions шире, тогда как у GitLab вы получаете цельный, но более рамочный опыт из коробки.

Частые ошибки

  • Ожидать, что Code Quality сам заблокирует MR — по умолчанию он информирует, а не блокирует; гейт настраивают отдельно.
  • Отдавать отчёт не в формате codequality — тогда виджет ничего не покажет.
  • Запускать тяжёлый анализ на каждый push без необходимости — лучше ограничить rules на MR.
  • Забыть форматтер (например, -f gitlab у ESLint) — линтер отработает, но виджет останется пустым.
  • Ставить гейт «ноль замечаний» на легаси-базе — он заблокирует невинные MR из-за чужого накопленного долга.

Итоги

  • Code Quality показывает в MR дельту замечаний: что добавил и что исправил данный MR.
  • Подключается готовым шаблоном include: template или своим линтером с отчётом codequality.
  • По умолчанию информирует, а не блокирует; гейт настраивают отдельно.
  • Дельта считается по стабильным fingerprint-ам замечаний, а не по номерам строк.
  • Разумный гейт блокирует только новые замечания MR, а не весь накопленный долг.
Проверьте себя
1. Что показывает виджет Code Quality в Merge Request?
AПолный список всех файлов
BДельту замечаний: что данный MR добавил и что исправил по сравнению с целевой веткой
CВремя выполнения пайплайна
DСписок раннеров
2. В каком формате нужно отдать отчёт линтера, чтобы он попал в виджет качества?
Ajunit
Bcodequality
Ccoverage_report
Ddotenv