From 47fb4003cf601f9c415cbde89423a3124305332c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Kautler?= Date: Thu, 19 Sep 2024 22:44:10 +0200 Subject: [PATCH] Automatically format generated files using ktlint if possible --- gradle-plugin/gradle.properties | 2 + .../karakum-gradle-plugin/build.gradle.kts | 4 ++ .../karakum/gradle/plugin/KarakumPlugin.kt | 11 ++++- .../gradle/plugin/service/KtLintService.kt | 18 ++++++++ .../gradle/plugin/tasks/KarakumGenerate.kt | 35 ++++++++++++++++ .../plugin/worker/KtLintFormatWorker.kt | 41 +++++++++++++++++++ gradle-plugin/settings.gradle.kts | 6 +++ 7 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 gradle-plugin/karakum-gradle-plugin/src/main/kotlin/io/github/sgrishchenko/karakum/gradle/plugin/service/KtLintService.kt create mode 100644 gradle-plugin/karakum-gradle-plugin/src/main/kotlin/io/github/sgrishchenko/karakum/gradle/plugin/worker/KtLintFormatWorker.kt diff --git a/gradle-plugin/gradle.properties b/gradle-plugin/gradle.properties index 66abf73..0143fd9 100644 --- a/gradle-plugin/gradle.properties +++ b/gradle-plugin/gradle.properties @@ -5,5 +5,7 @@ kotlinVersion = 2.0.0 publishVersion = 1.2.1 jacksonVersion = 2.17.2 +ktlintVersion = 1.3.1 +ec4jVersion = 1.0.0 typescriptVersion = ^5.5.3 diff --git a/gradle-plugin/karakum-gradle-plugin/build.gradle.kts b/gradle-plugin/karakum-gradle-plugin/build.gradle.kts index 5f84f56..5d382c5 100644 --- a/gradle-plugin/karakum-gradle-plugin/build.gradle.kts +++ b/gradle-plugin/karakum-gradle-plugin/build.gradle.kts @@ -12,6 +12,10 @@ dependencies { compileOnly(kotlin("gradle-plugin")) implementation(libs.jackson.databind) + implementation(libs.ktlint.ruleEngine) + implementation(libs.ktlint.ruleEngine.core) + implementation(libs.ktlint.ruleset.standard) + implementation(libs.ec4j) testImplementation(kotlin("test")) } diff --git a/gradle-plugin/karakum-gradle-plugin/src/main/kotlin/io/github/sgrishchenko/karakum/gradle/plugin/KarakumPlugin.kt b/gradle-plugin/karakum-gradle-plugin/src/main/kotlin/io/github/sgrishchenko/karakum/gradle/plugin/KarakumPlugin.kt index b7a4101..2aa90f2 100644 --- a/gradle-plugin/karakum-gradle-plugin/src/main/kotlin/io/github/sgrishchenko/karakum/gradle/plugin/KarakumPlugin.kt +++ b/gradle-plugin/karakum-gradle-plugin/src/main/kotlin/io/github/sgrishchenko/karakum/gradle/plugin/KarakumPlugin.kt @@ -1,18 +1,25 @@ package io.github.sgrishchenko.karakum.gradle.plugin +import io.github.sgrishchenko.karakum.gradle.plugin.service.KtLintService import io.github.sgrishchenko.karakum.gradle.plugin.tasks.KarakumConfig import io.github.sgrishchenko.karakum.gradle.plugin.tasks.KarakumGenerate import io.github.sgrishchenko.karakum.gradle.plugin.tasks.KarakumSync import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.file.FileTree +import org.gradle.api.services.BuildServiceRegistry import org.gradle.kotlin.dsl.create import org.gradle.kotlin.dsl.getValue import org.gradle.kotlin.dsl.provideDelegate +import org.gradle.kotlin.dsl.registerIfAbsent import org.gradle.kotlin.dsl.registering import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject +import javax.inject.Inject + +abstract class KarakumPlugin : Plugin { + @get:Inject + abstract val sharedServices: BuildServiceRegistry -class KarakumPlugin : Plugin { override fun apply(project: Project): Unit = with(project) { plugins.withId("org.jetbrains.kotlin.multiplatform") { val karakum = extensions.create("karakum").apply { @@ -51,6 +58,8 @@ class KarakumPlugin : Plugin { destinationDirectory.convention(layout.dir(npmProjectDirectory).map { it.dir("karakum") }) } + sharedServices.registerIfAbsent("ktlintService", KtLintService::class) + val generateKarakumExternals by tasks.registering(KarakumGenerate::class) { group = KARAKUM_GRADLE_PLUGIN_GROUP description = "Generates the Kotlin external declarations using Karakum." diff --git a/gradle-plugin/karakum-gradle-plugin/src/main/kotlin/io/github/sgrishchenko/karakum/gradle/plugin/service/KtLintService.kt b/gradle-plugin/karakum-gradle-plugin/src/main/kotlin/io/github/sgrishchenko/karakum/gradle/plugin/service/KtLintService.kt new file mode 100644 index 0000000..367e9ba --- /dev/null +++ b/gradle-plugin/karakum-gradle-plugin/src/main/kotlin/io/github/sgrishchenko/karakum/gradle/plugin/service/KtLintService.kt @@ -0,0 +1,18 @@ +package io.github.sgrishchenko.karakum.gradle.plugin.service + +import com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine +import com.pinterest.ktlint.ruleset.standard.StandardRuleSetProvider +import org.gradle.api.services.BuildService +import org.gradle.api.services.BuildServiceParameters.None + +abstract class KtLintService : BuildService { + val ktlint = KtLintRuleEngine( + ruleProviders = StandardRuleSetProvider().getRuleProviders() + ).apply { + // ktlint stores .editorconfig files in a companion object and thus as static state + // this is not good when running multiple builds within the same Gradle daemon + // and the .editorconfig file might have changed, so clear that cache + // so that .editorconfig files are read freshly + trimMemory() + } +} diff --git a/gradle-plugin/karakum-gradle-plugin/src/main/kotlin/io/github/sgrishchenko/karakum/gradle/plugin/tasks/KarakumGenerate.kt b/gradle-plugin/karakum-gradle-plugin/src/main/kotlin/io/github/sgrishchenko/karakum/gradle/plugin/tasks/KarakumGenerate.kt index 0eec2a6..e856176 100644 --- a/gradle-plugin/karakum-gradle-plugin/src/main/kotlin/io/github/sgrishchenko/karakum/gradle/plugin/tasks/KarakumGenerate.kt +++ b/gradle-plugin/karakum-gradle-plugin/src/main/kotlin/io/github/sgrishchenko/karakum/gradle/plugin/tasks/KarakumGenerate.kt @@ -5,17 +5,27 @@ import com.fasterxml.jackson.databind.node.ArrayNode import com.fasterxml.jackson.databind.node.ObjectNode import io.github.sgrishchenko.karakum.gradle.plugin.karakumDependency import io.github.sgrishchenko.karakum.gradle.plugin.kotlinJsCompilation +import io.github.sgrishchenko.karakum.gradle.plugin.service.KtLintService import io.github.sgrishchenko.karakum.gradle.plugin.typescriptDependency +import io.github.sgrishchenko.karakum.gradle.plugin.worker.KtLintFormatWorker +import org.ec4j.core.Resource.Resources +import org.ec4j.core.ResourcePropertiesService import org.gradle.api.DefaultTask import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.ProjectLayout import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.api.services.ServiceReference import org.gradle.api.tasks.* +import org.gradle.kotlin.dsl.submit import org.gradle.process.ExecOperations +import org.gradle.workers.WorkerExecutor import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrCompilation import org.jetbrains.kotlin.gradle.targets.js.npm.RequiresNpmDependencies import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject +import java.nio.file.Path import javax.inject.Inject +import kotlin.text.Charsets.UTF_8 abstract class KarakumGenerate : DefaultTask(), RequiresNpmDependencies { @get:Inject @@ -24,6 +34,12 @@ abstract class KarakumGenerate : DefaultTask(), RequiresNpmDependencies { @get:Inject abstract val layout: ProjectLayout + @get:Inject + abstract val workerExecutor: WorkerExecutor + + @get:ServiceReference + abstract val ktlintService: Property + @get:InputFile abstract val configFile: RegularFileProperty @@ -71,6 +87,18 @@ abstract class KarakumGenerate : DefaultTask(), RequiresNpmDependencies { } } + @get:InputFiles + @get:Optional + val editorConfigFiles = destinationDirectory.map { destinationDirectory -> + ResourcePropertiesService + .builder() + .build() + .queryProperties(Resources.ofPath(destinationDirectory.asFile.absoluteFile.toPath(), UTF_8)) + .editorConfigFiles + .map { it.getAdapter(Path::class.java) } + .let { layout.files(it) } + } + @TaskAction fun generate() { exec.exec { @@ -80,5 +108,12 @@ abstract class KarakumGenerate : DefaultTask(), RequiresNpmDependencies { args = listOf("--config", configFile.get().asFile.absolutePath) ) } + + logger.lifecycle("Formatting generated files") + destinationDirectory.get().asFileTree.forEach { file -> + workerExecutor.noIsolation().submit(KtLintFormatWorker::class) { + this.file.set(file) + } + } } } diff --git a/gradle-plugin/karakum-gradle-plugin/src/main/kotlin/io/github/sgrishchenko/karakum/gradle/plugin/worker/KtLintFormatWorker.kt b/gradle-plugin/karakum-gradle-plugin/src/main/kotlin/io/github/sgrishchenko/karakum/gradle/plugin/worker/KtLintFormatWorker.kt new file mode 100644 index 0000000..8ce7389 --- /dev/null +++ b/gradle-plugin/karakum-gradle-plugin/src/main/kotlin/io/github/sgrishchenko/karakum/gradle/plugin/worker/KtLintFormatWorker.kt @@ -0,0 +1,41 @@ +package io.github.sgrishchenko.karakum.gradle.plugin.worker + +import com.pinterest.ktlint.rule.engine.api.Code +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision.ALLOW_AUTOCORRECT +import com.pinterest.ktlint.rule.engine.core.api.AutocorrectDecision.NO_AUTOCORRECT +import io.github.sgrishchenko.karakum.gradle.plugin.service.KtLintService +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.logging.Logging +import org.gradle.api.provider.Property +import org.gradle.api.services.ServiceReference +import org.gradle.workers.WorkAction +import org.gradle.workers.WorkParameters + +abstract class KtLintFormatWorker : WorkAction { + private val logger = Logging.getLogger(KtLintFormatWorker::class.java) + + @get:ServiceReference + abstract val ktlintService: Property + + override fun execute() { + val file = parameters.file.get().asFile + logger.info("Formatting ${file.absolutePath}") + runCatching { + ktlintService + .get() + .ktlint + .format(Code.fromFile(file)) { lintError -> + if (lintError.canBeAutoCorrected) ALLOW_AUTOCORRECT else NO_AUTOCORRECT + }.also(file::writeText) + }.onFailure { + logger.info("Formatting failed for ${file.absolutePath}") + logger.info(it.stackTraceToString()) + }.onSuccess { + logger.info("Formatting successful for ${file.absolutePath}") + } + } + + interface Parameters : WorkParameters { + val file: RegularFileProperty + } +} diff --git a/gradle-plugin/settings.gradle.kts b/gradle-plugin/settings.gradle.kts index f18d2c9..f7f9d2f 100644 --- a/gradle-plugin/settings.gradle.kts +++ b/gradle-plugin/settings.gradle.kts @@ -15,6 +15,12 @@ dependencyResolutionManagement { val libs by creating { val jacksonVersion: String by settings library("jackson-databind", "com.fasterxml.jackson.core", "jackson-databind").version(jacksonVersion) + val ktlintVersion: String by settings + library("ktlint-ruleEngine", "com.pinterest.ktlint", "ktlint-rule-engine").version(ktlintVersion) + library("ktlint-ruleEngine-core", "com.pinterest.ktlint", "ktlint-rule-engine-core").version(ktlintVersion) + library("ktlint-ruleset-standard", "com.pinterest.ktlint", "ktlint-ruleset-standard").version(ktlintVersion) + val ec4jVersion: String by settings + library("ec4j", "org.ec4j.core", "ec4j-core").version(ec4jVersion) } } }