diff --git a/.github/workflows/build-on-ubuntu.yml b/.github/workflows/build-on-ubuntu.yml
index 03be0f532..27d9302b9 100644
--- a/.github/workflows/build-on-ubuntu.yml
+++ b/.github/workflows/build-on-ubuntu.yml
@@ -12,11 +12,12 @@ jobs:
with:
submodules: 'true'
- - uses: actions/setup-java@v4
+ - uses: actions/setup-java@v5
with:
java-version: 17
distribution: zulu
- cache: gradle
+
+ - uses: gradle/actions/setup-gradle@v6
- name: Build project and run tests
shell: bash
diff --git a/.github/workflows/build-on-windows.yml b/.github/workflows/build-on-windows.yml
index de72407ed..992272ce4 100644
--- a/.github/workflows/build-on-windows.yml
+++ b/.github/workflows/build-on-windows.yml
@@ -21,11 +21,12 @@ jobs:
submodules: recursive
fetch-depth: 0
- - uses: actions/setup-java@v4
+ - uses: actions/setup-java@v5
with:
java-version: 17
distribution: zulu
- cache: gradle
+
+ - uses: gradle/actions/setup-gradle@v6
# See: https://github.com/al-cheb/configure-pagefile-action
- name: Configure Pagefile
@@ -33,8 +34,7 @@ jobs:
- name: Build project and run tests
shell: cmd
- # For the reason on `--no-daemon` see https://github.com/actions/cache/issues/454
- run: gradlew.bat build --stacktrace --no-daemon
+ run: gradlew.bat build --stacktrace
# See: https://github.com/marketplace/actions/junit-report-action
- name: Publish Test Report
diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml
index 3fa235be0..6755ff910 100644
--- a/.github/workflows/check-links.yml
+++ b/.github/workflows/check-links.yml
@@ -33,7 +33,7 @@ jobs:
cancel-in-progress: true
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
# Detect the Hugo site root (`docs/` or `site/`) by looking for a Hugo
# config file. Hugo config may live directly in the site root or in a
diff --git a/.github/workflows/ensure-reports-updated.yml b/.github/workflows/ensure-reports-updated.yml
index 315cd202b..0cf35a6d9 100644
--- a/.github/workflows/ensure-reports-updated.yml
+++ b/.github/workflows/ensure-reports-updated.yml
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
with:
# Configure the checkout of all branches so that it is possible to run the comparison.
fetch-depth: 0
diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml
index 50eb05eb1..fc0872b4c 100644
--- a/.github/workflows/gradle-wrapper-validation.yml
+++ b/.github/workflows/gradle-wrapper-validation.yml
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout latest code
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Validate Gradle Wrapper
uses: gradle/actions/wrapper-validation@v4
diff --git a/.github/workflows/increment-guard.yml b/.github/workflows/increment-guard.yml
index 38ce6f4d3..6399c87af 100644
--- a/.github/workflows/increment-guard.yml
+++ b/.github/workflows/increment-guard.yml
@@ -14,15 +14,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
with:
submodules: 'true'
- - uses: actions/setup-java@v4
+ - uses: actions/setup-java@v5
with:
java-version: 17
distribution: zulu
- cache: gradle
+
+ - uses: gradle/actions/setup-gradle@v6
- name: Check version is not yet published
shell: bash
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index f7218c618..df8f6cd01 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -10,15 +10,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
with:
submodules: 'true'
- - uses: actions/setup-java@v4
+ - uses: actions/setup-java@v5
with:
java-version: 17
distribution: zulu
- cache: gradle
+
+ - uses: gradle/actions/setup-gradle@v6
- name: Decrypt CloudRepo credentials
run: ./config/scripts/decrypt.sh "$CLOUDREPO_CREDENTIALS_KEY" ./.github/keys/cloudrepo.properties.gpg ./cloudrepo.properties
diff --git a/.github/workflows/remove-obsolete-artifacts-from-packages.yaml b/.github/workflows/remove-obsolete-artifacts-from-packages.yaml
index f70617100..62242c7bb 100644
--- a/.github/workflows/remove-obsolete-artifacts-from-packages.yaml
+++ b/.github/workflows/remove-obsolete-artifacts-from-packages.yaml
@@ -39,7 +39,7 @@ jobs:
outputs:
package-names: ${{ steps.request-package-names.outputs.package-names }}
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
with:
submodules: 'true'
diff --git a/buildSrc/src/main/kotlin/config-tester.gradle.kts b/buildSrc/src/main/kotlin/config-tester.gradle.kts
index 7b64dac7d..2463f738b 100644
--- a/buildSrc/src/main/kotlin/config-tester.gradle.kts
+++ b/buildSrc/src/main/kotlin/config-tester.gradle.kts
@@ -42,10 +42,10 @@ val tempFolder = File("./tmp")
ConfigTester(config, tasks, tempFolder)
.addRepo(SpineRepos.baseTypes) // Builds `base-types` at `master`.
.addRepo(SpineRepos.base) // Builds `base` at `master`.
- .addRepo(SpineRepos.coreJava) // Builds `core-java` at `master`.
+ .addRepo(SpineRepos.coreJvm) // Builds `core-jvm` at `master`.
// This is how one builds a specific branch of some repository:
- // .addRepo(SpineRepos.coreJava, Branch("grpc-concurrency-fixes"))
+ // .addRepo(SpineRepos.coreJvm, Branch("grpc-concurrency-fixes"))
// Register the produced task under the selected name to invoke manually upon need.
.registerUnder("buildDependants")
diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/ConfigTester.kt b/buildSrc/src/main/kotlin/io/spine/gradle/ConfigTester.kt
index c3bbfbe11..f95f23ea0 100644
--- a/buildSrc/src/main/kotlin/io/spine/gradle/ConfigTester.kt
+++ b/buildSrc/src/main/kotlin/io/spine/gradle/ConfigTester.kt
@@ -39,8 +39,8 @@ import org.gradle.api.tasks.TaskContainer
* A tool to execute the Gradle `build` task in selected Git repositories
* with the local version of [config] contents.
*
- * Checks out the content of selected repositories into the specified [tempFolder]. The folder
- * is created if it does not exist. By default, uses `./tmp` as a temp folder.
+ * Checks out the content of selected repositories into the specified [tempFolder].
+ * The folder is created if it does not exist. By default, uses `./tmp` as a temp folder.
*
* Replaces the `config` and `buildSrc` folders in the checked out repository by the local versions
* of code. If the repository-under-test already contains its own `buildSrc` or `config` folders,
@@ -356,7 +356,7 @@ object SpineRepos {
val base: URI = library("base")
val baseTypes: URI = library("base-types")
- val coreJava: URI = library("core-java")
+ val coreJvm: URI = library("core-jvm")
val web: URI = library("web")
private fun library(repo: String) = URI(libsOrg + repo)
diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/report/coverage/SiblingCoverage.kt b/buildSrc/src/main/kotlin/io/spine/gradle/report/coverage/SiblingCoverage.kt
new file mode 100644
index 000000000..a680d7994
--- /dev/null
+++ b/buildSrc/src/main/kotlin/io/spine/gradle/report/coverage/SiblingCoverage.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.gradle.report.coverage
+
+import kotlinx.kover.gradle.plugin.dsl.KoverProjectExtension
+import org.gradle.api.Project
+import org.gradle.api.Task
+
+/**
+ * Credits the test coverage produced by the [contributor] module for the classes
+ * of this project to this project's own Kover report.
+ *
+ * Some modules' production classes are exercised only by the tests of a sibling
+ * module — for example, the language-neutral `psi` classes are tested through
+ * the Java-PSI fixtures that live in `psi-java`. Kover's per-module report sees
+ * only this module's own `test` execution data, so that cross-module coverage is
+ * otherwise missing from the per-module report (which is what Codecov consumes),
+ * even though the root aggregated report already accounts for it.
+ *
+ * This function adds the [contributor]'s JaCoCo execution data to this project's
+ * `total` report as an additional binary report. Only this project's classes are
+ * credited from it — coverage of unrelated classes in the same execution data is
+ * ignored, because a Kover report is scoped to the owning project's classes. The
+ * report tasks are wired to run after the contributor's `test` task so the data
+ * is present when a report is generated.
+ *
+ * Requires the Kover plugin to be applied to this project.
+ * A cross-project **task** dependency is used, not a project dependency,
+ * so it does not introduce a dependency cycle even when the [contributor]
+ * already depends on this project.
+ */
+fun Project.creditTestCoverageFrom(contributor: Project) {
+ val execFile = contributor.layout.buildDirectory.file(TEST_EXEC_FILE)
+ extensions.configure(KoverProjectExtension::class.java) {
+ reports {
+ total {
+ additionalBinaryReports.add(execFile.map { it.asFile })
+ }
+ }
+ }
+ tasks.matching { it.consumesCoverageBinaryReports() }.configureEach {
+ dependsOn("${contributor.path}:test")
+ }
+}
+
+/**
+ * The Kover/JaCoCo execution-data file produced by a module's `test` task when
+ * the coverage engine is pinned to JaCoCo via `useJacoco(...)`.
+ */
+private const val TEST_EXEC_FILE: String = "kover/bin-reports/test.exec"
+
+/**
+ * Tells whether this is a Kover task that reads the binary reports and therefore
+ * must run only after the [contributor's][creditTestCoverageFrom] test data exists.
+ *
+ * This matches both the report tasks (`koverXmlReport`, `koverHtmlReport`,
+ * `koverBinaryReport`) and the verification tasks (`koverVerify` and its
+ * cacheable companion `koverCachedVerify`) — the suffix test covers the
+ * `Cached*` variants Kover registers, which are the ones that actually consume
+ * the binary reports.
+ */
+private fun Task.consumesCoverageBinaryReports(): Boolean =
+ name.startsWith("kover") && (name.endsWith("Report") || name.endsWith("Verify"))
diff --git a/config b/config
index f8d5a1ddc..93dcf210d 160000
--- a/config
+++ b/config
@@ -1 +1 @@
-Subproject commit f8d5a1ddc6aaffca909b644905fcc9b6021e749f
+Subproject commit 93dcf210dc3c93b53cdcf75828a29976dd30f9a0
diff --git a/docs/dependencies/dependencies.md b/docs/dependencies/dependencies.md
index 4ff751068..3df896cd4 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.398`
+# Dependencies of `io.spine.tools:classic-codegen:2.0.0-SNAPSHOT.399`
## 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 **Mon Jun 08 18:51:45 WEST 2026** using
+This report was generated on **Tue Jun 09 19:36:00 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.398`
+# Dependencies of `io.spine.tools:gradle-plugin-api:2.0.0-SNAPSHOT.399`
## Runtime
1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.0.
@@ -1734,14 +1734,14 @@ This report was generated on **Mon Jun 08 18:51:45 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Mon Jun 08 18:51:45 WEST 2026** using
+This report was generated on **Tue Jun 09 19:36:00 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.398`
+# Dependencies of `io.spine.tools:gradle-plugin-api-test-fixtures:2.0.0-SNAPSHOT.399`
## Runtime
1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.0.
@@ -2212,14 +2212,14 @@ This report was generated on **Mon Jun 08 18:51:45 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Mon Jun 08 18:51:45 WEST 2026** using
+This report was generated on **Tue Jun 09 19:36:00 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.398`
+# Dependencies of `io.spine.tools:gradle-root-plugin:2.0.0-SNAPSHOT.399`
## Runtime
1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2.
@@ -3070,14 +3070,14 @@ This report was generated on **Mon Jun 08 18:51:45 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Mon Jun 08 18:51:45 WEST 2026** using
+This report was generated on **Tue Jun 09 19:36:00 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.398`
+# Dependencies of `io.spine.tools:intellij-platform:2.0.0-SNAPSHOT.399`
## Runtime
1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3.
@@ -4151,14 +4151,14 @@ This report was generated on **Mon Jun 08 18:51:45 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Mon Jun 08 18:51:46 WEST 2026** using
+This report was generated on **Tue Jun 09 19:36:00 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.398`
+# Dependencies of `io.spine.tools:intellij-platform-java:2.0.0-SNAPSHOT.399`
## Runtime
1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3.
@@ -5930,14 +5930,14 @@ This report was generated on **Mon Jun 08 18:51:46 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Mon Jun 08 18:51:46 WEST 2026** using
+This report was generated on **Tue Jun 09 19:36:01 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.398`
+# Dependencies of `io.spine.tools:jvm-tool-plugins:2.0.0-SNAPSHOT.399`
## Runtime
1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2.
@@ -6780,14 +6780,14 @@ This report was generated on **Mon Jun 08 18:51:46 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Mon Jun 08 18:51:45 WEST 2026** using
+This report was generated on **Tue Jun 09 19:36:00 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.398`
+# Dependencies of `io.spine.tools:jvm-tools:2.0.0-SNAPSHOT.399`
## Runtime
1. **Group** : org.jetbrains. **Name** : annotations. **Version** : 26.1.0.
@@ -7547,14 +7547,14 @@ This report was generated on **Mon Jun 08 18:51:45 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Mon Jun 08 18:51:45 WEST 2026** using
+This report was generated on **Tue Jun 09 19:36:00 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.398`
+# Dependencies of `io.spine.tools:plugin-base:2.0.0-SNAPSHOT.399`
## Runtime
1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2.
@@ -8405,14 +8405,14 @@ This report was generated on **Mon Jun 08 18:51:45 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Mon Jun 08 18:51:45 WEST 2026** using
+This report was generated on **Tue Jun 09 19:36:00 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.398`
+# Dependencies of `io.spine.tools:plugin-testlib:2.0.0-SNAPSHOT.399`
## Runtime
1. **Group** : com.google.auto.value. **Name** : auto-value-annotations. **Version** : 1.11.1.
@@ -9367,14 +9367,14 @@ This report was generated on **Mon Jun 08 18:51:45 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Mon Jun 08 18:51:45 WEST 2026** using
+This report was generated on **Tue Jun 09 19:36:00 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.398`
+# Dependencies of `io.spine.tools:protobuf-setup-plugins:2.0.0-SNAPSHOT.399`
## Runtime
1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2.
@@ -10237,14 +10237,14 @@ This report was generated on **Mon Jun 08 18:51:45 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Mon Jun 08 18:51:45 WEST 2026** using
+This report was generated on **Tue Jun 09 19:36:00 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.398`
+# Dependencies of `io.spine.tools:psi:2.0.0-SNAPSHOT.399`
## Runtime
1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3.
@@ -11345,14 +11345,14 @@ This report was generated on **Mon Jun 08 18:51:45 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Mon Jun 08 18:51:46 WEST 2026** using
+This report was generated on **Tue Jun 09 19:36:00 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.398`
+# Dependencies of `io.spine.tools:psi-java:2.0.0-SNAPSHOT.399`
## Runtime
1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3.
@@ -13167,14 +13167,14 @@ This report was generated on **Mon Jun 08 18:51:46 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Mon Jun 08 18:51:46 WEST 2026** using
+This report was generated on **Tue Jun 09 19:36:01 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.398`
+# Dependencies of `io.spine.tools:tool-base:2.0.0-SNAPSHOT.399`
## Runtime
1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2.
@@ -14054,6 +14054,6 @@ This report was generated on **Mon Jun 08 18:51:46 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Mon Jun 08 18:51:46 WEST 2026** using
+This report was generated on **Tue Jun 09 19:36:00 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 371afe2f5..cf5223281 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.398
+2.0.0-SNAPSHOT.399
2015
diff --git a/gradle-plugin-api-test-fixtures/build.gradle.kts b/gradle-plugin-api-test-fixtures/build.gradle.kts
index 3f8252da9..8e0b31ae6 100644
--- a/gradle-plugin-api-test-fixtures/build.gradle.kts
+++ b/gradle-plugin-api-test-fixtures/build.gradle.kts
@@ -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.
@@ -53,6 +53,10 @@ gradlePlugin {
id = "io.spine.test.settings"
implementationClass = "$packageName.StubSettingsPlugin"
}
+ create("nullDslSettingsPlugin") {
+ id = "io.spine.test.settings-no-dsl"
+ implementationClass = "$packageName.NullDslSettingsPlugin"
+ }
}
}
diff --git a/gradle-plugin-api-test-fixtures/src/main/kotlin/io/spine/tools/gradle/lib/given/StubSettingsPlugin.kt b/gradle-plugin-api-test-fixtures/src/main/kotlin/io/spine/tools/gradle/lib/given/StubSettingsPlugin.kt
index 70f9b3f0f..a0735f682 100644
--- a/gradle-plugin-api-test-fixtures/src/main/kotlin/io/spine/tools/gradle/lib/given/StubSettingsPlugin.kt
+++ b/gradle-plugin-api-test-fixtures/src/main/kotlin/io/spine/tools/gradle/lib/given/StubSettingsPlugin.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.
@@ -47,3 +47,12 @@ class StubSettingsPlugin : LibrarySettingsPlugin(
System.err.println(" %%%% Initializing the plugin `${this.javaClass.simpleName}`. %%%% ")
}
}
+
+/**
+ * A stub settings plugin that passes no [DslSpec] (`null`).
+ *
+ * Applying it after another settings plugin exercises both the already-present
+ * root extension path and the `dslSpec == null` path of
+ * [LibrarySettingsPlugin.apply][LibrarySettingsPlugin].
+ */
+class NullDslSettingsPlugin : LibrarySettingsPlugin(null)
diff --git a/gradle-plugin-api/src/test/kotlin/io/spine/tools/gradle/lib/LibrarySettingsPluginSpec.kt b/gradle-plugin-api/src/test/kotlin/io/spine/tools/gradle/lib/LibrarySettingsPluginSpec.kt
index 02a1860fc..d5039dd6b 100644
--- a/gradle-plugin-api/src/test/kotlin/io/spine/tools/gradle/lib/LibrarySettingsPluginSpec.kt
+++ b/gradle-plugin-api/src/test/kotlin/io/spine/tools/gradle/lib/LibrarySettingsPluginSpec.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.
@@ -64,11 +64,11 @@ internal class LibrarySettingsPluginSpec {
val text = """
plugins {
id("$PLUGIN_ID")
- }
-
+ }
+
spineSettings {
// This is the extension added by the plugin under the test.
- nameHolder.name.set("test-name")
+ nameHolder.name.set("test-name")
}
""".trimIndent()
Gradle.settingsFile.under(projectDir).writeText(text)
@@ -90,4 +90,39 @@ internal class LibrarySettingsPluginSpec {
result[taskName] shouldBe TaskOutcome.SUCCESS
result.output shouldContain BUILD_SUCCESSFUL
}
+
+ @Test
+ fun `reuse an existing root extension and tolerate a null DSL spec`() {
+ // Applying two settings plugins in a row drives both remaining
+ // branches of `LibrarySettingsPlugin.apply`:
+ // * the second plugin sees the root extension already created by
+ // the first one (the `hasRootExtension == true` path);
+ // * the second plugin has no `DslSpec` (the `dslSpec == null` path).
+ @Language("Groovy") // It's Kotlin, actually, but highlighting works better for Groovy.
+ val text = """
+ plugins {
+ id("$PLUGIN_ID")
+ id("$PLUGIN_ID-no-dsl")
+ }
+
+ spineSettings {
+ nameHolder.name.set("test-name")
+ }
+ """.trimIndent()
+ Gradle.settingsFile.under(projectDir).writeText(text)
+
+ @Suppress("UNCHECKED_CAST")
+ val pluginClass = StubSettingsPlugin::class.java as Class>
+ val testFixturesFile = pluginClass.classpathElement()
+
+ val gradleProject = GradleProject.setupAt(projectDir)
+ .withPluginClasspath(testFixturesFile)
+ .create()
+
+ val taskName = BaseTaskName.help
+ val result = gradleProject.executeTask(taskName)
+
+ result[taskName] shouldBe TaskOutcome.SUCCESS
+ result.output shouldContain BUILD_SUCCESSFUL
+ }
}
diff --git a/gradle.properties b/gradle.properties
index 49ad30c59..559cd0d15 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -6,7 +6,11 @@ org.gradle.java.installations.auto-download=true
# Use parallel builds for better performance.
org.gradle.parallel=true
-#org.gradle.caching=true
+
+# Reuse task outputs from the local build cache.
+# On CI, `gradle/actions/setup-gradle` persists `caches/build-cache-1` across runs,
+# so cold builds skip work whose inputs are unchanged.
+org.gradle.caching=true
# Dokka plugin eats more memory than usual. Therefore, all builds should have enough.
org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m -XX:+UseParallelGC -Dfile.encoding=UTF-8
diff --git a/plugin-base/src/test/kotlin/io/spine/tools/gradle/task/GradleTaskBuilderSpec.kt b/plugin-base/src/test/kotlin/io/spine/tools/gradle/task/GradleTaskBuilderSpec.kt
index 20f72e4f7..3789900a1 100644
--- a/plugin-base/src/test/kotlin/io/spine/tools/gradle/task/GradleTaskBuilderSpec.kt
+++ b/plugin-base/src/test/kotlin/io/spine/tools/gradle/task/GradleTaskBuilderSpec.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.
@@ -128,6 +128,22 @@ internal class GradleTaskBuilderSpec {
assertIllegalState { builder.applyNowTo(project) }
}
+ @Test
+ fun `wrap a task registration failure into an 'IllegalStateException'`() {
+ // Reserve the task name up front so that the builder's own
+ // `register(...)` call fails with a duplicate-name error, exercising
+ // the failure-handling branch of `applyNowTo()`.
+ project.tasks.register(preClean.name)
+
+ val builder = GradleTask.newBuilder(preClean, NoOp.action())
+ .insertBeforeTask(BaseTaskName.clean)
+
+ val error = shouldThrow {
+ builder.applyNowTo(project)
+ }
+ error.cause shouldNotBe null
+ }
+
@Test
fun `return build task description`() {
val desc = GradleTask.newBuilder(preClean, NoOp.action())
diff --git a/psi/build.gradle.kts b/psi/build.gradle.kts
index 96b3f3e5f..8bd59dfd4 100644
--- a/psi/build.gradle.kts
+++ b/psi/build.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2024, 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.
@@ -26,6 +26,7 @@
import io.spine.dependency.local.Base
import io.spine.dependency.local.TestLib
+import io.spine.gradle.report.coverage.creditTestCoverageFrom
plugins {
module
@@ -36,3 +37,22 @@ dependencies {
api(project(":intellij-platform"))
testImplementation(TestLib.lib)
}
+
+/**
+ * The language-neutral PSI classes of this module are exercised by the
+ * Java-PSI test fixtures in `psi-java`. Credit that coverage to this module's
+ * own Kover report, which otherwise sees only this module's `test` data.
+ */
+creditTestCoverageFrom(project(":psi-java"))
+
+/**
+ * The `intellij-platform` module assembles its artifact with the Shadow plugin
+ * (via `uber-jar-module`), which disables the regular `jar` task. A consumer that
+ * puts that JAR on its runtime classpath — such as this module's `test` task —
+ * therefore does not get an automatic task dependency on `:intellij-platform:shadowJar`,
+ * which Gradle's task-output validation rejects. Declare it explicitly, mirroring
+ * the workaround already used in `uber-jar-module` for publishing.
+ */
+tasks.named("test") {
+ dependsOn(":intellij-platform:shadowJar")
+}
diff --git a/psi/src/test/kotlin/io/spine/tools/psi/IdeaStandaloneExecutionSpec.kt b/psi/src/test/kotlin/io/spine/tools/psi/IdeaStandaloneExecutionSpec.kt
new file mode 100644
index 000000000..3378d3d3f
--- /dev/null
+++ b/psi/src/test/kotlin/io/spine/tools/psi/IdeaStandaloneExecutionSpec.kt
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.tools.psi
+
+import io.kotest.matchers.shouldBe
+import org.junit.jupiter.api.DisplayName
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.assertDoesNotThrow
+
+@DisplayName("`IdeaStandaloneExecution` should")
+internal class IdeaStandaloneExecutionSpec {
+
+ @Test
+ fun `set up the system properties for the standalone PSI execution`() {
+ // The headless flag is forced on only when it was undefined, so capture
+ // its prior value to assert that exact contract below.
+ val headlessBefore = System.getProperty("java.awt.headless")
+
+ IdeaStandaloneExecution.setUp()
+
+ System.getProperty("idea.io.use.nio2") shouldBe "true"
+ System.getProperty("project.structure.add.tools.jar.to.new.jdk") shouldBe "false"
+ System.getProperty("psi.track.invalidation") shouldBe "true"
+ System.getProperty("psi.incremental.reparse.depth.limit") shouldBe "1000"
+ System.getProperty("ide.hide.excluded.files") shouldBe "false"
+ System.getProperty("ast.loading.filter") shouldBe "false"
+ System.getProperty("idea.ignore.disabled.plugins") shouldBe "true"
+ System.getProperty("idea.plugins.compatible.build") shouldBe "999.SNAPSHOT"
+
+ // `java.awt.headless` is set to `"true"` only if it was previously
+ // undefined; an environment-provided value is left untouched.
+ val headlessAfter = System.getProperty("java.awt.headless")
+ if (headlessBefore == null) {
+ headlessAfter shouldBe "true"
+ } else {
+ headlessAfter shouldBe headlessBefore
+ }
+ }
+
+ @Test
+ fun `be idempotent across repeated calls`() {
+ IdeaStandaloneExecution.setUp()
+ assertDoesNotThrow {
+ // The second call must short-circuit on the `configured` flag.
+ IdeaStandaloneExecution.setUp()
+ }
+ }
+}
diff --git a/psi/src/test/kotlin/io/spine/tools/psi/PsiExtsSpec.kt b/psi/src/test/kotlin/io/spine/tools/psi/PsiExtsSpec.kt
new file mode 100644
index 000000000..9e934bfd1
--- /dev/null
+++ b/psi/src/test/kotlin/io/spine/tools/psi/PsiExtsSpec.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.tools.psi
+
+import io.kotest.matchers.shouldBe
+import org.junit.jupiter.api.DisplayName
+import org.junit.jupiter.api.Test
+
+@DisplayName("`PsiExts` should")
+internal class PsiExtsSpec {
+
+ @Test
+ fun `convert all kinds of line separators to the PSI form`() {
+ val mixed = "alpha\r\nbeta\rgamma\ndelta"
+ mixed.convertLineSeparators() shouldBe "alpha\nbeta\ngamma\ndelta"
+ }
+
+ @Test
+ fun `leave a string already using the PSI separators intact`() {
+ val text = "one\ntwo\nthree"
+ text.convertLineSeparators() shouldBe text
+ }
+}
diff --git a/version.gradle.kts b/version.gradle.kts
index 9df04f2c4..1c45a0692 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.398")
+val versionToPublish: String by extra("2.0.0-SNAPSHOT.399")