Add dependencies for gradle plugin (for custom rules)

Following the feature request https://github.com/arturbosch/detekt/issues/795,
this commit implements a way to configure Detekt plugins using Gradle plugin.
This commit is contained in:
Olivier Lemasle
2018-04-06 11:38:58 +02:00
committed by Artur Bosch
parent 53bf050cae
commit cc369068ff
7 changed files with 197 additions and 21 deletions

View File

@@ -37,6 +37,7 @@ It operates on the abstract syntax tree provided by the Kotlin compiler.
3. [in android projects](#gradleandroid)
4. [plugin tasks](#tasks)
5. [detekt-closure](#closure)
6. [custom rules](#gradleCustomRules)
3. [Standalone gradle task](#gradle)
4. [Standalone maven task](#maventask)
5. [Rule sets](#rulesets)
@@ -298,6 +299,26 @@ detekt {
For more information on using idea as a headless formatting/inspection tool see [here](https://www.jetbrains.com/help/idea/working-with-intellij-idea-features-from-command-line.html).
##### <a name="gradleCustomRules">Using custom rules with Gradle plugin</a>
When your _detekt_ custom rules are located in a module of your Gradle project (e.g. `:detekt-extensions`), you can
enable them with the following syntax:
```groovy
dependencies {
detekt project(':detekt-extensions')
}
detekt {
profile("main") {
input = "$projectDir/src/main/kotlin"
}
}
```
More generally, you can use dependencies on `detekt` configuration; these dependencies will be resolved, built if
needed, and added to _detekt_ classpath.
#### <a name="gradle">Using _detekt_ in custom gradle projects</a>
1. Add following lines to your build.gradle file.
@@ -597,7 +618,7 @@ If you contributed to detekt but your name is not in the list, please feel free
- [Svyatoslav Chatchenko](https://github.com/MyDogTom) - Active on Issues, NamingConventions and UnusedImport fixes
- [Sean Flanigan](https://github.com/seanf) - Config from classpath resource
- [Sebastian Schuberth](https://github.com/sschuberth) - Active on Issues, Windows support
- [Olivier Lemasle](https://github.com/olivierlemasle) - NP-Bugfix
- [Olivier Lemasle](https://github.com/olivierlemasle) - NP-Bugfix, fix TooGenericExceptionCaught, Gradle plugin improvement
- [Marc Prengemann](https://github.com/winterDroid) - Support for custom output formats, prototyped Rule-Context-Issue separation
- [Sebastiano Poggi](https://github.com/rock3r) - Enhanced milestone report script, Magic number fixes
- [Ilya Tretyakov](https://github.com/jvilya) - Sonar runs should not auto correct formatting.

View File

@@ -4,7 +4,7 @@ import io.gitlab.arturbosch.detekt.extensions.DetektExtension
import io.gitlab.arturbosch.detekt.extensions.INPUT_PARAMETER
import io.gitlab.arturbosch.detekt.extensions.OUTPUT_PARAMETER
import org.gradle.api.DefaultTask
import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
@@ -13,6 +13,7 @@ import java.io.File
/**
* @author Artur Bosch
* @author Marvin Ramin
* @author Olivier Lemasle
*/
open class DetektCheckTask : DefaultTask() {
@@ -22,6 +23,8 @@ open class DetektCheckTask : DefaultTask() {
@OutputDirectory
val output: File?
private val classpath: FileCollection
init {
description = "Analyze your kotlin code with detekt."
group = "verification"
@@ -33,19 +36,18 @@ open class DetektCheckTask : DefaultTask() {
val outputIndex = arguments.indexOf(OUTPUT_PARAMETER)
output = File(arguments[outputIndex + 1])
classpath = detektExtension.resolveClasspath(project)
dependsOn(classpath)
}
@TaskAction
fun check() {
val detektExtension = project.extensions.getByName("detekt") as DetektExtension
val configuration = project.buildscript.configurations.maybeCreate("detektCheck")
project.buildscript.dependencies.add(configuration.name, DefaultExternalModuleDependency(
"io.gitlab.arturbosch.detekt", "detekt-cli", detektExtension.version))
project.javaexec {
it.main = "io.gitlab.arturbosch.detekt.cli.Main"
it.classpath = configuration
it.classpath = classpath
it.args(detektExtension.resolveArguments(project))
}
}

View File

@@ -2,17 +2,24 @@ package io.gitlab.arturbosch.detekt
import io.gitlab.arturbosch.detekt.extensions.DetektExtension
import org.gradle.api.DefaultTask
import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.TaskAction
/**
* @author Artur Bosch
* @author Olivier Lemasle
*/
open class DetektCreateBaselineTask : DefaultTask() {
private val classpath: FileCollection
init {
description = "Creates a detekt baseline on the given --baseline path."
group = "verification"
val detektExtension = project.extensions.getByName("detekt") as DetektExtension
classpath = detektExtension.resolveClasspath(project)
dependsOn(classpath)
}
private val createBaseline = "--create-baseline"
@@ -21,13 +28,9 @@ open class DetektCreateBaselineTask : DefaultTask() {
fun baseline() {
val detektExtension = project.extensions.getByName("detekt") as DetektExtension
val configuration = project.buildscript.configurations.maybeCreate("detektBaseline")
project.buildscript.dependencies.add(configuration.name, DefaultExternalModuleDependency(
"io.gitlab.arturbosch.detekt", "detekt-cli", detektExtension.version))
project.javaexec {
it.main = "io.gitlab.arturbosch.detekt.cli.Main"
it.classpath = configuration
it.classpath = classpath
it.args(detektExtension.resolveArguments(project).plus(createBaseline))
}
}

View File

@@ -2,30 +2,31 @@ package io.gitlab.arturbosch.detekt
import io.gitlab.arturbosch.detekt.extensions.DetektExtension
import org.gradle.api.DefaultTask
import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.TaskAction
/**
* @author Artur Bosch
* @author Olivier Lemasle
*/
open class DetektGenerateConfigTask : DefaultTask() {
private val classpath: FileCollection
init {
description = "Generate a detekt configuration file inside your project."
group = "verification"
val detektExtension = project.extensions.getByName("detekt") as DetektExtension
classpath = detektExtension.resolveClasspath(project)
dependsOn(classpath)
}
@TaskAction
fun generateConfig() {
val detektExtension = project.extensions.getByName("detekt") as DetektExtension
val configuration = project.buildscript.configurations.maybeCreate("detektConfig")
project.buildscript.dependencies.add(configuration.name, DefaultExternalModuleDependency(
"io.gitlab.arturbosch.detekt", "detekt-cli", detektExtension.version))
project.javaexec {
it.main = "io.gitlab.arturbosch.detekt.cli.Main"
it.classpath = configuration
it.classpath = classpath
it.args("--input", project.projectDir.absolutePath, "--generate-config")
}
}

View File

@@ -9,6 +9,8 @@ import org.gradle.api.Project
class DetektPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.configurations.create("detekt")
val profilesContainer = project.container(ProfileExtension::class.java)
project.extensions.add(PROFILES_EXTENSION_NAME, profilesContainer)
profilesContainer.all { ProfileStorage.add(it) }

View File

@@ -2,10 +2,13 @@ package io.gitlab.arturbosch.detekt.extensions
import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
/**
* @author Artur Bosch
* @author Said Tahsin Dane
* @author Olivier Lemasle
*/
open class DetektExtension(open var version: String = SUPPORTED_DETEKT_VERSION,
open var debug: Boolean = DEFAULT_DEBUG_VALUE,
@@ -34,6 +37,17 @@ open class DetektExtension(open var version: String = SUPPORTED_DETEKT_VERSION,
}
}
fun resolveClasspath(project: Project): FileCollection = project
.configurations
.getByName("detekt")
.withDependencies {
it.add(
DefaultExternalModuleDependency(
"io.gitlab.arturbosch.detekt", "detekt-cli", version
)
)
}
fun resolveArguments(project: Project): List<String> {
return with(extractArguments()) {
if (!contains(INPUT_PARAMETER)) {

View File

@@ -0,0 +1,133 @@
package io.gitlab.arturbosch.detekt
import org.assertj.core.api.Assertions.assertThat
import org.jetbrains.spek.api.Spek
import org.jetbrains.spek.api.dsl.describe
import org.jetbrains.spek.api.dsl.it
import org.gradle.testkit.runner.GradleRunner
import org.gradle.testkit.runner.TaskOutcome
import java.io.File
/**
* @author Olivier Lemasle
*/
internal class FunctionalTest : Spek({
describe("The Detekt Gradle plugin") {
it("uses built-in rules") {
val rootDir = createTempDir(prefix = "withoutCustomRules")
writeFiles(rootDir)
// Using a custom "project-cache-dir" to avoid a Gradle error on Windows
val result = GradleRunner.create()
.withProjectDir(rootDir)
.withArguments("--project-cache-dir", createTempDir(prefix = "cache").absolutePath, "detektCheck")
.withPluginClasspath()
.build()
assertThat(result.output).contains("number of classes: 1")
assertThat(result.output).contains("Ruleset: comments")
assertThat(result.task(":detektCheck")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
// Asserts that the "custom" module is not built, and that custom ruleset is not enabled
assertThat(result.output).doesNotContain("Ruleset: test-custom")
assertThat(File(rootDir, "custom/build")).doesNotExist()
}
it("can use custom rules from a project's module") {
val rootDir = createTempDir(prefix = "withCustomRules")
writeFiles(rootDir)
File(rootDir, "build.gradle").appendText(
"""
|dependencies {
| detekt project(':custom')
|}
""".trimMargin()
)
val result = GradleRunner.create()
.withProjectDir(rootDir)
.withArguments("--project-cache-dir", createTempDir(prefix = "cache").absolutePath, "detektCheck")
.withPluginClasspath()
.build()
assertThat(result.output).contains("number of classes: 1")
assertThat(result.output).contains("Ruleset: comments")
assertThat(result.task(":detektCheck")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
// Asserts that the "custom" module is built, and that custom ruleset is enabled
assertThat(result.output).contains("Ruleset: test-custom")
assertThat(File(rootDir, "custom/build")).exists()
}
}
})
// build.gradle
private val buildFileContent = """
|plugins {
| id 'io.gitlab.arturbosch.detekt'
|}
|repositories {
| jcenter()
|}
|detekt {
| profile('main') {
| input = "${"$"}projectDir/src/main/kotlin"
| }
|}
|
""".trimMargin()
// settings.gradle
private const val settingsFileContent = """include ":custom""""
// src/main/kotlin/MyClass.kt
private val ktFileContent = """
|class MyClass
|
""".trimMargin()
// custom/build.gradle
private val customBuildFileContent = """
|plugins {
| id "org.jetbrains.kotlin.jvm" version "1.2.31"
|}
|repositories {
| jcenter()
|}
|dependencies {
| implementation "io.gitlab.arturbosch.detekt:detekt-api:1.0.0.RC6-4"
|}
""".trimMargin()
// custom/src/main/kotlin/RulesProvider.kt
private val customRulesProviderContent = """
|import io.gitlab.arturbosch.detekt.api.Config
|import io.gitlab.arturbosch.detekt.api.RuleSet
|import io.gitlab.arturbosch.detekt.api.RuleSetProvider
|
|class RulesProvider : RuleSetProvider {
| override val ruleSetId: String = "test-custom"
| override fun instance(config: Config) = RuleSet(ruleSetId, listOf())
|}
|
""".trimMargin()
// custom/src/main/resources/META-INF/services/io.gitlab.arturbosch.detekt.api.RuleSetProvider
private const val ruleSetProviderSpiContent = "RulesProvider"
private fun writeFiles(root: File) {
File(root, "build.gradle").writeText(buildFileContent)
File(root, "settings.gradle").writeText(settingsFileContent)
File(root, "src/main/kotlin").mkdirs()
File(root, "src/main/kotlin/MyClass.kt").writeText(ktFileContent)
File(root, "custom").mkdirs()
File(root, "custom/build.gradle").writeText(customBuildFileContent)
File(root, "custom/src/main/kotlin").mkdirs()
File(root, "custom/src/main/kotlin/RulesProvider.kt").writeText(customRulesProviderContent)
File(root, "custom/src/main/resources/META-INF/services").mkdirs()
File(root, "custom/src/main/resources/META-INF/services/io.gitlab.arturbosch.detekt.api.RuleSetProvider")
.writeText(ruleSetProviderSpiContent)
}