Перейти к содержимому

Detekt Dangers и кастомные отчеты

В моей предыдущей статье о настройке detekt и danger/kotlin я упоминал отличный плагин danger AckeeCZ/danger-kotlin-detekt для парсинга и отчетности результатов работы detekt.

Хотя он отлично справлялся со своей задачей для нас (большое спасибо ребятам), я чувствовал, что нам нужно больше возможностей для настройки того, что и как мы хотим сообщать на основе распарсенных результатов. Я рад представить вам плагин pavelkorolevxyz/danger-detekt-kotlin. Он сильно вдохновлен плагином от Ackee, но я оставил больше места для экспериментов.

Как это выглядит

Зависит от вас. Мы ограничены только внешним видом файлов и строк в danger/kotlin и возможностями markdown хостинга репозитория (например, GitHub). Все остальное настраивается.

Вот так

Comment

С инлайн комментариями

Inline

Или вот так

No warnings

Использование

Я предполагаю, что вы уже знакомы с настройкой danger-kotlin. Если нет, вы можете найти больше информации в официальной документации или моей предыдущей статье.

Dangerfile.df.kts - это основной конфигурационный файл любой настройки danger/kotlin. Чтобы использовать этот плагин, вы должны добавить его как зависимость в начале этого файла и вызвать register.

@file:DependsOn("xyz.pavelkorolev.danger.detekt:plugin:x.y.z")

register.plugin(DetektPlugin)

Сам плагин - это fat jar, опубликованный в mavenCentral, так что больше ничего делать не нужно.

Базовое использование

В основном, это то, что плагин от Ackee имел в качестве единственной опции.

Парсинг и отчет одного файла

Это делает то, что говорит. Если у вас есть один отчет detekt и вы не хотите никакой настройки - это, вероятно, ваш выбор.

DetektPlugin.parseAndReport(reportFile)

Парсинг и отчет нескольких файлов

На самом деле параметры всех функций parse - это varargs, так что вы можете передать столько файлов отчетов, сколько хотите.

DetektPlugin.parseAndReport(reportFile1, reportFile2, reportFile3)

или

val files: Array<File> = findReportFilesByYourself()
DetektPlugin.parseAndReport(*files)

Давайте рассмотрим parse и report по отдельности.

Parse

Это что-то новое. Вы также можете парсить файлы без немедленной отчетности.

val report: DetektReport = DetektPlugin.parse(files)

Этот DetektReport содержит все из распарсенных отчетов detekt, так что это может быть полезно, если вы хотите что-то проверить перед фактической отчетностью.

Report

Вы также можете сделать отчет вот так

DetektPlugin.report(report)

Обратите внимание, чтобы danger правильно сообщал о файлах, вы должны настроить detekt на возврат путей относительно рабочей директории.

detekt {
    basePath = rootDir.absolutePath
    // Другая конфигурация
}

Полный пример

@file:DependsOn("xyz.pavelkorolev.danger.detekt:plugin:x.y.z")

import systems.danger.kotlin.*
import systems.danger.kotlin.models.github.*
import xyz.pavelkorolev.danger.detekt.DetektPlugin
import java.io.File

register.plugin(DetektPlugin)

danger(args) {
    warnDetekt()
}

fun warnDetekt() {
    val file = File("build/reports/detekt/report.xml")
    if (!file.exists()) {
        warn(
            "🙈 Отчет detekt не найден",
        )
        return
    }
    with(DetektPlugin) {
        val report = parse(file)
        val count = report.count
        if (count == 0) {
            message("👏👏👏 Отличная работа! Detekt не нашел нарушений!")
            return
        }
        fail(
            "🙁 Найдены нарушения Detekt: **${report.count}**.\n" +
                    "Пожалуйста, исправьте их, чтобы продолжить. У нас политика нулевой терпимости к предупреждениям"
        )
        report(report)
    }
}

Похожий Dangerfile можно найти в основной ветке репозитория pavelkorolevxyz/detekt-danger-sample-android, который я использовал в предыдущей статье для демонстрации полной настройки danger-kotlin + detekt.

Кастомизация

Функции DetektPlugin.report и DetektPlugin.parseAndReport имеют параметр reporter: DetektErrorReporter, который на самом деле является функциональным интерфейсом с функцией report(error: DetektError, fileName: String?).

Реализуя это, вы можете настроить логику отчетности и внешний вид, как хотите.

По умолчанию есть DefaultDetektErrorReporter, который имеет свой собственный способ создания сообщений и учитывает серьезность нарушений.

DefaultDetektErrorReporter использует инлайн комментарии, если для ошибки указаны файл и строка. Если вы хотите использовать только глобальный отчет без инлайн комментариев, используйте DefaultDetektErrorReporter(context, isInlineEnabled = false).

Реализация

Допустим, вы хотите отправить все найденное в таблицу fail с эмодзи в конце. Напишите это так.

class FailReporter(private val context: DangerContext): DetektErrorReporter {

    override fun report(error: DetektError, fileName: String?) {
        val message = error.message ?: return
        context.fail("$message 💥💥💥")
    }
}

И используйте это так

DetektPlugin.report(report, reporter = FailReporter(context))

Или вы можете реализовать то же самое как инлайн reporter благодаря его природе функционального интерфейса, вот так.

plugin.report(report) { error, _ ->
    error.message?.let(context::fail)
}

Думаю, вы поняли идею.

Идеи

Я попытаюсь дать вам несколько идей о том, как использовать возможности настройки этого плагина.

  • Вы можете отправить предупреждение, если вообще нет отчетов detekt. Может быть, что-то пошло не так, и detekt даже не запустился для этой сборки.
  • Вы можете проверить количество нарушений перед отчетностью. Если ничего не найдено, скажите автору PR "Хорошая работа". Будьте благодарны за такие моменты, цените работу ваших коллег.
  • Вы можете проверить количество нарушений, чтобы НЕ отправлять огромные комментарии в PR. Например, GitHub имеет ограничение на размер комментария. Если нарушений много, Danger не сможет их отправить. Установите разумный лимит и выведите какое-нибудь общее сообщение вместо этого.
  • Вы даже можете проверить, есть ли нарушения в чьем-то модуле или директории, чтобы автоматически упомянуть их.
  • Добавьте кучу эмодзи. Все их любят. Шучу 😁. Или нет 🤔.

Заключение

Вот и все. Не стесняйтесь связаться со мной в репозитории GitHub или любым другим способом. Я с нетерпением жду, когда вы поделитесь своими идеями или вариантами использования.