mirror of
https://github.com/jlengrand/detekt.git
synced 2026-03-10 08:11:23 +00:00
* Include default config validation excludes for plugin rule sets - #2285 * Allow to validate configuration by a new ConfigValidator extension - #2285
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
package io.gitlab.arturbosch.detekt.api
|
||||
|
||||
/**
|
||||
* An extension which allows users to validate parts of the configuration.
|
||||
*
|
||||
* Rule authors can validate if specific properties do appear in their config
|
||||
* or if their value lies in a specified range.
|
||||
*/
|
||||
interface ConfigValidator : Extension {
|
||||
|
||||
/**
|
||||
* Executes queries on given config and reports any warnings or errors via [Notification]s.
|
||||
*/
|
||||
fun validate(config: Config): Collection<Notification>
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
package io.gitlab.arturbosch.detekt.cli
|
||||
|
||||
import io.gitlab.arturbosch.detekt.cli.config.InvalidConfig
|
||||
import io.gitlab.arturbosch.detekt.cli.runners.AstPrinter
|
||||
import io.gitlab.arturbosch.detekt.cli.runners.ConfigExporter
|
||||
import io.gitlab.arturbosch.detekt.cli.runners.Executable
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package io.gitlab.arturbosch.detekt.cli.config
|
||||
|
||||
import io.gitlab.arturbosch.detekt.api.ConfigValidator
|
||||
import io.gitlab.arturbosch.detekt.api.Notification
|
||||
import io.gitlab.arturbosch.detekt.cli.console.red
|
||||
import io.gitlab.arturbosch.detekt.core.ProcessingSettings
|
||||
import java.util.ServiceLoader
|
||||
|
||||
fun loadValidators(settings: ProcessingSettings): List<ConfigValidator> =
|
||||
ServiceLoader.load(ConfigValidator::class.java, settings.pluginLoader).toList()
|
||||
|
||||
fun checkConfiguration(settings: ProcessingSettings) {
|
||||
val props = settings.config.subConfig("config")
|
||||
val shouldValidate = props.valueOrDefault("validation", true)
|
||||
|
||||
if (shouldValidate) {
|
||||
val validators = loadValidators(settings) + DefaultPropertiesConfigValidator(settings)
|
||||
val notifications = validators.flatMap { it.validate(settings.config) }
|
||||
notifications.map(Notification::message).forEach(settings::info)
|
||||
val errors = notifications.filter(Notification::isError)
|
||||
if (errors.isNotEmpty()) {
|
||||
val propsString = if (errors.size == 1) "property" else "properties"
|
||||
throw InvalidConfig("Run failed with ${errors.size} invalid config $propsString.".red())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package io.gitlab.arturbosch.detekt.cli.config
|
||||
|
||||
import io.gitlab.arturbosch.detekt.api.Config
|
||||
import io.gitlab.arturbosch.detekt.api.ConfigValidator
|
||||
import io.gitlab.arturbosch.detekt.api.Notification
|
||||
import io.gitlab.arturbosch.detekt.api.internal.CommaSeparatedPattern
|
||||
import io.gitlab.arturbosch.detekt.api.internal.DEFAULT_PROPERTY_EXCLUDES
|
||||
import io.gitlab.arturbosch.detekt.api.internal.DefaultRuleSetProvider
|
||||
import io.gitlab.arturbosch.detekt.api.internal.validateConfig
|
||||
import io.gitlab.arturbosch.detekt.cli.loadDefaultConfig
|
||||
import io.gitlab.arturbosch.detekt.core.ProcessingSettings
|
||||
import io.gitlab.arturbosch.detekt.core.RuleSetLocator
|
||||
|
||||
class DefaultPropertiesConfigValidator(
|
||||
private val settings: ProcessingSettings
|
||||
) : ConfigValidator {
|
||||
|
||||
override fun validate(config: Config): Collection<Notification> {
|
||||
fun patterns(): Set<Regex> {
|
||||
val pluginExcludes = RuleSetLocator(settings).load()
|
||||
.filter { it !is DefaultRuleSetProvider }
|
||||
.joinToString(",") { "${it.ruleSetId}.*" }
|
||||
val configExcludes = config.subConfig("config").valueOrDefault("excludes", "")
|
||||
val allExcludes = "$configExcludes,$DEFAULT_PROPERTY_EXCLUDES,$pluginExcludes"
|
||||
return CommaSeparatedPattern(allExcludes).mapToRegex()
|
||||
}
|
||||
return validateConfig(config, loadDefaultConfig(), patterns())
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
package io.gitlab.arturbosch.detekt.cli
|
||||
package io.gitlab.arturbosch.detekt.cli.config
|
||||
|
||||
class InvalidConfig(override val message: String?) : RuntimeException(message, null, true, false)
|
||||
@@ -1,16 +1,12 @@
|
||||
package io.gitlab.arturbosch.detekt.cli.runners
|
||||
|
||||
import io.gitlab.arturbosch.detekt.api.Detektion
|
||||
import io.gitlab.arturbosch.detekt.api.Notification
|
||||
import io.gitlab.arturbosch.detekt.api.internal.CommaSeparatedPattern
|
||||
import io.gitlab.arturbosch.detekt.api.internal.DEFAULT_PROPERTY_EXCLUDES
|
||||
import io.gitlab.arturbosch.detekt.api.internal.validateConfig
|
||||
import io.gitlab.arturbosch.detekt.cli.BuildFailure
|
||||
import io.gitlab.arturbosch.detekt.cli.CliArgs
|
||||
import io.gitlab.arturbosch.detekt.cli.FilteredDetectionResult
|
||||
import io.gitlab.arturbosch.detekt.cli.InvalidConfig
|
||||
import io.gitlab.arturbosch.detekt.cli.OutputFacade
|
||||
import io.gitlab.arturbosch.detekt.cli.baseline.BaselineFacade
|
||||
import io.gitlab.arturbosch.detekt.cli.config.checkConfiguration
|
||||
import io.gitlab.arturbosch.detekt.cli.console.red
|
||||
import io.gitlab.arturbosch.detekt.cli.createClasspath
|
||||
import io.gitlab.arturbosch.detekt.cli.createFilters
|
||||
@@ -18,7 +14,6 @@ import io.gitlab.arturbosch.detekt.cli.createPlugins
|
||||
import io.gitlab.arturbosch.detekt.cli.getOrComputeWeightedAmountOfIssues
|
||||
import io.gitlab.arturbosch.detekt.cli.isValidAndSmallerOrEqual
|
||||
import io.gitlab.arturbosch.detekt.cli.loadConfiguration
|
||||
import io.gitlab.arturbosch.detekt.cli.loadDefaultConfig
|
||||
import io.gitlab.arturbosch.detekt.cli.maxIssues
|
||||
import io.gitlab.arturbosch.detekt.core.DetektFacade
|
||||
import io.gitlab.arturbosch.detekt.core.ProcessingSettings
|
||||
@@ -65,26 +60,6 @@ class Runner(
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkConfiguration(settings: ProcessingSettings) {
|
||||
val props = settings.config.subConfig("config")
|
||||
val shouldValidate = props.valueOrDefault("validation", true)
|
||||
|
||||
fun patterns(): Set<Regex> {
|
||||
val excludes = props.valueOrDefault("excludes", "") + ",$DEFAULT_PROPERTY_EXCLUDES"
|
||||
return CommaSeparatedPattern(excludes).mapToRegex()
|
||||
}
|
||||
|
||||
if (shouldValidate) {
|
||||
val notifications = validateConfig(settings.config, loadDefaultConfig(), patterns())
|
||||
notifications.map(Notification::message).forEach(settings::info)
|
||||
val errors = notifications.filter(Notification::isError)
|
||||
if (errors.isNotEmpty()) {
|
||||
val propsString = if (errors.size == 1) "property" else "properties"
|
||||
throw InvalidConfig("Run failed with ${errors.size} invalid config $propsString.".red())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkBuildFailureThreshold(result: Detektion, settings: ProcessingSettings) {
|
||||
val amount = result.getOrComputeWeightedAmountOfIssues(settings.config)
|
||||
val maxIssues = settings.config.maxIssues()
|
||||
|
||||
@@ -2,7 +2,7 @@ package io.gitlab.arturbosch.detekt.cli.runners
|
||||
|
||||
import io.gitlab.arturbosch.detekt.cli.BuildFailure
|
||||
import io.gitlab.arturbosch.detekt.cli.CliArgs
|
||||
import io.gitlab.arturbosch.detekt.cli.InvalidConfig
|
||||
import io.gitlab.arturbosch.detekt.cli.config.InvalidConfig
|
||||
import io.gitlab.arturbosch.detekt.test.resource
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package io.gitlab.arturbosch.detekt.sample.extensions
|
||||
|
||||
import io.gitlab.arturbosch.detekt.api.Config
|
||||
import io.gitlab.arturbosch.detekt.api.ConfigValidator
|
||||
import io.gitlab.arturbosch.detekt.api.Notification
|
||||
|
||||
class SampleConfigValidator : ConfigValidator {
|
||||
|
||||
override fun validate(config: Config): Collection<Notification> {
|
||||
val result = mutableListOf<Notification>()
|
||||
runCatching {
|
||||
config.subConfig("sample")
|
||||
.subConfig("TooManyFunctions")
|
||||
.valueOrNull<Boolean>("active")
|
||||
}.onFailure {
|
||||
result.add(SampleMessage("'active' property must be of type boolean."))
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
class SampleMessage(
|
||||
override val message: String,
|
||||
override val level: Notification.Level = Notification.Level.Error
|
||||
) : Notification
|
||||
@@ -5,7 +5,6 @@ import io.gitlab.arturbosch.detekt.api.OutputReport
|
||||
|
||||
class QualifiedNamesOutputReport : OutputReport() {
|
||||
|
||||
var fileName: String = "fqNames"
|
||||
override val ending: String = "txt"
|
||||
|
||||
override fun render(detektion: Detektion): String? {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
io.gitlab.arturbosch.detekt.sample.extensions.SampleConfigValidator
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.gitlab.arturbosch.detekt.sample.extensions
|
||||
|
||||
import io.gitlab.arturbosch.detekt.cli.CliArgs
|
||||
import io.gitlab.arturbosch.detekt.cli.InvalidConfig
|
||||
import io.gitlab.arturbosch.detekt.cli.config.InvalidConfig
|
||||
import io.gitlab.arturbosch.detekt.cli.console.red
|
||||
import io.gitlab.arturbosch.detekt.cli.runners.Runner
|
||||
import org.assertj.core.api.Assertions.assertThatCode
|
||||
@@ -15,15 +15,22 @@ class SupportConfigValidationSpec : Spek({
|
||||
|
||||
val testDir = Files.createTempDirectory("detekt-sample")
|
||||
|
||||
it("fails when new rule set is not excluded") {
|
||||
val args = CliArgs {
|
||||
input = testDir.toString()
|
||||
configResource = "included-config.yml"
|
||||
}
|
||||
context("failing cases") {
|
||||
arrayOf(
|
||||
"fails when new rule set is not excluded" to "included-config.yml",
|
||||
"fails due to no configuration property present for 'sample' rule set" to "wrong-property-config.yml"
|
||||
).forEach { (testCase, config) ->
|
||||
it(testCase) {
|
||||
val args = CliArgs {
|
||||
input = testDir.toString()
|
||||
configResource = config
|
||||
}
|
||||
|
||||
assertThatCode { Runner(args).execute() }
|
||||
.isInstanceOf(InvalidConfig::class.java)
|
||||
.hasMessage("Run failed with 1 invalid config property.".red())
|
||||
assertThatCode { Runner(args).execute() }
|
||||
.isInstanceOf(InvalidConfig::class.java)
|
||||
.hasMessage("Run failed with 1 invalid config property.".red())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it("passes with excluded new rule set") {
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
config:
|
||||
validation: true
|
||||
# 1. exclude rule set 'sample' and all its nested members
|
||||
# 2. exclude every property in every rule under the rule set 'sample'
|
||||
excludes: "sample.*,sample>.*>.*"
|
||||
# Additional properties can be useful when writing custom extensions.
|
||||
# However only properties defined in the default config are known to detekt.
|
||||
# All unknown properties are treated as errors and will get reported if not excluded in this config part.
|
||||
excludes: "my_additional_properties"
|
||||
|
||||
# Properties of custom rule sets get excluded by default.
|
||||
# If you want to validate them further, consider implementing a ConfigValidator.
|
||||
sample:
|
||||
TooManyFunctions:
|
||||
active: true
|
||||
|
||||
# This properties are unknown to detekt and must be excluded.
|
||||
my_additional_properties:
|
||||
magic_number: 7
|
||||
magic_string: "Hello World"
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
# Properties of custom rule sets get excluded by default.
|
||||
sample:
|
||||
TooManyFunctions:
|
||||
active: true
|
||||
|
||||
# This properties are unknown to detekt and must be excluded.
|
||||
my_additional_properties:
|
||||
magic_number: 7
|
||||
magic_string: "Hello World"
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Properties of custom rule sets get excluded by default.
|
||||
sample:
|
||||
TooManyFunctions:
|
||||
# This property is tested via the SampleConfigValidator
|
||||
active: 1 # should be true
|
||||
@@ -70,6 +70,13 @@ utility functions to retrieve specific or generic properties
|
||||
from the underlying detekt configuration file.
|
||||
|
||||
|
||||
|
|
||||
|
||||
##### [io.gitlab.arturbosch.detekt.api.ConfigValidator](../io.gitlab.arturbosch.detekt.api/-config-validator/index.html)
|
||||
|
||||
An extension which allows users to validate parts of the configuration.
|
||||
|
||||
|
||||
|
|
||||
|
||||
##### [io.gitlab.arturbosch.detekt.api.ConsoleReport](../io.gitlab.arturbosch.detekt.api/-console-report/index.html)
|
||||
|
||||
@@ -26,6 +26,7 @@ Currently supported extensions are:
|
||||
|
||||
### Inheritors
|
||||
|
||||
| [ConfigValidator](../-config-validator/index.html) | An extension which allows users to validate parts of the configuration.`interface ConfigValidator : `[`Extension`](./index.html) |
|
||||
| [ConsoleReport](../-console-report/index.html) | Extension point which describes how findings should be printed on the console.`abstract class ConsoleReport : `[`Extension`](./index.html) |
|
||||
| [FileProcessListener](../-file-process-listener/index.html) | Gather additional metrics about the analyzed kotlin file. Pay attention to the thread policy of each function!`interface FileProcessListener : `[`Extension`](./index.html) |
|
||||
| [OutputReport](../-output-report/index.html) | Translates detekt's result container - [Detektion](../-detektion/index.html) - into an output report which is written inside a file.`abstract class OutputReport : `[`Extension`](./index.html) |
|
||||
|
||||
@@ -16,6 +16,7 @@ title: io.gitlab.arturbosch.detekt.api - detekt-api
|
||||
| [CompositeConfig](-composite-config/index.html) | Wraps two different configuration which should be considered when retrieving properties.`class CompositeConfig : `[`Config`](-config/index.html)`, `[`ValidatableConfiguration`](../io.gitlab.arturbosch.detekt.api.internal/-validatable-configuration/index.html) |
|
||||
| [Config](-config/index.html) | A configuration holds information about how to configure specific rules.`interface Config` |
|
||||
| [ConfigAware](-config-aware/index.html) | Interface which is implemented by each Rule class to provide utility functions to retrieve specific or generic properties from the underlying detekt configuration file.`interface ConfigAware : `[`Config`](-config/index.html) |
|
||||
| [ConfigValidator](-config-validator/index.html) | An extension which allows users to validate parts of the configuration.`interface ConfigValidator : `[`Extension`](-extension/index.html) |
|
||||
| [ConsoleReport](-console-report/index.html) | Extension point which describes how findings should be printed on the console.`abstract class ConsoleReport : `[`Extension`](-extension/index.html) |
|
||||
| [Context](-context/index.html) | A context describes the storing and reporting mechanism of [Finding](-finding/index.html)'s inside a [Rule](-rule/index.html). Additionally it handles suppression and aliases management.`interface Context` |
|
||||
| [CorrectableCodeSmell](-correctable-code-smell/index.html) | Represents a code smell for that can be auto corrected.`open class CorrectableCodeSmell : `[`CodeSmell`](-code-smell/index.html) |
|
||||
|
||||
Reference in New Issue
Block a user