У меня сложилось впечатление, что как-то все очень плохо в индустрии с обеспечением качества кода, ну это или мне так везет. Несколько месяцев назад на предыдущей работе настраивал отображение покрытия тестами для PR в GitHub, с использованием Jenkins, JaCoCo и maven. Натрахался очень знатно: интеграции Maven ↔ Jenkins ↔ GitHub работали очень туго, постоянно были какие-то проблемы, которые лечились костылями (преимущественно помоечным кодом в Jenkins).

Недавно захотелось сделать аналогичную штуку в GitLab. Ну тут-то все должно быть нормально, правда же? GitLab ведь боготворит CI/CD… А на сoverage даже есть отдельный столбик в списке всех работ (кто туда правда смотрит — непонятно, потому что обычно пайплайн целиком интересует).

Форматирование и анализ кода #

Ранее я уже поразвлекался с интеграцией проверок от ktlint и detekt в пайплайн GitLab. Тут было не очень прикольно: пришлось допиливать напильником конвертер формата ktlint в GitLab, писать кастомный gradle-таск, чтобы смержить два отчета о покрытии, потому что GitLab это не умел, писать кастомный gradle-таск, чтобы валить пайплайн, потому что GitLab нельзя настроить, чтобы он этот code quality отчет хоть как-то принимал во внимание, а не просто списочком отображал.

И да, про отображение. Чтобы отобразить проблемы красивенько рядом с кодом — ПЛОТИ. Нищеброды могут посмотреть только на список, ищи дальше по файлу и номеру строки сам. Чтобы хоть как-то это облагородить, добавили интеграцию с reviewdog, который оставляет комменты к MR.

Формат отчета #

Берем Kover, плагин для gradle от JetBrains, который генерирует отчет с использованием того же движка, что и Intellij — круто же, если все будет одинаково работать? Спойлер — работает не одинаково, проценты разные получаются :(

GitLab поддерживает только формат Cobertura, который довольно древний, и я не нашел ни одного живого плагина для его поддержки в Gradle, что логично, потому что в Java-мире почти все уже используют JaCoCo. Что предлагает GitLab? Запустить отдельный контейнер, чтобы питонячим скриптом сконвертировать один формат в другой. Ответственное потребление энергии, однако. При этом GitLab не поленился сделать инструкции для нескольких языков, как конвертировать результаты, вместо поддержки других форматов. Ок, может Kover умеет делать отчет в нужном формате? Не умеет. Ладно, добавляем плагин gradle, это лучше лишнего контейнера.

Заливаем в GitLab как положено, смотрим на тестовый MR… И там ничего нет. Выясняется, что надо “немного подождать”, чтобы покрытие подсветилось в изменениях. Я конечно понимаю, что там микросервисы небось с микрофронтендами, но непонятно, что мешало сразу показать все.

Процент покрытия #

Ладно, а как посмотреть процент покрытия? А его нет. Надо прописать его извлечение в пайплайне. Т.е. я предоставил полный отчет о покрытии тестами всего кода, и GitLab не может посчитать процент из него? Серьезно?

Как же GitLab узнает покрытие? Регуляркой из вывода пайплайна. РЕГУЛЯРКОЙ. Думаете я шучу? Я тоже думал, что на какой-то фейковый сайт зашел, но нет. Еще раз, я предоставляю GitLab полный отчет в нужном ему формате, чтобы он все равно брал процент из другого места. Консистентненько!

Кто же будет выводить это число? Kover, конечно не умеет. Придется писать всратый gradle-таск, который парсит XML-отчет. Парсить XML уже погано, а парсить его gradle-таском на kts через встроенный XML-парсер, заточенный под Groovy, — еще более погано.

Разница #

Уф, процент вывели, как нам отследить разницу? Ну, чтобы понять, что общее покрытие стало лучше или хуже? Есть история, но это отдельное меню, в MR она не показывается. Единственный более менее внятный вариант — метрики. Их, разумеется, ничего генерировать не умеет, но благо есть парсинг отчета, можно добавить туда немного вывода в файл. Только потом заметил, что метрики — премиум-фича.

Но все покрытие целиком обычно не интересно смотреть, лучше смотреть на изменения. Тут казалось бы время выйти GitLab на сцену — он же знает все про то, какие строчки поменялись в нашем MR, и какое у них покрытие? Но нет. Накидываем еще кода в gradle-таск, чтобы у git спросить список изменений и посчитать все только для них (не забываем про приколы с точками и слешами в путях Kotlin-пакетов).

Обратная связь #

Надо бы еще как-то более вырвиглазно пометить файлы с плохим покрытием. Для GitLab это просто космос, поэтому накидываем пороговые значения, выводим еще в один файлик отчета информацию о файлах с фиговым покрытием и кормим это reviewdog.

Вообще покрытие ради покрытия — так себе идея, это скорее проверка, что сделано не совсем хреново и подсказка на случай если что-то забыл. Но не переживайте, GitLab не умеет блокировать MR из-за низкого покрытия, поэтому можно мержить что угодно :) Ну или… писать кастомный gradle-таск (но на это я уже забил).

Итого #

Хотелось казалось бы простой вещи — проверять форматирование, покрытие тестами и автоматизировать рутину code review. Чтобы смотреть на важное, а неважное подсвечивалось автоматом. Чтобы достичь этого с GitLab, понадобилось два gradle-плагина для конвертаций, 4 кастомных gradle-таска (всего на code quality потрачено 215 строк в build.gradle.kts), 40 строк в .gitlab-ci.yml и пяток промежуточных файлов. И ладно это все было каким-то стандартным бойлерплейтом, но нет. Без особых танцев подключились только результаты прогона тестов.

В итоге все конечно получилось, и жить стало веселее. Однако зрелость инструментов показывает, что либо этот путь многие повторяют, либо просто не парятся такими низменными вещами (“а чо, работает и ладно”). В обоих вариантах звучит грустно.