Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .agents/skills/bump-version/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ under these constraints:
- Stage only `version.gradle.kts`. Any other modified files are out of scope
for this skill's commit and must remain unstaged.
- Use the exact subject `` Bump version -> `<new>` `` (see step 4 of the
Checklist) with the actual new version value substituted.
Checklist) with the actual new version value substituted. Keep the
backticks around the version literal (for example, ``... -> `2.0.0``` ) and
do not escape them as ``\````.
- No `git push`, `git tag`, `git rebase`, `git commit --amend`, or any other
history-writing operation. Those require a separate authorization
(`.agents/safety-rules.md` → *Commits and history-writing*).
Expand Down Expand Up @@ -81,6 +83,12 @@ create the commit.
Bump version -> `2.0.0-SNAPSHOT.183`
```

Shell-safe example (no escaped backticks in the commit subject):

```bash
git commit -m 'Bump version -> `2.0.0-SNAPSHOT.183`' -- version.gradle.kts
```

Use the actual new version in the subject. Do not include unrelated files in
this commit.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ object CoreJvmCompiler {
/**
* The version used in the build classpath.
*/
const val dogfoodingVersion = "2.0.0-SNAPSHOT.065"
const val dogfoodingVersion = "2.0.0-SNAPSHOT.067"

/**
* The version to be used for integration tests.
*/
const val version = "2.0.0-SNAPSHOT.065"
const val version = "2.0.0-SNAPSHOT.067"

/**
* The ID of the Gradle plugin.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ object Validation {
/**
* The version of the Validation library artifacts.
*/
const val version = "2.0.0-SNAPSHOT.444"
const val version = "2.0.0-SNAPSHOT.445"

/**
* The last version of Validation compatible with ProtoData.
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -35,8 +35,8 @@ import org.gradle.api.file.FileTree
import org.gradle.api.tasks.SourceSetOutput

/**
* Serves to distinguish the `.java` and `.class` files built on top of the Protobuf definitions
* from the human-created production code.
* Serves to distinguish the generated `.java` and `.kt` files (and the `.class` files
* compiled from them) from the human-created production code.
*
* Works on top of the passed [source][srcDirs] and [output][outputDirs] directories, by analyzing
* the source file names and finding the corresponding compiler output.
Expand Down Expand Up @@ -70,26 +70,15 @@ internal class CodebaseFilter(
return humanProducedTree
}

private fun generatedClassNames(): List<String> {
val generatedSourceFiles = generatedOnly(srcDirs)
val generatedNames = mutableListOf<String>()
generatedSourceFiles
private fun generatedClassNames(): List<String> =
generatedOnly(srcDirs)
.filter { it.exists() && it.isDirectory }
.forEach { folder ->
folder.walk()
.flatMap { root ->
root.walk()
.filter { !it.isDirectory }
.forEach { file ->
file.parseName(
File::asJavaClassName,
File::asGrpcClassName,
File::asSpineClassName
)?.let { clsName ->
generatedNames.add(clsName)
}
}
.flatMap { it.classNamesIn(root) }
.toList()
}
return generatedNames
}

private fun log(message: String) {
project.logger.info(message)
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -36,6 +36,11 @@ internal enum class FileExtension(val value: String) {
*/
JAVA_SOURCE(".java"),

/**
* Extension of a Kotlin source file.
*/
KOTLIN_SOURCE(".kt"),

/**
* Extension of a Java compiled file.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -20,32 +20,42 @@
* 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
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF TE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package io.spine.gradle.report.coverage

import io.spine.gradle.report.coverage.FileExtension.COMPILED_CLASS
import io.spine.gradle.report.coverage.FileExtension.JAVA_SOURCE
import io.spine.gradle.report.coverage.FileExtension.KOTLIN_SOURCE
import io.spine.gradle.report.coverage.PathMarker.ANONYMOUS_CLASS
import io.spine.gradle.report.coverage.PathMarker.GENERATED
import io.spine.gradle.report.coverage.PathMarker.GRPC_SRC_FOLDER
import io.spine.gradle.report.coverage.PathMarker.JAVA_OUTPUT_FOLDER
import io.spine.gradle.report.coverage.PathMarker.JAVA_SRC_FOLDER
import io.spine.gradle.report.coverage.PathMarker.SPINE_JAVA_SRC_FOLDER
import io.spine.gradle.report.coverage.PathMarker.MAIN_OUTPUT_FOLDER
import java.io.File

/**
* This file contains extension methods and properties for `java.io.File`.
*/

/**
* The two-part extension used by `protoc-gen-kotlin` for proto-file-scoped Kotlin
* helpers (e.g., `FooProtoKt.proto.kt`).
*/
private const val PROTO_KOTLIN_SUFFIX = ".proto.kt"

/**
* Suffix that the Kotlin compiler appends to the file name when generating the
* synthetic file class for top-level declarations.
*/
private const val KOTLIN_FILE_CLASS_SUFFIX = "Kt"

/**
* Parses the name of a class from the absolute path of this file.
*
* Treats the fragment between the [precedingMarker] and [extension] as the value to look for.
* In case the fragment is located and it contains `/` symbols, they are treated
* as Java package delimiters and are replaced by `.` symbols before returning the value.
* as package delimiters and are replaced by `.` symbols before returning the value.
*
* If the absolute path of this file has either no [precedingMarker] or no [extension],
* returns `null`.
Expand All @@ -69,58 +79,59 @@ internal fun File.parseClassName(
}

/**
* Attempts to parse the file name with either of the specified [parsers],
* in their respective order.
*
* Returns the first non-`null` parsed value.
* Attempts to parse the fully-qualified class name from the absolute path of this file,
* treating it as a path to a compiled `.class` file produced by either `javac` or `kotlinc`.
*
* If none of the parsers returns non-`null` value, returns `null`.
*/
internal fun File.parseName(vararg parsers: (file: File) -> String?): String? {
for (parser in parsers) {
val className = parser.invoke(this)
if (className != null) {
return className
}
}
return null
}

/**
* Attempts to parse the Java fully-qualified class name from the absolute path of this file,
* treating it as a path to a human-produced `.java` file.
*/
internal fun File.asJavaClassName(): String? =
this.parseClassName(JAVA_SRC_FOLDER, JAVA_SOURCE)

/**
* Attempts to parse the Java fully-qualified class name from the absolute path of this file,
* treating it as a path to a compiled `.class` file.
*
* If the `.class` file corresponds to the anonymous class, only the name of the parent
* class is returned.
* If the `.class` file corresponds to the anonymous or nested class, only the name of the
* top-level enclosing class is returned.
*/
internal fun File.asJavaCompiledClassName(): String? {
var className = this.parseClassName(JAVA_OUTPUT_FOLDER, COMPILED_CLASS)
var className = this.parseClassName(MAIN_OUTPUT_FOLDER, COMPILED_CLASS)
if (className != null && className.contains(ANONYMOUS_CLASS.infix)) {
className = className.split(ANONYMOUS_CLASS.infix)[0]
}
return className
}

/**
* Attempts to parse the Java fully-qualified class name from the absolute path of this file,
* treating it as a path to a gRPC-generated `.java` file.
* Returns the fully-qualified names of compiled JVM classes that originate from this
* source file, assuming [sourceRoot] is the source-set root under which the file was
* discovered.
*
* The shape of the returned list depends on the source file extension:
*
* - `.java` — a single FQN derived from the path relative to [sourceRoot].
* - `.kt` — two FQNs: the declared file/class name, and the same name with `Kt`
* appended, which is the synthetic file class that Kotlin emits for top-level
* declarations.
* - `.proto.kt` — the two-part extension is stripped first; otherwise behaves
* like `.kt`. This is the convention used by `protoc-gen-kotlin` for files
* holding proto-file-scoped helpers.
* - Any other extension — an empty list.
*
* Returns an empty list if this file is not located under [sourceRoot].
*/
internal fun File.asGrpcClassName(): String? =
this.parseClassName(GRPC_SRC_FOLDER, JAVA_SOURCE)
internal fun File.classNamesIn(sourceRoot: File): List<String> {
if (!this.startsWith(sourceRoot)) {
return emptyList()
}
val relative = this.toRelativeString(sourceRoot)
return when {
relative.endsWith(PROTO_KOTLIN_SUFFIX) -> {
val base = relative.removeSuffix(PROTO_KOTLIN_SUFFIX).toFqn()
listOf(base, base + KOTLIN_FILE_CLASS_SUFFIX)
}
relative.endsWith(KOTLIN_SOURCE.value) -> {
val base = relative.removeSuffix(KOTLIN_SOURCE.value).toFqn()
listOf(base, base + KOTLIN_FILE_CLASS_SUFFIX)
}
relative.endsWith(JAVA_SOURCE.value) ->
listOf(relative.removeSuffix(JAVA_SOURCE.value).toFqn())
else -> emptyList()
}
}

/**
* Attempts to parse the Java fully-qualified class name from the absolute path of this file,
* treating it as a path to a Spine-generated `.java` file.
*/
internal fun File.asSpineClassName(): String? =
this.parseClassName(SPINE_JAVA_SRC_FOLDER, JAVA_SOURCE)
private fun String.toFqn(): String = this.replace(File.separatorChar, '.')

/**
* Tells whether this file is a part of the generated sources, and not produced by a human.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import java.io.File
import java.util.*
import org.gradle.api.Project
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.SourceSetContainer
Expand Down Expand Up @@ -150,13 +151,13 @@ class JacocoConfig(
copyReports: TaskProvider<Copy>
): TaskProvider<JacocoReport> {
val allSourceSets = Projects(projects).sourceSets()
val mainJavaSrcDirs = allSourceSets.mainJavaSrcDirs()
val mainSrcDirs = allSourceSets.mainSrcDirs()
val humanProducedSourceFolders =
FileFilter.producedByHuman(mainJavaSrcDirs)
FileFilter.producedByHuman(mainSrcDirs)

val filter = CodebaseFilter(
rootProject,
mainJavaSrcDirs,
mainSrcDirs,
allSourceSets.mainOutputs()
)
val humanProducedCompiledFiles = filter.humanProducedCompiledFiles()
Expand Down Expand Up @@ -228,12 +229,26 @@ private class SourceSets(
) {

/**
* Returns all Java source folders corresponding to the `main` source set type.
* Returns the union of Java and Kotlin source folders corresponding to the `main`
* source set across all underlying [SourceSetContainer]s.
*
* Kotlin source directories are registered as a separate [SourceDirectorySet]
* extension on the source set, not exposed via [allJava][org.gradle.api.tasks.SourceSet.getAllJava].
* They are surfaced explicitly here so that generated Kotlin code (for example,
* the output of `protoc-gen-kotlin`) is visible to the coverage filter alongside
* the Java sources.
*/
fun mainJavaSrcDirs(): Set<File> {
fun mainSrcDirs(): Set<File> {
return sourceSets
.asSequence()
.flatMap { it["main"].allJava.srcDirs }
.flatMap { container ->
val main = container["main"]
val javaDirs = main.allJava.srcDirs
val kotlinDirs = (main.extensions.findByName("kotlin") as? SourceDirectorySet)
?.srcDirs
?: emptySet()
javaDirs + kotlinDirs
}
.toSet()
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -36,26 +36,11 @@ internal enum class PathMarker(val infix: String) {
*/
GENERATED("generated"),

/**
* Files produced by humans and written in Java.
*/
JAVA_SRC_FOLDER("/java/"),

/**
* Java source files generated by Spine framework.
*/
SPINE_JAVA_SRC_FOLDER("main/spine/"),

/**
* Java source files generated by gRPC plugin.
*/
GRPC_SRC_FOLDER("/main/grpc/"),

/**
* Among compiler output folders, highlights those containing the compilation result
* of human-produced Java files.
* for the `main` source set, whether produced by `javac` or `kotlinc`.
*/
JAVA_OUTPUT_FOLDER("/main/"),
MAIN_OUTPUT_FOLDER("/main/"),

/**
* Anonymous class.
Expand Down
Loading
Loading