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.tools tool-base -2.0.0-SNAPSHOT.401 +2.0.0-SNAPSHOT.402 2015 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")