Repository: jeremymailen/kotlinter-gradle
Pull request #270 (2022-07-10 18:58:34)

Make the git hook tasks compatible with configuration cache
by not acessing the project at execution time.
Run all tests with configuration cache enabled.


Patch:
diff --git a/.editorconfig b/.editorconfig
index 56390ee..f8d6220 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -2,3 +2,6 @@
 max_line_length = 140
 indent_size = 4
 insert_final_newline = true
+ij_kotlin_allow_trailing_comma = true
+ij_kotlin_allow_trailing_comma_on_call_site = true
+disabled_rules=filename
diff --git a/build.gradle.kts b/build.gradle.kts
index 7bf5372..8010542 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -2,11 +2,11 @@ import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
-    kotlin("jvm") version "1.6.20"
-    id("com.gradle.plugin-publish") version "0.18.0"
+    kotlin("jvm") version "1.7.10"
+    id("com.gradle.plugin-publish") version "0.21.0"
     `java-gradle-plugin`
     `maven-publish`
-    id("org.jmailen.kotlinter") version "3.10.0"
+    id("org.jmailen.kotlinter") version "3.11.1"
     idea
 }
 
@@ -27,13 +27,18 @@ description = projectDescription
 object Versions {
     const val androidTools = "7.2.1"
     const val junit = "4.13.2"
-    const val ktlint = "0.46.1"
+    const val ktlint = "0.47.1"
     const val mockitoKotlin = "4.0.0"
 }
 
 configurations {
     register("testRuntimeDependencies") {
         extendsFrom(compileOnly.get())
+        attributes {
+            // KGP publishes multiple variants https://kotlinlang.org/docs/whatsnew17.html#support-for-gradle-plugin-variants
+            attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage.JAVA_RUNTIME))
+            attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category.LIBRARY))
+        }
     }
     configureEach {
         resolutionStrategy.eachDependency {
diff --git a/index.html b/index.html
deleted file mode 100644
index e69de29..0000000
diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/KotlinterPlugin.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/KotlinterPlugin.kt
index 28eb3c2..36b1a23 100644
--- a/src/main/kotlin/org/jmailen/gradle/kotlinter/KotlinterPlugin.kt
+++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/KotlinterPlugin.kt
@@ -19,7 +19,7 @@ class KotlinterPlugin : Plugin<Project> {
     private val extendablePlugins = mapOf(
         "org.jetbrains.kotlin.jvm" to KotlinJvmSourceSetApplier,
         "org.jetbrains.kotlin.multiplatform" to KotlinMultiplatformSourceSetApplier,
-        "kotlin-android" to AndroidSourceSetApplier
+        "kotlin-android" to AndroidSourceSetApplier,
     )
 
     override fun apply(project: Project) = with(project) {
@@ -32,7 +32,6 @@ class KotlinterPlugin : Plugin<Project> {
         // for known kotlin plugins, register tasks by convention.
         extendablePlugins.forEach { (pluginId, sourceResolver) ->
             pluginManager.withPlugin(pluginId) {
-
                 val lintKotlin = registerParentLintTask()
                 val formatKotlin = registerParentFormatTask()
 
@@ -45,7 +44,7 @@ class KotlinterPlugin : Plugin<Project> {
                                 kotlinterExtension.reporters.associateWith { reporter ->
                                     reportFile("$id-lint.${reporterFileExtension(reporter)}")
                                 }
-                            }
+                            },
                         )
                         lintTask.experimentalRules.set(provider { kotlinterExtension.experimentalRules })
                         lintTask.disabledRules.set(provider { kotlinterExtension.disabledRules.toList() })
diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/EditorConfigUtils.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/EditorConfigUtils.kt
similarity index 61%
rename from src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/EditorConfigUtils.kt
rename to src/main/kotlin/org/jmailen/gradle/kotlinter/support/EditorConfigUtils.kt
index 343a9ea..f78e559 100644
--- a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/EditorConfigUtils.kt
+++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/EditorConfigUtils.kt
@@ -1,8 +1,20 @@
-package org.jmailen.gradle.kotlinter.tasks
+package org.jmailen.gradle.kotlinter.support
 
+import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties
+import com.pinterest.ktlint.core.api.EditorConfigOverride
 import org.gradle.api.file.ProjectLayout
 import java.io.File
 
+internal fun editorConfigOverride(ktLintParams: KtLintParams): EditorConfigOverride {
+    val rules = ktLintParams.disabledRules
+
+    return if (rules.isEmpty()) {
+        EditorConfigOverride.emptyEditorConfigOverride
+    } else {
+        EditorConfigOverride.from(DefaultEditorConfigProperties.ktlintDisabledRulesProperty to rules.joinToString(separator = ","))
+    }
+}
+
 internal fun ProjectLayout.findApplicableEditorConfigFiles(): Sequence<File> {
     val projectEditorConfig = projectDirectory.file(".editorconfig").asFile
 
diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/support/KotlinterError.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/KotlinterError.kt
index f8a508d..fcf9019 100644
--- a/src/main/kotlin/org/jmailen/gradle/kotlinter/support/KotlinterError.kt
+++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/KotlinterError.kt
@@ -4,5 +4,5 @@ import org.gradle.api.GradleException
 
 class KotlinterError(
     message: String,
-    error: Throwable
+    error: Throwable,
 ) : GradleException(message, error)
diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/support/KtLintEditoConfigOverrides.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/KtLintEditoConfigOverrides.kt
deleted file mode 100644
index a06dc3c..0000000
--- a/src/main/kotlin/org/jmailen/gradle/kotlinter/support/KtLintEditoConfigOverrides.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.jmailen.gradle.kotlinter.support
-
-import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties.disabledRulesProperty
-import com.pinterest.ktlint.core.api.EditorConfigOverride
-import com.pinterest.ktlint.core.api.EditorConfigOverride.Companion.emptyEditorConfigOverride
-
-internal fun editorConfigOverride(ktLintParams: KtLintParams): EditorConfigOverride {
-    val rules = ktLintParams.disabledRules
-
-    return if (rules.isEmpty()) {
-        emptyEditorConfigOverride
-    } else {
-        EditorConfigOverride.from(disabledRulesProperty to rules.joinToString(separator = ","))
-    }
-}
diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/support/KtLintParams.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/KtLintParams.kt
index 2e69104..2d0effd 100644
--- a/src/main/kotlin/org/jmailen/gradle/kotlinter/support/KtLintParams.kt
+++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/KtLintParams.kt
@@ -4,5 +4,5 @@ import java.io.Serializable
 
 data class KtLintParams(
     val experimentalRules: Boolean,
-    val disabledRules: List<String>
+    val disabledRules: List<String>,
 ) : Serializable
diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/support/LintFailure.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/LintFailure.kt
index 60034c1..5f91484 100644
--- a/src/main/kotlin/org/jmailen/gradle/kotlinter/support/LintFailure.kt
+++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/LintFailure.kt
@@ -3,5 +3,5 @@ package org.jmailen.gradle.kotlinter.support
 import org.gradle.api.GradleException
 
 class LintFailure(
-    message: String
+    message: String,
 ) : GradleException(message)
diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/support/Reporters.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/Reporters.kt
index cf92ddc..f3b834e 100644
--- a/src/main/kotlin/org/jmailen/gradle/kotlinter/support/Reporters.kt
+++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/Reporters.kt
@@ -9,13 +9,15 @@ import com.pinterest.ktlint.reporter.sarif.SarifReporter
 import java.io.File
 import java.io.PrintStream
 
-enum class ReporterType(val fileExtension: String) {
+/* ktlint-disable enum-entry-name-case */
+internal enum class ReporterType(val fileExtension: String) {
     checkstyle("xml"),
     html("html"),
     json("json"),
     plain("txt"),
-    sarif("sarif.json")
+    sarif("sarif.json"),
 }
+/* ktlint-enable enum-entry-name-case */
 
 fun reporterFor(reporterName: String, output: File): Reporter {
     val out = PrintStream(output)
@@ -26,7 +28,7 @@ fun reporterFor(reporterName: String, output: File): Reporter {
             ReporterType.json -> JsonReporter(out)
             ReporterType.plain -> PlainReporter(out)
             ReporterType.sarif -> SarifReporter(out)
-        }
+        },
     )
 }
 
diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/support/RuleSets.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/RuleSets.kt
index 46401c1..8308860 100644
--- a/src/main/kotlin/org/jmailen/gradle/kotlinter/support/RuleSets.kt
+++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/RuleSets.kt
@@ -1,29 +1,29 @@
 package org.jmailen.gradle.kotlinter.support
 
-import com.pinterest.ktlint.core.RuleSet
-import com.pinterest.ktlint.core.RuleSetProvider
+import com.pinterest.ktlint.core.RuleProvider
+import com.pinterest.ktlint.core.RuleSetProviderV2
 import com.pinterest.ktlint.ruleset.experimental.ExperimentalRuleSetProvider
 import java.util.ServiceLoader
-import kotlin.comparisons.compareBy
 
-fun resolveRuleSets(
-    providers: Iterable<RuleSetProvider>,
-    includeExperimentalRules: Boolean = false
-): List<RuleSet> {
-    return providers
-        .filter { includeExperimentalRules || it !is ExperimentalRuleSetProvider }
-        .map { it.get() }
-        .sortedWith(
-            compareBy {
-                when (it.id) {
-                    "standard" -> 0
-                    else -> 1
-                }
+internal fun resolveRuleProviders(
+    providers: Iterable<RuleSetProviderV2>,
+    includeExperimentalRules: Boolean = false,
+): Set<RuleProvider> = providers
+    .asSequence()
+    .filter { includeExperimentalRules || it !is ExperimentalRuleSetProvider }
+    .sortedWith(
+        compareBy {
+            when (it.id) {
+                "standard" -> 0
+                else -> 1
             }
-        )
-}
+        },
+    )
+    .map(RuleSetProviderV2::getRuleProviders)
+    .flatten()
+    .toSet()
 
 // statically resolve providers from plugin classpath. ServiceLoader#load alone resolves classes lazily which fails when run in parallel
 // https://github.com/jeremymailen/kotlinter-gradle/issues/101
-val defaultRuleSetProviders: List<RuleSetProvider> =
-    ServiceLoader.load(RuleSetProvider::class.java).toList()
+val defaultRuleSetProviders: List<RuleSetProviderV2> =
+    ServiceLoader.load(RuleSetProviderV2::class.java).toList()
diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/support/SortedThreadSafeReporterWrapper.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/SortedThreadSafeReporterWrapper.kt
index e9418d4..e5d5d26 100644
--- a/src/main/kotlin/org/jmailen/gradle/kotlinter/support/SortedThreadSafeReporterWrapper.kt
+++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/SortedThreadSafeReporterWrapper.kt
@@ -11,7 +11,7 @@ import java.util.concurrent.ConcurrentSkipListSet
  * As a downside, the calls to the wrapped reporter are delayed until the end of the execution.
  */
 class SortedThreadSafeReporterWrapper(
-    private val wrapped: Reporter
+    private val wrapped: Reporter,
 ) : Reporter {
 
     private val callsToBefore: ConcurrentMap<String, Unit> = ConcurrentHashMap()
@@ -59,7 +59,7 @@ class SortedThreadSafeReporterWrapper(
 
     private data class LintErrorReport(
         val lintError: LintError,
-        val corrected: Boolean
+        val corrected: Boolean,
     ) : Comparable<LintErrorReport> {
         override fun compareTo(other: LintErrorReport) = when (lintError.line == other.lintError.line) {
             true -> lintError.col.compareTo(other.lintError.col)
diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/ConfigurableKtLintTask.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/ConfigurableKtLintTask.kt
index fd918c2..716b19f 100644
--- a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/ConfigurableKtLintTask.kt
+++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/ConfigurableKtLintTask.kt
@@ -16,6 +16,7 @@ import org.gradle.internal.exceptions.MultiCauseException
 import org.jmailen.gradle.kotlinter.KotlinterExtension.Companion.DEFAULT_DISABLED_RULES
 import org.jmailen.gradle.kotlinter.KotlinterExtension.Companion.DEFAULT_EXPERIMENTAL_RULES
 import org.jmailen.gradle.kotlinter.support.KtLintParams
+import org.jmailen.gradle.kotlinter.support.findApplicableEditorConfigFiles
 
 abstract class ConfigurableKtLintTask(
     projectLayout: ProjectLayout,
@@ -37,7 +38,7 @@ abstract class ConfigurableKtLintTask(
     @Internal
     protected fun getKtLintParams(): KtLintParams = KtLintParams(
         experimentalRules = experimentalRules.get(),
-        disabledRules = disabledRules.get()
+        disabledRules = disabledRules.get(),
     )
 }
 
diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/GitHookTasks.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/GitHookTasks.kt
index d1d9fdb..d729321 100644
--- a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/GitHookTasks.kt
+++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/GitHookTasks.kt
@@ -8,7 +8,7 @@ import org.gradle.api.tasks.TaskAction
 import org.jmailen.gradle.kotlinter.support.VersionProperties
 import java.io.File
 
-open class InstallPreCommitHookTask : InstallHookTask("pre-commit") {
+abstract class InstallPreCommitHookTask : InstallHookTask("pre-commit") {
     override val hookContent =
         """
             if ! ${'$'}GRADLEW formatKotlin ; then
@@ -18,7 +18,7 @@ open class InstallPreCommitHookTask : InstallHookTask("pre-commit") {
         """.trimIndent()
 }
 
-open class InstallPrePushHookTask : InstallHookTask("pre-push") {
+abstract class InstallPrePushHookTask : InstallHookTask("pre-push") {
     override val hookContent =
         """
             if ! ${'$'}GRADLEW lintKotlin ; then
@@ -33,9 +33,13 @@ open class InstallPrePushHookTask : InstallHookTask("pre-push") {
  * Install or update a kotlinter-gradle hook.
  */
 abstract class InstallHookTask(@get:Internal val hookFileName: String) : DefaultTask() {
+
     @Input
     val gitDirPath: Property<String> = project.objects.property(default = ".git")
 
+    @Input
+    val rootProjectDir = project.objects.property(default = project.rootProject.rootDir)
+
     @get:Internal
     abstract val hookContent: String
 
@@ -64,7 +68,7 @@ abstract class InstallHookTask(@get:Internal val hookFileName: String) : Default
                 val newHookFileContent = hookFileContent.replaceRange(
                     startIndex,
                     endIndex,
-                    generateHook(gradleCommand, hookContent, includeEndHook = false)
+                    generateHook(gradleCommand, hookContent, includeEndHook = false),
                 )
                 hookFile.writeText(newHookFileContent)
             }
@@ -74,7 +78,7 @@ abstract class InstallHookTask(@get:Internal val hookFileName: String) : Default
     }
 
     private fun getHookFile(warn: Boolean = false): File? {
-        val gitDir = project.rootProject.file(gitDirPath.get())
+        val gitDir = File(rootProjectDir.get(), gitDirPath.get())
         if (!gitDir.isDirectory) {
             if (warn) logger.warn("skipping hook creation because $gitDir is not a directory")
             return null
@@ -97,7 +101,7 @@ abstract class InstallHookTask(@get:Internal val hookFileName: String) : Default
             "gradlew"
         }
 
-        val gradlew = File(project.rootDir, gradlewFilename)
+        val gradlew = File(rootProjectDir.get(), gradlewFilename)
         if (gradlew.exists() && gradlew.isFile && gradlew.canExecute()) {
             logger.info("Using gradlew wrapper at ${gradlew.invariantSeparatorsPath}")
             gradlew.invariantSeparatorsPath
@@ -128,7 +132,7 @@ abstract class InstallHookTask(@get:Internal val hookFileName: String) : Default
             gradlew: String,
             hookContent: String,
             addShebang: Boolean = false,
-            includeEndHook: Boolean = true
+            includeEndHook: Boolean = true,
         ): String = (if (addShebang) shebang else "") +
             """
                 |$startHook
diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/format/FormatWorkerAction.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/format/FormatWorkerAction.kt
index f41174a..cbde073 100644
--- a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/format/FormatWorkerAction.kt
+++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/format/FormatWorkerAction.kt
@@ -2,7 +2,7 @@ package org.jmailen.gradle.kotlinter.tasks.format
 
 import com.pinterest.ktlint.core.KtLint
 import com.pinterest.ktlint.core.LintError
-import com.pinterest.ktlint.core.RuleSet
+import com.pinterest.ktlint.core.RuleProvider
 import org.gradle.api.logging.LogLevel
 import org.gradle.api.logging.Logger
 import org.gradle.api.logging.Logging
@@ -12,7 +12,7 @@ import org.jmailen.gradle.kotlinter.support.KotlinterError
 import org.jmailen.gradle.kotlinter.support.KtLintParams
 import org.jmailen.gradle.kotlinter.support.defaultRuleSetProviders
 import org.jmailen.gradle.kotlinter.support.editorConfigOverride
-import org.jmailen.gradle.kotlinter.support.resolveRuleSets
+import org.jmailen.gradle.kotlinter.support.resolveRuleProviders
 import org.jmailen.gradle.kotlinter.tasks.FormatTask
 import java.io.File
 
@@ -28,7 +28,7 @@ abstract class FormatWorkerAction : WorkAction<FormatWorkerParameters> {
         val fixes = mutableListOf<String>()
         try {
             files.forEach { file ->
-                val ruleSets = resolveRuleSets(defaultRuleSetProviders, ktLintParams.experimentalRules)
+                val ruleSets = resolveRuleProviders(defaultRuleSetProviders, ktLintParams.experimentalRules)
                 val sourceText = file.readText()
                 val relativePath = file.toRelativeString(projectDirectory)
 
@@ -64,28 +64,28 @@ abstract class FormatWorkerAction : WorkAction<FormatWorkerParameters> {
             when (fixes.isEmpty()) {
                 true -> "ok"
                 false -> fixes.joinToString("\n")
-            }
+            },
         )
     }
 
-    private fun formatKt(file: File, ruleSets: List<RuleSet>, onError: ErrorHandler) =
+    private fun formatKt(file: File, ruleSets: Set<RuleProvider>, onError: ErrorHandler) =
         format(file, ruleSets, onError, false)
 
-    private fun formatKts(file: File, ruleSets: List<RuleSet>, onError: ErrorHandler) =
+    private fun formatKts(file: File, ruleSets: Set<RuleProvider>, onError: ErrorHandler) =
         format(file, ruleSets, onError, true)
 
-    private fun format(file: File, ruleSets: List<RuleSet>, onError: ErrorHandler, script: Boolean): String {
+    private fun format(file: File, ruleProviders: Set<RuleProvider>, onError: ErrorHandler, script: Boolean): String {
         return KtLint.format(
             KtLint.ExperimentalParams(
                 fileName = file.path,
                 text = file.readText(),
-                ruleSets = ruleSets,
+                ruleProviders = ruleProviders,
                 script = script,
                 editorConfigOverride = editorConfigOverride(ktLintParams),
                 cb = { error, corrected ->
                     onError(error, corrected)
-                }
-            )
+                },
+            ),
         )
     }
 }
diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/lint/LintWorkerAction.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/lint/LintWorkerAction.kt
index 1b53527..9cd2ce0 100644
--- a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/lint/LintWorkerAction.kt
+++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/lint/LintWorkerAction.kt
@@ -3,7 +3,7 @@ package org.jmailen.gradle.kotlinter.tasks.lint
 import com.pinterest.ktlint.core.KtLint
 import com.pinterest.ktlint.core.LintError
 import com.pinterest.ktlint.core.Reporter
-import com.pinterest.ktlint.core.RuleSet
+import com.pinterest.ktlint.core.RuleProvider
 import org.gradle.api.logging.Logger
 import org.gradle.api.logging.Logging
 import org.gradle.internal.logging.slf4j.DefaultContextAwareTaskLogger
@@ -15,7 +15,7 @@ import org.jmailen.gradle.kotlinter.support.defaultRuleSetProviders
 import org.jmailen.gradle.kotlinter.support.editorConfigOverride
 import org.jmailen.gradle.kotlinter.support.reporterFor
 import org.jmailen.gradle.kotlinter.support.reporterPathFor
-import org.jmailen.gradle.kotlinter.support.resolveRuleSets
+import org.jmailen.gradle.kotlinter.support.resolveRuleProviders
 import org.jmailen.gradle.kotlinter.tasks.LintTask
 import java.io.File
 
@@ -35,7 +35,7 @@ abstract class LintWorkerAction : WorkAction<LintWorkerParameters> {
         try {
             reporters.onEach { it.beforeAll() }
             files.forEach { file ->
-                val ruleSets = resolveRuleSets(defaultRuleSetProviders, ktLintParams.experimentalRules)
+                val ruleSets = resolveRuleProviders(defaultRuleSetProviders, ktLintParams.experimentalRules)
                 val relativePath = file.toRelativeString(projectDirectory)
                 reporters.onEach { it.before(relativePath) }
                 logger.debug("$name linting: $relativePath")
@@ -68,22 +68,22 @@ abstract class LintWorkerAction : WorkAction<LintWorkerParameters> {
         }
     }
 
-    private fun lintKt(file: File, ruleSets: List<RuleSet>, onError: (error: LintError) -> Unit) =
+    private fun lintKt(file: File, ruleSets: Set<RuleProvider>, onError: (error: LintError) -> Unit) =
         lint(file, ruleSets, onError, false)
 
-    private fun lintKts(file: File, ruleSets: List<RuleSet>, onError: (error: LintError) -> Unit) =
+    private fun lintKts(file: File, ruleSets: Set<RuleProvider>, onError: (error: LintError) -> Unit) =
         lint(file, ruleSets, onError, true)
 
-    private fun lint(file: File, ruleSets: List<RuleSet>, onError: ErrorHandler, script: Boolean) =
+    private fun lint(file: File, ruleProviders: Set<RuleProvider>, onError: ErrorHandler, script: Boolean) =
         KtLint.lint(
             KtLint.ExperimentalParams(
                 fileName = file.path,
                 text = file.readText(),
-                ruleSets = ruleSets,
+                ruleProviders = ruleProviders,
                 script = script,
                 editorConfigOverride = editorConfigOverride(ktLintParams),
-                cb = { error, _ -> onError(error) }
-            )
+                cb = { error, _ -> onError(error) },
+            ),
         )
 }
 


Test patch:
diff --git a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/EditorConfigTest.kt b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/EditorConfigTest.kt
index cb9eb10..1215d97 100644
--- a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/EditorConfigTest.kt
+++ b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/EditorConfigTest.kt
@@ -42,7 +42,7 @@ internal class EditorConfigTest : WithGradleTest.Kotlin() {
                     |    val text: String
                     |}
                     |
-                """.trimMargin()
+                """.trimMargin(),
             )
         }
 
@@ -58,7 +58,7 @@ internal class EditorConfigTest : WithGradleTest.Kotlin() {
                 """
                     [*.{kt,kts}]
                     disabled_rules=filename
-                """.trimIndent()
+                """.trimIndent(),
             )
         }
         projectRoot.resolve("src/main/kotlin/FileName.kt") {
@@ -77,7 +77,7 @@ internal class EditorConfigTest : WithGradleTest.Kotlin() {
                 """
                     [*.{kt,kts}]
                     indent_size = 6
-                """.trimIndent()
+                """.trimIndent(),
             )
         }
         projectRoot.resolve("src/main/kotlin/FileName.kt") {
diff --git a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/InstallHookTaskTest.kt b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/InstallHookTaskTest.kt
index 2902d60..ebbc580 100644
--- a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/InstallHookTaskTest.kt
+++ b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/InstallHookTaskTest.kt
@@ -14,7 +14,7 @@ import java.io.File
 
 abstract class InstallHookTaskTest(
     private val taskName: String,
-    private val hookFile: String
+    private val hookFile: String,
 ) : WithGradleTest.Kotlin() {
     private lateinit var projectRoot: File
 
@@ -29,7 +29,7 @@ abstract class InstallHookTaskTest(
                             id("kotlin")
                             id("org.jmailen.kotlinter")
                         }
-                    """.trimIndent()
+                    """.trimIndent(),
                 )
             }
 
@@ -86,7 +86,7 @@ abstract class InstallHookTaskTest(
                 ${InstallHookTask.startHook}
                 $placeholder
                 ${InstallHookTask.endHook}
-                """.trimIndent()
+                """.trimIndent(),
             )
         }
 
diff --git a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/KotlinProjectTest.kt b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/KotlinProjectTest.kt
index 5f0f77e..9b606d6 100644
--- a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/KotlinProjectTest.kt
+++ b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/KotlinProjectTest.kt
@@ -41,7 +41,7 @@ internal class KotlinProjectTest : WithGradleTest.Kotlin() {
                 }
             }
 
-            """.trimIndent()
+            """.trimIndent(),
         )
 
         buildAndFail("lintKotlinMain").apply {
@@ -68,7 +68,7 @@ internal class KotlinProjectTest : WithGradleTest.Kotlin() {
                 }
             }
 
-            """.trimIndent()
+            """.trimIndent(),
         )
 
         build("lintKotlinMain").apply {
@@ -111,7 +111,7 @@ internal class KotlinProjectTest : WithGradleTest.Kotlin() {
             """
             object CustomObject
             
-            """.trimIndent()
+            """.trimIndent(),
         )
 
         build("check").apply {
@@ -129,7 +129,7 @@ internal class KotlinProjectTest : WithGradleTest.Kotlin() {
             """
             object CustomObject
             
-            """.trimIndent()
+            """.trimIndent(),
         )
 
         build("lintKotlin").apply {
@@ -164,7 +164,7 @@ internal class KotlinProjectTest : WithGradleTest.Kotlin() {
             """
             object CustomObject
             
-            """.trimIndent()
+            """.trimIndent(),
         )
 
         build("lintKotlin", "--configuration-cache").apply {
diff --git a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/WithGradleTest.kt b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/WithGradleTest.kt
index 5d9449c..f38c829 100644
--- a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/WithGradleTest.kt
+++ b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/WithGradleTest.kt
@@ -33,7 +33,7 @@ abstract class WithGradleTest {
             val androidDependencies = listOf(
                 ".*/com\\.android\\..*/.*".toRegex(),
                 ".*/androidx\\..*/.*".toRegex(),
-                ".*/com\\.google\\..*/.*".toRegex()
+                ".*/com\\.google\\..*/.*".toRegex(),
             )
             val noAndroid = classpath.filterNot { dependency -> androidDependencies.any { it.matches(dependency.path) } }
 
@@ -46,5 +46,5 @@ abstract class WithGradleTest {
 private fun WithGradleTest.defaultRunner(vararg args: String) =
     GradleRunner.create()
         .withProjectDir(testProjectDir.root)
-        .withArguments(args.toList() + "--stacktrace")
+        .withArguments(args.toList() + listOf("--stacktrace", "--configuration-cache"))
         .forwardOutput()
diff --git a/src/test/kotlin/org/jmailen/gradle/kotlinter/support/ReportersTest.kt b/src/test/kotlin/org/jmailen/gradle/kotlinter/support/ReportersTest.kt
index 9aea0e1..9c84457 100644
--- a/src/test/kotlin/org/jmailen/gradle/kotlinter/support/ReportersTest.kt
+++ b/src/test/kotlin/org/jmailen/gradle/kotlinter/support/ReportersTest.kt
@@ -30,22 +30,22 @@ class ReportersTest {
 
         assertEquals(
             absolutePath,
-            reporterPathFor(reporterFor("sarif", reportFile), output, projectDir)
+            reporterPathFor(reporterFor("sarif", reportFile), output, projectDir),
         )
 
         assertEquals(
             relativePath,
-            reporterPathFor(reporterFor("checkstyle", reportFile), output, projectDir)
+            reporterPathFor(reporterFor("checkstyle", reportFile), output, projectDir),
         )
 
         assertEquals(
             relativePath,
-            reporterPathFor(reporterFor("json", reportFile), output, projectDir)
+            reporterPathFor(reporterFor("json", reportFile), output, projectDir),
         )
 
         assertEquals(
             relativePath,
-            reporterPathFor(reporterFor("html", reportFile), output, projectDir)
+            reporterPathFor(reporterFor("html", reportFile), output, projectDir),
         )
     }
 }
diff --git a/src/test/kotlin/org/jmailen/gradle/kotlinter/support/RuleSetsTest.kt b/src/test/kotlin/org/jmailen/gradle/kotlinter/support/RuleSetsTest.kt
index 5da20d7..d6f59bd 100644
--- a/src/test/kotlin/org/jmailen/gradle/kotlinter/support/RuleSetsTest.kt
+++ b/src/test/kotlin/org/jmailen/gradle/kotlinter/support/RuleSetsTest.kt
@@ -2,9 +2,8 @@ package org.jmailen.gradle.kotlinter.support
 
 import com.pinterest.ktlint.core.KtLint
 import com.pinterest.ktlint.core.Rule
-import com.pinterest.ktlint.core.RuleSet
-import com.pinterest.ktlint.core.RuleSetProvider
-import org.jetbrains.kotlin.com.intellij.lang.ASTNode
+import com.pinterest.ktlint.core.RuleProvider
+import com.pinterest.ktlint.core.RuleSetProviderV2
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
 import org.junit.Test
@@ -13,29 +12,24 @@ class RuleSetsTest {
 
     @Test
     fun `resolveRuleSets loads from classpath providers`() {
-        val result = resolveRuleSets(defaultRuleSetProviders)
+        val standardOnly = resolveRuleProviders(defaultRuleSetProviders, includeExperimentalRules = false)
+        val withExperimentalRules = resolveRuleProviders(defaultRuleSetProviders, includeExperimentalRules = true)
 
-        assertEquals(listOf("standard"), result.map { it.id })
-    }
-
-    @Test
-    fun `resolveRuleSets loads from classpath providers including experimental rules`() {
-        val result = resolveRuleSets(defaultRuleSetProviders, true)
-
-        assertEquals(listOf("standard", "experimental"), result.map { it.id })
+        assertTrue(standardOnly.isNotEmpty())
+        assertTrue(standardOnly.size < withExperimentalRules.size)
     }
 
     @Test
     fun `resolveRuleSets puts standard rules first`() {
-        val standard = TestRuleSetProvider(RuleSet("standard", TestRule("one")))
-        val extra1 = TestRuleSetProvider(RuleSet("extra-one", TestRule("two")))
-        val extra2 = TestRuleSetProvider(RuleSet("extra-two", TestRule("three")))
+        val standard = TestRuleSetProvider("standard", setOf(TestRule("one")))
+        val extra1 = TestRuleSetProvider("extra-one", setOf(TestRule("two")))
+        val extra2 = TestRuleSetProvider("extra-two", setOf(TestRule("three")))
 
-        val result = resolveRuleSets(providers = listOf(extra2, standard, extra1))
+        val result = resolveRuleProviders(providers = listOf(extra2, standard, extra1)).map { it.createNewRuleInstance() }
 
         assertEquals(3, result.size)
-        assertEquals(standard.ruleSet, result.first())
-        assertTrue(result.containsAll(listOf(extra1.ruleSet, extra2.ruleSet)))
+        assertEquals(standard.ruleSet.single(), result.first())
+        assertTrue(result.containsAll(listOf(extra1.ruleSet.single(), extra2.ruleSet.single())))
     }
 
     @Test
@@ -53,21 +47,24 @@ class RuleSetsTest {
                     }
 
                 """.trimIndent(),
-                ruleSets = resolveRuleSets(defaultRuleSetProviders),
-                cb = { _, _ -> }
-            )
+                ruleProviders = resolveRuleProviders(defaultRuleSetProviders),
+                cb = { _, _ -> },
+            ),
         )
     }
 }
 
-class TestRuleSetProvider(val ruleSet: RuleSet) : RuleSetProvider {
-    override fun get() = ruleSet
+class TestRuleSetProvider(id: String, val ruleSet: Set<Rule>) : RuleSetProviderV2(
+    id = id,
+    about = About(
+        maintainer = "stub-maintainer",
+        description = "stub-description",
+        license = "stub-license",
+        repositoryUrl = "stub-repositoryUrl",
+        issueTrackerUrl = "stub-issueTrackerUrl",
+    ),
+) {
+    override fun getRuleProviders() = ruleSet.map { rule -> RuleProvider(provider = { rule }) }.toSet()
 }
 
-class TestRule(id: String) : Rule(id) {
-    override fun visit(
-        node: ASTNode,
-        autoCorrect: Boolean,
-        emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit
-    ) = Unit
-}
+class TestRule(id: String) : Rule(id)
diff --git a/test-project-android/build.gradle.kts b/test-project-android/build.gradle.kts
index 98fec1a..2c78bc0 100644
--- a/test-project-android/build.gradle.kts
+++ b/test-project-android/build.gradle.kts
@@ -1,5 +1,5 @@
 plugins {
-    kotlin("android") version "1.6.10"
+    kotlin("android") version "1.7.10"
     id("com.android.library")
     id("org.jmailen.kotlinter")
 }
diff --git a/test-project/build.gradle.kts b/test-project/build.gradle.kts
index cd7e673..afb5c45 100644
--- a/test-project/build.gradle.kts
+++ b/test-project/build.gradle.kts
@@ -1,4 +1,4 @@
 plugins {
-    kotlin("jvm") version "1.6.10"
+    kotlin("jvm") version "1.7.10"
     id("org.jmailen.kotlinter")
 }