Deprecate configuration of reports in detekt Gradle extension (#3687)

* Deprecate configuration of reports in detekt extension

* Deprecate customReportsDir

This is unnecessary as it just returns the value of reportsDir

* Introduce `required` and `outputLocation` report properties

This matches the property names & types used in Gradle's code quality
plugins.

* Fix issues detected by detekt

* Add changelog and migration guide for reporting changes

* Add warning when outputLocation set on detekt extension
This commit is contained in:
Matthew Haughton
2021-09-17 11:17:20 +10:00
committed by GitHub
parent 7962979fb3
commit bbbbd25767
16 changed files with 357 additions and 100 deletions

View File

@@ -159,7 +159,7 @@ open class Detekt @Inject constructor(
set(value) = basePathProp.set(value)
@get:Internal
var reports = DetektReports()
var reports: DetektReports = objects.newInstance(DetektReports::class.java)
@get:Internal
val reportsDir: Property<File> = project.objects.property(File::class.java)
@@ -187,7 +187,7 @@ open class Detekt @Inject constructor(
internal val customReportFiles: ConfigurableFileCollection
@OutputFiles
@Optional
get() = objects.fileCollection().from(reports.custom.mapNotNull { it.destination })
get() = objects.fileCollection().from(reports.custom.mapNotNull { it.outputLocation.asFile.orNull })
private val defaultReportsDir: Directory = project.layout.buildDirectory.get()
.dir(ReportingExtension.DEFAULT_REPORTS_DIR_NAME)
@@ -253,7 +253,7 @@ open class Detekt @Inject constructor(
private fun convertCustomReportsToArguments(): List<CustomReportArgument> = reports.custom.map {
val reportId = it.reportId
val destination = it.destination
val destination = it.outputLocation.asFile.orNull
checkNotNull(reportId) { "If a custom report is specified, the reportId must be present" }
check(!DetektReportType.isWellKnownReportId(reportId)) {
@@ -268,10 +268,10 @@ open class Detekt @Inject constructor(
private fun getTargetFileProvider(
report: DetektReport
): RegularFileProperty {
val isEnabled = report.enabled ?: DetektExtension.DEFAULT_REPORT_ENABLED_VALUE
val isEnabled = report.required.getOrElse(DetektExtension.DEFAULT_REPORT_ENABLED_VALUE)
val provider = objects.fileProperty()
if (isEnabled) {
val destination = report.destination ?: reportsDir.getOrElse(defaultReportsDir.asFile)
val destination = report.outputLocation.asFile.orNull ?: reportsDir.getOrElse(defaultReportsDir.asFile)
.resolve("${DetektReport.DEFAULT_FILENAME}.${report.type.extension}")
provider.set(destination)
}

View File

@@ -1,18 +1,29 @@
package io.gitlab.arturbosch.detekt.extensions
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.model.ObjectFactory
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.OutputFile
import java.io.File
import javax.inject.Inject
class CustomDetektReport {
open class CustomDetektReport @Inject constructor(objects: ObjectFactory) {
@Internal
var reportId: String? = null
@Deprecated("Use outputLocation.set(value)")
@get:Internal
var destination: File?
get() = outputLocation.asFile.getOrNull()
set(value) {
outputLocation.set(value)
}
@OutputFile
var destination: File? = null
val outputLocation: RegularFileProperty = objects.fileProperty()
override fun toString(): String {
return "CustomDetektReport(reportId=$reportId, destination=$destination)"
return "CustomDetektReport(reportId=$reportId, outputLocation=$outputLocation)"
}
}

View File

@@ -18,10 +18,12 @@ open class DetektExtension @Inject constructor(objects: ObjectFactory) : CodeQua
isIgnoreFailures = value
}
@Deprecated("Use reportsDir which is equivalent", ReplaceWith("reportsDir"))
val customReportsDir: File?
get() = reportsDir
val reports = DetektReports()
@Deprecated("Customise the reports on the Detekt task(s) instead.", level = DeprecationLevel.WARNING)
val reports: DetektReports = objects.newInstance(DetektReports::class.java)
@Deprecated(message = "Please use the source property instead.", replaceWith = ReplaceWith("source"))
var input: ConfigurableFileCollection
@@ -78,6 +80,8 @@ open class DetektExtension @Inject constructor(objects: ObjectFactory) : CodeQua
*/
var ignoredFlavors: List<String> = emptyList()
@Suppress("DeprecatedCallableAddReplaceWith", "DEPRECATION")
@Deprecated("Customise the reports on the Detekt task(s) instead.", level = DeprecationLevel.WARNING)
fun reports(configure: Action<DetektReports>) {
configure.execute(reports)
}

View File

@@ -1,15 +1,35 @@
package io.gitlab.arturbosch.detekt.extensions
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.OutputFile
import java.io.File
import javax.inject.Inject
class DetektReport(val type: DetektReportType) {
open class DetektReport @Inject constructor(val type: DetektReportType, objects: ObjectFactory) {
var enabled: Boolean? = null
@Deprecated("Use required.set(value)")
var enabled: Boolean?
get() = required.get()
set(value) = required.set(value)
var destination: File? = null
@Deprecated("Use outputLocation.set(value)")
var destination: File?
get() = outputLocation.asFile.getOrNull()
set(value) {
outputLocation.set(value)
}
@Input
val required: Property<Boolean> = objects.property(Boolean::class.java)
@OutputFile
val outputLocation: RegularFileProperty = objects.fileProperty()
override fun toString(): String {
return "DetektReport(type='$type', enabled=$enabled, destination=$destination)"
return "DetektReport(type='$type', required=$required, outputLocation=$outputLocation)"
}
companion object {

View File

@@ -5,18 +5,20 @@ import io.gitlab.arturbosch.detekt.extensions.DetektReportType.HTML
import io.gitlab.arturbosch.detekt.extensions.DetektReportType.SARIF
import io.gitlab.arturbosch.detekt.extensions.DetektReportType.TXT
import io.gitlab.arturbosch.detekt.extensions.DetektReportType.XML
import org.gradle.api.model.ObjectFactory
import org.gradle.util.ConfigureUtil
import javax.inject.Inject
@Suppress("TooManyFunctions")
class DetektReports {
open class DetektReports @Inject constructor(val objects: ObjectFactory) {
val xml = DetektReport(XML)
val xml: DetektReport = objects.newInstance(DetektReport::class.java, XML)
val html = DetektReport(HTML)
val html: DetektReport = objects.newInstance(DetektReport::class.java, HTML)
val txt = DetektReport(TXT)
val txt: DetektReport = objects.newInstance(DetektReport::class.java, TXT)
val sarif = DetektReport(SARIF)
val sarif: DetektReport = objects.newInstance(DetektReport::class.java, SARIF)
val custom = mutableListOf<CustomDetektReport>()
@@ -35,5 +37,6 @@ class DetektReports {
fun custom(configure: CustomDetektReport.() -> Unit): Unit = createAndAddCustomReport().configure()
fun custom(closure: Closure<*>): CustomDetektReport = ConfigureUtil.configure(closure, createAndAddCustomReport())
private fun createAndAddCustomReport() = CustomDetektReport().apply { custom.add(this) }
private fun createAndAddCustomReport() =
objects.newInstance(CustomDetektReport::class.java).apply { custom.add(this) }
}

View File

@@ -124,11 +124,34 @@ internal fun Project.registerAndroidDetektTask(
extension.baseline?.existingVariantOrBaseFile(variant.name)?.let { baselineFile ->
baseline.set(layout.file(project.provider { baselineFile }))
}
reports = extension.reports
reports.xml.setDefaultIfUnset(File(extension.reportsDir, variant.name + ".xml"))
reports.html.setDefaultIfUnset(File(extension.reportsDir, variant.name + ".html"))
reports.txt.setDefaultIfUnset(File(extension.reportsDir, variant.name + ".txt"))
reports.sarif.setDefaultIfUnset(File(extension.reportsDir, variant.name + ".sarif"))
reports.xml.outputLocation.convention(
layout.projectDirectory.file(
providers.provider {
File(extension.reportsDir, variant.name + ".xml").absolutePath
}
)
)
reports.html.outputLocation.convention(
layout.projectDirectory.file(
providers.provider {
File(extension.reportsDir, variant.name + ".html").absolutePath
}
)
)
reports.txt.outputLocation.convention(
layout.projectDirectory.file(
providers.provider {
File(extension.reportsDir, variant.name + ".txt").absolutePath
}
)
)
reports.sarif.outputLocation.convention(
layout.projectDirectory.file(
providers.provider {
File(extension.reportsDir, variant.name + ".sarif").absolutePath
}
)
)
description = "EXPERIMENTAL: Run detekt analysis for ${variant.name} classes with type resolution"
}

View File

@@ -32,11 +32,36 @@ internal class DetektJvm(private val project: Project) {
extension.baseline?.existingVariantOrBaseFile(sourceSet.name)?.let { baselineFile ->
baseline.set(layout.file(project.provider { baselineFile }))
}
reports = extension.reports
reports.xml.setDefaultIfUnset(File(extension.reportsDir, sourceSet.name + ".xml"))
reports.html.setDefaultIfUnset(File(extension.reportsDir, sourceSet.name + ".html"))
reports.txt.setDefaultIfUnset(File(extension.reportsDir, sourceSet.name + ".txt"))
reports.sarif.setDefaultIfUnset(File(extension.reportsDir, sourceSet.name + ".sarif"))
reports.xml.outputLocation.convention(
layout.projectDirectory.file(
providers.provider {
File(extension.reportsDir, sourceSet.name + ".xml").absolutePath
}
)
)
reports.html.outputLocation.convention(
layout.projectDirectory.file(
providers.provider {
File(extension.reportsDir, sourceSet.name + ".html").absolutePath
}
)
)
reports.txt.outputLocation.convention(
layout.projectDirectory.file(
providers.provider {
File(extension.reportsDir, sourceSet.name + ".txt").absolutePath
}
)
)
reports.sarif.outputLocation.convention(
layout.projectDirectory.file(
providers.provider {
File(extension.reportsDir, sourceSet.name + ".sarif").absolutePath
}
)
)
description = "EXPERIMENTAL: Run detekt analysis for ${sourceSet.name} classes with type resolution"
}
}

View File

@@ -25,6 +25,7 @@ internal class DetektMultiplatform(private val project: Project) {
}
}
@Suppress("LongMethod")
private fun Project.registerMultiplatformTasks(extension: DetektExtension) {
// We need another project.afterEvaluate as the Android target is attached on
// a project.afterEvaluate inside AGP. We should further investigate and potentially remove this.
@@ -90,6 +91,7 @@ internal class DetektMultiplatform(private val project: Project) {
}
}
@Suppress("LongMethod")
private fun Project.registerMultiplatformTasksForNonAndroidTarget(
compilation: KotlinCompilation<KotlinCommonOptions>,
target: KotlinTarget,
@@ -116,11 +118,34 @@ internal class DetektMultiplatform(private val project: Project) {
}?.let { baselineFile ->
baseline.set(layout.file(provider { baselineFile }))
}
reports = extension.reports
reports.xml.setDefaultIfUnset(File(extension.reportsDir, compilation.name + ".xml"))
reports.html.setDefaultIfUnset(File(extension.reportsDir, compilation.name + ".html"))
reports.txt.setDefaultIfUnset(File(extension.reportsDir, compilation.name + ".txt"))
reports.sarif.setDefaultIfUnset(File(extension.reportsDir, compilation.name + ".sarif"))
reports.xml.outputLocation.convention(
layout.projectDirectory.file(
providers.provider {
File(extension.reportsDir, compilation.name + ".xml").absolutePath
}
)
)
reports.html.outputLocation.convention(
layout.projectDirectory.file(
providers.provider {
File(extension.reportsDir, compilation.name + ".html").absolutePath
}
)
)
reports.txt.outputLocation.convention(
layout.projectDirectory.file(
providers.provider {
File(extension.reportsDir, compilation.name + ".txt").absolutePath
}
)
)
reports.sarif.outputLocation.convention(
layout.projectDirectory.file(
providers.provider {
File(extension.reportsDir, compilation.name + ".sarif").absolutePath
}
)
)
description =
"Run detekt analysis for target ${target.name} and source set ${compilation.name}"
if (runWithTypeResolution) {

View File

@@ -22,8 +22,7 @@ internal class DetektPlain(private val project: Project) {
setSource(existingInputDirectoriesProvider(project, extension))
setIncludes(DetektPlugin.defaultIncludes)
setExcludes(DetektPlugin.defaultExcludes)
reportsDir.set(project.provider { extension.customReportsDir })
reports = extension.reports
reportsDir.set(project.provider { extension.reportsDir })
}
tasks.matching { it.name == LifecycleBasePlugin.CHECK_TASK_NAME }.configureEach {

View File

@@ -3,10 +3,8 @@ package io.gitlab.arturbosch.detekt.internal
import io.gitlab.arturbosch.detekt.Detekt
import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask
import io.gitlab.arturbosch.detekt.extensions.DetektExtension
import io.gitlab.arturbosch.detekt.extensions.DetektReport
import org.gradle.api.Project
import org.gradle.api.tasks.TaskProvider
import java.io.File
internal fun Project.registerDetektTask(
name: String,
@@ -14,6 +12,33 @@ internal fun Project.registerDetektTask(
configuration: Detekt.() -> Unit
): TaskProvider<Detekt> =
tasks.register(name, Detekt::class.java) {
with(extension.reports) {
if (xml.outputLocation.isPresent) {
logger.warn(
"XML report location set on detekt {} extension will be ignored for $name task. See " +
"https://detekt.github.io/detekt/gradle.html#reports"
)
}
if (sarif.outputLocation.isPresent) {
logger.warn(
"SARIF report location set on detekt {} extension will be ignored for $name task. See " +
"https://detekt.github.io/detekt/gradle.html#reports"
)
}
if (txt.outputLocation.isPresent) {
logger.warn(
"TXT report location set on detekt {} extension will be ignored for $name task. See " +
"https://detekt.github.io/detekt/gradle.html#reports"
)
}
if (html.outputLocation.isPresent) {
logger.warn(
"HTML report location set on detekt {} extension will be ignored for $name task. See " +
"https://detekt.github.io/detekt/gradle.html#reports"
)
}
}
it.debugProp.set(provider { extension.debug })
it.parallelProp.set(provider { extension.parallel })
it.disableDefaultRuleSetsProp.set(provider { extension.disableDefaultRuleSets })
@@ -44,9 +69,3 @@ internal fun Project.registerCreateBaselineTask(
it.allRules.set(provider { extension.allRules })
configuration(it)
}
internal fun DetektReport.setDefaultIfUnset(default: File) {
if (destination == null) {
destination = default
}
}

View File

@@ -23,7 +23,7 @@ object DetektAndroidSpec : Spek({
buildFileContent = """
$APP_PLUGIN_BLOCK
$ANDROID_BLOCK
$DETEKT_BLOCK
$DETEKT_REPORTS_BLOCK
""".trimIndent(),
srcDirs = listOf("src/main/java", "src/debug/java", "src/test/java", "src/androidTest/java"),
baselineFiles = listOf(
@@ -82,7 +82,7 @@ object DetektAndroidSpec : Spek({
buildFileContent = """
$APP_PLUGIN_BLOCK
$ANDROID_BLOCK
$DETEKT_BLOCK
$DETEKT_REPORTS_BLOCK
""".trimIndent(),
srcDirs = listOf("src/main/java", "src/debug/java", "src/test/java", "src/androidTest/java")
)
@@ -117,7 +117,7 @@ object DetektAndroidSpec : Spek({
buildFileContent = """
$LIB_PLUGIN_BLOCK
$ANDROID_BLOCK
$DETEKT_BLOCK
$DETEKT_REPORTS_BLOCK
""".trimIndent(),
srcDirs = listOf("src/main/java", "src/debug/java", "src/test/java", "src/androidTest/java"),
baselineFiles = listOf(
@@ -177,7 +177,7 @@ object DetektAndroidSpec : Spek({
buildFileContent = """
$LIB_PLUGIN_BLOCK
$ANDROID_BLOCK_WITH_FLAVOR
$DETEKT_BLOCK
$DETEKT_REPORTS_BLOCK
""".trimIndent(),
srcDirs = listOf("src/main/java", "src/debug/java", "src/test/java", "src/androidTest/java")
)
@@ -419,8 +419,8 @@ private val ANDROID_BLOCK_WITH_FLAVOR = """
}
""".trimIndent()
private val DETEKT_BLOCK = """
detekt {
private val DETEKT_REPORTS_BLOCK = """
tasks.withType(io.gitlab.arturbosch.detekt.Detekt).configureEach {
reports {
txt.enabled = false
}

View File

@@ -8,49 +8,121 @@ import org.spekframework.spek2.style.specification.describe
object DetektJvmSpec : Spek({
describe("When applying detekt in a JVM project") {
context("disabled TXT report") {
val gradleRunner = DslGradleRunner(
projectLayout = ProjectLayout(numberOfSourceFilesInRootPerSourceDir = 1),
buildFileName = "build.gradle",
baselineFiles = listOf("detekt-baseline.xml", "detekt-baseline-main.xml", "detekt-baseline-test.xml"),
mainBuildFileContent = """
plugins {
id "org.jetbrains.kotlin.jvm"
id "io.gitlab.arturbosch.detekt"
}
repositories {
mavenCentral()
mavenLocal()
}
detekt {
reports {
txt.enabled = false
val gradleRunner = DslGradleRunner(
projectLayout = ProjectLayout(numberOfSourceFilesInRootPerSourceDir = 1),
buildFileName = "build.gradle",
baselineFiles = listOf("detekt-baseline.xml", "detekt-baseline-main.xml", "detekt-baseline-test.xml"),
mainBuildFileContent = """
plugins {
id "org.jetbrains.kotlin.jvm"
id "io.gitlab.arturbosch.detekt"
}
}
""".trimIndent(),
dryRun = true
)
gradleRunner.setupProject()
it("configures detekt type resolution task main") {
gradleRunner.runTasksAndCheckResult(":detektMain") { buildResult ->
assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-main.xml """)
assertThat(buildResult.output).contains("--report xml:")
assertThat(buildResult.output).contains("--report sarif:")
assertThat(buildResult.output).doesNotContain("--report txt:")
assertThat(buildResult.output).contains("--classpath")
repositories {
mavenCentral()
mavenLocal()
}
tasks.withType(io.gitlab.arturbosch.detekt.Detekt).configureEach {
reports {
txt.enabled = false
}
}
""".trimIndent(),
dryRun = true
)
gradleRunner.setupProject()
it("configures detekt type resolution task main") {
gradleRunner.runTasksAndCheckResult(":detektMain") { buildResult ->
assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-main.xml """)
assertThat(buildResult.output).contains("--report xml:")
assertThat(buildResult.output).contains("--report sarif:")
assertThat(buildResult.output).doesNotContain("--report txt:")
assertThat(buildResult.output).contains("--classpath")
}
}
it("configures detekt type resolution task test") {
gradleRunner.runTasksAndCheckResult(":detektTest") { buildResult ->
assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-test.xml """)
assertThat(buildResult.output).contains("--report xml:")
assertThat(buildResult.output).contains("--report sarif:")
assertThat(buildResult.output).doesNotContain("--report txt:")
assertThat(buildResult.output).contains("--classpath")
}
}
}
it("configures detekt type resolution task test") {
gradleRunner.runTasksAndCheckResult(":detektTest") { buildResult ->
assertThat(buildResult.output).containsPattern("""--baseline \S*[/\\]detekt-baseline-test.xml """)
assertThat(buildResult.output).contains("--report xml:")
assertThat(buildResult.output).contains("--report sarif:")
assertThat(buildResult.output).doesNotContain("--report txt:")
assertThat(buildResult.output).contains("--classpath")
context("report location set on extension & task") {
val gradleRunner = DslGradleRunner(
projectLayout = ProjectLayout(numberOfSourceFilesInRootPerSourceDir = 1),
buildFileName = "build.gradle",
mainBuildFileContent = """
plugins {
id "org.jetbrains.kotlin.jvm"
id "io.gitlab.arturbosch.detekt"
}
repositories {
mavenCentral()
mavenLocal()
}
detekt {
reports {
txt.destination = file("output-path.txt")
}
}
tasks.withType(io.gitlab.arturbosch.detekt.Detekt).configureEach {
reports {
txt.destination = file("output-path2.txt")
}
}
""".trimIndent(),
dryRun = false
)
gradleRunner.setupProject()
it("logs a warning") {
gradleRunner.runTasksAndCheckResult(":detektMain") { buildResult ->
assertThat(buildResult.output).contains("TXT report location set on detekt {} extension will be ignored for detektMain task.")
}
}
}
context("report location set on task only") {
val gradleRunner = DslGradleRunner(
projectLayout = ProjectLayout(numberOfSourceFilesInRootPerSourceDir = 1),
buildFileName = "build.gradle",
mainBuildFileContent = """
plugins {
id "org.jetbrains.kotlin.jvm"
id "io.gitlab.arturbosch.detekt"
}
repositories {
mavenCentral()
mavenLocal()
}
tasks.withType(io.gitlab.arturbosch.detekt.Detekt).configureEach {
reports {
txt.destination = file("output-path2.txt")
}
}
""".trimIndent(),
dryRun = false
)
gradleRunner.setupProject()
it("logs a warning") {
gradleRunner.runTasksAndCheckResult(":detektMain") { buildResult ->
assertThat(buildResult.output).doesNotContain("report location set on detekt {} extension will be ignored")
}
}
}
}

View File

@@ -352,7 +352,7 @@ private val KMM_PLUGIN_BLOCK = """
""".trimIndent()
private val DETEKT_BLOCK = """
detekt {
tasks.withType(io.gitlab.arturbosch.detekt.Detekt).configureEach {
reports.txt.enabled = false
}
""".trimIndent()

View File

@@ -52,7 +52,7 @@ object DetektPlainSpec : Spek({
mavenLocal()
}
detekt {
tasks.withType(io.gitlab.arturbosch.detekt.Detekt).configureEach {
reports {
sarif.enabled = true
txt.enabled = false

View File

@@ -202,11 +202,6 @@ internal object DetektTaskDslSpec : Spek({
val config = """
|detekt {
| reportsDir = file("build/detekt-reports")
| reports {
| sarif {
| enabled = true
| }
| }
|}
"""
@@ -244,6 +239,9 @@ internal object DetektTaskDslSpec : Spek({
val config = """
|detekt {
| reportsDir = file("build/detekt-reports")
|}
|
|tasks.detekt {
| reports {
| xml.destination = file("build/xml-reports/custom-detekt.xml")
| }
@@ -277,7 +275,7 @@ internal object DetektTaskDslSpec : Spek({
beforeGroup {
val config = """
|detekt {
|tasks.detekt {
| reports {
| xml.enabled = false
| html {
@@ -309,7 +307,7 @@ internal object DetektTaskDslSpec : Spek({
beforeGroup {
val config = """
|detekt {
|tasks.detekt {
| reports {
| custom {
| reportId = "customXml"
@@ -342,7 +340,7 @@ internal object DetektTaskDslSpec : Spek({
beforeGroup {
val config = """
|detekt {
|tasks.withType(io.gitlab.arturbosch.detekt.Detekt).configureEach {
| reports {
| custom {
| destination = file("build/reports/custom.xml")
@@ -363,7 +361,7 @@ internal object DetektTaskDslSpec : Spek({
beforeGroup {
val config = """
|detekt {
|tasks.withType(io.gitlab.arturbosch.detekt.Detekt).configureEach {
| reports {
| custom {
| reportId = "customJson"
@@ -386,7 +384,7 @@ internal object DetektTaskDslSpec : Spek({
val aDirectory = "\${rootDir}/src"
val config = """
|detekt {
|tasks.withType(io.gitlab.arturbosch.detekt.Detekt).configureEach {
| reports {
| custom {
| reportId = "foo"
@@ -410,7 +408,7 @@ internal object DetektTaskDslSpec : Spek({
beforeGroup {
val config = """
|detekt {
|tasks.withType(io.gitlab.arturbosch.detekt.Detekt).configureEach {
| reports {
| custom {
| reportId = "${wellKnownType.reportId}"

View File

@@ -6,6 +6,64 @@ permalink: changelog.html
toc: true
---
#### SNAPSHOT (unreleased)
##### Notable Changes
- Report configuration is changing in the Gradle plugin. The `reports` extension on the `detekt` extension has been
deprecated. See the Migration section below for steps to migrate to the new recommended configuration - [#3687](https://github.com/detekt/detekt/pull/3687)
##### Migration
Configuring reports in the Gradle plugin should be done at the task level instead of at the extension (or global) level.
The previous recommendation resulted in the report output for multiple tasks overwriting each other when multiple detekt
tasks were executed in the same Gradle run.
Before this release the recommended way to configure reports was using the `detekt` extension:
```kotlin
detekt {
reports {
xml {
enabled = true
destination = file("build/reports/detekt/detekt.xml")
}
}
}
```
This meant all detekt tasks would output the report to the same destination. From this detekt release you should enable
and disable reports for all tasks using the `withType` Gradle method:
```kotlin
// Kotlin DSL
tasks.withType<Detekt>().configureEach {
reports {
xml.required.set(true)
}
}
```
```groovy
// Groovy DSL
tasks.withType(Detekt).configureEach {
reports {
xml.required.set(true)
}
}
```
To customize the report output location configure the task individually:
```kotlin
tasks.detektMain {
reports {
xml {
outputLocation.set(file("build/reports/detekt/customPath.xml"))
required.set(true) // reports can also be enabled and disabled at the task level as needed
}
}
}
```
#### 1.18.1 - 2021-08-30
This is a point release for Detekt `1.18.0` containing bugfixes for problems that got discovered just after the release.