diff --git a/.claude/settings.json b/.claude/settings.json
index 08c47574..84a0e53d 100644
--- a/.claude/settings.json
+++ b/.claude/settings.json
@@ -67,6 +67,10 @@
{
"matcher": "Bash",
"hooks": [
+ {
+ "type": "command",
+ "command": "$CLAUDE_PROJECT_DIR/.agents/scripts/secret-scan-gate.sh"
+ },
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.agents/scripts/pre-pr-gate.sh"
diff --git a/.github/workflows/build-on-ubuntu.yml b/.github/workflows/build-on-ubuntu.yml
index 516f347e..01f6fd71 100644
--- a/.github/workflows/build-on-ubuntu.yml
+++ b/.github/workflows/build-on-ubuntu.yml
@@ -1,11 +1,27 @@
name: Ubuntu CI
-on: push
+# Triggers:
+# * push to a default or release-line branch — those ending in `master` or
+# `main`, the same set `increment-guard.yml` guards as PR bases (e.g.
+# `master`, `2.x-jdk8-master`). These post-merge runs are the only source
+# of base-branch coverage, since `Publish` runs `publish -x test` and
+# uploads none; `target: auto` in `.codecov.yml` compares each pull request
+# against its base baseline.
+# * pull_request — gates a change on its merge result, not the branch tip.
+on:
+ push:
+ branches:
+ - '**master'
+ - '**main'
+ pull_request:
jobs:
build:
name: Build on Ubuntu
runs-on: ubuntu-latest
+ concurrency: # Avoid canceling in-progress runs for the same branch.
+ group: ubuntu-ci-${{ github.ref }}
+ cancel-in-progress: false
steps:
- uses: actions/checkout@v6
@@ -19,13 +35,24 @@ jobs:
- uses: gradle/actions/setup-gradle@v6
+ # Mirrors the pagefile step in build-on-windows.yml. The Linux runner
+ # ships with effectively no swap, so a memory peak becomes an instant
+ # OOM kill; this gives the kernel somewhere to fall back to.
+ - name: Add swap space
+ uses: pierotofy/set-swap-space@v1.0
+ with:
+ swap-size-gb: 8
+
+ - name: Build project, run tests
+ shell: bash
+ run: ./gradlew build --stacktrace
+
# `build` does not run Dokka — its tasks are gated to the publishing
# graph — so `dokkaGenerate` is appended to surface documentation
- # warnings on each push, before merge, instead of only in the post-merge
- # `Publish` job. `failOnWarning` is enabled in the Dokka setup.
- - name: Build project, run tests, and check documentation
- shell: bash
- run: ./gradlew build dokkaGenerate --stacktrace
+ # warnings before merge, instead of only in the post-merge `Publish`
+ # job. `failOnWarning` is enabled in the Dokka setup.
+ - name: Check documentation
+ run: ./gradlew dokkaGenerate --stacktrace
# See: https://github.com/marketplace/actions/junit-report-action
- name: Publish Test Report
@@ -35,7 +62,31 @@ jobs:
report_paths: '**/build/test-results/**/TEST-*.xml'
require_tests: true # will fail workflow if test reports not found
+ # Probe whether the upload token is available without exposing its value
+ # to the build/test steps. Scoping `CODECOV_TOKEN` to this trivial step
+ # (and to the upload step's `with.token`) keeps PR-authored code in
+ # `./gradlew build` from ever seeing the secret. The `secrets` context is
+ # not available in a step `if:`, so the upload gates on this output.
+ - name: Detect Codecov token
+ id: codecov
+ env:
+ CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
+ run: |
+ if [ -n "$CODECOV_TOKEN" ]; then
+ echo "available=true" >> "$GITHUB_OUTPUT"
+ else
+ echo "available=false" >> "$GITHUB_OUTPUT"
+ fi
+
+ # On `push` (master) always upload — these runs are the only source of
+ # the `target: auto` baseline, so an absent token there is a
+ # misconfiguration that should fail loudly rather than silently stop
+ # refreshing coverage. On `pull_request`, skip when the token is absent:
+ # forked and Dependabot PRs run without secrets, and `fail_ci_if_error`
+ # would otherwise redden a healthy PR (coverage gating is meaningless
+ # there anyway).
- name: Upload code coverage report
+ if: steps.codecov.outputs.available == 'true' || github.event_name == 'push'
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
diff --git a/.github/workflows/secret-scan.yml b/.github/workflows/secret-scan.yml
new file mode 100644
index 00000000..6f0130e2
--- /dev/null
+++ b/.github/workflows/secret-scan.yml
@@ -0,0 +1,70 @@
+name: Secret scan
+
+# Defense-in-depth behind the local `secret-scan` pre-commit hook and the
+# `.gitignore` secret patterns: if a credential is committed despite those, this
+# fails the pull request before it can merge. Distributed to every Spine repo by
+# `./config/pull`.
+
+on:
+ pull_request:
+ push:
+ branches:
+ - master
+ - main
+
+permissions:
+ contents: read
+
+jobs:
+ gitleaks:
+ name: gitleaks
+ runs-on: ubuntu-latest
+ env:
+ # Pinned gitleaks version — bump through the usual dependency-update process.
+ GITLEAKS_VERSION: "8.21.2"
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v6
+ with:
+ # Full history so a pull request's commit range can be scanned.
+ fetch-depth: 0
+
+ - name: Install gitleaks
+ # Run gitleaks as the runner user against the checkout it owns — no
+ # container, so no "dubious ownership" git error and no GitHub Action
+ # org-licence requirement.
+ run: |
+ curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \
+ | tar -xzf - gitleaks
+ ./gitleaks version
+
+ - name: Scan
+ env:
+ EVENT: ${{ github.event_name }}
+ BASE: ${{ github.event.pull_request.base.sha }}
+ HEAD: ${{ github.event.pull_request.head.sha }}
+ BEFORE: ${{ github.event.before }}
+ AFTER: ${{ github.sha }}
+ run: |
+ if [ "$EVENT" = pull_request ]; then
+ # Scan the PR's own commit RANGE: a secret added in one commit and
+ # deleted in a later commit of the same PR is still caught (a
+ # working-tree scan would miss it, yet merging keeps the secret-bearing
+ # commit reachable), while already-rotated secrets in older history
+ # outside base..head are not re-flagged.
+ ./gitleaks git --log-opts="$BASE..$HEAD" --redact --verbose --exit-code=1 .
+ else
+ # Push to a default branch: scan the pushed commit RANGE (before..after)
+ # so an add-then-remove batch is caught here too, not only on PRs — the
+ # leaked commit would otherwise stay reachable on the default branch. A
+ # branch's first push reports an all-zero `before` (no range); fall back
+ # to a working-tree scan then.
+ if [ -n "$BEFORE" ] && [ "$BEFORE" != "0000000000000000000000000000000000000000" ]; then
+ ./gitleaks git --log-opts="$BEFORE..$AFTER" --redact --verbose --exit-code=1 .
+ else
+ # Branch's first push (all-zero `before`): no range to diff against, so
+ # scan the whole history reachable from the pushed tip as the initial
+ # import — an add-then-remove within those commits is still caught.
+ ./gitleaks git --log-opts="$AFTER" --redact --verbose --exit-code=1 .
+ fi
+ fi
diff --git a/.gitignore b/.gitignore
index 38e5ad4b..dfb4774d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+# >>> shared config (managed by ./config/pull -- do not edit inside this block) >>>
#
# Copyright 2025, TeamDev. All rights reserved.
#
@@ -103,14 +104,48 @@ gradle-app.setting
# Spine internal directory for storing intermediate artifacts
**/.spine/**
-# Login details to Maven repository.
-# Each workstation should have developer's login defined in this file.
+# ---------------------------------------------------------------------------
+# Secrets — NEVER commit these.
+#
+# Encrypted credentials live under `.github/keys/*.gpg` and ARE committed.
+# `config/scripts/decrypt.sh` turns each into its PLAINTEXT twin at build / CI /
+# publish time (e.g. `spine-dev-framework-ci.json.gpg` -> `spine-dev.json`). The
+# decrypted twins below — and any private key or service-account file — must stay
+# out of Git. The shared `secret-scan` pre-commit hook is the backstop if one ever
+# slips past these patterns.
+# ---------------------------------------------------------------------------
+
+# Maven repository login details; each workstation defines its own.
credentials.tar
credentials.properties
cloudrepo.properties
deploy_key_rsa
gcs-auth-key.json
+# Decrypted Google / GCP service-account keys (plaintext twins of *.gpg).
+spine-dev.json
+spine-dev-*.json
+maven-publisher.json
+firebase-sa.json
+*-sa.json
+*service-account*.json
+
+# Decrypted credential property files and portal / publisher secrets.
+*.secret.properties
+
+# Private SSH keys (public keys are *.pub and remain committable).
+*_rsa
+*_dsa
+*_ecdsa
+*_ed25519
+id_rsa
+id_dsa
+id_ecdsa
+id_ed25519
+
+# ...but always keep the committed ENCRYPTED forms.
+!*.gpg
+
# Log files
*.log
@@ -152,3 +187,38 @@ __pycache__/
docs/_preview/node_modules/
docs/_preview/public/
docs/_preview/resources/
+# <<< shared config <<<
+
+# >>> repo-local entries (preserved across ./config/pull) >>>
+!.idea/misc.xml
+!.idea/codeStyleSettings.xml
+!.idea/codeStyles/
+!.idea/copyright/
+!**/src/**/build/**
+!gradle-wrapper.jar
+# Login details to Maven repository.
+# Each workstation should have developer's login defined in this file.
+# <<< repo-local entries <<<
+
+# >>> secret ignores re-asserted last (managed by ./config/pull -- do not edit) >>>
+credentials.tar
+credentials.properties
+cloudrepo.properties
+deploy_key_rsa
+gcs-auth-key.json
+spine-dev.json
+spine-dev-*.json
+maven-publisher.json
+firebase-sa.json
+*-sa.json
+*service-account*.json
+*.secret.properties
+*_rsa
+*_dsa
+*_ecdsa
+*_ed25519
+id_rsa
+id_dsa
+id_ecdsa
+id_ed25519
+# <<< secret ignores <<<
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
index 5c4b3ef9..1b15ab44 100644
--- a/.idea/kotlinc.xml
+++ b/.idea/kotlinc.xml
@@ -3,8 +3,12 @@
+
+
+
+
-
+
-
\ No newline at end of file
+
diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/lib/GoogleApis.kt b/buildSrc/src/main/kotlin/io/spine/dependency/lib/GoogleApis.kt
index 7fd33403..3d5a6967 100644
--- a/buildSrc/src/main/kotlin/io/spine/dependency/lib/GoogleApis.kt
+++ b/buildSrc/src/main/kotlin/io/spine/dependency/lib/GoogleApis.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.
@@ -36,13 +36,13 @@ object GoogleApis {
const val client = "com.google.api-client:google-api-client:1.32.2"
// https://github.com/googleapis/api-common-java
- const val common = "com.google.api:api-common:2.1.1"
+ const val common = "com.google.api:api-common:2.64.0"
// https://github.com/googleapis/java-common-protos
- const val commonProtos = "com.google.api.grpc:proto-google-common-protos:2.7.0"
+ const val commonProtos = "com.google.api.grpc:proto-google-common-protos:2.72.0"
// https://github.com/googleapis/gax-java
- const val gax = "com.google.api:gax:2.7.1"
+ const val gax = "com.google.api:gax:2.80.0"
// https://github.com/googleapis/java-iam
const val protoAim = "com.google.api.grpc:proto-google-iam-v1:1.2.0"
@@ -52,7 +52,7 @@ object GoogleApis {
// https://github.com/googleapis/google-auth-library-java
object AuthLibrary {
- const val version = "1.3.0"
+ const val version = "1.47.0"
const val credentials = "com.google.auth:google-auth-library-credentials:$version"
const val oAuth2Http = "com.google.auth:google-auth-library-oauth2-http:$version"
}
diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/lib/GoogleCloud.kt b/buildSrc/src/main/kotlin/io/spine/dependency/lib/GoogleCloud.kt
index b755168a..0aea8672 100644
--- a/buildSrc/src/main/kotlin/io/spine/dependency/lib/GoogleCloud.kt
+++ b/buildSrc/src/main/kotlin/io/spine/dependency/lib/GoogleCloud.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.
@@ -26,18 +26,21 @@
package io.spine.dependency.lib
+/**
+ * https://github.com/googleapis/google-cloud-java
+ */
@Suppress("unused", "ConstPropertyName")
object GoogleCloud {
- // https://github.com/googleapis/java-core
- const val core = "com.google.cloud:google-cloud-core:2.3.3"
+ // https://github.com/googleapis/google-cloud-java/tree/main/sdk-platform-java/java-core
+ const val core = "com.google.cloud:google-cloud-core:2.71.0"
- // https://github.com/googleapis/java-pubsub/tree/main/proto-google-cloud-pubsub-v1
- const val pubSubGrpcApi = "com.google.api.grpc:proto-google-cloud-pubsub-v1:1.97.0"
+ // https://github.com/googleapis/google-cloud-java/tree/main/java-pubsub/proto-google-cloud-pubsub-v1
+ const val pubSubGrpcApi = "com.google.api.grpc:proto-google-cloud-pubsub-v1:1.151.0"
- // https://github.com/googleapis/java-trace
- const val trace = "com.google.cloud:google-cloud-trace:2.1.0"
+ // https://github.com/googleapis/google-cloud-java/tree/main/java-trace
+ const val trace = "com.google.cloud:google-cloud-trace:2.93.0"
- // https://github.com/googleapis/java-datastore
- const val datastore = "com.google.cloud:google-cloud-datastore:2.2.1"
+ // https://github.com/googleapis/google-cloud-java/tree/main/java-datastore
+ const val datastore = "com.google.cloud:google-cloud-datastore:2.31.2"
}
diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/lib/PerfMark.kt b/buildSrc/src/main/kotlin/io/spine/dependency/lib/PerfMark.kt
new file mode 100644
index 00000000..9a156c36
--- /dev/null
+++ b/buildSrc/src/main/kotlin/io/spine/dependency/lib/PerfMark.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.dependency.lib
+
+// https://github.com/perfmark/perfmark
+@Suppress("unused", "ConstPropertyName")
+object PerfMark {
+ private const val version = "0.27.0"
+ const val api = "io.perfmark:perfmark-api:$version"
+}
diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/Compiler.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Compiler.kt
index d118d7c6..caa002a5 100644
--- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Compiler.kt
+++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Compiler.kt
@@ -72,7 +72,7 @@ object Compiler : Dependency() {
* The version of the Compiler dependencies.
*/
override val version: String
- private const val fallbackVersion = "2.0.0-SNAPSHOT.053"
+ private const val fallbackVersion = "2.0.0-SNAPSHOT.054"
/**
* The distinct version of the Compiler used by other build tools.
@@ -81,7 +81,7 @@ object Compiler : Dependency() {
* transitive dependencies, this is the version used to build the project itself.
*/
val dogfoodingVersion: String
- private const val fallbackDfVersion = "2.0.0-SNAPSHOT.053"
+ private const val fallbackDfVersion = "2.0.0-SNAPSHOT.054"
/**
* The artifact for the Compiler Gradle plugin.
diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvm.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvm.kt
index 805fdb0c..54029eb0 100644
--- a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvm.kt
+++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvm.kt
@@ -39,7 +39,7 @@ typealias CoreJava = CoreJvm
@Suppress("ConstPropertyName", "unused")
object CoreJvm {
const val group = Spine.group
- const val version = "2.0.0-SNAPSHOT.376"
+ const val version = "2.0.0-SNAPSHOT.380"
const val coreArtifact = "spine-core"
const val clientArtifact = "spine-client"
diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt
index 1f91cf24..4754ad5b 100644
--- a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt
+++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt
@@ -46,12 +46,12 @@ object CoreJvmCompiler {
/**
* The version used in the build classpath.
*/
- const val dogfoodingVersion = "2.0.0-SNAPSHOT.077"
+ const val dogfoodingVersion = "2.0.0-SNAPSHOT.079"
/**
* The version to be used for integration tests.
*/
- const val version = "2.0.0-SNAPSHOT.077"
+ const val version = "2.0.0-SNAPSHOT.079"
/**
* The ID of the Gradle plugin.
diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/ToolBase.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/ToolBase.kt
index 54c6ef90..d39bfb08 100644
--- a/buildSrc/src/main/kotlin/io/spine/dependency/local/ToolBase.kt
+++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/ToolBase.kt
@@ -34,8 +34,8 @@ package io.spine.dependency.local
@Suppress("ConstPropertyName", "unused")
object ToolBase {
const val group = Spine.toolsGroup
- const val version = "2.0.0-SNAPSHOT.400"
- const val dogfoodingVersion = "2.0.0-SNAPSHOT.400"
+ const val version = "2.0.0-SNAPSHOT.401"
+ const val dogfoodingVersion = "2.0.0-SNAPSHOT.401"
const val lib = "$group:tool-base:$version"
const val classicCodegen = "$group:classic-codegen:$version"
diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/test/Testcontainers.kt b/buildSrc/src/main/kotlin/io/spine/dependency/test/Testcontainers.kt
new file mode 100644
index 00000000..bb3b22e3
--- /dev/null
+++ b/buildSrc/src/main/kotlin/io/spine/dependency/test/Testcontainers.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.dependency.test
+
+// https://github.com/testcontainers/testcontainers-java
+@Suppress("unused", "ConstPropertyName")
+object Testcontainers {
+ private const val version = "1.21.4"
+ private const val group = "org.testcontainers"
+ const val lib = "$group:testcontainers:$version"
+ const val junitJupiter = "$group:junit-jupiter:$version"
+ const val gcloud = "$group:gcloud:$version"
+}
diff --git a/config b/config
index 89ddc759..0728520a 160000
--- a/config
+++ b/config
@@ -1 +1 @@
-Subproject commit 89ddc75919d75cc9ceee8822c2fdc93a24784e8b
+Subproject commit 0728520aa2b6e418d9908ca226c2d331c8d94c83
diff --git a/docs/dependencies/dependencies.md b/docs/dependencies/dependencies.md
index d0ec489f..55e9b666 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.401`
+# Dependencies of `io.spine.tools:classic-codegen:2.0.0-SNAPSHOT.402`
## 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 **Fri Jun 19 11:10:55 WEST 2026** using
+This report was generated on **Wed Jun 24 19:40:34 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.401`
+# Dependencies of `io.spine.tools:gradle-plugin-api:2.0.0-SNAPSHOT.402`
## Runtime
1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.0.
@@ -1734,14 +1734,14 @@ This report was generated on **Fri Jun 19 11:10:55 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Fri Jun 19 11:10:55 WEST 2026** using
+This report was generated on **Wed Jun 24 19:40:34 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.401`
+# Dependencies of `io.spine.tools:gradle-plugin-api-test-fixtures:2.0.0-SNAPSHOT.402`
## Runtime
1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.0.
@@ -2212,14 +2212,14 @@ This report was generated on **Fri Jun 19 11:10:55 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Fri Jun 19 11:10:54 WEST 2026** using
+This report was generated on **Wed Jun 24 19:40:33 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.401`
+# Dependencies of `io.spine.tools:gradle-root-plugin:2.0.0-SNAPSHOT.402`
## Runtime
1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2.
@@ -3070,14 +3070,14 @@ This report was generated on **Fri Jun 19 11:10:54 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Fri Jun 19 11:10:55 WEST 2026** using
+This report was generated on **Wed Jun 24 19:40:34 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.401`
+# Dependencies of `io.spine.tools:intellij-platform:2.0.0-SNAPSHOT.402`
## Runtime
1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3.
@@ -4151,14 +4151,14 @@ This report was generated on **Fri Jun 19 11:10:55 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Fri Jun 19 11:10:55 WEST 2026** using
+This report was generated on **Wed Jun 24 19:40:34 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.401`
+# Dependencies of `io.spine.tools:intellij-platform-java:2.0.0-SNAPSHOT.402`
## Runtime
1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3.
@@ -5930,14 +5930,14 @@ This report was generated on **Fri Jun 19 11:10:55 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Fri Jun 19 11:10:56 WEST 2026** using
+This report was generated on **Wed Jun 24 19:40:34 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.401`
+# Dependencies of `io.spine.tools:jvm-tool-plugins:2.0.0-SNAPSHOT.402`
## Runtime
1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2.
@@ -6780,14 +6780,14 @@ This report was generated on **Fri Jun 19 11:10:56 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Fri Jun 19 11:10:55 WEST 2026** using
+This report was generated on **Wed Jun 24 19:40:34 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.401`
+# Dependencies of `io.spine.tools:jvm-tools:2.0.0-SNAPSHOT.402`
## Runtime
1. **Group** : org.jetbrains. **Name** : annotations. **Version** : 26.1.0.
@@ -7547,14 +7547,14 @@ This report was generated on **Fri Jun 19 11:10:55 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Fri Jun 19 11:10:55 WEST 2026** using
+This report was generated on **Wed Jun 24 19:40:34 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.401`
+# Dependencies of `io.spine.tools:plugin-base:2.0.0-SNAPSHOT.402`
## Runtime
1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2.
@@ -8405,14 +8405,14 @@ This report was generated on **Fri Jun 19 11:10:55 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Fri Jun 19 11:10:55 WEST 2026** using
+This report was generated on **Wed Jun 24 19:40:34 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.401`
+# Dependencies of `io.spine.tools:plugin-testlib:2.0.0-SNAPSHOT.402`
## Runtime
1. **Group** : com.google.auto.value. **Name** : auto-value-annotations. **Version** : 1.11.1.
@@ -9367,14 +9367,14 @@ This report was generated on **Fri Jun 19 11:10:55 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Fri Jun 19 11:10:55 WEST 2026** using
+This report was generated on **Wed Jun 24 19:40:34 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.401`
+# Dependencies of `io.spine.tools:protobuf-setup-plugins:2.0.0-SNAPSHOT.402`
## Runtime
1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2.
@@ -10237,14 +10237,14 @@ This report was generated on **Fri Jun 19 11:10:55 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Fri Jun 19 11:10:55 WEST 2026** using
+This report was generated on **Wed Jun 24 19:40:34 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.401`
+# Dependencies of `io.spine.tools:psi:2.0.0-SNAPSHOT.402`
## Runtime
1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3.
@@ -11345,14 +11345,14 @@ This report was generated on **Fri Jun 19 11:10:55 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Fri Jun 19 11:10:55 WEST 2026** using
+This report was generated on **Wed Jun 24 19:40:34 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.401`
+# Dependencies of `io.spine.tools:psi-java:2.0.0-SNAPSHOT.402`
## Runtime
1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3.
@@ -13167,14 +13167,14 @@ This report was generated on **Fri Jun 19 11:10:55 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Fri Jun 19 11:10:56 WEST 2026** using
+This report was generated on **Wed Jun 24 19:40:34 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.401`
+# Dependencies of `io.spine.tools:tool-base:2.0.0-SNAPSHOT.402`
## Runtime
1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2.
@@ -14054,6 +14054,6 @@ This report was generated on **Fri Jun 19 11:10:56 WEST 2026** using
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Fri Jun 19 11:10:55 WEST 2026** using
+This report was generated on **Wed Jun 24 19:40:34 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 43922c44..fb1223a2 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.toolstool-base
-2.0.0-SNAPSHOT.401
+2.0.0-SNAPSHOT.4022015
diff --git a/init-submodules b/init-submodules
index 0c12a281..2ae143d8 100755
--- a/init-submodules
+++ b/init-submodules
@@ -84,4 +84,16 @@ git submodule status 2>/dev/null | awk '$1 ~ /^-/ { print $2 }' | while read -r
fi
done
+# Route Git hooks to the shared hooks directory so the secret-scan `pre-commit`
+# hook is active even in a brand-new worktree, before `./config/pull` runs. The
+# path floats with the `.agents/shared` submodule; until that submodule is
+# initialized the hook simply does not fire (Git skips a missing hook). Set only
+# when unset or already ours — never override a repo's own `core.hooksPath`.
+desired_hooks=".agents/scripts/git-hooks"
+current_hooks=$(git config --local --get core.hooksPath 2>/dev/null || true)
+if [ -z "$current_hooks" ] || [ "$current_hooks" = "$desired_hooks" ]; then
+ git config --local core.hooksPath "$desired_hooks" \
+ && echo "init-submodules: Git hooks routed to '$desired_hooks' (secret-scan pre-commit active)."
+fi
+
exit 0
diff --git a/protobuf-setup-plugins/src/main/kotlin/io/spine/tools/protobuf/gradle/plugin/DescriptorSetFilePlugin.kt b/protobuf-setup-plugins/src/main/kotlin/io/spine/tools/protobuf/gradle/plugin/DescriptorSetFilePlugin.kt
index 8731db6a..b1b06bd2 100644
--- a/protobuf-setup-plugins/src/main/kotlin/io/spine/tools/protobuf/gradle/plugin/DescriptorSetFilePlugin.kt
+++ b/protobuf-setup-plugins/src/main/kotlin/io/spine/tools/protobuf/gradle/plugin/DescriptorSetFilePlugin.kt
@@ -47,6 +47,12 @@ public class DescriptorSetFilePlugin : ProtobufSetupPlugin() {
* The ID of this Gradle plugin.
*/
const val id = "io.spine.descriptor-set-file"
+
+ /**
+ * The name of the [GenerateProtoTask] input property holding the descriptor set
+ * file name.
+ */
+ const val DESCRIPTOR_SET_NAME_PROPERTY = "descriptorSetName"
}
override fun setup(task: GenerateProtoTask) {
@@ -77,6 +83,7 @@ public class DescriptorSetFilePlugin : ProtobufSetupPlugin() {
DescriptorSetReferenceFile.create(descriptorsDir, descriptorSetFile)
}
task.declareReferenceFileOutput(descriptorsDir)
+ task.declareDescriptorSetNameInput(descriptorSetFile)
task.dependOnProcessResourcesTask()
}
@@ -102,6 +109,32 @@ private fun GenerateProtoTask.declareReferenceFileOutput(descriptorsDir: File) {
.withPropertyName(REFERENCE_FILE_PROPERTY)
}
+/**
+ * Declares the descriptor set file name as an explicit input of this task so that
+ * a change to the name invalidates the cached descriptor set.
+ *
+ * The descriptor set file name is derived from the project's Maven coordinates — group,
+ * artifact ID, version, and classifier (see [descriptorSetFile]) — and the
+ * [reference file][DescriptorSetReferenceFile] written in the `doLast` action points to
+ * that name. [GenerateProtoTask] is a cacheable task that keys its up-to-date check and
+ * build-cache entry on the `.proto` sources and the compiler configuration only — not on
+ * the names of the produced files.
+ *
+ * After a coordinate-only change — most commonly a version bump — the Proto sources are
+ * unchanged, so the task is restored from the build cache, bringing back a descriptor set
+ * produced under the previous name while the reference file points to the new name. The
+ * mismatch leaves Protobuf types unresolvable at runtime, surfacing as an
+ * `UnknownTypeException`.
+ *
+ * Declaring the descriptor set file name as an input makes the build cache regenerate the
+ * descriptor set and its reference file whenever the name changes — covering version,
+ * group, artifact ID, and classifier changes alike — while preserving caching for all
+ * other changes.
+ */
+private fun GenerateProtoTask.declareDescriptorSetNameInput(descriptorSetFile: File) {
+ inputs.property(DescriptorSetFilePlugin.DESCRIPTOR_SET_NAME_PROPERTY, descriptorSetFile.name)
+}
+
/**
* Make the `processResources` task depend on this `GenerateProtoTask`.
*/
diff --git a/protobuf-setup-plugins/src/test/kotlin/io/spine/tools/protobuf/gradle/plugin/BuildCacheSpec.kt b/protobuf-setup-plugins/src/test/kotlin/io/spine/tools/protobuf/gradle/plugin/BuildCacheSpec.kt
index e54ca0e8..fb11593e 100644
--- a/protobuf-setup-plugins/src/test/kotlin/io/spine/tools/protobuf/gradle/plugin/BuildCacheSpec.kt
+++ b/protobuf-setup-plugins/src/test/kotlin/io/spine/tools/protobuf/gradle/plugin/BuildCacheSpec.kt
@@ -70,7 +70,12 @@ internal class BuildCacheSpec : ProtobufPluginTest() {
private val descRef: File
get() = File(descriptorsDir, DescriptorSetReferenceFile.NAME)
private val expectedDescriptorName: String
- get() = "${group}_${projectDir.name}_${version}.desc"
+ get() = descriptorName()
+
+ private fun descriptorName(
+ group: String = this.group,
+ version: String = this.version
+ ): String = "${group}_${projectDir.name}_${version}.desc"
@Test
fun `restore generated code and the reference file from the cache after 'clean'`() {
@@ -111,9 +116,86 @@ internal class BuildCacheSpec : ProtobufPluginTest() {
assertCodegenComplete(result)
}
+ @Test
+ fun `regenerate the descriptor set after a version change with the cache on`() {
+ setupProject()
+
+ // Seed the build cache at the initial version.
+ assertCodegenComplete(build(buildCacheArg))
+
+ // Bump the project version, leaving the Proto sources unchanged.
+ val newVersion = "1.0.1"
+ bumpVersionTo(newVersion)
+
+ // Drop the build outputs. The local build cache lives outside `build/`,
+ // so it survives `clean` and could still serve the previous descriptor set.
+ runGradleBuild(projectDir, listOf("clean", buildCacheArg))
+
+ val result = build(buildCacheArg)
+
+ // The descriptor set file name (which embeds the version) is a `generateProto`
+ // input, so the task re-executes instead of restoring a descriptor set produced
+ // for the previous version.
+ result.task(generateProto.path())?.outcome shouldBe SUCCESS
+
+ // The descriptor set and its reference file are regenerated for the new version
+ // and stay consistent with each other.
+ val newDescriptorName = descriptorName(version = newVersion)
+ descRef.readText().trim() shouldBe newDescriptorName
+ File(descriptorsDir, newDescriptorName).exists() shouldBe true
+ File(projectDir, "build/resources/main/$newDescriptorName").exists() shouldBe true
+ File(projectDir, "build/resources/main/${DescriptorSetReferenceFile.NAME}")
+ .readText().trim() shouldBe newDescriptorName
+ }
+
+ @Test
+ fun `regenerate the descriptor set after a group change with the cache on`() {
+ setupProject()
+
+ // Seed the build cache at the initial Maven coordinates.
+ assertCodegenComplete(build(buildCacheArg))
+
+ // Change the project group, leaving the version and the Proto sources unchanged.
+ // The version alone is therefore not enough to distinguish the two builds.
+ val newGroup = "cache.test.renamed"
+ changeGroupTo(newGroup)
+
+ runGradleBuild(projectDir, listOf("clean", buildCacheArg))
+
+ val result = build(buildCacheArg)
+
+ // The descriptor set file name embeds the group, so a coordinate change other than
+ // the version still re-runs the task instead of restoring a stale descriptor set.
+ result.task(generateProto.path())?.outcome shouldBe SUCCESS
+
+ val newDescriptorName = descriptorName(group = newGroup)
+ descRef.readText().trim() shouldBe newDescriptorName
+ File(descriptorsDir, newDescriptorName).exists() shouldBe true
+ File(projectDir, "build/resources/main/$newDescriptorName").exists() shouldBe true
+ }
+
private fun build(vararg options: String): BuildResult =
runGradleBuild(projectDir, listOf("build") + options)
+ /**
+ * Bumps the project version in the build file, leaving the rest of the project intact.
+ */
+ private fun bumpVersionTo(newVersion: String) {
+ replaceInBuildFile("version = \"$version\"", "version = \"$newVersion\"")
+ }
+
+ /**
+ * Changes the project group in the build file, leaving the rest of the project intact.
+ */
+ private fun changeGroupTo(newGroup: String) {
+ replaceInBuildFile("group = \"$group\"", "group = \"$newGroup\"")
+ }
+
+ private fun replaceInBuildFile(old: String, new: String) {
+ val buildFile = Gradle.buildFile.under(projectDir)
+ buildFile.writeText(buildFile.readText().replace(old, new))
+ }
+
/**
* Asserts that the [result] is successful and all the files produced by
* the plugins are in place.
diff --git a/version.gradle.kts b/version.gradle.kts
index be50dc95..b82214e6 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.401")
+val versionToPublish: String by extra("2.0.0-SNAPSHOT.402")