From 42fbe4f72c3f6335060a802778e564d6adb602b9 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Jun 2026 03:21:48 +0000 Subject: [PATCH 01/16] Remove `CodeGeneratorRequestWriter`, moving it to ToolBase The class is protoc-plugin tooling, not runtime API: its only consumers are the protoc-plugin entry points of the Compiler and ProtoTap. It moves to the `tool-base` module of ToolBase as `io.spine.tools.code.proto.CodeGeneratorRequestWriter`. `CodeGeneratorRequestParsingSpec` and `CodeGeneratorRequestsJavaSpec` stay because they test the `io.spine.type` parsing API, which remains in `base`. See #938 --- .../tasks/938-move-codegen-request-writer.md | 38 ++++++++ .../code/proto/CodeGeneratorRequestWriter.kt | 97 ------------------- .../proto/CodeGeneratorRequestWriterSpec.kt | 77 --------------- 3 files changed, 38 insertions(+), 174 deletions(-) create mode 100644 .agents/tasks/938-move-codegen-request-writer.md delete mode 100644 base/src/main/kotlin/io/spine/code/proto/CodeGeneratorRequestWriter.kt delete mode 100644 base/src/test/kotlin/io/spine/code/proto/CodeGeneratorRequestWriterSpec.kt diff --git a/.agents/tasks/938-move-codegen-request-writer.md b/.agents/tasks/938-move-codegen-request-writer.md new file mode 100644 index 0000000000..5c1830e3c4 --- /dev/null +++ b/.agents/tasks/938-move-codegen-request-writer.md @@ -0,0 +1,38 @@ +--- +slug: 938-move-codegen-request-writer +branch: claude/busy-dirac-wwflbm +owner: claude +status: in-progress +started: 2026-06-10 +--- + +## Goal + +`io.spine.code.proto.CodeGeneratorRequestWriter` is removed from `base` +(it is protoc-plugin tooling, not runtime API), and the build is green. +Closes [#938](https://github.com/SpineEventEngine/base-libraries/issues/938) +together with the receiving change in `tool-base`. + +## Context + +- The class moves to the `tool-base` module of the ToolBase repository under + `io.spine.tools.code.proto` (same-named branch there). +- The only consumers are the protoc-plugin entry points of the Compiler and + ProtoTap; they migrate by switching the import once both PRs are published. +- `CodeGeneratorRequestParsingSpec.kt` and `CodeGeneratorRequestsJavaSpec.java` + stay: they test `io.spine.type` parsing APIs which remain in `base`, and the + Java spec still uses the `constructRequest` helper declared in the former. +- Removing public API is a breaking change: the snapshot version advances to + the next multiple of 10. + +## Plan + +- [ ] Remove `base/src/main/kotlin/io/spine/code/proto/CodeGeneratorRequestWriter.kt`. +- [ ] Remove `base/src/test/kotlin/io/spine/code/proto/CodeGeneratorRequestWriterSpec.kt`. +- [ ] Bump version `2.0.0-SNAPSHOT.404` -> `2.0.0-SNAPSHOT.410` (breaking). +- [ ] `./gradlew build` green; commit regenerated dependency reports if any. +- [ ] Push and open a draft PR; merge after the tool-base PR. + +## Log + +- 2026-06-10 — drafted; executing autonomously per issue #938. diff --git a/base/src/main/kotlin/io/spine/code/proto/CodeGeneratorRequestWriter.kt b/base/src/main/kotlin/io/spine/code/proto/CodeGeneratorRequestWriter.kt deleted file mode 100644 index afa339f294..0000000000 --- a/base/src/main/kotlin/io/spine/code/proto/CodeGeneratorRequestWriter.kt +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2024, 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.code.proto - -import com.google.protobuf.compiler.PluginProtos.CodeGeneratorRequest -import io.spine.io.replaceExtension -import io.spine.string.decodeBase64 -import io.spine.type.ExtensionRegistryHolder.extensionRegistry -import io.spine.type.parse -import io.spine.type.toJson -import java.io.File -import java.io.InputStream -import java.nio.file.StandardOpenOption.CREATE -import java.nio.file.StandardOpenOption.TRUNCATE_EXISTING -import kotlin.io.path.writeBytes - -/** - * Parses a [CodeGeneratorRequest] from given [input] and writes it into - * files in [binary][writeBinary] and [JSON][writeJson] format. - * - * @param input The input stream containing binary version of the request. - */ -public class CodeGeneratorRequestWriter( - private val input: InputStream -) { - /** - * Lazily evaluated [CodeGeneratorRequest] parsed from [input] using [extensionRegistry]. - */ - public val request: CodeGeneratorRequest by lazy { - CodeGeneratorRequest::class.parse(input) - } - - /** - * The target file for writing the request in the binary form. - * - * The name of the request is passed as the [parameter][CodeGeneratorRequest.getParameter] of - * the request as a Base64 encoded file path. - */ - public val requestFile: File by lazy { - File(request.parameter.decodeBase64()) - } - - /** - * The path to the request file in JSON format. - * - * The file has the same name as [requestFile] and the extension of `".pb.json"`. - */ - public val requestFileInJson: File by lazy { - requestFile.replaceExtension("pb.json") - } - - /** - * Writes the request into the location specified in [requestFile]. - */ - public fun writeBinary() { - ensureDirectory() - requestFile.toPath().writeBytes(request.toByteArray(), CREATE, TRUNCATE_EXISTING) - } - - /** - * Writes the request in JSON format to the location specified in [requestFileInJson]. - */ - public fun writeJson() { - val json = request.toJson() - ensureDirectory() - requestFileInJson.writeText(json) - } - - private fun ensureDirectory() { - val targetDir = requestFile.parentFile - targetDir.mkdirs() - } -} diff --git a/base/src/test/kotlin/io/spine/code/proto/CodeGeneratorRequestWriterSpec.kt b/base/src/test/kotlin/io/spine/code/proto/CodeGeneratorRequestWriterSpec.kt deleted file mode 100644 index e85231b8d1..0000000000 --- a/base/src/test/kotlin/io/spine/code/proto/CodeGeneratorRequestWriterSpec.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2024, 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.code.proto - -import io.kotest.matchers.shouldBe -import io.spine.io.replaceExtension -import io.spine.string.toBase64Encoded -import java.io.File -import java.io.InputStream -import java.nio.file.Path -import kotlin.io.path.inputStream -import kotlin.io.path.writeBytes -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.io.TempDir - -@DisplayName("`CodeGeneratorRequestWriter` should") -internal class CodeGeneratorRequestWriterSpec { - - private lateinit var requestFile: File - private lateinit var writer: CodeGeneratorRequestWriter - private lateinit var input: InputStream - - @BeforeEach - fun prepareInput(@TempDir dir: Path) { - val inputFile = dir.resolve("input.stream") - // Request the file in the directory which does not exist. - requestFile = dir.resolve("nested/request.binbp").toFile() - val request = constructRequest(requestFile.absolutePath.toBase64Encoded()) - inputFile.writeBytes(request.toByteArray()) - input = inputFile.inputStream() - writer = CodeGeneratorRequestWriter(input) - } - - @AfterEach - fun closeInput() { - input.close() - } - - @Test - fun `write binary version of the request`() { - writer.writeBinary() - requestFile.exists() shouldBe true - } - - @Test - fun `write JSON version of the request`() { - writer.writeJson() - requestFile.replaceExtension("pb.json").exists() shouldBe true - } -} From bcfa83d56c1fb21b97776125a94bd9d47e9cad21 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Jun 2026 03:22:04 +0000 Subject: [PATCH 02/16] Bump version -> `2.0.0-SNAPSHOT.410` --- version.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.gradle.kts b/version.gradle.kts index c5226ba68a..2a2a38a5a9 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.404") +val versionToPublish: String by extra("2.0.0-SNAPSHOT.410") From 3dc19b316f4aa3688e66dbbd59a36d44cea8e207 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Jun 2026 03:23:02 +0000 Subject: [PATCH 03/16] Update copyright year in `version.gradle.kts` --- version.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.gradle.kts b/version.gradle.kts index 2a2a38a5a9..36a5dde535 100644 --- a/version.gradle.kts +++ b/version.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. From 67d879d5f4bc3f67f0ce9eebd3e8ffd904e683b0 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Jun 2026 03:27:38 +0000 Subject: [PATCH 04/16] Update task plan --- .agents/tasks/938-move-codegen-request-writer.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.agents/tasks/938-move-codegen-request-writer.md b/.agents/tasks/938-move-codegen-request-writer.md index 5c1830e3c4..476ce91fc8 100644 --- a/.agents/tasks/938-move-codegen-request-writer.md +++ b/.agents/tasks/938-move-codegen-request-writer.md @@ -27,12 +27,20 @@ together with the receiving change in `tool-base`. ## Plan -- [ ] Remove `base/src/main/kotlin/io/spine/code/proto/CodeGeneratorRequestWriter.kt`. -- [ ] Remove `base/src/test/kotlin/io/spine/code/proto/CodeGeneratorRequestWriterSpec.kt`. -- [ ] Bump version `2.0.0-SNAPSHOT.404` -> `2.0.0-SNAPSHOT.410` (breaking). +- [x] Remove `base/src/main/kotlin/io/spine/code/proto/CodeGeneratorRequestWriter.kt`. +- [x] Remove `base/src/test/kotlin/io/spine/code/proto/CodeGeneratorRequestWriterSpec.kt`. +- [x] Bump version `2.0.0-SNAPSHOT.404` -> `2.0.0-SNAPSHOT.410` (breaking). - [ ] `./gradlew build` green; commit regenerated dependency reports if any. -- [ ] Push and open a draft PR; merge after the tool-base PR. + - Blocked in the sandbox: all Spine artifact repositories return 403 for + the buildscript dependency `io.spine.tools:protobuf-setup-plugins`, so + no Gradle build can run here at all. Verification is delegated to PR CI. + - Repo-wide greps confirm no remaining references to the removed class; + the surviving `CodeGeneratorRequest*` specs do not use it. +- [x] Push and open a draft PR; merge after the tool-base PR. ## Log - 2026-06-10 — drafted; executing autonomously per issue #938. +- 2026-06-10 — removal committed and version bumped; sandbox cannot resolve + Spine snapshot artifacts (403 on all repos), so the build runs on PR CI + instead. From 27a157f787070fa4b8421f597a85bc71a2210107 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Jun 2026 03:30:56 +0000 Subject: [PATCH 05/16] Update dependency reports --- docs/dependencies/dependencies.md | 8 ++++---- docs/dependencies/pom.xml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/dependencies/dependencies.md b/docs/dependencies/dependencies.md index cb32f01cab..f4f908f261 100644 --- a/docs/dependencies/dependencies.md +++ b/docs/dependencies/dependencies.md @@ -1,6 +1,6 @@ -# Dependencies of `io.spine:spine-annotations:2.0.0-SNAPSHOT.404` +# Dependencies of `io.spine:spine-annotations:2.0.0-SNAPSHOT.410` ## Runtime 1. **Group** : org.jetbrains. **Name** : annotations. **Version** : 26.1.0. @@ -767,7 +767,7 @@ This report was generated on **Mon Jun 08 18:37:28 WEST 2026** using -# Dependencies of `io.spine:spine-base:2.0.0-SNAPSHOT.404` +# Dependencies of `io.spine:spine-base:2.0.0-SNAPSHOT.410` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -1611,7 +1611,7 @@ This report was generated on **Mon Jun 08 18:37:29 WEST 2026** using -# Dependencies of `io.spine:spine-environment:2.0.0-SNAPSHOT.404` +# Dependencies of `io.spine:spine-environment:2.0.0-SNAPSHOT.410` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -2437,7 +2437,7 @@ This report was generated on **Mon Jun 08 18:37:28 WEST 2026** using -# Dependencies of `io.spine:spine-format:2.0.0-SNAPSHOT.404` +# Dependencies of `io.spine:spine-format:2.0.0-SNAPSHOT.410` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.0. diff --git a/docs/dependencies/pom.xml b/docs/dependencies/pom.xml index c487c5f049..df9b10ba9b 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 base-libraries -2.0.0-SNAPSHOT.404 +2.0.0-SNAPSHOT.410 2015 From 5201e70ad4850eda643d5bb82e8e178c7e6431b7 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Jun 2026 03:46:32 +0000 Subject: [PATCH 06/16] Move `MessageFile` out of `io.spine.base` The enum encodes a code-generation file-naming convention (`commands.proto`, `events.proto`, `rejections.proto`) and its only consumer across the organisation is `CoreJvmCompilerSettings` in CoreJvmCompiler, where it now lives as `io.spine.tools.core.jvm.MessageFile`. The usages internal to `base` are refactored to do without the enum: - `FileName` now checks the conventional suffixes directly. - `FieldDeclaration.isCommandsFile()` uses `FileName.isCommands()`. The unit test moves to CoreJvmCompiler along with the enum, so `MessageFileTest` and its test-only proto file are removed here. See #941. https://claude.ai/code/session_01RoCv4qxE8c32kPMLWVKw9M --- .../move-message-file-to-core-jvm-compiler.md | 65 ++++++++++++++++ .../main/java/io/spine/base/MessageFile.java | 77 ------------------- .../io/spine/code/proto/FieldDeclaration.java | 7 +- .../java/io/spine/code/proto/FileName.java | 24 ++++-- .../java/io/spine/base/MessageFileTest.java | 59 -------------- .../test/base/message_file_test_events.proto | 39 ---------- 6 files changed, 84 insertions(+), 187 deletions(-) create mode 100644 .agents/tasks/move-message-file-to-core-jvm-compiler.md delete mode 100644 base/src/main/java/io/spine/base/MessageFile.java delete mode 100644 base/src/test/java/io/spine/base/MessageFileTest.java delete mode 100644 base/src/test/proto/spine/test/base/message_file_test_events.proto diff --git a/.agents/tasks/move-message-file-to-core-jvm-compiler.md b/.agents/tasks/move-message-file-to-core-jvm-compiler.md new file mode 100644 index 0000000000..da55e8d8be --- /dev/null +++ b/.agents/tasks/move-message-file-to-core-jvm-compiler.md @@ -0,0 +1,65 @@ +--- +slug: move-message-file-to-core-jvm-compiler +branch: claude/cool-lamport-rnwzpu +owner: claude +status: in-progress +started: 2026-06-10 +--- + +## Goal + +Resolve [#941](https://github.com/SpineEventEngine/base-libraries/issues/941): +`base` no longer ships `io.spine.base.MessageFile`; the enum lives in +CoreJvmCompiler (`core-jvm-base`, package `io.spine.tools.core.jvm`), where its +only org-wide consumer (`CoreJvmCompilerSettings`) resides. `base` compiles and +its tests pass without the enum. + +## Context + +- The enum encodes a *code-generation* file-naming convention + (`commands.proto`, `events.proto`, `rejections.proto`), so it belongs to + the tooling chain, not the runtime `base` artifact. +- Strategy: **coordinated hard move** (no `@Deprecated` shim), following the + precedent of `move-fs-dir-types-to-tool-base` (archived task, approved + 2026-06-04). Cross-repo move without `git mv` is accepted for the same + reason. +- Internal usages inside `base` (overlooked by the issue text) must be + refactored first: + - `io.spine.code.proto.FileName.matches(MessageFile)` — private helper + behind `isCommands()/isEvents()/isRejections()`. + - `io.spine.code.proto.FieldDeclaration.isCommandsFile()` — uses + `MessageFile.COMMANDS.test(...)`. +- `FileName.isCommands()/isEvents()/isRejections()` keep direct coverage in + `FileNameSpec.kt`, so no coverage is lost by the refactoring. +- The CoreJvmCompiler side is tracked in that repo's task file of the same + slug (branch `claude/cool-lamport-rnwzpu` there as well). The two PRs are + independent: the new enum lives in a different package, and CoreJvmCompiler + pins a published `spine-base` that still contains the old class until its + `Base` dependency is bumped later. + +## Plan + +- [x] Refactor `FileName.java`: inline the three suffix constants, replace + `matches(MessageFile)` with a private `hasSuffix(String)`. +- [x] Refactor `FieldDeclaration.isCommandsFile()` to + `FileName.from(file).isCommands()`. +- [x] `git rm base/src/main/java/io/spine/base/MessageFile.java`. +- [x] `git rm base/src/test/java/io/spine/base/MessageFileTest.java` and + `base/src/test/proto/spine/test/base/message_file_test_events.proto` + (test ported to CoreJvmCompiler). +- [x] Update copyright headers of modified files to 2026. +- [x] Bump `version.gradle.kts` → `2.0.0-SNAPSHOT.405`; project version + strings in `docs/dependencies/*` updated to match (no dependency + changed, so the regenerated content differs only in those strings). +- [ ] Build `:base` and run its tests — **blocked locally**: the remote + session's network policy returns 403 for the Spine artifact + repositories (GitHub Packages, CloudRepo, Artifact Registry), so + Gradle cannot resolve `io.spine.tools:protobuf-setup-plugins` and + friends. Verification delegated to PR CI. +- [x] Commit, push, draft PR referencing #941. + +## Log + +- 2026-06-10 — drafted; executing (work authorized by the issue assignment). +- 2026-06-10 — sources done; local Gradle build impossible (403 from all + Spine repos under the session network policy); relying on PR CI. diff --git a/base/src/main/java/io/spine/base/MessageFile.java b/base/src/main/java/io/spine/base/MessageFile.java deleted file mode 100644 index 1f74189db4..0000000000 --- a/base/src/main/java/io/spine/base/MessageFile.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2022, 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 - * - * http://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.base; - -import com.google.protobuf.DescriptorProtos.FileDescriptorProto; -import io.spine.code.proto.FileName; - -import java.util.function.Predicate; - -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * A enumeration of file naming conventions for pre-defined types of messages. - */ -public enum MessageFile implements Predicate { - - /** - * Commands are declared in a file which name ends with {@code "commands.proto"}. - */ - COMMANDS("commands"), - - /** - * Events are declared in a file which name ends with {@code "events.proto"}. - */ - EVENTS("events"), - - /** - * Rejections are declared in a file which name ends with {@code "rejections.proto"}. - */ - REJECTIONS("rejections"); - - private final String suffix; - - MessageFile(String name) { - this.suffix = checkNotNull(name) + FileName.EXTENSION; - } - - /** - * Checks if the name of the given file matches this suffix. - */ - @Override - public boolean test(FileDescriptorProto file) { - var name = file.getName(); - return name.endsWith(suffix); - } - - /** - * Obtains a suffix required for this kind of files. - */ - public String suffix() { - return suffix; - } -} diff --git a/base/src/main/java/io/spine/code/proto/FieldDeclaration.java b/base/src/main/java/io/spine/code/proto/FieldDeclaration.java index ccf793dbfd..2a64ca83af 100644 --- a/base/src/main/java/io/spine/code/proto/FieldDeclaration.java +++ b/base/src/main/java/io/spine/code/proto/FieldDeclaration.java @@ -1,11 +1,11 @@ /* - * Copyright 2022, 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. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 @@ -34,7 +34,6 @@ import com.google.protobuf.Descriptors.FieldDescriptor.JavaType; import com.google.protobuf.Message; import io.spine.annotation.Internal; -import io.spine.base.MessageFile; import io.spine.code.java.ClassName; import io.spine.option.EntityOption; import io.spine.option.OptionsProto; @@ -353,7 +352,7 @@ private boolean isFirstField() { private boolean isCommandsFile() { var file = field.getFile(); - var result = MessageFile.COMMANDS.test(file.toProto()); + var result = FileName.from(file).isCommands(); return result; } diff --git a/base/src/main/java/io/spine/code/proto/FileName.java b/base/src/main/java/io/spine/code/proto/FileName.java index 4f595f6d92..f476c09796 100644 --- a/base/src/main/java/io/spine/code/proto/FileName.java +++ b/base/src/main/java/io/spine/code/proto/FileName.java @@ -1,11 +1,11 @@ /* - * Copyright 2022, 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. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 @@ -30,7 +30,6 @@ import com.google.errorprone.annotations.Immutable; import com.google.protobuf.DescriptorProtos.FileDescriptorProto; import com.google.protobuf.Descriptors.FileDescriptor; -import io.spine.base.MessageFile; import io.spine.code.fs.AbstractFileName; import java.io.Serial; @@ -55,6 +54,15 @@ public class FileName extends AbstractFileName implements UnderscoredN /** The file system separator as defined by Protobuf. Not platform-dependent. */ private static final char PATH_SEPARATOR = '/'; + /** The conventional suffix of a file declaring command messages. */ + private static final String COMMANDS_SUFFIX = "commands" + EXTENSION; + + /** The conventional suffix of a file declaring event messages. */ + private static final String EVENTS_SUFFIX = "events" + EXTENSION; + + /** The conventional suffix of a file declaring rejection messages. */ + private static final String REJECTIONS_SUFFIX = "rejections" + EXTENSION; + private FileName(String value) { super(value); } @@ -131,8 +139,8 @@ public String nameWithoutExtension() { return result; } - private boolean matches(MessageFile file) { - var result = value().endsWith(file.suffix()); + private boolean hasSuffix(String suffix) { + var result = value().endsWith(suffix); return result; } @@ -140,20 +148,20 @@ private boolean matches(MessageFile file) { * Returns {@code true} if the name of the file matches convention for command message files. */ public boolean isCommands() { - return matches(MessageFile.COMMANDS); + return hasSuffix(COMMANDS_SUFFIX); } /** * Returns {@code true} if the name of the file matches convention for event message files. */ public boolean isEvents() { - return matches(MessageFile.EVENTS); + return hasSuffix(EVENTS_SUFFIX); } /** * Returns {@code true} if the name of the file matches convention for rejection message files. */ public boolean isRejections() { - return matches(MessageFile.REJECTIONS); + return hasSuffix(REJECTIONS_SUFFIX); } } diff --git a/base/src/test/java/io/spine/base/MessageFileTest.java b/base/src/test/java/io/spine/base/MessageFileTest.java deleted file mode 100644 index a5f2efb3e2..0000000000 --- a/base/src/test/java/io/spine/base/MessageFileTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2022, 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 - * - * http://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.base; - -import com.google.protobuf.Any; -import io.spine.base.given.MessageFileEventsProto; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@DisplayName("`MessageFile` should") -class MessageFileTest { - - @Nested - @DisplayName("test a file descriptor") - class ProvidePredicate { - - @Test - @DisplayName("accepting the file with matching suffix") - void acceptingEligibleFile() { - var descriptor = MessageFileEventsProto.getDescriptor(); - assertTrue(MessageFile.EVENTS.test(descriptor.toProto())); - } - - @Test - @DisplayName("rejecting the file with non-matching suffix") - void rejectingNonEligibleFile() { - var descriptor = Any.getDescriptor().getFile(); - assertFalse(MessageFile.EVENTS.test(descriptor.toProto())); - } - } -} diff --git a/base/src/test/proto/spine/test/base/message_file_test_events.proto b/base/src/test/proto/spine/test/base/message_file_test_events.proto deleted file mode 100644 index d1a623cf79..0000000000 --- a/base/src/test/proto/spine/test/base/message_file_test_events.proto +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2022, 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 - * - * http://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. - */ -syntax = "proto3"; - -package spine.test.base; - -import "spine/options.proto"; - -option (type_url_prefix) = "type.spine.io"; -option java_package = "io.spine.base.given"; -option java_multiple_files = true; -option java_outer_classname = "MessageFileEventsProto"; - -message SomeEvent { - string value = 1; -} From 41c27c806ffee4850f70d03be447610e4132029b Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Jun 2026 03:46:41 +0000 Subject: [PATCH 07/16] Bump version -> `2.0.0-SNAPSHOT.405` https://claude.ai/code/session_01RoCv4qxE8c32kPMLWVKw9M --- version.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.gradle.kts b/version.gradle.kts index c5226ba68a..9e7b6ebdbd 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.404") +val versionToPublish: String by extra("2.0.0-SNAPSHOT.405") From 20fc1fb1ea4751db53a58eb9c63d4e96be7bfb6f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Jun 2026 03:46:41 +0000 Subject: [PATCH 08/16] Update dependency reports No dependency changed in this branch; the project version strings in the reports are brought in line with `2.0.0-SNAPSHOT.405`. https://claude.ai/code/session_01RoCv4qxE8c32kPMLWVKw9M --- docs/dependencies/dependencies.md | 8 ++++---- docs/dependencies/pom.xml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/dependencies/dependencies.md b/docs/dependencies/dependencies.md index cb32f01cab..755bc9cefc 100644 --- a/docs/dependencies/dependencies.md +++ b/docs/dependencies/dependencies.md @@ -1,6 +1,6 @@ -# Dependencies of `io.spine:spine-annotations:2.0.0-SNAPSHOT.404` +# Dependencies of `io.spine:spine-annotations:2.0.0-SNAPSHOT.405` ## Runtime 1. **Group** : org.jetbrains. **Name** : annotations. **Version** : 26.1.0. @@ -767,7 +767,7 @@ This report was generated on **Mon Jun 08 18:37:28 WEST 2026** using -# Dependencies of `io.spine:spine-base:2.0.0-SNAPSHOT.404` +# Dependencies of `io.spine:spine-base:2.0.0-SNAPSHOT.405` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -1611,7 +1611,7 @@ This report was generated on **Mon Jun 08 18:37:29 WEST 2026** using -# Dependencies of `io.spine:spine-environment:2.0.0-SNAPSHOT.404` +# Dependencies of `io.spine:spine-environment:2.0.0-SNAPSHOT.405` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -2437,7 +2437,7 @@ This report was generated on **Mon Jun 08 18:37:28 WEST 2026** using -# Dependencies of `io.spine:spine-format:2.0.0-SNAPSHOT.404` +# Dependencies of `io.spine:spine-format:2.0.0-SNAPSHOT.405` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.0. diff --git a/docs/dependencies/pom.xml b/docs/dependencies/pom.xml index c487c5f049..27d21b7f4d 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 base-libraries -2.0.0-SNAPSHOT.404 +2.0.0-SNAPSHOT.405 2015 From 834dd60f72966817248b3a473cd28831caa61c1e Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 10 Jun 2026 20:18:58 +0100 Subject: [PATCH 09/16] Fix doc language --- base/src/main/java/io/spine/code/proto/FileName.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/java/io/spine/code/proto/FileName.java b/base/src/main/java/io/spine/code/proto/FileName.java index f476c09796..158c0dd31d 100644 --- a/base/src/main/java/io/spine/code/proto/FileName.java +++ b/base/src/main/java/io/spine/code/proto/FileName.java @@ -113,7 +113,7 @@ private String nameOnly() { } /** - * Returns the file name with extension but without path. + * Returns the file name with an extension but without a path. */ public String nameWithExtension() { var fullName = value(); From 5b4485368ff791503b624f3a60dbec99305c2308 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 10 Jun 2026 20:41:57 +0100 Subject: [PATCH 10/16] Revert "Move `MessageFile` out of `io.spine.base`" This reverts commit 5201e70ad4850eda643d5bb82e8e178c7e6431b7. --- .../move-message-file-to-core-jvm-compiler.md | 65 ---------------- .../main/java/io/spine/base/MessageFile.java | 77 +++++++++++++++++++ .../io/spine/code/proto/FieldDeclaration.java | 7 +- .../java/io/spine/code/proto/FileName.java | 24 ++---- .../java/io/spine/base/MessageFileTest.java | 59 ++++++++++++++ .../test/base/message_file_test_events.proto | 39 ++++++++++ 6 files changed, 187 insertions(+), 84 deletions(-) delete mode 100644 .agents/tasks/move-message-file-to-core-jvm-compiler.md create mode 100644 base/src/main/java/io/spine/base/MessageFile.java create mode 100644 base/src/test/java/io/spine/base/MessageFileTest.java create mode 100644 base/src/test/proto/spine/test/base/message_file_test_events.proto diff --git a/.agents/tasks/move-message-file-to-core-jvm-compiler.md b/.agents/tasks/move-message-file-to-core-jvm-compiler.md deleted file mode 100644 index 536da76be9..0000000000 --- a/.agents/tasks/move-message-file-to-core-jvm-compiler.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -slug: move-message-file-to-core-jvm-compiler -branch: claude/cool-lamport-rnwzpu -owner: claude -status: in-progress -started: 2026-06-10 ---- - -## Goal - -Resolve [#941](https://github.com/SpineEventEngine/base-libraries/issues/941): -`base` no longer ships `io.spine.base.MessageFile`; the enum lives in -CoreJvmCompiler (`core-jvm-base`, package `io.spine.tools.core.jvm`), where its -only org-wide consumer (`CoreJvmCompilerSettings`) resides. `base` compiles and -its tests pass without the enum. - -## Context - -- The enum encodes a *code-generation* file-naming convention - (`commands.proto`, `events.proto`, `rejections.proto`), so it belongs to - the tooling chain, not the runtime `base` artifact. -- Strategy: **coordinated hard move** (no `@Deprecated` shim), following the - precedent of `move-fs-dir-types-to-tool-base` (archived task, approved - 2026-06-04). Cross-repo move without `git mv` is accepted for the same - reason. -- Internal usages inside `base` (overlooked by the issue text) must be - refactored first: - - `io.spine.code.proto.FileName.matches(MessageFile)` — private helper - behind `isCommands()/isEvents()/isRejections()`. - - `io.spine.code.proto.FieldDeclaration.isCommandsFile()` — uses - `MessageFile.COMMANDS.test(...)`. -- `FileName.isCommands()/isEvents()/isRejections()` keep direct coverage in - `FileNameSpec.kt`, so no coverage is lost by the refactoring. -- The CoreJvmCompiler side is tracked in that repo's task file of the same - slug (branch `claude/cool-lamport-rnwzpu` there as well). The two PRs are - independent: the new enum lives in a different package, and CoreJvmCompiler - pins a published `spine-base` that still contains the old class until its - `Base` dependency is bumped later. - -## Plan - -- [x] Refactor `FileName.java`: inline the three suffix constants, replace - `matches(MessageFile)` with a private `hasSuffix(String)`. -- [x] Refactor `FieldDeclaration.isCommandsFile()` to - `FileName.from(file).isCommands()`. -- [x] `git rm base/src/main/java/io/spine/base/MessageFile.java`. -- [x] `git rm base/src/test/java/io/spine/base/MessageFileTest.java` and - `base/src/test/proto/spine/test/base/message_file_test_events.proto` - (test ported to CoreJvmCompiler). -- [x] Update copyright headers of modified files to 2026. -- [x] Bump `version.gradle.kts` → `2.0.0-SNAPSHOT.410` (combined with PR #943); project version - strings in `docs/dependencies/*` updated to match (no dependency - changed, so the regenerated content differs only in those strings). -- [ ] Build `:base` and run its tests — **blocked locally**: the remote - session's network policy returns 403 for the Spine artifact - repositories (GitHub Packages, CloudRepo, Artifact Registry), so - Gradle cannot resolve `io.spine.tools:protobuf-setup-plugins` and - friends. Verification delegated to PR CI. -- [x] Commit, push, draft PR referencing #941. - -## Log - -- 2026-06-10 — drafted; executing (work authorized by the issue assignment). -- 2026-06-10 — sources done; local Gradle build impossible (403 from all - Spine repos under the session network policy); relying on PR CI. diff --git a/base/src/main/java/io/spine/base/MessageFile.java b/base/src/main/java/io/spine/base/MessageFile.java new file mode 100644 index 0000000000..1f74189db4 --- /dev/null +++ b/base/src/main/java/io/spine/base/MessageFile.java @@ -0,0 +1,77 @@ +/* + * Copyright 2022, 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 + * + * http://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.base; + +import com.google.protobuf.DescriptorProtos.FileDescriptorProto; +import io.spine.code.proto.FileName; + +import java.util.function.Predicate; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * A enumeration of file naming conventions for pre-defined types of messages. + */ +public enum MessageFile implements Predicate { + + /** + * Commands are declared in a file which name ends with {@code "commands.proto"}. + */ + COMMANDS("commands"), + + /** + * Events are declared in a file which name ends with {@code "events.proto"}. + */ + EVENTS("events"), + + /** + * Rejections are declared in a file which name ends with {@code "rejections.proto"}. + */ + REJECTIONS("rejections"); + + private final String suffix; + + MessageFile(String name) { + this.suffix = checkNotNull(name) + FileName.EXTENSION; + } + + /** + * Checks if the name of the given file matches this suffix. + */ + @Override + public boolean test(FileDescriptorProto file) { + var name = file.getName(); + return name.endsWith(suffix); + } + + /** + * Obtains a suffix required for this kind of files. + */ + public String suffix() { + return suffix; + } +} diff --git a/base/src/main/java/io/spine/code/proto/FieldDeclaration.java b/base/src/main/java/io/spine/code/proto/FieldDeclaration.java index 2a64ca83af..ccf793dbfd 100644 --- a/base/src/main/java/io/spine/code/proto/FieldDeclaration.java +++ b/base/src/main/java/io/spine/code/proto/FieldDeclaration.java @@ -1,11 +1,11 @@ /* - * Copyright 2026, TeamDev. All rights reserved. + * Copyright 2022, 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 + * http://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 @@ -34,6 +34,7 @@ import com.google.protobuf.Descriptors.FieldDescriptor.JavaType; import com.google.protobuf.Message; import io.spine.annotation.Internal; +import io.spine.base.MessageFile; import io.spine.code.java.ClassName; import io.spine.option.EntityOption; import io.spine.option.OptionsProto; @@ -352,7 +353,7 @@ private boolean isFirstField() { private boolean isCommandsFile() { var file = field.getFile(); - var result = FileName.from(file).isCommands(); + var result = MessageFile.COMMANDS.test(file.toProto()); return result; } diff --git a/base/src/main/java/io/spine/code/proto/FileName.java b/base/src/main/java/io/spine/code/proto/FileName.java index 158c0dd31d..0d92bda65a 100644 --- a/base/src/main/java/io/spine/code/proto/FileName.java +++ b/base/src/main/java/io/spine/code/proto/FileName.java @@ -1,11 +1,11 @@ /* - * Copyright 2026, TeamDev. All rights reserved. + * Copyright 2022, 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 + * http://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 @@ -30,6 +30,7 @@ import com.google.errorprone.annotations.Immutable; import com.google.protobuf.DescriptorProtos.FileDescriptorProto; import com.google.protobuf.Descriptors.FileDescriptor; +import io.spine.base.MessageFile; import io.spine.code.fs.AbstractFileName; import java.io.Serial; @@ -54,15 +55,6 @@ public class FileName extends AbstractFileName implements UnderscoredN /** The file system separator as defined by Protobuf. Not platform-dependent. */ private static final char PATH_SEPARATOR = '/'; - /** The conventional suffix of a file declaring command messages. */ - private static final String COMMANDS_SUFFIX = "commands" + EXTENSION; - - /** The conventional suffix of a file declaring event messages. */ - private static final String EVENTS_SUFFIX = "events" + EXTENSION; - - /** The conventional suffix of a file declaring rejection messages. */ - private static final String REJECTIONS_SUFFIX = "rejections" + EXTENSION; - private FileName(String value) { super(value); } @@ -139,8 +131,8 @@ public String nameWithoutExtension() { return result; } - private boolean hasSuffix(String suffix) { - var result = value().endsWith(suffix); + private boolean matches(MessageFile file) { + var result = value().endsWith(file.suffix()); return result; } @@ -148,20 +140,20 @@ private boolean hasSuffix(String suffix) { * Returns {@code true} if the name of the file matches convention for command message files. */ public boolean isCommands() { - return hasSuffix(COMMANDS_SUFFIX); + return matches(MessageFile.COMMANDS); } /** * Returns {@code true} if the name of the file matches convention for event message files. */ public boolean isEvents() { - return hasSuffix(EVENTS_SUFFIX); + return matches(MessageFile.EVENTS); } /** * Returns {@code true} if the name of the file matches convention for rejection message files. */ public boolean isRejections() { - return hasSuffix(REJECTIONS_SUFFIX); + return matches(MessageFile.REJECTIONS); } } diff --git a/base/src/test/java/io/spine/base/MessageFileTest.java b/base/src/test/java/io/spine/base/MessageFileTest.java new file mode 100644 index 0000000000..a5f2efb3e2 --- /dev/null +++ b/base/src/test/java/io/spine/base/MessageFileTest.java @@ -0,0 +1,59 @@ +/* + * Copyright 2022, 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 + * + * http://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.base; + +import com.google.protobuf.Any; +import io.spine.base.given.MessageFileEventsProto; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@DisplayName("`MessageFile` should") +class MessageFileTest { + + @Nested + @DisplayName("test a file descriptor") + class ProvidePredicate { + + @Test + @DisplayName("accepting the file with matching suffix") + void acceptingEligibleFile() { + var descriptor = MessageFileEventsProto.getDescriptor(); + assertTrue(MessageFile.EVENTS.test(descriptor.toProto())); + } + + @Test + @DisplayName("rejecting the file with non-matching suffix") + void rejectingNonEligibleFile() { + var descriptor = Any.getDescriptor().getFile(); + assertFalse(MessageFile.EVENTS.test(descriptor.toProto())); + } + } +} diff --git a/base/src/test/proto/spine/test/base/message_file_test_events.proto b/base/src/test/proto/spine/test/base/message_file_test_events.proto new file mode 100644 index 0000000000..d1a623cf79 --- /dev/null +++ b/base/src/test/proto/spine/test/base/message_file_test_events.proto @@ -0,0 +1,39 @@ +/* + * Copyright 2022, 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 + * + * http://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. + */ +syntax = "proto3"; + +package spine.test.base; + +import "spine/options.proto"; + +option (type_url_prefix) = "type.spine.io"; +option java_package = "io.spine.base.given"; +option java_multiple_files = true; +option java_outer_classname = "MessageFileEventsProto"; + +message SomeEvent { + string value = 1; +} From 0dfd605f2951b623e765feda9de549dc0f12676a Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 10 Jun 2026 20:43:46 +0100 Subject: [PATCH 11/16] Update copyright header in `FileName.java` Co-Authored-By: Claude Fable 5 --- base/src/main/java/io/spine/code/proto/FileName.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/src/main/java/io/spine/code/proto/FileName.java b/base/src/main/java/io/spine/code/proto/FileName.java index 0d92bda65a..dbbb5d3325 100644 --- a/base/src/main/java/io/spine/code/proto/FileName.java +++ b/base/src/main/java/io/spine/code/proto/FileName.java @@ -1,11 +1,11 @@ /* - * Copyright 2022, 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. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 From 8fb1519ce3553893fcfd3ef42e726633420ce024 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 10 Jun 2026 20:49:12 +0100 Subject: [PATCH 12/16] Add tests for `MessageFile` --- .../kotlin/io/spine/base/MessageFileSpec.kt | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 base/src/test/kotlin/io/spine/base/MessageFileSpec.kt diff --git a/base/src/test/kotlin/io/spine/base/MessageFileSpec.kt b/base/src/test/kotlin/io/spine/base/MessageFileSpec.kt new file mode 100644 index 0000000000..47a6f1d634 --- /dev/null +++ b/base/src/test/kotlin/io/spine/base/MessageFileSpec.kt @@ -0,0 +1,63 @@ +/* + * 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.base + +import com.google.protobuf.Any +import com.google.protobuf.DescriptorProtos.FileDescriptorProto +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test + +@DisplayName("`MessageFile` should") +internal class MessageFileSpec { + + @Test + fun `expose the suffix required for the corresponding kind of files`() { + MessageFile.COMMANDS.suffix() shouldBe "commands.proto" + MessageFile.EVENTS.suffix() shouldBe "events.proto" + MessageFile.REJECTIONS.suffix() shouldBe "rejections.proto" + } + + @Nested internal inner class + `test a file descriptor` { + + @Test + fun `accepting the file with matching suffix`() { + val file = FileDescriptorProto.newBuilder() + .setName("given_events.proto") + .build() + MessageFile.EVENTS.test(file) shouldBe true + } + + @Test + fun `rejecting the file with non-matching suffix`() { + val file = Any.getDescriptor().file.toProto() + MessageFile.EVENTS.test(file) shouldBe false + } + } +} From a61a9314c9146420722fa4c3c81204bd2c318505 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 10 Jun 2026 21:02:04 +0100 Subject: [PATCH 13/16] Update `config` --- .github/workflows/build-on-ubuntu.yml | 5 +- .github/workflows/build-on-windows.yml | 8 +- .github/workflows/check-links.yml | 2 +- .github/workflows/ensure-reports-updated.yml | 16 ++- .../workflows/gradle-wrapper-validation.yml | 2 +- .github/workflows/increment-guard.yml | 25 ++-- .github/workflows/publish.yml | 7 +- ...move-obsolete-artifacts-from-packages.yaml | 2 +- .idea/misc.xml | 31 ++--- .../src/main/kotlin/config-tester.gradle.kts | 4 +- .../kotlin/io/spine/dependency/local/Base.kt | 4 +- .../kotlin/io/spine/gradle/ConfigTester.kt | 6 +- .../io/spine/gradle/publish/IncrementGuard.kt | 66 +++++----- .../gradle/report/coverage/KoverConfig.kt | 4 +- .../gradle/report/coverage/SiblingCoverage.kt | 114 ++++++++++++++++++ .../gradle/publish/IncrementGuardTest.kt | 79 ++++++++++++ config | 2 +- gradle.properties | 6 +- 18 files changed, 294 insertions(+), 89 deletions(-) create mode 100644 buildSrc/src/main/kotlin/io/spine/gradle/report/coverage/SiblingCoverage.kt create mode 100644 buildSrc/src/test/kotlin/io/spine/gradle/publish/IncrementGuardTest.kt diff --git a/.github/workflows/build-on-ubuntu.yml b/.github/workflows/build-on-ubuntu.yml index 03be0f5326..27d9302b9d 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 de72407ede..992272ce4f 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 3fa235be0b..6755ff910e 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 315cd202b7..20deb3f695 100644 --- a/.github/workflows/ensure-reports-updated.yml +++ b/.github/workflows/ensure-reports-updated.yml @@ -1,19 +1,29 @@ # Ensures that the license report files were modified in this PR. +# +# The check runs only for pull requests targeting a default (`master`/`main`) or +# a release-line (e.g. `2.x-jdk8-master`) branch. The report files embed the project +# version, so they are refreshed by the branches which bump it. Pull requests +# targeting auxiliary branches are not checked. +# +# The base branch is checked inside the job rather than via the `branches` filter: +# a workflow skipped by branch filtering leaves its check in the `Pending` state, +# blocking PRs which require it, while a job skipped via `if` reports `skipped`, +# which satisfies required status checks. name: License Reports on: pull_request: - branches: - - '**' jobs: check: name: Ensure license reports are updated runs-on: ubuntu-latest + # Default and release-line branches, e.g. `master`, `main`, `2.x-jdk8-master`. + if: endsWith(github.base_ref, 'master') || endsWith(github.base_ref, 'main') 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 50eb05eb15..fc0872b4c9 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 38ce6f4d3e..f20b4bed6e 100644 --- a/.github/workflows/increment-guard.yml +++ b/.github/workflows/increment-guard.yml @@ -1,28 +1,39 @@ -# Ensures that the current lib version is not yet published but executing the Gradle +# Ensures that the current lib version is not yet published by executing the Gradle # `checkVersionIncrement` task. +# +# The check runs only for pull requests targeting a default (`master`/`main`) or +# a release-line (e.g. `2.x-jdk8-master`) branch. It is the responsibility of a branch +# which aims to merge into such a branch to bump the version. Auxiliary branches +# do not deal with the versions in the release cycle and are not guarded. +# +# The base branch is checked inside the job rather than via the `branches` filter: +# a workflow skipped by branch filtering leaves its check in the `Pending` state, +# blocking PRs which require it, while a job skipped via `if` reports `skipped`, +# which satisfies required status checks. name: Version Guard on: - push: - branches: - - '**' + pull_request: jobs: check: name: Check version increment runs-on: ubuntu-latest + # Default and release-line branches, e.g. `master`, `main`, `2.x-jdk8-master`. + if: endsWith(github.base_ref, 'master') || endsWith(github.base_ref, 'main') 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 f7218c618e..df8f6cd01a 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 f706171007..62242c7bbb 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/.idea/misc.xml b/.idea/misc.xml index c6c419e979..264f823015 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -21,39 +21,24 @@ - - \ No newline at end of file + + diff --git a/buildSrc/src/main/kotlin/config-tester.gradle.kts b/buildSrc/src/main/kotlin/config-tester.gradle.kts index 7b64dac7d1..2463f738b2 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/dependency/local/Base.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Base.kt index 787b8cfb7e..f2f0507d72 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Base.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Base.kt @@ -33,8 +33,8 @@ package io.spine.dependency.local */ @Suppress("ConstPropertyName", "unused") object Base { - const val version = "2.0.0-SNAPSHOT.400" - const val versionForBuildScript = "2.0.0-SNAPSHOT.400" + const val version = "2.0.0-SNAPSHOT.404" + const val versionForBuildScript = "2.0.0-SNAPSHOT.404" const val group = Spine.group private const val prefix = "spine" const val libModule = "$prefix-base" diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/ConfigTester.kt b/buildSrc/src/main/kotlin/io/spine/gradle/ConfigTester.kt index c3bbfbe115..f95f23ea07 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/publish/IncrementGuard.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/IncrementGuard.kt index 1243b04522..195a514631 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/publish/IncrementGuard.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/publish/IncrementGuard.kt @@ -40,7 +40,23 @@ import org.gradle.api.Project class IncrementGuard : Plugin { companion object { + const val taskName = "checkVersionIncrement" + + /** + * Tells whether the version increment must be verified for the given + * GitHub Actions event and the base branch of the pull request. + * + * The version is guarded only for pull requests targeting a default or + * a release-line branch, i.e. a branch with the name ending with `master` + * or `main`. For example: `master`, `main`, `2.x-jdk8-master`, `2.x-jdk8-main`. + */ + internal fun shouldCheckVersion(event: String?, baseBranch: String?): Boolean { + if (event != "pull_request" || baseBranch == null) { + return false + } + return baseBranch.endsWith("master") || baseBranch.endsWith("main") + } } /** @@ -48,11 +64,15 @@ class IncrementGuard : Plugin { * * The task is created anyway, but it is enabled only if: * 1. The project is built on GitHub CI, and - * 2. The job is a pull request. + * 2. The job is a pull request targeting a default (`master` or `main`) or + * a release-line (e.g. `2.x-jdk8-master`) branch. * - * The task only runs on non-master branches on GitHub Actions. - * This is done to prevent unexpected CI fails when re-building `master` multiple times, - * creating git tags, and in other cases that go outside the "usual" development cycle. + * It is the responsibility of a branch which aims to merge into a default + * (or otherwise protected) branch to bump the version. Auxiliary branches do not + * deal with the versions in the release cycle, so pull requests targeting them, + * direct pushes, and tag builds do not run the check. This also prevents unexpected + * CI fails when re-building `master` multiple times, creating git tags, and in other + * cases that go outside the "usual" development cycle. */ override fun apply(target: Project) { val tasks = target.tasks @@ -64,7 +84,8 @@ class IncrementGuard : Plugin { if (!shouldCheckVersion()) { logger.info( - "The build does not represent a GitHub Actions feature branch job, " + + "The build does not represent a GitHub Actions pull request job " + + "targeting a default or a release-line branch, " + "the `checkVersionIncrement` task is disabled." ) this.enabled = false @@ -73,40 +94,19 @@ class IncrementGuard : Plugin { } /** - * Returns `true` if the current build is a GitHub Actions build which represents a push - * to a feature branch. - * - * Returns `false` if the associated reference is not a branch (e.g., a tag) or if it has - * the name which ends with `master` or `main`. + * Returns `true` if the current build is a GitHub Actions build of a pull request + * targeting a default (`master` or `main`) or a release-line branch, + * such as `2.x-jdk8-master`. * - * For example, on the following branches the method would return `false`: - * - * 1. `master`. - * 2. `main`. - * 3. `2.x-jdk8-master`. - * 4. `2.x-jdk8-main`. + * Returns `false` for all other builds, including direct pushes, tag builds, + * and pull requests targeting auxiliary branches. * * @see * List of default environment variables provided for GitHub Actions builds */ private fun shouldCheckVersion(): Boolean { val event = System.getenv("GITHUB_EVENT_NAME") - val reference = System.getenv("GITHUB_REF") - if (event != "push" || reference == null) { - return false - } - val branch = branchName(reference) - return when { - branch == null -> false - branch.endsWith("master") -> false - branch.endsWith("main") -> false - else -> true - } - } - - private fun branchName(gitHubRef: String): String? { - val matches = Regex("refs/heads/(.+)").matchEntire(gitHubRef) - val branch = matches?.let { it.groupValues[1] } - return branch + val baseBranch = System.getenv("GITHUB_BASE_REF") + return shouldCheckVersion(event, baseBranch) } } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/report/coverage/KoverConfig.kt b/buildSrc/src/main/kotlin/io/spine/gradle/report/coverage/KoverConfig.kt index ed2bd0290d..e3bd5b98dd 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/report/coverage/KoverConfig.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/report/coverage/KoverConfig.kt @@ -276,7 +276,7 @@ class KoverConfig private constructor( .flatMap { root -> root.walk() .filter { !it.isDirectory } - .flatMap { it.fqnsRelativeTo(root).asSequence() } + .flatMap { it.classNamesIn(root).asSequence() } } .distinct() .toList() @@ -359,7 +359,7 @@ private fun KotlinSourceSet.isMainSourceSet(): Boolean = * * Returns an empty list if this file is not under [root]. */ -private fun File.fqnsRelativeTo(root: File): List { +internal fun File.classNamesIn(root: File): List { if (!startsWith(root)) { return emptyList() } 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 0000000000..1c9d1f54da --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/gradle/report/coverage/SiblingCoverage.kt @@ -0,0 +1,114 @@ +/* + * 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 java.io.File +import kotlinx.kover.gradle.plugin.dsl.KoverProjectExtension +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.TaskCollection +import org.gradle.api.tasks.testing.Test + +/** + * 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 additional binary reports. Only this project's classes are + * credited from them — 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 JVM test tasks so the + * data is present when a report is generated. + * + * The contributor's JVM test tasks are discovered by type rather than by name, so + * the helper works regardless of the module convention: a `jvm-module` contributes + * through its `test` task, a `kmp-module` through `jvmTest`, and any additional + * JVM test tasks are picked up as well. Non-JVM Kotlin test tasks (`*Native`, + * `*Js`, …) are not of type [Test] and are correctly ignored — Kover instruments + * only JVM test tasks. + * + * 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 contributorTests = contributor.tasks.withType(Test::class.java) + extensions.configure(KoverProjectExtension::class.java) { + reports { + total { + additionalBinaryReports.addAll(contributor.execFilesOf(contributorTests)) + } + } + } + tasks.matching { it.consumesCoverageBinaryReports() }.configureEach { + dependsOn(contributorTests) + } +} + +/** + * Lazy `Provider` of the JaCoCo execution-data files produced by [testTasks] + * of this project. + * + * When the coverage engine is pinned to JaCoCo via `useJacoco(...)`, Kover writes + * one binary report per instrumented JVM test task at `build/`[BIN_REPORTS_DIR] + * `/.exec`, so the file name follows the task name. Resolved at + * task-graph time, after the contributor's test tasks have been registered. + */ +private fun Project.execFilesOf(testTasks: TaskCollection): Provider> { + val binReports = layout.buildDirectory.dir(BIN_REPORTS_DIR) + return provider { + testTasks.map { binReports.get().file("${it.name}.exec").asFile } + } +} + +/** + * The directory under a module's `build/` where Kover writes the per-test-task + * binary execution-data files. + */ +private const val BIN_REPORTS_DIR: String = "kover/bin-reports" + +/** + * 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/buildSrc/src/test/kotlin/io/spine/gradle/publish/IncrementGuardTest.kt b/buildSrc/src/test/kotlin/io/spine/gradle/publish/IncrementGuardTest.kt new file mode 100644 index 0000000000..5633c30d4d --- /dev/null +++ b/buildSrc/src/test/kotlin/io/spine/gradle/publish/IncrementGuardTest.kt @@ -0,0 +1,79 @@ +/* + * 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.publish + +import io.kotest.matchers.shouldBe +import io.spine.gradle.publish.IncrementGuard.Companion.shouldCheckVersion +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test + +@DisplayName("`IncrementGuard` should") +class IncrementGuardTest { + + @Nested + inner class `require the version check` { + + @Test + fun `for pull requests targeting default branches`() { + shouldCheckVersion("pull_request", "master") shouldBe true + shouldCheckVersion("pull_request", "main") shouldBe true + } + + @Test + fun `for pull requests targeting release-line branches`() { + shouldCheckVersion("pull_request", "2.x-jdk8-master") shouldBe true + shouldCheckVersion("pull_request", "2.x-jdk8-main") shouldBe true + } + } + + @Nested + inner class `not require the version check` { + + @Test + fun `for pull requests targeting auxiliary branches`() { + shouldCheckVersion("pull_request", "epic-feature") shouldBe false + shouldCheckVersion("pull_request", "master-fixes") shouldBe false + } + + @Test + fun `for push events`() { + shouldCheckVersion("push", "master") shouldBe false + shouldCheckVersion("push", null) shouldBe false + } + + @Test + fun `for pull request events without a base branch`() { + shouldCheckVersion("pull_request", null) shouldBe false + } + + @Test + fun `outside GitHub Actions`() { + shouldCheckVersion(null, null) shouldBe false + } + } +} diff --git a/config b/config index a844dc7b77..234233e0fa 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit a844dc7b77989dd0a227f22e5625444acef5c09a +Subproject commit 234233e0fa407df296ff4742887723653c3dcc95 diff --git a/gradle.properties b/gradle.properties index 49ad30c59b..559cd0d15f 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 From 41a91897e9bc0b209e3c38aacc9bde6c9cfcf29d Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 10 Jun 2026 21:05:23 +0100 Subject: [PATCH 14/16] Disable Gradle cache --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 559cd0d15f..3e4fd9de7d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,7 +10,7 @@ org.gradle.parallel=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 +#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 From 770934bdec291a0246a9cb48136730ccb85d7eb6 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 10 Jun 2026 21:05:38 +0100 Subject: [PATCH 15/16] Bump ToolBase --- .../src/main/kotlin/io/spine/dependency/local/ToolBase.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 c0c4238d92..6073038f35 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.381" - const val dogfoodingVersion = "2.0.0-SNAPSHOT.381" + const val version = "2.0.0-SNAPSHOT.399" + const val dogfoodingVersion = "2.0.0-SNAPSHOT.399" const val lib = "$group:tool-base:$version" const val classicCodegen = "$group:classic-codegen:$version" From b6a777af83067578b5a30592dda636fc1f4e0640 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 10 Jun 2026 21:06:01 +0100 Subject: [PATCH 16/16] Update build time --- docs/dependencies/dependencies.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/dependencies/dependencies.md b/docs/dependencies/dependencies.md index f4f908f261..863514c5cb 100644 --- a/docs/dependencies/dependencies.md +++ b/docs/dependencies/dependencies.md @@ -760,7 +760,7 @@ The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Mon Jun 08 18:37:28 WEST 2026** using +This report was generated on **Wed Jun 10 21:05:52 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). @@ -1604,7 +1604,7 @@ This report was generated on **Mon Jun 08 18:37:28 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:37:29 WEST 2026** using +This report was generated on **Wed Jun 10 21:05:52 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). @@ -2430,7 +2430,7 @@ This report was generated on **Mon Jun 08 18:37:29 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:37:28 WEST 2026** using +This report was generated on **Wed Jun 10 21:05:52 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). @@ -3336,6 +3336,6 @@ This report was generated on **Mon Jun 08 18:37:28 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:37:28 WEST 2026** using +This report was generated on **Wed Jun 10 21:05:52 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