From 6ab86d896620b76dc55634e79cbc79ffe4cfe1f1 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Mon, 29 Jun 2026 17:35:54 +0100 Subject: [PATCH 1/9] Update IDEA settings --- .idea/kotlinc.xml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 1b15ab44..5c4b3ef9 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -3,12 +3,8 @@ - - - + \ No newline at end of file From 5158849290e9a4ba69928348ca9960d878efb424 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Mon, 29 Jun 2026 18:02:48 +0100 Subject: [PATCH 2/9] Register generated Kotlin sources via the Kotlin Gradle plugin API Register the copied generated Kotlin directory through `KotlinSourceSet.generatedKotlin` instead of the plain `kotlin` source set, so that build tooling and IDEs can tell generated code apart from hand-written sources. The protoc output dirs are still excluded from the plain `kotlin` set, as before. Add a TestKit spec covering the Kotlin path, which previously had no coverage. It applies the Kotlin Gradle plugin from the plugin-under-test classpath so the plugin and KGP share a classloader and the plugin can resolve the Kotlin Gradle plugin API at runtime. Closes #185. Co-Authored-By: Claude Opus 4.8 --- protobuf-setup-plugins/build.gradle.kts | 11 +++ .../gradle/plugin/GeneratedSourcePlugin.kt | 21 ++++- .../plugin/GeneratedSourcePluginSpec.kt | 84 ++++++++++++++++++- 3 files changed, 114 insertions(+), 2 deletions(-) diff --git a/protobuf-setup-plugins/build.gradle.kts b/protobuf-setup-plugins/build.gradle.kts index 46e14eb4..ae3655cc 100644 --- a/protobuf-setup-plugins/build.gradle.kts +++ b/protobuf-setup-plugins/build.gradle.kts @@ -24,6 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import io.spine.dependency.lib.Kotlin import io.spine.dependency.lib.Protobuf import io.spine.dependency.local.Base import io.spine.dependency.local.Logging @@ -111,6 +112,16 @@ dependencies { testImplementation(project(":plugin-testlib")) } +// Put the Kotlin Gradle plugin on the plugin-under-test classpath so the TestKit +// spec that applies it shares a classloader with this plugin, which uses the Kotlin +// Gradle plugin API (`KotlinSourceSet.generatedKotlin`). +val kotlinGradlePlugin = configurations.detachedConfiguration( + dependencies.create(Kotlin.GradlePlugin.lib) +) +tasks.withType().configureEach { + pluginClasspath.from(kotlinGradlePlugin) +} + publishing.publications.withType().configureEach { when (name) { "fatJar" -> { diff --git a/protobuf-setup-plugins/src/main/kotlin/io/spine/tools/protobuf/gradle/plugin/GeneratedSourcePlugin.kt b/protobuf-setup-plugins/src/main/kotlin/io/spine/tools/protobuf/gradle/plugin/GeneratedSourcePlugin.kt index 9307a516..8a34589c 100644 --- a/protobuf-setup-plugins/src/main/kotlin/io/spine/tools/protobuf/gradle/plugin/GeneratedSourcePlugin.kt +++ b/protobuf-setup-plugins/src/main/kotlin/io/spine/tools/protobuf/gradle/plugin/GeneratedSourcePlugin.kt @@ -44,6 +44,9 @@ import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.SourceDirectorySet import org.gradle.api.tasks.SourceSet import org.gradle.plugins.ide.idea.GenerateIdeaModule +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.KotlinBaseExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask /** @@ -152,8 +155,13 @@ private object GeneratedSubdir { * the tasks consuming the source sets (e.g., compilation, `sourcesJar`) run after * the generated code is copied. The dependency carried by the directories excluded * by this function is severed, so it must be re-established this way. + * + * Generated Kotlin sources are registered through the Kotlin Gradle plugin's dedicated + * `generatedKotlin` source directory set rather than the plain `kotlin` one, so that + * build tooling and IDEs can tell them apart from the hand-written code. */ @Internal +@OptIn(ExperimentalKotlinGradlePluginApi::class) context(_: GeneratedDirectoryContext) public fun GenerateProtoTask.configureSourceSetDirs() { val project = project @@ -196,7 +204,9 @@ public fun GenerateProtoTask.configureSourceSetDirs() { fun SourceDirectorySet.setup() { excludeFor(this@setup) - srcDir(generatedSrc(KOTLIN)) + project.findKotlinSourceSet(sourceSet.name) + ?.generatedKotlin + ?.srcDir(generatedSrc(KOTLIN)) } if (project.hasKotlin()) { @@ -208,6 +218,15 @@ public fun GenerateProtoTask.configureSourceSetDirs() { } } +/** + * Obtains the [KotlinSourceSet] with the given [name], or `null` if the project has no + * Kotlin extension, or it does not contain a source set with such a name. + */ +private fun Project.findKotlinSourceSet(name: String): KotlinSourceSet? = + extensions.findByType(KotlinBaseExtension::class.java) + ?.sourceSets + ?.findByName(name) + private fun File.residesIn(directory: File): Boolean = canonicalFile.startsWith(directory.absolutePath) diff --git a/protobuf-setup-plugins/src/test/kotlin/io/spine/tools/protobuf/gradle/plugin/GeneratedSourcePluginSpec.kt b/protobuf-setup-plugins/src/test/kotlin/io/spine/tools/protobuf/gradle/plugin/GeneratedSourcePluginSpec.kt index 1d92a8af..921693b0 100644 --- a/protobuf-setup-plugins/src/test/kotlin/io/spine/tools/protobuf/gradle/plugin/GeneratedSourcePluginSpec.kt +++ b/protobuf-setup-plugins/src/test/kotlin/io/spine/tools/protobuf/gradle/plugin/GeneratedSourcePluginSpec.kt @@ -1,5 +1,5 @@ /* - * Copyright 2025, TeamDev. All rights reserved. + * Copyright 2026, TeamDev. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ package io.spine.tools.protobuf.gradle.plugin import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain +import io.kotest.matchers.string.shouldNotContain import io.spine.tools.gradle.testing.Gradle import io.spine.tools.gradle.testing.Gradle.BUILD_SUCCESSFUL import io.spine.tools.gradle.testing.runGradleBuild @@ -97,4 +98,85 @@ class GeneratedSourcePluginSpec : ProtobufPluginTest() { val sampleOuter = File(generatedJava, "sample/Sample.java") sampleOuter.exists() shouldBe true } + + @Test + fun `register generated Kotlin sources under the 'generatedKotlin' source set`() { + + // Settings file (empty is fine for single-project build). + Gradle.settingsFile.under(projectDir).writeText("") + + // Create a minimal proto file. The Kotlin `protoc` builtin (enabled by our + // plugin) generates a Kotlin DSL file for the message. + File(protoDir, "sample.proto").writeText( + """ + syntax = "proto3"; + package sample; + message Msg {} + """.trimIndent() + ) + + // Build file applying the Kotlin JVM, Protobuf, and our plugins. + // The `printKotlinSourceDirs` task reports which source set the generated + // Kotlin directory is registered under. + Gradle.buildFile.under(projectDir).writeText( + """ + import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi + import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet + + plugins { + // No version: resolved from the plugin-under-test classpath. + id("org.jetbrains.kotlin.jvm") + id("${ProtobufGradlePlugin.id}") version "${ProtobufGradlePlugin.version}" + id("${GeneratedSourcePlugin.id}") + } + + group = "$group" + version = "$version" + + repositories { + mavenCentral() + } + + protobuf { + protoc { artifact = "${ProtobufProtoc.dependency.artifact.coordinates}" } + } + + @OptIn(ExperimentalKotlinGradlePluginApi::class) + fun generatedKotlinSrcDirs(set: KotlinSourceSet) = + set.generatedKotlin.srcDirs + + val main = kotlin.sourceSets.getByName("main") + tasks.register("printKotlinSourceDirs") { + doLast { + println("KOTLIN_DIRS=" + main.kotlin.srcDirs) + println("GENERATED_KOTLIN_DIRS=" + generatedKotlinSrcDirs(main)) + } + } + """.trimIndent() + ) + + val generateProto = ProtobufTaskName.generateProto + val result = runGradleBuild( + projectDir, + listOf(generateProto.name(), "printKotlinSourceDirs"), + debug = false + ) + + result.task(generateProto.path())?.outcome shouldBe TaskOutcome.SUCCESS + result.output shouldContain BUILD_SUCCESSFUL + + // The copied directory is registered under `generatedKotlin`, and is absent + // from the plain `kotlin` source set — this is the behavior under test. + val output = result.output + val generatedKotlinLine = + output.lineSequence().first { it.startsWith("GENERATED_KOTLIN_DIRS=") } + val kotlinLine = output.lineSequence().first { it.startsWith("KOTLIN_DIRS=") } + val generatedKotlinSubpath = "generated/main/kotlin" + generatedKotlinLine shouldContain generatedKotlinSubpath + kotlinLine shouldNotContain generatedKotlinSubpath + + // The generated Kotlin sources are copied under `$projectDir/generated/main/kotlin`. + val generatedKotlinDir = File(projectDir, "generated/main/kotlin") + generatedKotlinDir.walkTopDown().any { it.extension == "kt" } shouldBe true + } } From 41004868117e780cd0a7a62a082c84a26609f97e Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Mon, 29 Jun 2026 18:03:25 +0100 Subject: [PATCH 3/9] Bump version -> `2.0.0-SNAPSHOT.403` Co-Authored-By: Claude Opus 4.8 --- version.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.gradle.kts b/version.gradle.kts index b82214e6..63087138 100644 --- a/version.gradle.kts +++ b/version.gradle.kts @@ -24,4 +24,4 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -val versionToPublish: String by extra("2.0.0-SNAPSHOT.402") +val versionToPublish: String by extra("2.0.0-SNAPSHOT.403") From 504f2b4a2fa66d7118ebb7cee63759e7d58ecd03 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Mon, 29 Jun 2026 18:03:26 +0100 Subject: [PATCH 4/9] Update dependency reports Co-Authored-By: Claude Opus 4.8 --- docs/dependencies/dependencies.md | 56 +++++++++++++++---------------- docs/dependencies/pom.xml | 2 +- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/docs/dependencies/dependencies.md b/docs/dependencies/dependencies.md index 55e9b666..8612efab 100644 --- a/docs/dependencies/dependencies.md +++ b/docs/dependencies/dependencies.md @@ -1,6 +1,6 @@ -# Dependencies of `io.spine.tools:classic-codegen:2.0.0-SNAPSHOT.402` +# Dependencies of `io.spine.tools:classic-codegen:2.0.0-SNAPSHOT.403` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -828,14 +828,14 @@ The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using +This report was generated on **Mon Jun 29 17:55:58 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:gradle-plugin-api:2.0.0-SNAPSHOT.402` +# Dependencies of `io.spine.tools:gradle-plugin-api:2.0.0-SNAPSHOT.403` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.0. @@ -1734,14 +1734,14 @@ This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using +This report was generated on **Mon Jun 29 17:55:58 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:gradle-plugin-api-test-fixtures:2.0.0-SNAPSHOT.402` +# Dependencies of `io.spine.tools:gradle-plugin-api-test-fixtures:2.0.0-SNAPSHOT.403` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.0. @@ -2212,14 +2212,14 @@ This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Jun 24 19:40:33 WEST 2026** using +This report was generated on **Mon Jun 29 17:55:58 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:gradle-root-plugin:2.0.0-SNAPSHOT.402` +# Dependencies of `io.spine.tools:gradle-root-plugin:2.0.0-SNAPSHOT.403` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -3070,14 +3070,14 @@ This report was generated on **Wed Jun 24 19:40:33 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using +This report was generated on **Mon Jun 29 17:55:58 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:intellij-platform:2.0.0-SNAPSHOT.402` +# Dependencies of `io.spine.tools:intellij-platform:2.0.0-SNAPSHOT.403` ## Runtime 1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. @@ -4151,14 +4151,14 @@ This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using +This report was generated on **Mon Jun 29 17:55:58 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:intellij-platform-java:2.0.0-SNAPSHOT.402` +# Dependencies of `io.spine.tools:intellij-platform-java:2.0.0-SNAPSHOT.403` ## Runtime 1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. @@ -5930,14 +5930,14 @@ This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using +This report was generated on **Mon Jun 29 17:55:58 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:jvm-tool-plugins:2.0.0-SNAPSHOT.402` +# Dependencies of `io.spine.tools:jvm-tool-plugins:2.0.0-SNAPSHOT.403` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -6780,14 +6780,14 @@ This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using +This report was generated on **Mon Jun 29 17:55:58 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:jvm-tools:2.0.0-SNAPSHOT.402` +# Dependencies of `io.spine.tools:jvm-tools:2.0.0-SNAPSHOT.403` ## Runtime 1. **Group** : org.jetbrains. **Name** : annotations. **Version** : 26.1.0. @@ -7547,14 +7547,14 @@ This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using +This report was generated on **Mon Jun 29 17:55:58 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:plugin-base:2.0.0-SNAPSHOT.402` +# Dependencies of `io.spine.tools:plugin-base:2.0.0-SNAPSHOT.403` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -8405,14 +8405,14 @@ This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using +This report was generated on **Mon Jun 29 17:55:58 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:plugin-testlib:2.0.0-SNAPSHOT.402` +# Dependencies of `io.spine.tools:plugin-testlib:2.0.0-SNAPSHOT.403` ## Runtime 1. **Group** : com.google.auto.value. **Name** : auto-value-annotations. **Version** : 1.11.1. @@ -9367,14 +9367,14 @@ This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using +This report was generated on **Mon Jun 29 17:55:58 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:protobuf-setup-plugins:2.0.0-SNAPSHOT.402` +# Dependencies of `io.spine.tools:protobuf-setup-plugins:2.0.0-SNAPSHOT.403` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -10237,14 +10237,14 @@ This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using +This report was generated on **Mon Jun 29 17:55:58 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:psi:2.0.0-SNAPSHOT.402` +# Dependencies of `io.spine.tools:psi:2.0.0-SNAPSHOT.403` ## Runtime 1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. @@ -11345,14 +11345,14 @@ This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using +This report was generated on **Mon Jun 29 17:55:58 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:psi-java:2.0.0-SNAPSHOT.402` +# Dependencies of `io.spine.tools:psi-java:2.0.0-SNAPSHOT.403` ## Runtime 1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. @@ -13167,14 +13167,14 @@ This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using +This report was generated on **Mon Jun 29 17:55:58 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:tool-base:2.0.0-SNAPSHOT.402` +# Dependencies of `io.spine.tools:tool-base:2.0.0-SNAPSHOT.403` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -14054,6 +14054,6 @@ This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Wed Jun 24 19:40:34 WEST 2026** using +This report was generated on **Mon Jun 29 17:55:58 WEST 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). \ No newline at end of file diff --git a/docs/dependencies/pom.xml b/docs/dependencies/pom.xml index fb1223a2..1a90cb62 100644 --- a/docs/dependencies/pom.xml +++ b/docs/dependencies/pom.xml @@ -10,7 +10,7 @@ all modules and does not describe the project structure per-subproject. --> io.spine.tools tool-base -2.0.0-SNAPSHOT.402 +2.0.0-SNAPSHOT.403 2015 From 92b244c9afc107ccdadced92444542d5431203c6 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Mon, 29 Jun 2026 18:09:04 +0100 Subject: [PATCH 5/9] Update `config` --- .claude/settings.json | 1 + .github/workflows/build-on-ubuntu.yml | 2 +- .github/workflows/increment-guard.yml | 70 +++++- .github/workflows/publish.yml | 15 ++ .github/workflows/revalidate-versions.yml | 57 +++++ .idea/kotlinc.xml | 2 +- .../src/main/kotlin/DependencyResolution.kt | 6 +- .../src/main/kotlin/dokka-setup.gradle.kts | 11 +- .../kotlin/io/spine/dependency/Dependency.kt | 11 + .../io/spine/dependency/boms/BomsPlugin.kt | 5 +- .../kotlin/io/spine/dependency/local/Base.kt | 4 +- .../io/spine/dependency/local/Compiler.kt | 4 +- .../io/spine/dependency/local/CoreJvm.kt | 2 +- .../spine/dependency/local/CoreJvmCompiler.kt | 4 +- .../io/spine/dependency/local/Logging.kt | 2 +- .../io/spine/dependency/local/ToolBase.kt | 4 +- .../kotlin/io/spine/dependency/storage/H2.kt | 40 ++++ .../io/spine/dependency/storage/Hikari.kt | 40 ++++ .../io/spine/dependency/storage/HsqlDb.kt | 41 ++++ .../io/spine/dependency/storage/MySql.kt | 41 ++++ .../io/spine/dependency/storage/PostgreSql.kt | 40 ++++ .../io/spine/dependency/storage/QueryDsl.kt | 45 ++++ .../spine/dependency/test/Testcontainers.kt | 28 ++- .../io/spine/gradle/VersionComparator.kt | 115 +++++++++ .../io/spine/gradle/VersionGradleFile.kt | 157 ++++++++++++ .../io/spine/gradle/kotlin/KotlinConfig.kt | 20 +- .../gradle/publish/CheckVersionIncrement.kt | 161 ++++++++++--- .../io/spine/gradle/publish/IncrementGuard.kt | 138 +++++++++-- .../io/spine/gradle/publish/MavenMetadata.kt | 84 +++++++ .../gradle/report/pom/DependencyWriter.kt | 1 + .../src/main/kotlin/jvm-module.gradle.kts | 4 + .../src/main/kotlin/kmp-module.gradle.kts | 4 + .../io/spine/gradle/VersionComparatorSpec.kt | 87 +++++++ .../io/spine/gradle/VersionGradleFileSpec.kt | 86 +++++++ .../gradle/publish/IncrementGuardTest.kt | 143 +++++++++++ .../spine/gradle/publish/MavenMetadataSpec.kt | 53 +++++ config | 2 +- docs/dependencies/dependencies.md | 224 ++++++++++++++++-- docs/dependencies/pom.xml | 2 +- 39 files changed, 1652 insertions(+), 104 deletions(-) create mode 100644 .github/workflows/revalidate-versions.yml create mode 100644 buildSrc/src/main/kotlin/io/spine/dependency/storage/H2.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/dependency/storage/Hikari.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/dependency/storage/HsqlDb.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/dependency/storage/MySql.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/dependency/storage/PostgreSql.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/dependency/storage/QueryDsl.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/gradle/VersionComparator.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/gradle/VersionGradleFile.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/gradle/publish/MavenMetadata.kt create mode 100644 buildSrc/src/test/kotlin/io/spine/gradle/VersionComparatorSpec.kt create mode 100644 buildSrc/src/test/kotlin/io/spine/gradle/VersionGradleFileSpec.kt create mode 100644 buildSrc/src/test/kotlin/io/spine/gradle/publish/MavenMetadataSpec.kt diff --git a/.claude/settings.json b/.claude/settings.json index 84a0e53d..357650cf 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -2,6 +2,7 @@ "$schema": "https://json.schemastore.org/claude-code-settings.json", "permissions": { "allow": [ + "Edit(version.gradle.kts)", "Bash(./gradlew:*)", "Bash(./config/gradlew:*)", "Bash(git status:*)", diff --git a/.github/workflows/build-on-ubuntu.yml b/.github/workflows/build-on-ubuntu.yml index 01f6fd71..b07bf0fb 100644 --- a/.github/workflows/build-on-ubuntu.yml +++ b/.github/workflows/build-on-ubuntu.yml @@ -87,7 +87,7 @@ jobs: # there anyway). - name: Upload code coverage report if: steps.codecov.outputs.available == 'true' || github.event_name == 'push' - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v7 with: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: true diff --git a/.github/workflows/increment-guard.yml b/.github/workflows/increment-guard.yml index f20b4bed..9a6333f0 100644 --- a/.github/workflows/increment-guard.yml +++ b/.github/workflows/increment-guard.yml @@ -1,5 +1,9 @@ -# Ensures that the current lib version is not yet published by executing the Gradle -# `checkVersionIncrement` task. +# Guards the project version by executing the Gradle `checkVersionIncrement` task, +# which verifies that the version is both (a) strictly greater than the base branch +# version in `version.gradle.kts` and (b) not already published. The result is +# published as the `Version Guard` commit status — the context required by branch +# protection and re-published by `revalidate-versions.yml` on the heads of other open +# PRs when the base branch advances (so a stale duplicate bump turns red before merge). # # The check runs only for pull requests targeting a default (`master`/`main`) or # a release-line (e.g. `2.x-jdk8-master`) branch. It is the responsibility of a branch @@ -15,19 +19,45 @@ name: Version Guard on: pull_request: + # Beyond the default activity types (`opened`, `synchronize`, `reopened`), two more are + # needed because they change what the guard must compare against without a new head SHA, + # which would otherwise leave a stale-green `Version Guard` status mergeable: + # * `ready_for_review` — a draft that went stale while in draft becomes ready; and + # * `edited` — the base branch is retargeted (e.g. a release line -> `master`), so the + # strict comparison must be recomputed against the new base. + types: [opened, synchronize, reopened, ready_for_review, edited] jobs: check: name: Check version increment runs-on: ubuntu-latest - # Default and release-line branches, e.g. `master`, `main`, `2.x-jdk8-master`. - if: endsWith(github.base_ref, 'master') || endsWith(github.base_ref, 'main') + # Default and release-line branches, e.g. `master`, `main`, `2.x-jdk8-master`. For an + # `edited` event, run only when the base actually changed (a retarget carries + # `changes.base.ref.from`); title/body edits carry no `changes.base` and are skipped, so + # the guard is not rebuilt needlessly. + if: >- + (endsWith(github.base_ref, 'master') || endsWith(github.base_ref, 'main')) + && (github.event.action != 'edited' || github.event.changes.base.ref.from != '') + + # `statuses: write` lets the job publish the `Version Guard` commit status that + # branch protection requires. + permissions: + contents: read + statuses: write steps: - uses: actions/checkout@v6 with: submodules: 'true' + # `checkVersionIncrement` reads `origin/:version.gradle.kts`. The pull request + # checkout does not include the base branch, so fetch its tip into the expected ref. + - name: Fetch the base branch + shell: bash + run: | + git fetch --no-tags --depth=1 \ + origin "+refs/heads/${GITHUB_BASE_REF}:refs/remotes/origin/${GITHUB_BASE_REF}" + - uses: actions/setup-java@v5 with: java-version: 17 @@ -35,6 +65,36 @@ jobs: - uses: gradle/actions/setup-gradle@v6 - - name: Check version is not yet published + - name: Check version increment + id: guard shell: bash + # `VERSION_GUARD` enables the strict base-branch comparison in `checkVersionIncrement`. + # Only this workflow fetches the base ref (the step above), so the comparison is gated + # to it: other CI builds pull the task in via `publishToMavenLocal` on a shallow + # checkout and must not attempt to read `origin/`. + env: + VERSION_GUARD: "true" run: ./gradlew checkVersionIncrement --stacktrace + + # Publish the verdict as the `Version Guard` commit status on the PR head. Posting it + # on every run (success or failure) is what lets a later re-bump clear a failure that + # `revalidate-versions.yml` set when the base branch advanced. + # + # Skipped for fork PRs: `GITHUB_TOKEN` is read-only for them, so the status cannot be + # posted (and a fork head SHA is not in this repo). The Spine agent workflow pushes PR + # branches to the same repository; fork contributions are handled by a maintainer, who + # owns the version bump. + - name: Report the Version Guard status + if: always() && github.event.pull_request.head.repo.fork == false + shell: bash + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + state=failure + if [ "${{ steps.guard.outcome }}" = "success" ]; then + state=success + fi + gh api -X POST "repos/${{ github.repository }}/statuses/${{ github.event.pull_request.head.sha }}" \ + -f state="${state}" \ + -f context="Version Guard" \ + -f description="Version increment check" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index df8f6cd0..27c11c0f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -64,3 +64,18 @@ jobs: REPO_SLUG: ${{ github.repository }} # e.g. SpineEventEngine/core-jvm GOOGLE_APPLICATION_CREDENTIALS: ./maven-publisher.json NPM_TOKEN: ${{ secrets.NPM_SECRET }} + + # A failed publication on `master` is most often a version collision: a stale + # duplicate bump merged before `revalidate-versions.yml` could turn it red (the + # narrow auto-merge race). The artifact is safe — the registry rejects the + # overwrite — but the fix needs a human/agent, so make the failure loud and + # actionable instead of a quiet red run. + - name: Report a failed publication + if: failure() + shell: bash + run: | + echo "::error title=Publish failed::Publishing to Maven failed on the base branch. If this is a version collision, the version is already published (immutable). Bump 'version.gradle.kts' on the base branch (e.g. via a small PR) and re-run this workflow." + echo "Publish failed. If the cause is a version collision:" + echo " 1. Bump 'version.gradle.kts' on the base branch to the next free version." + echo " 2. Re-run this 'Publish' workflow." + echo "Stale duplicate bumps are normally caught before merge by 'revalidate-versions.yml'." diff --git a/.github/workflows/revalidate-versions.yml b/.github/workflows/revalidate-versions.yml new file mode 100644 index 00000000..2d2ecb9c --- /dev/null +++ b/.github/workflows/revalidate-versions.yml @@ -0,0 +1,57 @@ +# Re-judges every other open pull request when the base branch advances. +# +# Publishing runs on every push to a release base branch, so once one pull request merges +# and bumps the version, any other open PR that bumped to the same (or a lower) value is +# now stale: its publish would collide. GitHub does not re-run a PR's checks when its base +# advances, so this workflow does it actively — for each other open PR whose +# `version.gradle.kts` version is `<=` the new base version, it posts a failing +# `Version Guard` commit status on the PR head, blocking the merge until the author +# re-bumps. The status self-clears: the re-bump push runs `increment-guard.yml`, which +# posts a fresh `success` on the new head. +# +# This narrows, but does not close, the race against auto-merge. A PR that is already +# mergeable can merge in the seconds before this fan-out marks it stale; that late merge +# produces a publish collision which the immutable Maven registry rejects (a loud, +# recoverable red Publish), never an overwrite. The deterministic guarantee is the +# registry's immutability, not this signal. + +name: Revalidate Versions + +on: + push: + # Matches the PR guard's `endsWith(base_ref, 'master'|'main')` and the same scope as + # `build-on-ubuntu.yml`. `**` (unlike `*`) also crosses `/`, so slash-named release + # lines such as `release/2.x-master` are covered. Keeping these definitions identical + # ensures every branch guarded on the PR side is also revalidated here. + branches: + - '**master' + - '**main' + +permissions: + contents: read + statuses: write + pull-requests: read + +concurrency: + # Only the newest tip of a given base matters; a later push to the same ref cancels an + # in-flight fan-out for it. Different bases run independently. + group: revalidate-versions-${{ github.ref }} + cancel-in-progress: true + +jobs: + revalidate: + name: Revalidate open PRs + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + submodules: 'true' + + - name: Revalidate open PRs against the new base version + shell: bash + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + BASE_REF: ${{ github.ref_name }} + # Invoked via `bash` so it does not depend on the script's committed executable bit. + run: bash ./config/scripts/revalidate-versions.sh diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 5c4b3ef9..85d8f550 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -7,4 +7,4 @@