告别繁琐配置:Catchpig Detekt Plugin 让 Kotlin/Android 代码检查开箱即用

14 阅读5分钟

告别繁琐配置:Catchpig Detekt Plugin 让 Kotlin/Android 代码检查开箱即用

前言

在 Kotlin/Android 项目中,保证代码质量是每个团队都在努力的事情。detekt 作为 Kotlin 生态最流行的静态代码分析工具,相信大家都不陌生。但实际项目中,配置 detekt 往往是一件繁琐的事情:

  • 需要在每个模块的 build.gradle.kts 中手动配置
  • 规则集需要单独管理
  • 与 CI/CD 的集成需要额外步骤
  • 多模块项目配置更是指数级复杂

今天要介绍的 Catchpig Detekt Plugin,正是为了解决这些痛点而生。它是一个 Gradle 插件,只需几行配置,就能在打包时自动完成代码质量检查。

项目简介

Catchpig Detekt Plugin 是一款基于 detekt 构建的 Gradle 插件,专注于为 Android/Java 项目提供零配置的静态代码分析能力。

GitHub 地址github.com/catchpig/de…

核心特性

  • 零配置开箱即用 - 内置默认规则集,无需额外配置
  • 自动触发检查 - 打包时自动执行,无需手动运行
  • 多模块自动支持 - 自动递归应用到所有子项目
  • 灵活的构建类型控制 - 支持 debug/release/dev/all 等构建类型
  • 外部配置支持 - 可自定义 detekt.yml 配置文件路径

快速开始

1. 添加插件依赖

settings.gradle.kts 中添加 JitPack 仓库:

pluginManagement {
    repositories {
        maven { url = uri("https://jitpack.io") }
    }
}

2. 应用插件

在根项目的 build.gradle.kts 中应用插件:

plugins {
    id("com.github.catchpig.detekt-plugin") version "1.0.0"
}

3. 配置扩展属性(可选)

catchpigDetekt {
    // 触发检查的构建类型: "debug" (默认) / "release" / "dev" / "all"
    buildType = "debug"

    // 是否启用所有规则,默认 true
    allRules = true

    // 自定义 detekt.yml 配置文件路径(相对于 rootProject)
    configPath = null
}

4. 完成!自动检查

现在,在你执行打包命令时,detekt 会自动运行:

# Android Application 模块 - assembleDebug 时自动检查
./gradlew assembleDebug

# Android Library 模块 - compileDebugKotlin 时自动检查
./gradlew :mylibrary:compileDebugKotlin

# Java Library 模块 - compileJava 时自动检查
./gradlew :myjavalib:compileJava

支持的项目类型

项目类型触发任务检查时机
com.android.applicationassemble{BuildType}APK 打包时
com.android.librarycompile{BuildType}KotlinKotlin 编译时
java-librarycompileJavaJava 编译时

配置详解

扩展属性说明

属性类型默认值说明
buildTypeString"debug"触发检查的构建类型
allRulesBooleantrue是否启用所有规则
configPathString?null自定义配置文件路径

buildType 可选值

行为
"debug"仅在 debug 构建类型时触发(默认)
"release"仅在 release 构建类型时触发
"dev"仅在 dev 构建类型时触发
"all"在所有构建类型时都触发

配置文件优先级

  1. configPath 扩展属性指定的外部配置
  2. 插件内置的默认规则集

自定义规则配置

如果需要使用自定义规则,只需指定配置文件路径:

catchpigDetekt {
    // configPath 相对于 rootProject
    configPath = "config/detekt.yml"
}

注意:configPath 配置的路径是相对于根项目的路径。

内置规则集

插件内置的规则集覆盖了以下维度:

规则集说明
comments文档和注释相关规则
complexity代码复杂度规则
coroutines协程使用规范
empty-blocks空代码块检查
exceptions异常处理规范
naming命名规范
performance性能相关规则
potential-bugs潜在 Bug 检测
style代码风格规则

多模块项目支持

插件会自动递归应用到所有子项目。假设你的项目结构如下:

root/
├── app/
├── library/
├── common/
└── build.gradle.kts  <-- 插件配置在这里

只需在根项目的 build.gradle.kts 中配置一次,插件会自动将 detekt 能力传递到所有子模块。

实现原理简析

插件的核心实现基于 Gradle 的响应式编程模型,下面从几个关键维度来解析:

1. 双扩展机制

插件定义了两层扩展:

  • CatchpigDetektExtension:面向用户的 DSL 扩展,用户通过 catchpigDetekt { } 代码块配置
  • DetektExtension:detekt 官方扩展,用于配置 detekt 本身

关键实现:只在 rootProject 创建扩展,使用 afterEvaluate 延迟读取配置确保用户配置生效:

if (target == target.rootProject) {
    // 1. 创建 catchpigDetekt 扩展
    val configExtension = target.extensions.create("catchpigDetekt", CatchpigDetektExtension::class.java)

    // 2. 递归应用插件到所有子项目
    target.subprojects {
        plugins.apply(DetektPlugin::class.java)
    }

    // 3. 使用 afterEvaluate 确保用户的 catchpigDetekt { } 配置已生效后再读取
    target.afterEvaluate {
        // 此时用户的配置已生效
        configureDetekt(target, configExtension)
        configureTaskDependencies(target, configExtension.buildType)
    }
}
// 子项目只应用 detekt 插件,配置从 rootProject 获取

2. afterEvaluate 延迟读取策略

使用 plugins.withId() 实现延迟配置,这是插件实现的关键:

target.plugins.withId("com.android.application") {
    val taskPattern = if (buildType == "all") {
        { _: String -> true }  // 匹配所有 assemble 任务
    } else {
        { taskName: String -> taskName == "assemble$buildTypeCapitalized" }
    }
    target.tasks.matching { taskPattern(it.name) }.configureEach {
        dependsOn("detekt")  // assemble 任务依赖 detekt
    }
}

这样做的好处是:不用关心插件和应用插件的顺序,而是等到对应插件就绪后再响应式地追加配置。

3. 任务依赖绑定

通过 dependsOn("detekt") 将构建任务与检查任务绑定,确保打包前先完成代码检查

项目类型触发的构建任务绑定的检查任务
Android ApplicationassembleDebugdetekt
Android LibrarycompileDebugKotlindetekt
Java LibrarycompileJavadetekt

4. 配置文件加载策略

插件采用两级优先级的配置文件加载策略:

private fun configureDetekt(target: Project, configExtension: CatchpigDetektExtension) {
    val detektExtension = target.extensions.getByType(DetektExtension::class.java)
    detektExtension.buildUponDefaultConfig = true
    detektExtension.allRules = configExtension.allRules

    // 配置文件路径(相对于 rootProject)
    val externalConfig = configExtension.configPath?.let { target.file(it) }
    when {
        externalConfig != null && externalConfig.exists() -> {
            detektExtension.config.setFrom(externalConfig)
        }
        else -> {
            // 使用插件内置配置,从 JAR 复制到临时文件
            val tempConfigFile = target.layout.buildDirectory.file("detekt-temp-config.yml").get().asFile
            if (!tempConfigFile.exists()) {
                tempConfigFile.parentFile.mkdirs()
                tempConfigFile.writeText(
                    javaClass.classLoader.getResourceAsStream("detekt.yml")!!.bufferedReader().readText()
                )
            }
            detektExtension.config.setFrom(tempConfigFile)
        }
    }
}

5. 递归应用机制

仅在根项目执行时,将插件递归应用到所有子项目:

if (target == target.rootProject) {
    // 递归应用到所有子项目(尽早应用)
    target.subprojects {
        plugins.apply(DetektPlugin::class.java)
    }

    // 在 afterEvaluate 中创建任务,确保配置已读取
    target.afterEvaluate {
        target.tasks.create("detektAll") { ... }
        target.tasks.create("checkWithDetekt") { ... }
    }
}

6. 任务执行流程图

执行 ./gradlew assembleDebug
           │
           ▼
    ┌──────────────────┐
    │  assembleDebug   │
    │    (Android App) │
    └────────┬─────────┘
             │ dependsOn("detekt")
             ▼
    ┌──────────────────┐
    │     detekt       │
    │  (代码静态检查)   │
    └────────┬─────────┘
             │ no issues found
             ▼
    ┌──────────────────┐
    │  继续 APK 打包    │
    └──────────────────┘

通过这种设计,插件实现了:零配置 -> 自动触发 -> 多模块统一的体验。

环境要求

  • JDK 17+
  • Gradle 8.0+
  • Kotlin 项目或 Android 项目

结语

Catchpig Detekt Plugin 通过极简的配置,让 detekt 这把"利剑"变得触手可及。无需在每个模块中重复配置,无需记住复杂的命令,打包即检查,真正实现了代码质量检查的无感知化。

如果你正在为团队寻找一个低门槛的 Kotlin/Android 代码质量解决方案,不妨试试这个插件。

项目地址github.com/catchpig/de…

欢迎 Star & Fork & PR!