Repository: kotest/kotest (kotlin)

Issue:
Kotlin 2.1.20 support
**Please describe the feature you'd like to see including any solutions in mind if you have any**
Add support for Kotlin 2.1.20. There is a PR existing for this (#4816) but the builds seem to break.

Building this on my end gives errors regarding the `java` and `application` plugins from gradle:
```
e: ❌ 'application' Plugin Incompatible with 'org.jetbrains.kotlin.multiplatform' Plugin
'application' (also applies 'java' plugin) Gradle plugin is not compatible with 'org.jetbrains.kotlin.multiplatform' plugin.
Consider the new KMP/JVM binaries DSL as a replacement: https://kotl.in/jvm-binaries-dsl
```
[It looks like it was deprecated in Kotlin 2.0.20](https://kotlinlang.org/docs/whatsnew2020.html#deprecated-compatibility-with-kotlin-multiplatform-gradle-plugin-and-gradle-java-plugins). The build error does link to [a fix](https://youtrack.jetbrains.com/issue/KT-41409/Gradle-Support-binaries.executable-for-jvm-targets#focus=Comments-27-11387464.0-0), but it is marked with an experimental API. 

Only two projects actually used the `java` or `application` plugins: the base Kotest project and `kotest-framework-standalone`. Removing the `java` plugin from the base project seems to fix it. `kotest-framework-standalone` has a more involved fix:
```kts
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi

plugins {
   id("kotest-jvm-conventions")
   alias(libs.plugins.shadowjar)
}

kotlin {
   jvm {
      @OptIn(ExperimentalKotlinGradlePluginApi::class)
      binaries {
         executable {
            mainClass.set("io.kotest.engine.launcher.MainKt")
         }
      }
   }
}

tasks {
   // for some reason, the `shadowJar` block can't be accessed here and I have
   // to get the task by it's type
   val shadowJar = withType<ShadowJar> {
      archiveClassifier.set(null as String?)
      archiveBaseName.set("kotest-framework-standalone-jvm")
      exclude("**/module-info.class")
      mergeServiceFiles()
      manifest {
         attributes(Pair("Main-Class", "io.kotest.engine.launcher.MainKt"))
      }
   }

   // same with the `startScripts` block
   withType<CreateStartScripts> {
      dependsOn(shadowJar)
   }
}

// dependencies below
```
Setting `jvm.binaries.executable` creates two new files, which are shell scripts to launch the executable jar:
```
build/jvm/scripts/kotest-framework-standalone
build/jvm/scripts/kotest-framework-standalone.bat
```
The `jar` task was part of the `java-library` and `application` plugins, and removing it does change the outputs: 
```
-build/distributions/kotest-framework-standalone.tar
+build/distributions/kotest-framework-standalone-jvm.tar
-build/distributions/kotest-framework-standalone.zip
+build/distributions/kotest-framework-standalone-jvm.zip

```

Pull request:
Fixes #4821

Patch:
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 5e6e6d687cf..2ccc2db0c53 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -9,7 +9,7 @@ junit4 = "4.13.2"
 junit-platform = "1.8.2"
 junit-jupiter = "5.8.2"
 kaml = "0.66.0"
-kotlin = "2.1.0"
+kotlin = "2.1.20"
 kotlinx-coroutines = "1.10.0"
 kotlinx-serialization = "1.6.3"
 ktor = "2.3.13"


Test patch:
diff --git a/buildSrc/src/main/kotlin/kotest-jvm-conventions.gradle.kts b/buildSrc/src/main/kotlin/kotest-jvm-conventions.gradle.kts
index f1ebb9cd137..c6e455d4c12 100644
--- a/buildSrc/src/main/kotlin/kotest-jvm-conventions.gradle.kts
+++ b/buildSrc/src/main/kotlin/kotest-jvm-conventions.gradle.kts
@@ -36,7 +36,7 @@ fun VersionCatalog.findJvmVersion(name: String): Provider<JavaLanguageVersion> =
 /** The minimum Java version that Kotest supports. */
 val jvmMinTargetVersion: Provider<JavaLanguageVersion> = versionCatalog.findJvmVersion("jvmMinTarget")
 
-/** The minimum Java version that Kotest supports. */
+/** The maximum Java version that Kotest supports. */
 val jvmMaxTargetVersion: Provider<JavaLanguageVersion> = versionCatalog.findJvmVersion("jvmMaxTarget")
 
 /** The Java version used for compilation. */
diff --git a/kotest-extensions/build.gradle.kts b/kotest-extensions/build.gradle.kts
index 6c6975976c4..d11dde3c270 100644
--- a/kotest-extensions/build.gradle.kts
+++ b/kotest-extensions/build.gradle.kts
@@ -23,4 +23,8 @@ kotlin {
          }
       }
    }
-}
\ No newline at end of file
+}
+
+tasks.withType<Test>().configureEach {
+   jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED")
+}
diff --git a/kotest-framework/kotest-framework-standalone/build.gradle.kts b/kotest-framework/kotest-framework-standalone/build.gradle.kts
index 725cd0a1662..0712ef72859 100644
--- a/kotest-framework/kotest-framework-standalone/build.gradle.kts
+++ b/kotest-framework/kotest-framework-standalone/build.gradle.kts
@@ -1,19 +1,24 @@
+import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
+import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
+
 plugins {
-   `java-library`
    id("kotest-jvm-conventions")
-   application
    alias(libs.plugins.shadowjar)
 }
 
-application {
-   mainClass.set("io.kotest.engine.launcher.MainKt")
+kotlin {
+   jvm {
+      @OptIn(ExperimentalKotlinGradlePluginApi::class)
+      binaries {
+         executable {
+            mainClass.set("io.kotest.engine.launcher.MainKt")
+         }
+      }
+   }
 }
 
 tasks {
-   jar {
-      archiveClassifier.set("default")
-   }
-   shadowJar {
+   val shadowJar = withType<ShadowJar> {
       archiveClassifier.set(null as String?)
       archiveBaseName.set("kotest-framework-standalone-jvm")
       exclude("**/module-info.class")
@@ -26,7 +31,7 @@ tasks {
       finalizedBy(shadowJar)
    }
 
-   startScripts {
+   withType<CreateStartScripts> {
       dependsOn(shadowJar)
    }
 }