diff --git a/README.md b/README.md index 0f3a8f5295..83569aa7c0 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,30 @@ diktat { } ``` +Also `diktat` extension has different reporters. You can specify `json`, `html`, `checkstyle`, `plain` (default) or your own custom reporter: +```kotlin +diktat { + reporter = "json" // "html", "checkstyle", "plain" +} +``` + +Example of your custom reporter: +```kotlin +diktat { + reporter = "custom:name:pathToJar" +} +``` +Name parameter is the name of your reporter and as the last parameter you should specify path to jar, which contains your reporter. +[Example of the junit custom reporter.](https://github.com/kryanod/ktlint-junit-reporter) + +You can also specify an output. +```kotlin +diktat { + reporter = "json" + output = "someFile.json" +} +``` + You can run diktat checks using task `diktatCheck` and automatically fix errors with tasks `diktatFix`. ## Customizations via `diktat-analysis.yml` diff --git a/diktat-gradle-plugin/build.gradle.kts b/diktat-gradle-plugin/build.gradle.kts index 5bf9b08466..fa1dc9540f 100644 --- a/diktat-gradle-plugin/build.gradle.kts +++ b/diktat-gradle-plugin/build.gradle.kts @@ -29,7 +29,6 @@ dependencies { implementation("com.pinterest.ktlint:ktlint-core:$ktlintVersion") { exclude("com.pinterest.ktlint", "ktlint-ruleset-standard") } - implementation("com.pinterest.ktlint:ktlint-reporter-plain:$ktlintVersion") implementation("org.cqfn.diktat:diktat-rules:$diktatVersion") testImplementation("org.junit.jupiter:junit-jupiter-api:$junitVersion") diff --git a/diktat-gradle-plugin/src/functionalTest/kotlin/org/cqfn/diktat/plugin/gradle/DiktatGradlePluginFunctionalTest.kt b/diktat-gradle-plugin/src/functionalTest/kotlin/org/cqfn/diktat/plugin/gradle/DiktatGradlePluginFunctionalTest.kt index 1f7f3372d7..cb9125b0b0 100644 --- a/diktat-gradle-plugin/src/functionalTest/kotlin/org/cqfn/diktat/plugin/gradle/DiktatGradlePluginFunctionalTest.kt +++ b/diktat-gradle-plugin/src/functionalTest/kotlin/org/cqfn/diktat/plugin/gradle/DiktatGradlePluginFunctionalTest.kt @@ -4,6 +4,7 @@ import org.cqfn.diktat.plugin.gradle.DiktatGradlePlugin.Companion.DIKTAT_CHECK_T import org.gradle.buildinit.plugins.internal.modifiers.BuildInitDsl import org.gradle.internal.impldep.org.junit.rules.TemporaryFolder import org.gradle.testkit.runner.TaskOutcome +import org.jetbrains.kotlin.com.intellij.util.ObjectUtils.assertNotNull import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach @@ -39,6 +40,28 @@ class DiktatGradlePluginFunctionalTest { ) } + @Test + fun `should have json reporter files`() { + buildFile.appendText( + """${System.lineSeparator()} + diktat { + inputs = files("src/**/*.kt") + reporterType = "json" + output = "test.txt" + } + """.trimIndent() + ) + val result = runDiktat(testProjectDir, shouldSucceed = false) + + val diktatCheckBuildResult = result.task(":$DIKTAT_CHECK_TASK") + requireNotNull(diktatCheckBuildResult) + Assertions.assertEquals(TaskOutcome.FAILED, diktatCheckBuildResult.outcome) + val file = assertNotNull(testProjectDir.root.walkTopDown().filter { it.name == "test.txt" }.first()) + Assertions.assertTrue( + file.readLines().any { it.contains("[HEADER_MISSING_OR_WRONG_COPYRIGHT]") } + ) + } + @Test fun `should execute diktatCheck with explicit inputs`() { buildFile.appendText( diff --git a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatExtension.kt b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatExtension.kt index 33f2521551..6311944b17 100644 --- a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatExtension.kt +++ b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatExtension.kt @@ -1,6 +1,5 @@ package org.cqfn.diktat.plugin.gradle -import com.pinterest.ktlint.core.Reporter import org.gradle.api.file.FileCollection import java.io.File @@ -18,6 +17,17 @@ open class DiktatExtension { */ var debug = false + /** + * Type of the reporter to use + */ + var reporterType: String = "plain" + + /** + * Type of output + * Default: System.out + */ + var output: String = "" + /** * Path to diktat yml config file. Can be either absolute or relative to project's root directory. * Default value: `diktat-analysis.yml` in rootDir. @@ -29,12 +39,6 @@ open class DiktatExtension { */ lateinit var excludes: FileCollection - /** - * Ktlint's [Reporter] which will be used during run. - * Private until I find a way to configure it. - */ - internal lateinit var reporter: Reporter - /** * Paths that will be scanned for .kt(s) files */ diff --git a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatGradlePlugin.kt b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatGradlePlugin.kt index 97237e0b7f..8223efec9d 100644 --- a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatGradlePlugin.kt +++ b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatGradlePlugin.kt @@ -1,6 +1,5 @@ package org.cqfn.diktat.plugin.gradle -import com.pinterest.ktlint.reporter.plain.PlainReporter import generated.DIKTAT_VERSION import generated.KTLINT_VERSION import org.gradle.api.Plugin @@ -22,7 +21,6 @@ class DiktatGradlePlugin : Plugin { } diktatConfigFile = project.rootProject.file("diktat-analysis.yml") excludes = project.files() - reporter = PlainReporter(System.out) } // only gradle 7+ (or maybe 6.8) will embed kotlin 1.4+, kx.serialization is incompatible with kotlin 1.3, so until then we have to use JavaExec wrapper diff --git a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskBase.kt b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskBase.kt index 49d608b78a..b705d03d0b 100644 --- a/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskBase.kt +++ b/diktat-gradle-plugin/src/main/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskBase.kt @@ -3,6 +3,7 @@ package org.cqfn.diktat.plugin.gradle import org.cqfn.diktat.plugin.gradle.DiktatGradlePlugin.Companion.DIKTAT_CHECK_TASK import org.cqfn.diktat.plugin.gradle.DiktatGradlePlugin.Companion.DIKTAT_FIX_TASK import org.cqfn.diktat.ruleset.rules.DIKTAT_CONF_PROPERTY +import org.cqfn.diktat.ruleset.utils.log import generated.DIKTAT_VERSION import generated.KTLINT_VERSION @@ -50,6 +51,10 @@ open class DiktatJavaExecTaskBase @Inject constructor( } else { main = "com.pinterest.ktlint.Main" } + // Plain, checkstyle and json reporter are provided out of the box in ktlint + if (diktatExtension.reporterType == "html") { + diktatConfiguration.dependencies.add(project.dependencies.create("com.pinterest.ktlint:ktlint-reporter-html:$KTLINT_VERSION")) + } classpath = diktatConfiguration project.logger.debug("Setting diktatCheck classpath to ${diktatConfiguration.dependencies.toSet()}") if (diktatExtension.debug) { @@ -83,6 +88,8 @@ open class DiktatJavaExecTaskBase @Inject constructor( diktatExtension.excludes.files.forEach { addPattern(it, negate = true) } + + add(createReporterFlag(diktatExtension)) } logger.debug("Setting JavaExec args to $args") } @@ -107,6 +114,40 @@ open class DiktatJavaExecTaskBase @Inject constructor( @Suppress("FUNCTION_BOOLEAN_PREFIX") override fun getIgnoreFailures(): Boolean = ignoreFailuresProp.getOrElse(false) + private fun createReporterFlag(diktatExtension: DiktatExtension): String { + val flag: StringBuilder = StringBuilder() + + // Plain, checkstyle and json reporter are provided out of the box in ktlint + when (diktatExtension.reporterType) { + "json" -> flag.append("--reporter=json") + "html" -> flag.append("--reporter=html") + "checkstyle" -> flag.append("--reporter=checkstyle") + else -> customReporter(diktatExtension, flag) + } + + if (diktatExtension.output.isNotEmpty()) { + flag.append(",output=${diktatExtension.output}") + } + + return flag.toString() + } + + private fun customReporter(diktatExtension: DiktatExtension, flag: java.lang.StringBuilder) { + if (diktatExtension.reporterType.startsWith("custom")) { + val name = diktatExtension.reporterType.split(":")[1] + val jarPath = diktatExtension.reporterType.split(":")[2] + if (name.isEmpty() || jarPath.isEmpty()) { + log.warn("Either name or path to jar is not specified. Falling to plain reporter") + flag.append("--reporter=plain") + } else { + flag.append("--reporter=$name,artifact=$jarPath") + } + } else { + flag.append("--reporter=plain") + log.warn("Unknown reporter was specified. Falling back to plain reporter.") + } + } + @Suppress("MagicNumber") private fun isMainClassPropertySupported(gradleVersionString: String) = GradleVersion.version(gradleVersionString) >= GradleVersion.version("6.4") diff --git a/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatGradlePluginTest.kt b/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatGradlePluginTest.kt index 7587186137..676e6237a0 100644 --- a/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatGradlePluginTest.kt +++ b/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatGradlePluginTest.kt @@ -32,4 +32,10 @@ class DiktatGradlePluginTest { Assertions.assertIterableEquals(project.fileTree("src").files, diktatExtension.inputs.files) Assertions.assertTrue(diktatExtension.inputs.files.isNotEmpty()) } + + @Test + fun `check that the right reporter dependency added`() { + val diktatExtension = project.extensions.getByName("diktat") as DiktatExtension + Assertions.assertTrue(diktatExtension.reporterType == "plain") + } } diff --git a/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskTest.kt b/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskTest.kt index 533c6ab497..fa1c8a2840 100644 --- a/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskTest.kt +++ b/diktat-gradle-plugin/src/test/kotlin/org/cqfn/diktat/plugin/gradle/DiktatJavaExecTaskTest.kt @@ -23,7 +23,7 @@ class DiktatJavaExecTaskTest { @Test fun `check command line for various inputs`() { assertCommandLineEquals( - listOf(null, combinePathParts("src", "**", "*.kt")) + listOf(null, combinePathParts("src", "**", "*.kt"), "--reporter=plain") ) { inputs = project.files("src/**/*.kt") } @@ -32,7 +32,7 @@ class DiktatJavaExecTaskTest { @Test fun `check command line in debug mode`() { assertCommandLineEquals( - listOf(null, "--debug", combinePathParts("src", "**", "*.kt")) + listOf(null, "--debug", combinePathParts("src", "**", "*.kt"), "--reporter=plain") ) { inputs = project.files("src/**/*.kt") debug = true @@ -43,7 +43,7 @@ class DiktatJavaExecTaskTest { fun `check command line with excludes`() { assertCommandLineEquals( listOf(null, combinePathParts("src", "**", "*.kt"), - "!${combinePathParts("src", "main", "kotlin", "generated")}" + "!${combinePathParts("src", "main", "kotlin", "generated")}", "--reporter=plain" ) ) { inputs = project.files("src/**/*.kt") @@ -76,6 +76,57 @@ class DiktatJavaExecTaskTest { Assertions.assertEquals(File(project.projectDir.parentFile, "diktat-analysis.yml").absolutePath, task.systemProperties[DIKTAT_CONF_PROPERTY]) } + @Test + fun `check command line has reporter type and output`() { + assertCommandLineEquals( + listOf(null, "--reporter=json,output=some.txt") + ) { + inputs = project.files() + diktatConfigFile = project.file("../diktat-analysis.yml") + reporterType = "json" + output = "some.txt" + } + } + + @Test + fun `check command line has reporter type without output`() { + assertCommandLineEquals( + listOf(null, "--reporter=json") + ) { + inputs = project.files() + diktatConfigFile = project.file("../diktat-analysis.yml") + reporterType = "json" + } + } + + @Test + fun `check command line has custom reporter type with output`() { + assertCommandLineEquals( + listOf(null, "--reporter=customName,artifact=customPath") + ) { + inputs = project.files() + diktatConfigFile = project.file("../diktat-analysis.yml") + reporterType = "custom:customName:customPath" + } + } + + @Test + fun `check that project has html dependency`() { + val task = project.registerDiktatTask { + inputs = project.files() + diktatConfigFile = project.file("../diktat-analysis.yml") + reporterType = "html" + } + + Assertions.assertTrue( + project + .configurations + .getByName("diktat") + .dependencies + .any { it.name == "ktlint-reporter-html" }) + Assertions.assertEquals(File(project.projectDir.parentFile, "diktat-analysis.yml").absolutePath, task.systemProperties[DIKTAT_CONF_PROPERTY]) + } + @Test fun `check system property with multiproject build with default config`() { setupMultiProject()