Skip to content

Commit

Permalink
[WIP] Add config generator for custom rules
Browse files Browse the repository at this point in the history
  • Loading branch information
VitalyVPinchuk committed Aug 11, 2022
1 parent 6d39566 commit 088ebb7
Show file tree
Hide file tree
Showing 9 changed files with 60 additions and 95 deletions.
Expand Up @@ -47,13 +47,6 @@ class CliArgs {
)
var generateConfig: Boolean = false

@Parameter(
names = ["--generate-custom-rule-config", "-gcrc"],
description = "Generate config for user-defined rules. " +
"Path to user rules can be specified with --input option"
)
var generateCustomRuleConfig: Boolean = false

@Parameter(
names = ["--plugins", "-p"],
description = "Extra paths to plugin jars separated by ',' or ';'."
Expand Down
Expand Up @@ -20,7 +20,7 @@ class CliRunner : DetektCli {

val specialRunner = when {
arguments.showVersion -> VersionPrinter(outputChannel)
arguments.generateConfig || arguments.generateCustomRuleConfig -> ConfigExporter(arguments, outputChannel)
arguments.generateConfig -> ConfigExporter(arguments, outputChannel)
arguments.printAst -> AstPrinter(arguments, outputChannel)
else -> null
}
Expand Down
Expand Up @@ -54,7 +54,7 @@ fun buildRunner(
val arguments = parseArguments(args)
return when {
arguments.showVersion -> VersionPrinter(outputPrinter)
arguments.generateConfig || arguments.generateCustomRuleConfig -> ConfigExporter(arguments, outputPrinter)
arguments.generateConfig -> ConfigExporter(arguments, outputPrinter)
arguments.printAst -> AstPrinter(arguments, outputPrinter)
else -> Runner(arguments, outputPrinter, errorPrinter)
}
Expand Down
Expand Up @@ -4,8 +4,6 @@ import io.github.detekt.tooling.api.DefaultConfigurationProvider
import io.github.detekt.tooling.api.spec.ProcessingSpec
import io.gitlab.arturbosch.detekt.cli.CliArgs
import io.gitlab.arturbosch.detekt.cli.MultipleExistingPathConverter
import java.io.File
import java.net.URLClassLoader
import java.nio.file.Paths

class ConfigExporter(
Expand All @@ -14,14 +12,6 @@ class ConfigExporter(
) : Executable {

override fun execute() {
if (arguments.generateCustomRuleConfig) {
generateCustomRuleConfig()
} else {
generateConfig()
}
}

private fun generateConfig() {
val configPath = Paths.get(arguments.config ?: "detekt.yml")
val spec = ProcessingSpec {
extensions {
Expand All @@ -32,33 +22,4 @@ class ConfigExporter(
DefaultConfigurationProvider.load(spec.extensionsSpec).copy(configPath)
outputPrinter.appendLine("Successfully copied default config to ${configPath.toAbsolutePath()}")
}

private fun generateCustomRuleConfig() {
@Suppress("UnsafeCallOnNullableType")
val rootDir = File(arguments.classpath!!)
val urls = rootDir.walkTopDown()
.filter { it.name.endsWith(".jar") }
.map { it.toURI().toURL() }
.toList()
.toTypedArray()
val classLoader = URLClassLoader(urls, null)

val clazz = classLoader.loadClass("io.gitlab.arturbosch.detekt.generator.Main")
val methodMain = clazz.getMethod("main", Array<String>::class.java)

val args = arrayOf(
arguments::generateCustomRuleConfig.name.toParam(),
arguments::input.name.toParam(),
arguments.input,
)
methodMain.invoke(null, args)
outputPrinter.appendLine("Successfully generated custom rules config to /resources/config/")
}

private fun String.toParam() =
@Suppress("UnsafeCallOnNullableType")
arguments.javaClass.declaredFields.find { it.name == this }!!
.getAnnotation(com.beust.jcommander.Parameter::class.java)
.names
.first()
}
Expand Up @@ -59,6 +59,7 @@ class Generator(
.groupBy { (_, folder) -> folder }
.toList()
.forEach { (folder, list) ->
val collector = DetektCollector()
list.forEach { (file, _) ->
collector.visit(file)
}
Expand Down
@@ -0,0 +1,52 @@
package io.gitlab.arturbosch.detekt.generator.printer

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import java.io.File
import java.nio.file.Files
import java.nio.file.Paths

class GeneratorSpec {
@Test
fun `config files generated successfully`() {
assertThat(File("$folder1$configPath")).exists()
assertThat(File("$folder2$configPath")).exists()
}

@Test
fun `config files have proper content`() {
assertThat(File("$folder1$configPath").readText())
.contains("complexity:")
.doesNotContain("coroutines:")

assertThat(File("$folder2$configPath").readText())
.contains("coroutines:")
.doesNotContain("complexity:")
}

companion object {
private const val folder1 = "../detekt-rules-complexity"
private const val folder2 = "../detekt-rules-coroutines"
private const val configPath = "/src/main/resources/config/config.yml"

@JvmStatic
@BeforeAll
fun init() {
val args = arrayOf(
"--generate-custom-rule-config",
"--input",
"$folder1, $folder2",
)
io.gitlab.arturbosch.detekt.generator.main(args)
}

@JvmStatic
@AfterAll
fun tearDown() {
Files.deleteIfExists(Paths.get(folder1, configPath))
Files.deleteIfExists(Paths.get(folder2, configPath))
}
}
}
Expand Up @@ -2,23 +2,17 @@ package io.gitlab.arturbosch.detekt

import io.gitlab.arturbosch.detekt.DetektPlugin.Companion.CONFIG_DIR_NAME
import io.gitlab.arturbosch.detekt.DetektPlugin.Companion.CONFIG_FILE
import io.gitlab.arturbosch.detekt.invoke.ClasspathArgument
import io.gitlab.arturbosch.detekt.invoke.CliArgument
import io.gitlab.arturbosch.detekt.invoke.ConfigArgument
import io.gitlab.arturbosch.detekt.invoke.DetektInvoker
import io.gitlab.arturbosch.detekt.invoke.GenerateConfigArgument
import io.gitlab.arturbosch.detekt.invoke.GenerateCustomRuleConfigArgument
import io.gitlab.arturbosch.detekt.invoke.InputArgument
import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Classpath
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Optional
Expand Down Expand Up @@ -57,47 +51,17 @@ abstract class DetektGenerateConfigTask : DefaultTask() {
config.last()
}

@get:Input
val generateOnlyFromCustomRules: Property<Boolean> = project.objects
.property(Boolean::class.javaObjectType)
.convention(false)

@get:InputFiles
@get:Optional
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val source: ConfigurableFileCollection

private val defaultSourcePath = project.rootDir.toPath().resolve(SOURCE_DIR_NAME)

private val sourceToUse: File
get() = if (source.isEmpty) {
defaultSourcePath.toFile()
} else {
source.last()
}

private val projectRoot = project.rootDir

@get:Internal
internal val arguments: Provider<List<String>> = project.provider {
if (generateOnlyFromCustomRules.get()) {
listOf(
GenerateCustomRuleConfigArgument,
InputArgument(sourceToUse),
ClasspathArgument(projectRoot),
ConfigArgument(configurationToUse)
)
} else {
listOf(
GenerateConfigArgument,
ConfigArgument(configurationToUse)
)
}.flatMap(CliArgument::toArgument)
listOf(
GenerateConfigArgument,
ConfigArgument(configurationToUse)
).flatMap(CliArgument::toArgument)
}

@TaskAction
fun generateConfig() {
if (configurationToUse.exists() && !generateOnlyFromCustomRules.get()) {
if (configurationToUse.exists()) {
logger.warn("Skipping config file generation; file already exists at $configurationToUse")
return
}
Expand Down
Expand Up @@ -117,7 +117,6 @@ class DetektPlugin : Plugin<Project> {
internal val defaultExcludes = listOf("build/")
internal val defaultIncludes = listOf("**/*.kt", "**/*.kts")
internal const val CONFIG_DIR_NAME = "config/detekt"
internal const val SOURCE_DIR_NAME = "detekt-rules-complexity"
internal const val CONFIG_FILE = "detekt.yml"

internal const val DETEKT_ANDROID_DISABLED_PROPERTY = "detekt.android.disabled"
Expand Down
Expand Up @@ -17,7 +17,6 @@ private const val FAIL_FAST_PARAMETER = "--fail-fast"
private const val ALL_RULES_PARAMETER = "--all-rules"
private const val REPORT_PARAMETER = "--report"
private const val GENERATE_CONFIG_PARAMETER = "--generate-config"
private const val GENERATE_CUSTOM_RULE_CONFIG_PARAMETER = "--generate-custom-rule-config"
private const val CREATE_BASELINE_PARAMETER = "--create-baseline"
private const val CLASSPATH_PARAMETER = "--classpath"
private const val LANGUAGE_VERSION_PARAMETER = "--language-version"
Expand All @@ -36,10 +35,6 @@ internal object GenerateConfigArgument : CliArgument() {
override fun toArgument() = listOf(GENERATE_CONFIG_PARAMETER)
}

internal object GenerateCustomRuleConfigArgument : CliArgument() {
override fun toArgument() = listOf(GENERATE_CUSTOM_RULE_CONFIG_PARAMETER)
}

internal data class InputArgument(val fileCollection: FileCollection) : CliArgument() {
override fun toArgument() = listOf(INPUT_PARAMETER, fileCollection.joinToString(",") { it.absolutePath })
}
Expand Down

0 comments on commit 088ebb7

Please sign in to comment.