Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Override the Kotlin Version on Gradle Kotlin DSL #6198

Open
nico-arianto opened this issue Jun 9, 2023 · 55 comments
Open

Override the Kotlin Version on Gradle Kotlin DSL #6198

nico-arianto opened this issue Jun 9, 2023 · 55 comments
Labels
discussion gradle-plugin never gets stale Mark Issues/PRs that should not be closed as they won't get stale support

Comments

@nico-arianto
Copy link

Expected Behavior

Code:

plugins {
    val kotlinVersion = "1.8.22"
    kotlin("jvm") version kotlinVersion
    kotlin("kapt") version kotlinVersion
    kotlin("plugin.spring") version kotlinVersion
    kotlin("plugin.jpa") version kotlinVersion
    id("io.gitlab.arturbosch.detekt") version "1.23.0"
}

detekt {
    kotlinVersion = "1.8.21"
    source.from("src/main")
    buildUponDefaultConfig = true
    allRules = true
}

This line kotlinVersion = "1.8.21" will override the Kotlin version and being used by detekt

Note: Or other alternative approach also can, as long can overcome the Kotlin compatibility issue.

Current Behavior

Code:

plugins {
    val kotlinVersion = "1.8.22"
    kotlin("jvm") version kotlinVersion
    kotlin("kapt") version kotlinVersion
    kotlin("plugin.spring") version kotlinVersion
    kotlin("plugin.jpa") version kotlinVersion
    id("io.gitlab.arturbosch.detekt") version "1.23.0"
}

detekt {
    source.from("src/main")
    buildUponDefaultConfig = true
    allRules = true
}

Gradle Task:

> Task :detekt FAILED
Time elapsed [detekt] : 0s
///// Time Consumption Summary /////
[dependencyCheckAnalyze          ] : 4s
[compileJava                     ] : 1s
[assemble                        ] : 0s
[bootJar                         ] : 0s
[classes                         ] : 0s
[compileKotlin                   ] : 0s
[detekt                          ] : 0s
[jar                             ] : 0s
[kaptGenerateStubsKotlin         ] : 0s
[kaptKotlin                      ] : 0s
[processResources                ] : 0s
[resolveMainClassName            ] : 0s

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':detekt'.
> detekt was compiled with Kotlin 1.8.21 but is currently running with 1.8.22.
  This is not supported. See https://detekt.dev/docs/gettingstarted/gradle#dependencies for more information

Context

detekt didn't locked us to use the latest Kotlin version for other Gradle tasks

@3flex
Copy link
Member

3flex commented Jun 9, 2023

Did you check the link shown in the log for details on how to fix this?

Adding a version parameter here won't allow us to override Gradle's dependency resolution rules so I don't see how this will solve the issue.

@nico-arianto
Copy link
Author

The solution in https://detekt.dev/docs/gettingstarted/gradle#dependencies, it will cause the dependencies version that we declared in

plugins {
    val kotlinVersion = "1.8.22"
    kotlin("jvm") version kotlinVersion
    ...
}

to be override to 1.8.21, which is the same as

plugins {
    val kotlinVersion = "1.8.21"
    kotlin("jvm") version kotlinVersion
    ...
}

I'm wondering if possible for only detekt task to be able to override the dependencies to specific Kotlin version, but other tasks still using the latest Kotlin version

Or maybe the detekt plugin is using a shaded jar to avoid the Kotlin compatibility issue, if possible.

@3flex
Copy link
Member

3flex commented Jun 10, 2023

Can you please share a build scan, a reproducer, or a full copy of the build file? There's most likely an issue with your Gradle config and it will be simpler to explain with more info.

@nico-arianto
Copy link
Author

here is the build scan: https://gradle.com/s/sgxok4toctxju

@alwyn
Copy link

alwyn commented Jun 10, 2023

@nico-arianto I don't know if this is the best way to do this, but I think this works in your project build:

configurations.matching { it.name == "detekt" }.all {
    resolutionStrategy.eachDependency {
        if (requested.group == "org.jetbrains.kotlin") {
            useVersion("1.8.21")
        }
    }
}

I have a multi-project and have to put it in each sub-project. It seems to limit the 1.8.21 dependency to only detekt.
Will probably have to keep on using it since this problem will probably still be around when Kotlin goes 1.9 or 2.0.

@nico-arianto
Copy link
Author

@alwyn Yes, it's working as I wanted it to. Thanks

I hope this version overridden can be done automatically within this piece of code shortly in the next release

plugins {
    id("io.gitlab.arturbosch.detekt") version "1.23.0"
}

@3flex
Copy link
Member

3flex commented Jun 13, 2023

This could be something we look at, but there are a few things to consider. Ultimately though this would just be working around problems caused by build scripts or other plugins, not the detekt plugin itself, and the error message shows a link to documentation that explains what the issue is and how to address it.

I'd rather make changes to the documentation since it seems there's room for improvement - suggestions and PRs are welcome.

@nico-arianto
Copy link
Author

nico-arianto commented Jun 13, 2023

@3flex 🗒️, yeah, we can modify the documentation first and revisit it for improvement.

want me to close this issue or leave it open?

@schowave
Copy link

For me, this finally worked:

project.afterEvaluate {
    configurations["detekt"].resolutionStrategy.eachDependency {
        if (requested.group == "org.jetbrains.kotlin") {
            useVersion("1.8.21")
        }
    }
}

@xRomZak
Copy link

xRomZak commented Jul 6, 2023

Since Kotlin 1.9.0 is released today

detekt was compiled with Kotlin 1.8.21 but is currently running with 1.9.0

@3flex
Copy link
Member

3flex commented Jul 7, 2023

Have you read https://detekt.dev/docs/gettingstarted/gradle#dependencies?

This is not a detekt defect. It's an issue with either the Gradle build config or another plugin that's affecting detekt. If the documentation linked above is not clear or doesn't address the issue then we're open to suggestions on how to improve it.

@kkocel
Copy link
Contributor

kkocel commented Jul 7, 2023

@3flex The problem with the solution described in the docs is that it is very cumbersome and makes it impossible to get detekt updated automatically by dependabot and other similar tools. I would prefer to have single place where detekt version is specified. I think that shadowing jar could be the best option here.

@3flex
Copy link
Member

3flex commented Jul 7, 2023

Ok the docs are obviously not clear, because it's not suggesting to hard code the detekt or Kotlin version anywhere, so auto update with dependabot or renovate should not be an issue at all - I'm not sure why that's the takeaway?

I'm open to suggestions on how to improve it.

Shadow jar will fix the issue but it blows out the size of the artifact, slows down builds, complicates our build setup, and the embeddable Kotlin version can't be reused as a dependency by other libraries on user's machines.

@BraisGabin
Copy link
Member

I agree with @3flex here. We need your help to make the documentation more clear. This is not a detekt's issue. This is an issue in how the projects apply the kotlin dependency.

I have my projects with detekt and kotlin and I can update/downgrade any of them at my will without the need of any type of hack or workaround. It just work. BUT if the project's configuration overrides the version that detekt uses 💥💥💥 it explodes.

So please help us make this more clear. Show us more examples of how you configure kotlin so we can add more examples... We don't know how to improve that documentation.

@kkocel
Copy link
Contributor

kkocel commented Jul 7, 2023

I would be for providing a shadow variant alongside the normal one. If detect can't work with any Kotlin version then it is on detekt plate.

@kkocel
Copy link
Contributor

kkocel commented Jul 7, 2023

I've seen similar cases in the past - there was wiremock which used an old jetty that was incompatible with new Spring Boot. What wiremock maintainers did? They provided a standalone version with all dependencies shadowed that just /worked/.

@n0rthdev
Copy link

n0rthdev commented Jul 7, 2023

We use the gradle buildSrc to specify our custom plugins. This looks like the following:

plugins {
    `kotlin-dsl`
    `kotlin-dsl-precompiled-script-plugins`
}

repositories {
    gradlePluginPortal() // so that external plugins can be resolved in dependencies section
}

val kotlinVersion = "1.8.21"
val nodePluginVersion = "5.0.0"
val sonarPluginVersion = "4.2.0.3129"
val detektVersion = "1.23.0"
val jacocoToCoberturaVersion = "1.1.2"

dependencies {
    implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
    implementation("org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion")
    implementation("org.jetbrains.kotlin:kotlin-noarg:$kotlinVersion")
    implementation("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:$detektVersion")

    implementation("com.github.node-gradle:gradle-node-plugin:$nodePluginVersion")

    implementation("org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:$sonarPluginVersion")

    implementation("net.razvan:JacocoToCoberturaPlugin:$jacocoToCoberturaVersion")
}

so we only include the kotlin-gradle-plugin in the (most current) version we use for our application. We don't at least not intentionally override the kotlin version for all configurations. (like shown in https://detekt.dev/docs/gettingstarted/gradle/#dependencies)

However, we specify the kotlin language version like this.

val KOTLIN_LANGUAGE = KotlinVersion.KOTLIN_1_8

tasks.withType<KotlinCompile>().configureEach {
    compilerOptions{
        jvmTarget.set(ToolVersions.JVM_TARGET)
        apiVersion.set(ToolVersions.KOTLIN_LANGUAGE)
        languageVersion.set(ToolVersions.KOTLIN_LANGUAGE)
        freeCompilerArgs.add("-Xjvm-default=all-compatibility")
    }
}

I am not sure how helpful this is for you ( @3flex @BraisGabin ) to understand how the this issue might arise for detekt users. Let me know if you need additional information.

@credmond-git
Copy link

Hello, would it be possible to get a 1.23.1 with Kotlin 1.9.0 and JDK 20 support? This is currently blocking our projects from upgrading to kotlin 1.9.0.
I along with the community would be very appreciative if we could get a 1.23.1 with minimal changes quickly, but that will allow us to run our builds with detekt. instead of waiting weeks for a new build with proper Kotlin 1.9.0 updates for the new features.

@3flex
Copy link
Member

3flex commented Jul 11, 2023

@credmond-git can you please open a new issue? Thanks.

Our snapshot version is updated to Kotlin 1.9.0 which supports Java 20 targets so you could try that in the meantime.

@arnodel
Copy link

arnodel commented Aug 3, 2023

I am not sure I understand the root of the issue discussed, but I am in the current situation. Our project has just be updated to be configured to use Kotlin 1.8.22 (from 1.8.20)

  • when depending on detekt 1.22.0, everything is fine with either version of Kotlin.
  • when depending on detekt 1.23.0 we get the following failure:
    detekt was compiled with Kotlin 1.8.21 but is currently running with 1.8.22.
  • when depending on detekt 1.23.1 we get the following failure:
    detekt was compiled with Kotlin 1.9.0 but is currently running with 1.8.22.

What has changed between 1.22 and 1.23 to make detekt so sensitive to particular versions of Kotlin?

I have followed the link to https://detekt.dev/docs/gettingstarted/gradle/#dependencies but I cannot see that we are in the situation described, so I cannot apply the proposed fix (as far as I understand). It looks as if it is going to be difficult to update detekt from now on for people like me who are unable to dive into the subtleties of gradle configurations.

In future I am not sure how we will be able to keep updating detekt.

@xRomZak
Copy link

xRomZak commented Aug 24, 2023

Update update to Kotlin 1.9.10

detekt was compiled with Kotlin 1.9.0 but is currently running with 1.9.10.

@3flex
Copy link
Member

3flex commented Sep 1, 2023

Use useVersion("1.9.0") with detekt 1.23.1.

It needs to be set to what detekt needs.

@kkocel
Copy link
Contributor

kkocel commented Sep 1, 2023

I have the following project (generated from spring starter page): https://github.com/kkocel/detektrepro
It works on 1.9.0 but when switched to branch https://github.com/kkocel/detektrepro/tree/1910 (Kotlin 1.9.10) it doesn't.

@3flex
Copy link
Member

3flex commented Sep 1, 2023

Is that a public repo? I get a 404 on both links

@kkocel
Copy link
Contributor

kkocel commented Sep 1, 2023

@3flex I've just changed it to public

@3flex
Copy link
Member

3flex commented Sep 1, 2023

@kkocel please see my comment above, you need to apply the workaround withuseVersion("1.9.0")

@kkocel
Copy link
Contributor

kkocel commented Sep 1, 2023

@3flex This workaround has lots of downsides - eg. dependabot won't know about this workaround, so I need to update the version in the workaround code every time when detekt updates. Once again, please reconsider providing a version of the detekt plugin with shadowed Kotlin dependency - it will resolve this issue permanently.

@BraisGabin
Copy link
Member

OK, so it feels to be confirmed that io.spring.dependency-management generates this issue. (We still don't know if everyone that faced this issue is for that reason so please, keep answering @3flex comment).

But, which should be our action plan for the "incompatibility" with that plugin? Some ideas:

  • Open an issue to io.spring.dependency-management.
  • Update our documentation and explain this problem.
  • Check on our gradle plugin if this plugin is applied and, if it is, show a different error message. That different error message I don't know if it should point to the issue to io.spring.dependency-management or just print the workaround.

@BraisGabin
Copy link
Member

@3flex This workaround has lots of downsides - eg. dependabot won't know about this workaround, so I need to update the version in the workaround code every time when detekt updates. Once again, please reconsider providing a version of the detekt plugin with shadowed Kotlin dependency - it will resolve this issue permanently.

I don't think we should do that. This is not our issue. It is an issue at io.spring.dependency-management. Also, we are asking you to see if the workaround works to be sure that we understand the problem. You don't need to add it to your code base if you don't want.

@kkocel
Copy link
Contributor

kkocel commented Sep 1, 2023

Yes, the workaround works but it's lame.

I see things differently - it's the detekt that strictly requires the exact Kotlin version (and breaks otherwise), so yes it's your issue.
If detekt so desperately requires the exact Kotlin version then it should bundle it, instead of requiring every other plugin to adjust.

@3flex
Copy link
Member

3flex commented Sep 2, 2023

the workaround works

Thanks for confirming!

requiring every other plugin to adjust

This is not what we're saying. There's a workaround that allows you to use detekt & the Spring dependency management plugin together. We are not saying that any other plugin has to adjust. There might also be better workarounds, so any suggestions for improvement to the documented workaround would be welcome. I see the Spring dependency management plugin is configurable so there might be another way to work around if both plugins are in use.

detekt that strictly requires the exact Kotlin version (and breaks otherwise), so yes it's your issue.

detekt interfaces with a lot of core compiler code and it sensitive to the version in use. This is not going to change. If detekt runs with an unexpected Kotlin compiler version that creates other issues, which get raised and have to be investigated. Instead, detekt errors when this is the case, and asks for this to be fixed, so we don't get strange errors being reported. The error is only shown when the build is misconfigured.

In terms of whether this is our issue or not - it's not. detekt's Gradle plugin configures itself correctly. Another plugin is overriding that configuration. That's not for us to fix.

dependabot won't know about this workaround, so I need to update the version in the workaround code every time when detekt updates

To be frank, this is not our problem. detekt needs to run with a certain version of Kotlin in its classpath. If the Spring dependency management plugin clobbers the configuration, that's caused by that plugin, and if the workaround is not ideal or has some other issues, that's really not in our hands.

You are of course free to stop using the Spring dependency management plugin, come up with an improved workaround and suggest improvements to our documentation, or even stop using detekt. Obviously we want as many people as possible to use detekt as many contributors and the maintainers have put a lot of time and effort into making detekt what it is today, and we like to see others getting value from it.

@3flex
Copy link
Member

3flex commented Sep 2, 2023

Back to the options we might consider:

Open an issue to io.spring.dependency-management

If anyone's affected by this directly then it's worth raising here. Perhaps the maintainers of that plugin have other suggested workarounds, even if they don't deliver an actual fix.

Update our documentation and explain this problem

Yes, we should definitely do this.

Check on our gradle plugin if this plugin is applied and, if it is, show a different error message. That different error message I don't know if it should point to the issue to io.spring.dependency-management or just print the workaround.

This is a good idea - though we probably don't want to show it every single time. I think it's better to update the CLI error message to reference Spring since it's going to continue being a common source of this issue.

@wilkinsona
Copy link

Hi, all. I'm the primary maintainer of the io.spring.dependency-management plugin, I'm also a member of the Spring Boot team. A colleague of mine, @marcingrzejszczak, asked me to take a look at this issue.

If you apply only the dependency management plugin to a project, it won't have any effect at all on the version of Kotlin that's used. If you apply both the dependency management plugin and Spring Boot's plugin, the latter will configure the former to import the spring-boot-dependencies bom and this bom includes dependency management for Kotlin. Furthermore, when you apply a specific version of a Kotlin plugin, Spring Boot's plugin automatically detects that and overrides the managed version of Kotlin to match. This dependency management is applied to every configuration which is why you're seeing an override of the version of Kotlin in the detekt configuration.

Generally speaking, keeping dependency versions consistent across configurations is useful. However, it sounds like that's not the case here. From what I understand reading the above, Detekt itself needs to run using a specific version of Kotlin, irrespective of the version of Kotlin that's being used elsewhere in the same project.

Assuming that I have understood this situation correctly, I'm afraid I don't have any suggestions for a better workaround. It sounds like what's really needed is a way to tell the dependency management plugin that "global" dependency management should not be applied to the detekt configuration. That would leave it free to resolve whatever versions Detekt needs.

@kkocel
Copy link
Contributor

kkocel commented Sep 7, 2023

Thanks for the input @wilkinsona

@3flex @BraisGabin I have a suggestion on how to improve the current workaround - It would be great to introduce a constant with the supported Kotlin version.

so the workaround could look like this:

import io.gitlab.arturbosch.detekt.SUPPORTED_KOTLIN_VERSION

configurations.matching { it.name == "detekt" }.all {
    resolutionStrategy.eachDependency {
        if (requested.group == "org.jetbrains.kotlin") {
            useVersion(SUPPORTED_KOTLIN_VERSION)
        }
    }
}

The benefit of the constant is that detekt after being updated (eg. by dependabot) won't require an update for the Kotlin version.

Right now the whichKotlin method is part of internal API:
import io.gitlab.arturbosch.detekt.api.internal.whichKotlin

@OksiBlack
Copy link

OksiBlack commented Sep 8, 2023

For me with such kotlin compile settings

object CompilerContext {
const val JDK_VERSION = "17"

const val KOTLIN_VERSION = "1.9"

}

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
logger.lifecycle("Configuring $name with version ${project.getKotlinPluginVersion()} in project ${project.name}")

kotlinOptions {
    freeCompilerArgs = listOf("-Xjsr305=strict")
    jvmTarget = JDK_VERSION
    languageVersion = KOTLIN_VERSION
    apiVersion = KOTLIN_VERSION
}

compilerOptions {
    verbose = true
    javaParameters = true
    languageVersion = KotlinVersion.fromVersion(KOTLIN_VERSION)
    apiVersion = KotlinVersion.fromVersion(KOTLIN_VERSION)
}

dependsOn("spotlessApply")

}

configurations.detekt {
resolutionStrategy.eachDependency {
if (requested.group == "org.jetbrains.kotlin") {
useVersion("1.9.0") // Add the version of Kotlin that detekt needs
}
}
}

dependencyManagement {

imports {
    mavenBom("org.springframework.boot:spring-boot-dependencies:$springBootVersion") {
        bomProperty("kotlin.version", "1.9.10")
    }
}

}

workaround not works

Also, I think it is the wrong strategy - fail build and require such workarounds. Before detekt wasn't breaking build, just producing warning.

@detekt-ci
Copy link
Collaborator

This issue is waiting for authors feedback since 30 days. Please comment providing the requested feedback or this issue will be closed in 7 days.

@cortinico cortinico added discussion never gets stale Mark Issues/PRs that should not be closed as they won't get stale and removed stale labels Oct 22, 2023
@CaiqueCoelho
Copy link

Hey guys quick question we want to bump kotlin("plugin.spring") to version "1.9.10", but I'm getting the following error

Execution failed for task ':detekt'.
> detekt was compiled with Kotlin 1.9.0 but is currently running with 1.9.10.
  This is not supported. See https://detekt.dev/docs/gettingstarted/gradle#dependencies for more information.

Captura de Tela 2023-10-25 às 10 32 17
Captura de Tela 2023-10-25 às 10 32 07

The following workaround works, but we are afraid of the side effects this may have, the static analysis will run in one version and the code in another. Could anyone tell us if doing this would have any risks? cc @3flex

configurations.matching { it.name == "detekt" }.all {
    resolutionStrategy.eachDependency {
        if (requested.group == "org.jetbrains.kotlin") {
            useVersion("1.9.0")
        }
    }
}

@BraisGabin
Copy link
Member

This issue seems really promising: spring-gradle-plugins/dependency-management-plugin#369

That would remove this issue completely.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
discussion gradle-plugin never gets stale Mark Issues/PRs that should not be closed as they won't get stale support
Projects
None yet
Development

No branches or pull requests