Detekt is a static code analysis tool, but it does a lot more than that, from being able to add your own custom rule to formatting your code (internally uses ktlint).
It’s especially useful tool for when you are working on a large project or with a team and you need to ensure uniformity across code development setup.
Adding detekt β
To add detekt to our project we need to do the following things…
[versions]
..
..
detekt = "1.23.1" // Latest as of Oct 2023
detektCompose = "0.3.0" // Latest as of Oct 2023
[libraries]
..
..
detekt-gradle = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" }
detekt-compose = { module = "io.nlopez.compose.rules:detekt", version.ref = "detektCompose" }
[plugins]
..
..
detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
We need to define related dependencies in libs.versions.toml
Let’s now add the following to Project’s build.gradle.kts
plugin {
..
..
alias(libs.plugins.detekt)
}
And in build.gradle.kts of our convention plugin. This will allow us to use DetektExtension class and other classes in detekt plugin which allows us to configure detekt.
Before we dive into the code, let’s understand what a convention plugin is.
In simple words convention plugin is gradle’s way of abstracting a responsibility and
sharing build logic across gradle module, this way we are able to keep build gradle files of our module concise and ensure that we are not writing repetitive code.
Convention plugin can be used for anything, from adding dependencies to customizing build flavours.
Now in Android is a great project to explore if you would like to see how convention plugins can make your life easy.
But for now, let’s check our DetektConventionPlugin.kt
class DetektConventionPlugin : Plugin<Project> {
override fun apply(project: Project) {
with(project) {
val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")
// Apply detekt plugin to module
pluginManager.apply(libs.findPlugin("detekt").get().get().pluginId)
// Configure jvmTarget for gradle task `detekt`
tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {
jvmTarget = JavaVersion.VERSION_17.toString()
}
// Configure jvmTarget for gradle task `detektGenerateBaseline`
tasks.withType<io.gitlab.arturbosch.detekt.DetektCreateBaselineTask>().configureEach {
jvmTarget = JavaVersion.VERSION_17.toString()
}
// Configure detekt
extensions.getByType<DetektExtension>().apply {
buildUponDefaultConfig = true // preconfigure defaults.
allRules = false // activate all available (even unstable) rules.
autoCorrect = false // To enable or disable auto formatting.
parallel = true // To enable or disable parallel execution of detekt on multiple submodules.
config.setFrom("config/detekt/detekt.yml") // point to your custom config defining rules to run, overwriting default behavior.
baseline = file("config/detekt/detekt-baseline.xml") // a way of suppressing issues before introducing detekt.
}
tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {
reports {
// observe findings in your browser with structure and code snippets
html.required.set(true)
// similar to the console output, contains issue signature to manually edit baseline files
txt.required.set(true)
// simple Markdown format
md.required.set(true)
}
}
dependencies.apply {
// You can add more detektPlugins like shown below.
add("detektPlugins", libs.findLibrary("detekt-compose").get()) // Add this in case you want compose rules with detekt
}
}
}
}
We have successfully created our first plugin, but this plugin is not usable yet, one last step before we put it to use.
Registering Plugin β
We need to register this plugin, let’s go back to our convention plugin’s build.gradle.kts file and do that.
..
..
gradlePlugin { // Create this block if it doesn't exist
plugins {
register("detekt") { // A unique string
id = "vlr.detekt" // This is the name we'll be using in all our modules to apply this plugin.
implementationClass = "DetektConventionPlugin" // Class name of the plugin file which implements `Plugin` interface.
}
}
}
π tada… our plugin is ready for use, let’s add it in project’s build.gradle.kts
plugin {
..
..
alias(libs.plugins.detekt)
id("vlr.detekt") // <-- Add the `id` you've given above in gradle plugin registration.
}
and then add it to all the modules where we want to leverage detekt (for example let’s take app module) so in app/build.gradle.kts
plugin {
..
..
id("vlr.detekt")
}
Running detekt βΆοΈ
If we have done the above steps correctly then our ./gradlew tasks should return a task named detekt.
Now let’s run detekt task, we can do that by running the following command and it should run detekt for us.
./gradlew detekt
Note: It is possible while setting up this plugin you may run into some issues around compatibility, make sure you refer this compatibility table
Configuring detekt’s behaviour π―
Now let’s generate detekt config file, this file is responsible for detekt’s behaviour in your project. This file contains all the rules and their configuration which detekt is going to abide by.
To generate this yml file run the following command
./gradlew detektGenerateConfig
Next step is to ensure the configuration YAML file’s path is known to detekt plugin, we can do that by ensuring
that config.setFrom(..<path of config files seperated by ','>) contains that path in our DetektConventionPlugin.
If you run into a lot of warning from detekt and you want to suppress them, you can do that by generating a baseline file
./gradlew detektBaseline
Let’s ensure the baseline file’s path is known to detekt plugin, we can do that by ensuring
that baseline = file(<path of baseline file>) has the correct path in our DetektConventionPlugin.
πWith that we have finished the integration of detekt in our project.π
Closing note π€
Detekt is customizable and has a lot of capabilities, with it being actively developed and open source, the future seems bright for Detekt.
You can also write a simple workflow file to run detekt on every PR or before generating a build or even add a pre-commit hook.
Detekt documentation is done very well and can be really insightful for someone trying to integrate it or trying to customize it.
You can check out my integration of detekt in this repository