告别繁琐配置: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.application | assemble{BuildType} | APK 打包时 |
com.android.library | compile{BuildType}Kotlin | Kotlin 编译时 |
java-library | compileJava | Java 编译时 |
配置详解
扩展属性说明
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
buildType | String | "debug" | 触发检查的构建类型 |
allRules | Boolean | true | 是否启用所有规则 |
configPath | String? | null | 自定义配置文件路径 |
buildType 可选值
| 值 | 行为 |
|---|---|
"debug" | 仅在 debug 构建类型时触发(默认) |
"release" | 仅在 release 构建类型时触发 |
"dev" | 仅在 dev 构建类型时触发 |
"all" | 在所有构建类型时都触发 |
配置文件优先级
configPath扩展属性指定的外部配置- 插件内置的默认规则集
自定义规则配置
如果需要使用自定义规则,只需指定配置文件路径:
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 Application | assembleDebug | detekt |
| Android Library | compileDebugKotlin | detekt |
| Java Library | compileJava | detekt |
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 代码质量解决方案,不妨试试这个插件。
欢迎 Star & Fork & PR!