I’m writing a convention plugin which applies ktlint (using kotlinter) but I want all projects to share the same config. This plugin reads the config from the .editorconfig file, but I obviously don’t want each project to have to define it itself. How can I publish the .editorconfig file along with the convention plugin?
I’ve solved it by defining a task which should always run before linting and which writes to .editorconfig:
tasks.register("setUpEditorconfig") {
tasks.findByName("lintKotlin").dependsOn(this)
doLast {
rootDir.resolve(".editorconfig").writeText("""
# your config here
""".trimIndent())
}
}
But I wanted to keep the .editorconfig contents in a separate file rather than a string and because I couldn’t get getResource to work from a precompiled script plugin, I’ve extracted it to a separate kotlin file, which I’ve placed next to the script plugin:
abstract class SetUpEditorconfigTask : DefaultTask() {
@TaskAction
fun setUpEditorconfig() {
val editorconfigContents = javaClass.getResource("/.editorconfig").readText()
project.rootDir.resolve(".editorconfig").writeText(editorconfigContents)
}
}
Yeah, that is a possibility of course.
Some quick notes:
getResource should also work fine from precompiled script plugin, but hard to tell what the problem might be without MCVE
if you ever hope to use configuration cache, you must not access project at execution time
you register your setup task and its configuration make lintKotlin depend on it. The way you do it means, that if your setup task is configured, you break task-configuration avoidance of lintKotlin and realize it even if it is not going to be executed. And at the same time, just executing lintKotlin will not automatically trigger your setup task, unless you broke task-configuration avoidance of the setup task too, because if normally the setup task is only configured when necessary and as the dependency is only set during its configuration it is not know that it will be necessary. So better move the dependency declaration out the configuration of the setup task and instead make the lintKotlin task depend on the return value of register. And to not break the configuration avoidance of lintKotlin use named and then notget(). Aaaand finally any explicit dependsOn that does not have a lifecycle task on the left-hand side is a code smell. Better configure the output file of your setup task properly and then mark the setup task as input file of the lint task. This will then make the necessary task dependency implicit and also properly define the input for the lint task.
For your second point, I guess that until the plugin I’m using allows for a different way of configuration then there’s no way around that.
And for your last point, in my project I was actually setting the dependency outside of the task configuration but I wanted to keep the example simple and didn’t realize it was wrong! I’ll update my previous comment at a later time.
You can use ktlint-gradle instead which allows editorConfig properties to be set on the ktlint extension directly without dealing with the main .editorconfig file at all. Note you must set the plugin to use ktlint >= 0.50 for this to work properly
I should add, while kind of defeats the purpose of .editorconfig, it is useful for controlling ktkint-specific properties such disabling certain rules, etc. It is also useful when you have .editorconfig in nonstandard places ktlint can’t find
For your second point, I guess that until the plugin I’m using allows for a different way of configuration then there’s no way around that.
Why?
I said you must not access project at execution time, not you must not access it at all.
Just access it only at configuration time to get the root project directory and save that value in a property.