diff --git a/.agents/_TOC.md b/.agents/_TOC.md new file mode 100644 index 0000000..065df13 --- /dev/null +++ b/.agents/_TOC.md @@ -0,0 +1,16 @@ +# Table of Contents + +1. [Quick Reference Card](quick-reference-card.md) +2. [Project overview](project-overview.md) +3. [Coding guidelines](coding-guidelines.md) +4. [Documentation & comments](documentation-guidelines.md) +5. [Documentation tasks](documentation-tasks.md) +6. [Running builds](running-builds.md) +7. [Version policy](version-policy.md) +8. [Project structure expectations](project-structure-expectations.md) +9. [Testing](testing.md) +10. [Safety rules](safety-rules.md) +11. [Advanced safety rules](advanced-safety-rules.md) +12. [Refactoring guidelines](refactoring-guidelines.md) +13. [Common tasks](common-tasks.md) +14. [Java to Kotlin conversion](java-kotlin-conversion.md) diff --git a/.agents/advanced-safety-rules.md b/.agents/advanced-safety-rules.md new file mode 100644 index 0000000..e410581 --- /dev/null +++ b/.agents/advanced-safety-rules.md @@ -0,0 +1,6 @@ +# ๐Ÿšจ Advanced safety rules + +- Do **not** auto-update external dependencies without explicit request. +- Do **not** inject analytics or telemetry code. +- Flag any usage of unsafe constructs (e.g., reflection, I/O on the main thread). +- Avoid generating blocking calls inside coroutines. diff --git a/.agents/coding-guidelines.md b/.agents/coding-guidelines.md new file mode 100644 index 0000000..3297d8a --- /dev/null +++ b/.agents/coding-guidelines.md @@ -0,0 +1,39 @@ +# ๐Ÿงพ Coding guidelines + +## Core principles + +- Adhere to [Spine Event Engine Documentation][spine-docs] for coding style. +- Generate code that compiles cleanly and passes static analysis. +- Respect existing architecture, naming conventions, and project structure. +- Write clear, incremental commits with descriptive messages. +- Include automated tests for any code change that alters functionality. + +## Kotlin best practices + +### โœ… Prefer +- **Kotlin idioms** over Java-style approaches: + - Extension functions + - `when` expressions + - Smart casts + - Data classes and sealed classes + - Immutable data structures +- **Simple nouns** over composite nouns (`user` > `userAccount`) +- **Generic parameters** over explicit variable types (`val list = mutableList()`) +- **Java interop annotations** only when needed (`@file:JvmName`, `@JvmStatic`) +- **Kotlin DSL** for Gradle files + +### โŒ Avoid +- Mutable data structures +- Java-style verbosity (builders with setters) +- Redundant null checks (`?.let` misuse) +- Using `!!` unless clearly justified +- Type names in variable names (`userObject`, `itemList`) +- String duplication (use constants in companion objects) +- Mixing Groovy and Kotlin DSLs in build logic +- Reflection unless specifically requested + +## Text formatting + - โœ… Remove double empty lines in the code. + - โœ… Remove trailing space characters in the code. + +[spine-docs]: https://github.com/SpineEventEngine/documentation/wiki diff --git a/.agents/common-tasks.md b/.agents/common-tasks.md new file mode 100644 index 0000000..5ee954d --- /dev/null +++ b/.agents/common-tasks.md @@ -0,0 +1,6 @@ +# ๐Ÿ“‹ Common tasks + +- **Adding a new dependency**: Update relevant files in `buildSrc` directory. +- **Creating a new module**: Follow existing module structure patterns. +- **Documentation**: Use KDoc style for public and internal APIs. +- **Testing**: Create comprehensive tests using Kotest assertions. diff --git a/.agents/documentation-guidelines.md b/.agents/documentation-guidelines.md new file mode 100644 index 0000000..914dcc8 --- /dev/null +++ b/.agents/documentation-guidelines.md @@ -0,0 +1,14 @@ +# Documentation & comments + +## Commenting guidelines +- Avoid inline comments in production code unless necessary. +- Inline comments are helpful in tests. +- When using TODO comments, follow the format on the [dedicated page][todo-comments]. +- File and directory names should be formatted as code. + +## Avoid widows, runts, orphans, or rivers + +Agents should **AVOID** text flow patters illustrated +on [this diagram](widow-runt-orphan-river.jpg). + +[todo-comments]: https://github.com/SpineEventEngine/documentation/wiki/TODO-comments diff --git a/.agents/documentation-tasks.md b/.agents/documentation-tasks.md new file mode 100644 index 0000000..8ac4660 --- /dev/null +++ b/.agents/documentation-tasks.md @@ -0,0 +1,20 @@ +# ๐Ÿ“„ Documentation tasks + +1. Ensure all public and internal APIs have KDoc examples. +2. Add in-line code blocks for clarity in tests. +3. Convert inline API comments in Java to KDoc in Kotlin: + ```java + // Literal string to be inlined whenever a placeholder references a non-existent argument. + private final String missingArgumentMessage = "[MISSING ARGUMENT]"; + ``` + transforms to: + ```kotlin + /** + * Literal string to be inlined whenever a placeholder references a non-existent argument. + */ + private val missingArgumentMessage = "[MISSING ARGUMENT]" + ``` + +4. Javadoc -> KDoc conversion tasks: + - Remove `

` tags in the line with text: `"

This"` -> `"This"`. + - Replace `

` with empty line if the tag is the only text in the line. diff --git a/.agents/java-kotlin-conversion.md b/.agents/java-kotlin-conversion.md new file mode 100644 index 0000000..95cf929 --- /dev/null +++ b/.agents/java-kotlin-conversion.md @@ -0,0 +1,43 @@ +# ๐Ÿช„ Converting Java code to Kotlin + +* Java code API comments are Javadoc format. +* Kotlin code API comments are in KDoc format. + +## Javadoc to KDoc conversion + +* The wording of original Javadoc comments must be preserved. + +## Treating nullability + +* Use nullable Kotlin type only if the type in Java is annotated as `@Nullable`. + +## Efficient Conversion Workflow + +* First, analyze the entire Java file structure before beginning conversion to understand dependencies and class relationships. +* Convert Java code to Kotlin systematically: imports first, followed by class definitions, methods, and finally expressions. +* Preserve all existing functionality and behavior during conversion. +* Maintain original code structure and organization to ensure readability. + +## Common Java to Kotlin Patterns + +* Convert Java getters/setters to Kotlin properties with appropriate visibility modifiers. +* Transform Java static methods to companion object functions or top-level functions as appropriate. +* Replace Java anonymous classes with Kotlin lambda expressions when possible. +* Convert Java interfaces with default methods to Kotlin interfaces with implementations. +* Transform Java builders to Kotlin DSL patterns when appropriate. + +## Error Prevention + +* Pay special attention to Java's checked exceptions versus Kotlin's unchecked exceptions. +* Be cautious with Java wildcards (`? extends`, `? super`) conversion to Kotlin's `out` and `in` type parameters. +* Ensure proper handling of Java static initialization blocks in Kotlin companion objects. +* Verify that Java overloaded methods convert correctly with appropriate default parameter values in Kotlin. +* Remember that Kotlin has smart casts which can eliminate explicit type casting needed in Java. + +## Documentation Conversion + +* Convert `@param` to `@param` with the same description. +* Convert `@return` to `@return` with the same description. +* Convert `@throws` to `@throws` with the same description. +* Convert `{@link}` to `[name][fully.qualified.Name]` format. +* Convert `{@code}` to inline code with backticks (`). diff --git a/.agents/project-overview.md b/.agents/project-overview.md new file mode 100644 index 0000000..dfac73f --- /dev/null +++ b/.agents/project-overview.md @@ -0,0 +1,7 @@ +# ๐Ÿ› ๏ธ Project overview + +- **Languages**: Kotlin (primary), Java (secondary). +- **Build tool**: Gradle with Kotlin DSL. +- **Static analysis**: detekt, ErrorProne, Checkstyle, PMD. +- **Testing**: JUnit 5, Kotest Assertions, Codecov. +- **Tools used**: Gradle plugins, IntelliJ IDEA Platform, KSP, KotlinPoet, Dokka. diff --git a/.agents/project-structure-expectations.md b/.agents/project-structure-expectations.md new file mode 100644 index 0000000..81b8e1a --- /dev/null +++ b/.agents/project-structure-expectations.md @@ -0,0 +1,21 @@ +# ๐Ÿ“ Project structure expectations + +```yaml +.github +buildSrc/ + + src/ + โ”œโ”€โ”€ main/ + โ”‚ โ”œโ”€โ”€ kotlin/ # Kotlin source files + โ”‚ โ””โ”€โ”€ java/ # Legacy Java code + โ”œโ”€โ”€ test/ + โ”‚ โ””โ”€โ”€ kotlin/ # Unit and integration tests + build.gradle.kts # Kotlin-based build configuration + + +build.gradle.kts # Kotlin-based build configuration +settings.gradle.kts # Project structure and settings +README.md # Project overview +AGENTS.md # Entry point for LLM agent instructions +version.gradle.kts # Declares the project version. +``` diff --git a/.agents/quick-reference-card.md b/.agents/quick-reference-card.md new file mode 100644 index 0000000..6c25b9a --- /dev/null +++ b/.agents/quick-reference-card.md @@ -0,0 +1,10 @@ +# ๐Ÿ“ Quick Reference Card + +``` +๐Ÿ”‘ Key Information: +- Kotlin/Java project with CQRS architecture +- Use ChatGPT for documentation, Codex for code generation, GPT-4o for complex analysis +- Follow coding guidelines in Spine Event Engine docs +- Always include tests with code changes +- Version bump required for all PRs +``` diff --git a/.agents/refactoring-guidelines.md b/.agents/refactoring-guidelines.md new file mode 100644 index 0000000..191db49 --- /dev/null +++ b/.agents/refactoring-guidelines.md @@ -0,0 +1,3 @@ +# โš™๏ธ Refactoring guidelines + +- Do NOT replace Kotest assertions with standard Kotlin's built-in test assertions. diff --git a/.agents/running-builds.md b/.agents/running-builds.md new file mode 100644 index 0000000..db0338d --- /dev/null +++ b/.agents/running-builds.md @@ -0,0 +1,18 @@ +# Running builds + +1. When modifying code, run: + ```bash + ./gradlew build + ``` + +2. If Protobuf (`.proto`) files are modified run: + ```bash + ./gradlew clean build + ``` + +3. Documentation-only changes in Kotlin or Java sources run: + ```bash + ./gradlew dokka + ``` + +4. Documentation-only changes do not require running tests! diff --git a/.agents/safety-rules.md b/.agents/safety-rules.md new file mode 100644 index 0000000..08e9b33 --- /dev/null +++ b/.agents/safety-rules.md @@ -0,0 +1,7 @@ +# Safety rules + +- โœ… All code must compile and pass static analysis. +- โœ… Do not auto-update external dependencies. +- โŒ Never use reflection or unsafe code without an explicit approval. +- โŒ No analytics or telemetry code. +- โŒ No blocking calls inside coroutines. diff --git a/.agents/testing.md b/.agents/testing.md new file mode 100644 index 0000000..f81bdbf --- /dev/null +++ b/.agents/testing.md @@ -0,0 +1,8 @@ +# ๐Ÿงช Testing + +- Do not use mocks, use stubs. +- Prefer [Kotest assertions][kotest-assertions] over assertions from JUnit or Google Truth. +- Generate unit tests for APIs (handles edge cases/scenarios). +- Supply scaffolds for typical Kotlin patterns (`when`, sealed classes). + +[kotest-assertions]: https://kotest.io/docs/assertions/assertions.html diff --git a/.agents/version-policy.md b/.agents/version-policy.md new file mode 100644 index 0000000..65dc457 --- /dev/null +++ b/.agents/version-policy.md @@ -0,0 +1,30 @@ +# Version policy + +## We use semver +The version of the project is kept in the `version.gradle.kts` file in the root of the project. + +The version numbers in these files follow the conventions of +[Semantic Versioning 2.0.0](https://semver.org/). + +## Quick checklist for versioning +1. Increment the patch version in `version.gradle.kts`. + Retain zero-padding if applicable: + - Example: `"2.0.0-SNAPSHOT.009"` โ†’ `"2.0.0-SNAPSHOT.010"` +2. Commit the version bump separately with this comment: + ```text + Bump version โ†’ `$newVersion` + ``` +3. Rebuild using `./gradlew clean build`. +4. Update `pom.xml`, `dependencies.md` and commit changes with: `Update dependency reports` + +Remember: PRs without version bumps will fail CI (conflict resolution detailed above). + +## Resolving conflicts in `version.gradle.kts` +A branch conflict over the version number should be resolved as described below. + * If a merged branch has a number which is less than that of the current branch, the version of + the current branch stays. + * If the merged branch has the number which is greater or equal to that of the current branch, + the number should be increased by one. + +## When to bump the version? + - When a new branch is created. diff --git a/.github/workflows/detekt-analysis.yml b/.github/workflows/detekt-analysis.yml deleted file mode 100644 index 0523e79..0000000 --- a/.github/workflows/detekt-analysis.yml +++ /dev/null @@ -1,103 +0,0 @@ -# This workflow performs a static analysis of your Kotlin source code using -# Detekt. -# -# Scans are triggered: -# 1. On every push to default and protected branches -# 2. On every Pull Request targeting the default branch -# 3. On a weekly schedule -# 4. Manually, on demand, via the "workflow_dispatch" event -# -# The workflow should work with no modifications, but you might like to use a -# later version of the Detekt CLI by modifing the $DETEKT_RELEASE_TAG -# environment variable. -name: Scan with Detekt - -on: - # Triggers the workflow on push or pull request events but only for default and protected branches - push: - branches: [ master ] - pull_request: - branches: [ master ] - schedule: - - cron: '19 17 * * 4' - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -env: - # Release tag associated with version of Detekt to be installed - # SARIF support (required for this workflow) was introduced in Detekt v1.15.0 - DETEKT_RELEASE_TAG: v1.15.0 - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - # This workflow contains a single job called "scan" - scan: - name: Scan - # The type of runner that the job will run on - runs-on: ubuntu-latest - - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 - - # Gets the download URL associated with the $DETEKT_RELEASE_TAG - - name: Get Detekt download URL - id: detekt_info - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - DETEKT_DOWNLOAD_URL=$( gh api graphql --field tagName=$DETEKT_RELEASE_TAG --raw-field query=' - query getReleaseAssetDownloadUrl($tagName: String!) { - repository(name: "detekt", owner: "detekt") { - release(tagName: $tagName) { - releaseAssets(name: "detekt", first: 1) { - nodes { - downloadUrl - } - } - } - } - } - ' | \ - jq --raw-output '.data.repository.release.releaseAssets.nodes[0].downloadUrl' ) - echo "::set-output name=download_url::$DETEKT_DOWNLOAD_URL" - - # Sets up the detekt cli - - name: Setup Detekt - run: | - dest=$( mktemp -d ) - curl --request GET \ - --url ${{ steps.detekt_info.outputs.download_url }} \ - --silent \ - --location \ - --output $dest/detekt - chmod a+x $dest/detekt - echo $dest >> $GITHUB_PATH - - # Performs static analysis using Detekt - - name: Run Detekt - continue-on-error: true - run: | - detekt --input ${{ github.workspace }} --report sarif:${{ github.workspace }}/detekt.sarif.json - - # Modifies the SARIF output produced by Detekt so that absolute URIs are relative - # This is so we can easily map results onto their source files - # This can be removed once relative URI support lands in Detekt: https://git.io/JLBbA - - name: Make artifact location URIs relative - continue-on-error: true - run: | - echo "$( - jq \ - --arg github_workspace ${{ github.workspace }} \ - '. | ( .runs[].results[].locations[].physicalLocation.artifactLocation.uri |= if test($github_workspace) then .[($github_workspace | length | . + 1):] else . end )' \ - ${{ github.workspace }}/detekt.sarif.json - )" > ${{ github.workspace }}/detekt.sarif.json - - # Uploads results to GitHub repository using the upload-sarif action - - uses: github/codeql-action/upload-sarif@v2 - with: - # Path to SARIF file relative to the root of the repository - sarif_file: ${{ github.workspace }}/detekt.sarif.json - checkout_path: ${{ github.workspace }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8b5b4d5..7de0c51 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -58,6 +58,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} FORMAL_GIT_HUB_PAGES_AUTHOR: developers@spine.io # https://docs.github.com/en/actions/reference/environment-variables - REPO_SLUG: $GITHUB_REPOSITORY # e.g. SpineEventEngine/core-java + REPO_SLUG: ${{ github.repository }} # e.g. SpineEventEngine/core-jvm GOOGLE_APPLICATION_CREDENTIALS: ./maven-publisher.json NPM_TOKEN: ${{ secrets.NPM_SECRET }} diff --git a/.gitignore b/.gitignore index 419020f..48de9f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,11 @@ # -# Copyright 2022, TeamDev. All rights reserved. +# Copyright 2025, 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 @@ -25,6 +31,9 @@ # # Therefore, instructions below are superset of instructions required for all the projects. +# Temporary output of AI agents. +.output + # `jenv` local configuration. .java-version @@ -32,7 +41,7 @@ .fleet/ # Kotlin temp directories. -**/.kotlin/** +**/.kotlin/ # IntelliJ IDEA modules and interim config files. *.iml @@ -49,11 +58,27 @@ !.idea/codeStyles/ !.idea/copyright/ +# Ignore IDEA config files under `tests` +/tests/.idea/** + # Gradle interim configs **/.gradle/** +# Temp directory for Gradle TestKit runners +**/.gradle-test-kit/** + +# Integration test log files +/tests/_out/** + # Generated source code **/generated/** +**/*.pb.dart +**/*.pbenum.dart +**/*.pbserver.dart +**/*.pbjson.dart + +# Generated source code with custom path under `tests` +/tests/**/proto-gen/** # Gradle build files **/build/** @@ -77,9 +102,6 @@ gradle-app.setting # Spine internal directory for storing intermediate artifacts **/.spine/** -# Spine model compiler auto-generated resources -/tools/gradle-plugins/model-compiler/src/main/resources/spine-protoc.gradle - # Login details to Maven repository. # Each workstation should have developer's login defined in this file. credentials.tar @@ -104,17 +126,7 @@ hs_err_pid* .packages pubspec.lock +# Ignore the `tmp` directory used for building dependant repositories. +/tmp -# -# The gradle.properties file should contain settings specific to a developer's workstation. -# -# See sample file for a Mac OS X workstation below. -# ------- -# # Set Java home to point to JDK8. This is need to generate classes working with Java8 API. -# # Otherwise the following warning appears during the build: -# # warning: [options] bootstrap class path not set in conjunction with -source 1.8 -# # -# # suppress inspection "UnusedProperty" -# org.gradle.java.home=/Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home/ -# ------- -gradle.properties +.gradle-test-kit/ diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 809943c..f60c273 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,5 +1,6 @@ + - + \ No newline at end of file diff --git a/.idea/dictionaries/common.xml b/.idea/dictionaries/common.xml index e62952a..d1c3a7b 100644 --- a/.idea/dictionaries/common.xml +++ b/.idea/dictionaries/common.xml @@ -24,6 +24,8 @@ handshaker hohpe idempotency + jspecify + kotest lempira liskov melnik @@ -66,4 +68,4 @@ yevsyukov - + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 229f1d3..0bd1d9d 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -255,18 +255,6 @@

{@link Runtime#exec(String[], String[], File) Executes} the given {@code String} array as - * a CLI command. If the execution is successful, returns the command output. Throws - * an {@link IllegalStateException} otherwise. + * [Executes][Runtime.exec] the given `String` array as a CLI command. + * + * If the execution is successful, returns the command output. + * Throws an [IllegalStateException] otherwise. * - * @param command the command to execute - * @return the command line output - * @throws IllegalStateException upon an execution error + * @param command the command to execute. + * @return the command line output. + * @throws IllegalStateException if the execution fails. */ fun execute(vararg command: String): String { val outWriter = StringWriter() @@ -89,7 +86,7 @@ class Cli(private val workingFolder: File) { * Asynchronously reads all lines from this [InputStream] and appends them * to the passed [StringWriter]. */ -fun InputStream.pourTo(dest: StringWriter) { +private fun InputStream.pourTo(dest: StringWriter) { Thread { val sc = Scanner(this) while (sc.hasNextLine()) { diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/ConfigTester.kt b/buildSrc/src/main/kotlin/io/spine/gradle/ConfigTester.kt index 3de99c4..e5c3007 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/ConfigTester.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/ConfigTester.kt @@ -195,7 +195,7 @@ class GitRepository( * to the specified [destinationFolder]. * * The source code is put to the sub-folder named after the repository. - * E.g. for `https://github.com/acme-org/foobar` the code is placed under + * E.g., for `https://github.com/acme-org/foobar` the code is placed under * the `destinationFolder/foobar` folder. * * If the supplied folder does not exist, it is created. diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/ProjectExtensions.kt b/buildSrc/src/main/kotlin/io/spine/gradle/ProjectExtensions.kt index 3b43246..deda203 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/ProjectExtensions.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/ProjectExtensions.kt @@ -40,6 +40,15 @@ import org.gradle.kotlin.dsl.getByType * This file contains extension methods and properties for the Gradle `Project`. */ +/** + * Logs the result of the function using the project logger at `INFO` level. + */ +fun Project.log(message: () -> String) { + if (logger.isInfoEnabled) { + logger.info(message.invoke()) + } +} + /** * Obtains the Java plugin extension of the project. */ @@ -68,7 +77,7 @@ fun Project.applyPlugin(cls: Class>) { * the generic parameter `T`. */ @Suppress("UNCHECKED_CAST") /* See the method docs. */ -fun Project.findTask(name: String): T { +fun Project.getTask(name: String): T { val task = this.tasks.findByName(name) ?: error("Unable to find a task named `$name` in the project `${this.name}`.") return task as T diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/Repositories.kt b/buildSrc/src/main/kotlin/io/spine/gradle/Repositories.kt deleted file mode 100644 index 8ec449e..0000000 --- a/buildSrc/src/main/kotlin/io/spine/gradle/Repositories.kt +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Copyright 2025, 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. - */ - -@file:Suppress("TooManyFunctions") // Deprecated functions will be kept for a while. - -package io.spine.gradle - -import io.spine.gradle.publish.CloudRepo -import io.spine.gradle.publish.PublishingRepos -import io.spine.gradle.publish.PublishingRepos.gitHub -import java.io.File -import java.net.URI -import java.util.* -import org.gradle.api.Project -import org.gradle.api.artifacts.dsl.RepositoryHandler -import org.gradle.api.artifacts.repositories.MavenArtifactRepository -import org.gradle.kotlin.dsl.ScriptHandlerScope -import org.gradle.kotlin.dsl.maven - -/** - * Applies [standard][doApplyStandard] repositories to this [ScriptHandlerScope] - * optionally adding [gitHub] repositories for Spine-only components, if - * names of such repositories are given. - * - * @param buildscript - * a [ScriptHandlerScope] to work with. Pass `this` under `buildscript { }`. - * @param rootProject - * a root project where the `buildscript` is declared. - * @param gitHubRepo - * a list of short repository names, or empty list if only - * [standard repositories][doApplyStandard] are required. - */ -@Suppress("unused") -@Deprecated( - message = "Please use `standardSpineSdkRepositories()`.", - replaceWith = ReplaceWith("standardSpineSdkRepositories()") -) -fun applyWithStandard( - buildscript: ScriptHandlerScope, - rootProject: Project, - vararg gitHubRepo: String -) { - val repositories = buildscript.repositories - gitHubRepo.iterator().forEachRemaining { repo -> - repositories.applyGitHubPackages(repo, rootProject) - } - repositories.standardToSpineSdk() -} - -/** - * Registers the selected GitHub Packages repos as Maven repositories. - * - * To be used in `buildscript` clauses when a fully-qualified call must be made. - * - * @param repositories - * the handler to accept registration of the GitHub Packages repository - * @param shortRepositoryName - * the short name of the GitHub repository (e.g. "core-java") - * @param project - * the project which is going to consume artifacts from the repository - * @see applyGitHubPackages - */ -@Suppress("unused") -@Deprecated( - message = "Please use `standardSpineSdkRepositories()`.", - replaceWith = ReplaceWith("standardSpineSdkRepositories()") -) -fun doApplyGitHubPackages( - repositories: RepositoryHandler, - shortRepositoryName: String, - project: Project -) = repositories.applyGitHubPackages(shortRepositoryName, project) - -/** - * Registers the standard set of Maven repositories. - * - * To be used in `buildscript` clauses when a fully-qualified call must be made. - */ -@Suppress("unused") -@Deprecated( - message = "Please use `standardSpineSdkRepositories()`.", - replaceWith = ReplaceWith("standardSpineSdkRepositories()") -) -fun doApplyStandard(repositories: RepositoryHandler) = repositories.standardToSpineSdk() - -/** - * Applies the repository hosted at GitHub Packages, to which Spine artifacts were published. - * - * This method should be used by those wishing to have Spine artifacts published - * to GitHub Packages as dependencies. - * - * @param shortRepositoryName - * short names of the GitHub repository (e.g. "base", "core-java", "model-tools") - * @param project - * the project which is going to consume artifacts from repositories - */ -fun RepositoryHandler.applyGitHubPackages(shortRepositoryName: String, project: Project) { - val repository = gitHub(shortRepositoryName) - val credentials = repository.credentials(project) - - credentials?.let { - spineMavenRepo(it, repository.releases) - spineMavenRepo(it, repository.snapshots) - } -} - -/** - * Applies the repositories hosted at GitHub Packages, to which Spine artifacts were published. - * - * This method should be used by those wishing to have Spine artifacts published - * to GitHub Packages as dependencies. - * - * @param shortRepositoryName - * the short name of the GitHub repository (e.g. "core-java") - * @param project - * the project which is going to consume or publish artifacts from - * the registered repository - */ -fun RepositoryHandler.applyGitHubPackages(project: Project, vararg shortRepositoryName: String) { - for (name in shortRepositoryName) { - applyGitHubPackages(name, project) - } -} - -/** - * Applies [standard][applyStandard] repositories to this [RepositoryHandler] - * optionally adding [applyGitHubPackages] repositories for Spine-only components, if - * names of such repositories are given. - * - * @param project - * a project to which we add dependencies - * @param gitHubRepo - * a list of short repository names, or empty list if only - * [standard repositories][applyStandard] are required. - */ -@Suppress("unused") -@Deprecated( - message = "Please use `standardToSpineSdk()`.", - replaceWith = ReplaceWith("standardToSpineSdk()") -) -fun RepositoryHandler.applyStandardWithGitHub(project: Project, vararg gitHubRepo: String) { - gitHubRepo.iterator().forEachRemaining { repo -> - applyGitHubPackages(repo, project) - } - standardToSpineSdk() -} - -/** - * A scrambled version of PAT generated with the only "read:packages" scope. - * - * The scrambling around PAT is necessary because GitHub analyzes commits for the presence - * of tokens and invalidates them. - * - * @see - * How to make GitHub packages to the public - */ -object Pat { - private const val shade = "_phg->8YlN->MFRA->gxIk->HVkm->eO6g->FqHJ->z8MS->H4zC->ZEPq" - private const val separator = "->" - private val chunks: Int = shade.split(separator).size - 1 - - fun credentials(): Credentials { - val pass = shade.replace(separator, "").splitAndReverse(chunks, "") - return Credentials("public", pass) - } - - /** - * Splits this string to the chunks, reverses each chunk, and joins them - * back to a string using the [separator]. - */ - private fun String.splitAndReverse(numChunks: Int, separator: String): String { - check(length / numChunks >= 2) { - "The number of chunks is too big. Must be <= ${length / 2}." - } - val chunks = chunked(length / numChunks) - val reversedChunks = chunks.map { chunk -> chunk.reversed() } - return reversedChunks.joinToString(separator) - } -} - -/** - * Adds a read-only view to all artifacts of the SpineEventEngine - * GitHub organization. - */ -fun RepositoryHandler.spineArtifacts(): MavenArtifactRepository = maven { - url = URI("https://maven.pkg.github.com/SpineEventEngine/*") - includeSpineOnly() - val pat = Pat.credentials() - credentials { - username = pat.username - password = pat.password - } -} - -val RepositoryHandler.intellijReleases: MavenArtifactRepository - get() = maven("https://www.jetbrains.com/intellij-repository/releases") - -val RepositoryHandler.jetBrainsCacheRedirector: MavenArtifactRepository - get() = maven("https://cache-redirector.jetbrains.com/intellij-dependencies") - -/** - * Applies repositories commonly used by Spine Event Engine projects. - */ -fun RepositoryHandler.standardToSpineSdk() { - spineArtifacts() - - val spineRepos = listOf( - Repos.spine, - Repos.spineSnapshots, - Repos.artifactRegistry, - Repos.artifactRegistrySnapshots - ) - - spineRepos - .map { URI(it) } - .forEach { - maven { - url = it - includeSpineOnly() - } - } - - intellijReleases - jetBrainsCacheRedirector - - maven { - url = URI(Repos.sonatypeSnapshots) - } - - mavenCentral() - gradlePluginPortal() - mavenLocal().includeSpineOnly() -} - -@Deprecated( - message = "Please use `standardToSpineSdk() instead.", - replaceWith = ReplaceWith("standardToSpineSdk()") -) -fun RepositoryHandler.applyStandard() = this.standardToSpineSdk() - -/** - * A Maven repository. - */ -data class Repository( - val releases: String, - val snapshots: String, - private val credentialsFile: String? = null, - private val credentialValues: ((Project) -> Credentials?)? = null, - val name: String = "Maven repository `$releases`" -) { - - /** - * Obtains the publishing password credentials to this repository. - * - * If the credentials are represented by a `.properties` file, reads the file and parses - * the credentials. The file must have properties `user.name` and `user.password`, which store - * the username and the password for the Maven repository auth. - */ - fun credentials(project: Project): Credentials? = when { - credentialValues != null -> credentialValues.invoke(project) - credentialsFile != null -> credsFromFile(credentialsFile, project) - else -> throw IllegalArgumentException( - "Credentials file or a supplier function should be passed." - ) - } - - private fun credsFromFile(fileName: String, project: Project): Credentials? { - val file = project.rootProject.file(fileName) - if (file.exists().not()) { - return null - } - - val log = project.logger - log.info("Using credentials from `$fileName`.") - val creds = file.parseCredentials() - log.info("Publishing build as `${creds.username}`.") - return creds - } - - private fun File.parseCredentials(): Credentials { - val properties = Properties().apply { load(inputStream()) } - val username = properties.getProperty("user.name") - val password = properties.getProperty("user.password") - return Credentials(username, password) - } - - override fun toString(): String { - return name - } -} - -/** - * Password credentials for a Maven repository. - */ -data class Credentials( - val username: String?, - val password: String? -) - -/** - * Defines names of additional repositories commonly used in the Spine SDK projects. - * - * @see [applyStandard] - */ -private object Repos { - val spine = CloudRepo.published.releases - val spineSnapshots = CloudRepo.published.snapshots - val artifactRegistry = PublishingRepos.cloudArtifactRegistry.releases - val artifactRegistrySnapshots = PublishingRepos.cloudArtifactRegistry.snapshots - - @Suppress("unused") - @Deprecated( - message = "Sonatype release repository redirects to the Maven Central", - replaceWith = ReplaceWith("sonatypeSnapshots"), - level = DeprecationLevel.ERROR - ) - const val sonatypeReleases = "https://oss.sonatype.org/content/repositories/snapshots" - const val sonatypeSnapshots = "https://oss.sonatype.org/content/repositories/snapshots" -} - -/** - * Registers the Maven repository with the passed [repoCredentials] for authorization. - * - * Only includes the Spine-related artifact groups. - */ -private fun RepositoryHandler.spineMavenRepo( - repoCredentials: Credentials, - repoUrl: String -) { - maven { - url = URI(repoUrl) - includeSpineOnly() - credentials { - username = repoCredentials.username - password = repoCredentials.password - } - } -} - -/** - * Narrows down the search for this repository to Spine-related artifact groups. - */ -private fun MavenArtifactRepository.includeSpineOnly() { - content { - includeGroupByRegex("io\\.spine.*") - } -} diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/VersionWriter.kt b/buildSrc/src/main/kotlin/io/spine/gradle/VersionWriter.kt deleted file mode 100644 index d3d4323..0000000 --- a/buildSrc/src/main/kotlin/io/spine/gradle/VersionWriter.kt +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2025, 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 - -import java.util.* -import org.gradle.api.DefaultTask -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.provider.MapProperty -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.OutputDirectory -import org.gradle.api.tasks.TaskAction - -/** - * A task that generates a dependency versions `.properties` file. - */ -abstract class WriteVersions : DefaultTask() { - - /** - * Versions to add to the file. - * - * The map key is a string in the format of `_`, and the value - * is the version corresponding to those group ID and artifact name. - * - * @see WriteVersions.version - */ - @get:Input - abstract val versions: MapProperty - - /** - * The directory that hosts the generated file. - */ - @get:OutputDirectory - abstract val versionsFileLocation: DirectoryProperty - - /** - * Adds a dependency version to write into the file. - * - * The given dependency notation is a Gradle artifact string of format: - * `"::"`. - * - * @see WriteVersions.versions - * @see WriteVersions.includeOwnVersion - */ - fun version(dependencyNotation: String) { - val parts = dependencyNotation.split(":") - check(parts.size == 3) { "Invalid dependency notation: `$dependencyNotation`." } - versions.put("${parts[0]}_${parts[1]}", parts[2]) - } - - /** - * Enables the versions file to include the version of the project that owns this task. - * - * @see WriteVersions.version - * @see WriteVersions.versions - */ - fun includeOwnVersion() { - val groupId = project.group.toString() - val artifactId = project.artifactId - val version = project.version.toString() - versions.put("${groupId}_${artifactId}", version) - } - - /** - * Creates a `.properties` file with versions, named after the value - * of [Project.artifactId] property. - * - * The name of the file would be: `versions-.properties`. - * - * By default, value of [Project.artifactId] property is a project's name with "spine-" prefix. - * For example, if a project's name is "tools", then the name of the file would be: - * `versions-spine-tools.properties`. - */ - @TaskAction - fun writeFile() { - versions.finalizeValue() - versionsFileLocation.finalizeValue() - - val values = versions.get() - val properties = Properties() - properties.putAll(values) - val outputDir = versionsFileLocation.get().asFile - outputDir.mkdirs() - val fileName = resourceFileName() - val file = outputDir.resolve(fileName) - file.createNewFile() - file.writer().use { - properties.store(it, "Dependency versions supplied by the `$path` task.") - } - } - - private fun resourceFileName(): String { - val artifactId = project.artifactId - return "versions-${artifactId}.properties" - } -} - -/** - * A plugin that enables storing dependency versions into a resource file. - * - * Dependency version may be used by Gradle plugins at runtime. - * - * The plugin adds one task โ€” `writeVersions`, which generates a `.properties` file with some - * dependency versions. - * - * The generated file will be available in classpath of the target project under the name: - * `versions-.properties`, where `` is the name of the target - * Gradle project. - */ -@Suppress("unused") -class VersionWriter : Plugin { - - override fun apply(target: Project): Unit = with (target.tasks) { - val task = register("writeVersions", WriteVersions::class.java) { - versionsFileLocation.convention(project.layout.buildDirectory.dir(name)) - includeOwnVersion() - project.sourceSets - .getByName("main") - .resources - .srcDir(versionsFileLocation) - } - getByName("processResources").dependsOn(task) - } -} diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/checkstyle/CheckStyleConfig.kt b/buildSrc/src/main/kotlin/io/spine/gradle/checkstyle/CheckStyleConfig.kt index 122a604..bb2a181 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/checkstyle/CheckStyleConfig.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/checkstyle/CheckStyleConfig.kt @@ -56,7 +56,7 @@ object CheckStyleConfig { plugin(CheckstylePlugin::class.java) } - val configDir = project.rootDir.resolve("config/quality/") + val configDir = project.rootDir.resolve("buildSrc/quality/") with(project.the()) { toolVersion = CheckStyle.version diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/fs/LazyTempPath.kt b/buildSrc/src/main/kotlin/io/spine/gradle/fs/LazyTempPath.kt index f6e1777..0344819 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/fs/LazyTempPath.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/fs/LazyTempPath.kt @@ -57,7 +57,7 @@ class LazyTempPath(private val prefix: String) : Path { vararg modifiers: WatchEvent.Modifier? ): WatchKey = delegate.register(watcher, events, *modifiers) - override fun register(watcher: WatchService, vararg events: WatchEvent.Kind<*>?): WatchKey = + override fun register(watcher: WatchService, vararg events: WatchEvent.Kind<*>): WatchKey = delegate.register(watcher, *events) override fun getFileSystem(): FileSystem = delegate.fileSystem @@ -101,7 +101,7 @@ class LazyTempPath(private val prefix: String) : Path { override fun toAbsolutePath(): Path = delegate.toAbsolutePath() - override fun toRealPath(vararg options: LinkOption?): Path = delegate.toRealPath(*options) + override fun toRealPath(vararg options: LinkOption): Path = delegate.toRealPath(*options) override fun toFile(): File = delegate.toFile() diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/git/Repository.kt b/buildSrc/src/main/kotlin/io/spine/gradle/git/Repository.kt index 55ce67f..e0ce827 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/git/Repository.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/git/Repository.kt @@ -26,8 +26,11 @@ package io.spine.gradle.git +import com.google.common.util.concurrent.Uninterruptibles.sleepUninterruptibly import io.spine.gradle.Cli import io.spine.gradle.fs.LazyTempPath +import java.util.concurrent.TimeUnit.MILLISECONDS +import org.gradle.api.Project /** * Interacts with a real Git repository. @@ -43,26 +46,18 @@ import io.spine.gradle.fs.LazyTempPath * NOTE: This class creates a temporal folder, so it holds resources. For the proper * release of resources please use the provided functionality inside a `use` block or * call the `close` method manually. + * + * @property project The Gradle project in which context the repo operations are held. + * @property sshUrl The GitHub SSH URL to the underlying repository. + * @property user Current user configuration. + * This configuration determines what ends up in the `author` and `committer` fields of a commit. + * @property currentBranch The currently checked-out branch. */ class Repository private constructor( - - /** - * The GitHub SSH URL to the underlying repository. - */ + private val project: Project, private val sshUrl: String, - - /** - * Current user configuration. - * - * This configuration determines what ends up in author and committer fields of a commit. - */ private var user: UserInfo, - - /** - * Currently checked out branch. - */ - private var currentBranch: String - + private var currentBranch: String, ) : AutoCloseable { /** @@ -80,14 +75,21 @@ class Repository private constructor( /** * Executes a command in the [location]. */ - private fun repoExecute(vararg command: String): String = - Cli(location.toFile()).execute(*command) + private fun repoExecute(vararg command: String): String { + val cmd = command.toList().joinToString(" ") + val msg = "[Repo (${project.path})] Executing command: `$cmd`." + System.err.println(msg) + return Cli(location.toFile()).execute(*command) + } /** * Checks out the branch by its name. + * + * IMPORTANT. The branch must exist in the upstream repository. */ fun checkout(branch: String) { repoExecute("git", "checkout", branch) + repoExecute("git", "pull") currentBranch = branch } @@ -128,10 +130,15 @@ class Repository private constructor( } /** - * Pushes local repository to the remote. + * Pushes the current branch of the repository to the remote. + * + * Performs a pull with rebase before pushing to ensure the local branch is up-to-date. */ fun push() { - repoExecute("git", "push") + withRetries(description = "Pushing to $sshUrl, branch = '$currentBranch'") { + repoExecute("git", "pull", "--rebase") + repoExecute("git", "push") + } } override fun close() { @@ -139,18 +146,27 @@ class Repository private constructor( } companion object Factory { + /** * Clones the repository with the provided SSH URL in a temporal folder. - * Configures the username and the email of the Git user. See [configureUser] - * documentation for more information. Performs checkout of the branch in - * case it was passed. By default, [master][Branch.master] is checked out. + * + * Configures the username and the email of the Git user. + * See [configureUser] documentation for more information. + * + * Performs checkout of the branch in case it was passed. + * By default, [master][Branch.master] is checked out. * * @throws IllegalArgumentException if SSH URL is an empty string. */ - fun of(sshUrl: String, user: UserInfo, branch: String = Branch.master): Repository { + fun clone( + project: Project, + sshUrl: String, + user: UserInfo, + branch: String = Branch.master, + ): Repository { require(sshUrl.isNotBlank()) { "SSH URL cannot be an empty string." } - val repo = Repository(sshUrl, user, branch) + val repo = Repository(project, sshUrl, user, branch) repo.clone() repo.configureUser(user) @@ -162,3 +178,44 @@ class Repository private constructor( } } } + +/** + * Executes a given operation with retries using exponential backoff strategy. + * + * If the operation fails, it will be retried up to the specified number of times + * with increasing delays between attempts. + * The delay increases exponentially but is capped at the specified maximum value. + * + * If all retries fail, the exception from the final attempt will be thrown to the caller. + * + * @param T the type of value returned by the operation + * @param times the maximum number of attempts to execute the operation (default: 3) + * @param initialDelay the delay before the first retry in milliseconds (default: 100ms) + * @param maxDelay the maximum delay between retries in milliseconds (default: 2000ms) + * @param factor the multiplier used to increase delay after each failure (default: 2.0) + * @param description a description of the operation for error reporting (default: empty string) + * @param block the operation to execute + * @return the result of the successful operation execution + */ +@Suppress("TooGenericExceptionCaught", "LongParameterList") +private fun withRetries( + times: Int = 5, + initialDelay: Long = 2000, // ms + maxDelay: Long = 20000, // ms + factor: Double = 2.0, + description: String = "", + block: () -> T +): T { + var currentDelay = initialDelay + repeat(times - 1) { + try { + return block() + } catch (e: Exception) { + System.err.println("'$description' failed. " + + "Message: '${e.message}'. Retrying in $currentDelay ms.") + } + sleepUninterruptibly(currentDelay, MILLISECONDS) + currentDelay = (currentDelay * factor).toLong().coerceAtMost(maxDelay) + } + return block() +} diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/RepositoryExtensions.kt b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/RepositoryExtensions.kt index 94ab6ac..de75295 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/RepositoryExtensions.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/RepositoryExtensions.kt @@ -26,10 +26,11 @@ package io.spine.gradle.github.pages -import io.spine.gradle.RepoSlug import io.spine.gradle.git.Branch import io.spine.gradle.git.Repository import io.spine.gradle.git.UserInfo +import io.spine.gradle.repo.RepoSlug +import org.gradle.api.Project /** * Clones the current project repository with the branch dedicated to publishing @@ -38,14 +39,15 @@ import io.spine.gradle.git.UserInfo * The repository's GitHub SSH URL is derived from the `REPO_SLUG` environment * variable. The [branch][Branch.documentation] dedicated to publishing documentation * is automatically checked out in this repository. Also, the username and the email - * of the git user are automatically configured. The username is set - * to "UpdateGitHubPages Plugin", and the email is derived from + * of the git user are automatically configured. + * + * The username is set to `"UpdateGitHubPages Plugin"`, and the email is derived from * the `FORMAL_GIT_HUB_PAGES_AUTHOR` environment variable. * * @throws org.gradle.api.GradleException if any of the environment variables described above * is not set. */ -internal fun Repository.Factory.forPublishingDocumentation(): Repository { +internal fun Repository.Factory.forPublishingDocumentation(project: Project): Repository { val host = RepoSlug.fromVar().gitHost() val username = "UpdateGitHubPages Plugin" @@ -54,5 +56,5 @@ internal fun Repository.Factory.forPublishingDocumentation(): Repository { val branch = Branch.documentation - return of(host, user, branch) + return clone(project, host, user, branch) } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/SshKey.kt b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/SshKey.kt index 186c474..68be42a 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/SshKey.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/SshKey.kt @@ -29,24 +29,43 @@ package io.spine.gradle.github.pages import io.spine.gradle.Cli import java.io.File import org.gradle.api.GradleException +import org.gradle.api.logging.Logger /** - * Registers SSH key for further operations with GitHub Pages. + * Registers the SSH key for further operations with GitHub Pages. + * + * @property rootProjectFolder The folder of the project for which we build the documentation. + * @property logger The logger for placing diagnostic messages of this class. */ -internal class SshKey(private val rootProjectFolder: File) { +internal class SshKey( + private val rootProjectFolder: File, + private val logger: Logger +) { + + private fun log(message: () -> String) { + if (logger.isInfoEnabled) { + logger.info("[SshKey] " + message()) + } + } + /** * Creates an SSH key with the credentials and registers it by invoking the * `register-ssh-key.sh` script. */ fun register() { + log { "Registering using ${rootProjectFolder.absolutePath}." } val gitHubAccessKey = gitHubKey() + log { "Obtained the key file at ${gitHubAccessKey.absolutePath}." } val sshConfigFile = sshConfigFile() + log { "Located the SSH key file at ${sshConfigFile.absolutePath}." } sshConfigFile.appendPublisher(gitHubAccessKey) + log { "SSH config file appended." } execute( "${rootProjectFolder.absolutePath}/config/scripts/register-ssh-key.sh", gitHubAccessKey.absolutePath ) + log { "The SSH key registered." } } /** @@ -59,7 +78,7 @@ internal class SshKey(private val rootProjectFolder: File) { * publishing. * * Thus, we configure the SSH agent to use the `deploy_rsa_key` only for specific - * references, namely in `github.com-publish`. + * references, namely in `github-publish`. * * @throws GradleException if `deploy_key_rsa` is not found. */ @@ -91,9 +110,10 @@ internal class SshKey(private val rootProjectFolder: File) { val nl = System.lineSeparator() this.appendText( nl + - "Host github.com-publish" + nl + - "User git" + nl + - "IdentityFile ${privateKey.absolutePath}" + nl + "Host github-publish" + nl + + " HostName github.com" + nl + + " User git" + nl + + " IdentityFile ${privateKey.absolutePath}" + nl ) } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/TaskName.kt b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/TaskName.kt index f5e3bfc..72b8fd3 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/TaskName.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/TaskName.kt @@ -34,14 +34,14 @@ object TaskName { const val updateGitHubPages = "updateGitHubPages" /** - * The name of the helper task to gather the generated Javadoc before updating - * GitHub Pages. + * The name of the helper task to gather the generated Javadoc format + * documentation generated by Dokka before updating GitHub Pages. */ - const val copyJavadoc = "copyJavadoc" + const val copyJavadocDocs = "copyJavadocDocs" /** - * The name of the helper task to gather Dokka-generated documentation before - * updating GitHub Pages. + * The name of the helper task to gather HTML documentation + * generated by Dokka before updating GitHub Pages. */ - const val copyDokka = "copyDokka" + const val copyHtmlDocs = "copyHtmlDocs" } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/Update.kt b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/Update.kt index 0f9a0f5..785162e 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/Update.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/Update.kt @@ -41,23 +41,19 @@ import org.gradle.api.logging.Logger fun Task.updateGhPages(project: Project) { val plugin = project.plugins.getPlugin(UpdateGitHubPages::class.java) - with(plugin) { - SshKey(rootFolder).register() - } + SshKey(plugin.rootFolder, logger).register() - val repository = Repository.forPublishingDocumentation() + val repository = Repository.forPublishingDocumentation(project) - val updateJavadoc = with(plugin) { - UpdateJavadoc(project, javadocOutputFolder, repository, logger) - } + val updateJavadocFormat = + UpdateJavadocFormat(project, plugin.javadocOutputFolder, repository, logger) - val updateDokka = with(plugin) { - UpdateDokka(project, dokkaOutputFolder, repository, logger) - } + val updateHtmlFormat = + UpdateHtmlFormat(project, plugin.htmlOutputFolder, repository, logger) repository.use { - updateJavadoc.run() - updateDokka.run() + updateJavadocFormat.run() + updateHtmlFormat.run() repository.push() } } @@ -80,17 +76,17 @@ private abstract class UpdateDocumentation( protected abstract val docsDestinationFolder: String /** - * The name of the tool used to generate the documentation to update. + * The name of the format of the documentation to update. * * This name will appear in logs as part of a message. */ - protected abstract val toolName: String + protected abstract val formatName: String private val mostRecentFolder by lazy { File("${repository.location}/${docsDestinationFolder}/${project.name}") } - private fun logDebug(message: () -> String) { + private fun log(message: () -> String) { if (logger.isDebugEnabled) { logger.debug(message()) } @@ -98,25 +94,24 @@ private abstract class UpdateDocumentation( fun run() { val module = project.name - logDebug {"Update of the $toolName documentation for module `$module` started." } + log { "Update of the `$formatName` documentation for the module `$module` started." } val documentation = replaceMostRecentDocs() copyIntoVersionDir(documentation) val version = project.version val updateMessage = - "Update `$toolName` documentation for module `$module` as for version $version" + "Update `$formatName` documentation for the module" + + " `$module` with the version `$version`." repository.commitAllChanges(updateMessage) - logDebug { "Update of the `$toolName` documentation for `$module` successfully finished." } + log { "Update of the `$formatName` documentation for `$module` successfully finished." } } private fun replaceMostRecentDocs(): ConfigurableFileCollection { val generatedDocs = project.files(docsSourceFolder) - logDebug { - "Replacing the most recent `$toolName` documentation in `${mostRecentFolder}`." - } + log { "Replacing the most recent `$formatName` documentation in `$mostRecentFolder`." } copyDocs(generatedDocs, mostRecentFolder) return generatedDocs @@ -133,14 +128,12 @@ private abstract class UpdateDocumentation( private fun copyIntoVersionDir(generatedDocs: ConfigurableFileCollection) { val versionedDocDir = File("$mostRecentFolder/v/${project.version}") - logDebug { - "Storing the new version of `$toolName` documentation in `${versionedDocDir}`." - } + log { "Storing the new version of `$formatName` documentation in `${versionedDocDir}`." } copyDocs(generatedDocs, versionedDocDir) } } -private class UpdateJavadoc( +private class UpdateJavadocFormat( project: Project, docsSourceFolder: Path, repository: Repository, @@ -148,12 +141,12 @@ private class UpdateJavadoc( ) : UpdateDocumentation(project, docsSourceFolder, repository, logger) { override val docsDestinationFolder: String - get() = "reference" - override val toolName: String - get() = "Javadoc" + get() = "javadoc" + override val formatName: String + get() = "javadoc" } -private class UpdateDokka( +private class UpdateHtmlFormat( project: Project, docsSourceFolder: Path, repository: Repository, @@ -161,7 +154,7 @@ private class UpdateDokka( ) : UpdateDocumentation(project, docsSourceFolder, repository, logger) { override val docsDestinationFolder: String - get() = "dokka-reference" - override val toolName: String - get() = "Dokka" + get() = "reference" + override val formatName: String + get() = "html" } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/UpdateGitHubPages.kt b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/UpdateGitHubPages.kt index e46a565..cdfd2c4 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/UpdateGitHubPages.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/UpdateGitHubPages.kt @@ -27,13 +27,11 @@ package io.spine.gradle.github.pages import dokkaHtmlTask +import dokkaJavadocTask import io.spine.gradle.fs.LazyTempPath -import io.spine.gradle.github.pages.TaskName.copyDokka -import io.spine.gradle.github.pages.TaskName.copyJavadoc +import io.spine.gradle.github.pages.TaskName.copyHtmlDocs +import io.spine.gradle.github.pages.TaskName.copyJavadocDocs import io.spine.gradle.github.pages.TaskName.updateGitHubPages -import io.spine.gradle.isSnapshot -import io.spine.gradle.javadoc.ExcludeInternalDoclet -import io.spine.gradle.javadoc.javadocTask import java.io.File import org.gradle.api.Plugin import org.gradle.api.Project @@ -43,30 +41,12 @@ import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.TaskProvider /** - * Registers the `updateGitHubPages` task which performs the update of the GitHub - * Pages with the documentation generated by Javadoc and Dokka for a particular - * Gradle project. The generated documentation is appended to the `spine.io` site - * via GitHub pages by pushing commits to the `gh-pages` branch. - * - * Please note that the update is only performed for the projects which are - * NOT snapshots. + * Registers the `updateGitHubPages` task which performs the update of + * the GitHub Pages with the documentation generated in Javadoc and HTML format + * for a particular Gradle project. * - * Users may supply [allowInternalJavadoc][UpdateGitHubPagesExtension.allowInternalJavadoc] - * to configure documentation generated by Javadoc. The documentation for the code - * marked `@Internal` is included when the option is set to `true`. By default, this - * option is `false`. - * - * Usage: - * ``` - * updateGitHubPages { - * - * // Include `@Internal`-annotated code. - * allowInternalJavadoc.set(true) - * - * // Propagate the full path to the local folder of the repository root. - * rootFolder.set(rootDir.absolutePath) - * } - * ``` + * The generated documentation is appended to the `spine.io` site + * via GitHub pages by pushing commits to the `gh-pages` branch. * * In order to work, the script needs a `deploy_key_rsa` private RSA key file in the * repository root. It is recommended to encrypt it in the repository and then decrypt @@ -104,16 +84,16 @@ class UpdateGitHubPages : Plugin { private lateinit var includedInputs: Set /** - * Path to the temp folder used to gather the Javadoc output before submitting it - * to the GitHub Pages update. + * Path to the temp folder used to gather the Javadoc format output (generated by Dokka) + * before submitting it to the GitHub Pages update. */ internal val javadocOutputFolder = LazyTempPath("javadoc") /** - * Path to the temp folder used to gather the documentation generated by Dokka - * before submitting it to the GitHub Pages update. + * Path to the temp folder used to gather the HTML documentation + * generated by Dokka before submitting it to the GitHub Pages update. */ - internal val dokkaOutputFolder = LazyTempPath("dokka") + internal val htmlOutputFolder = LazyTempPath("html") /** * Applies the plugin to the specified [project]. @@ -127,12 +107,15 @@ class UpdateGitHubPages : Plugin { override fun apply(project: Project) { val extension = UpdateGitHubPagesExtension.createIn(project) project.afterEvaluate { - val projectVersion = project.version.toString() - if (projectVersion.isSnapshot()) { - registerNoOpTask() - } else { - registerTasks(extension) - } + //TODO:2025-11-20:alexander.yevsyukov: Remove this line and uncomment the below block + // when new publishing procedure is finalized. + registerTasks(extension) +// val projectVersion = project.version.toString() +// if (projectVersion.isSnapshot()) { +// registerNoOpTask() +// } else { +// registerTasks(extension) +// } } } @@ -141,6 +124,7 @@ class UpdateGitHubPages : Plugin { * the message telling the update is skipped, since the project is in * its `SNAPSHOT` version. */ + @Suppress("unused") private fun Project.registerNoOpTask() { tasks.register(updateGitHubPages) { doLast { @@ -154,41 +138,31 @@ class UpdateGitHubPages : Plugin { } private fun Project.registerTasks(extension: UpdateGitHubPagesExtension) { - val allowInternalJavadoc = extension.allowInternalJavadoc() rootFolder = extension.rootFolder() includedInputs = extension.includedInputs() - if (!allowInternalJavadoc) { - val doclet = ExcludeInternalDoclet(extension.excludeInternalDocletVersion) - doclet.registerTaskIn(this) - } - - tasks.registerCopyJavadoc(allowInternalJavadoc) + tasks.registerCopyJavadoc() tasks.registerCopyDokka() val updatePagesTask = tasks.registerUpdateTask() updatePagesTask.configure { - dependsOn(copyJavadoc) - dependsOn(copyDokka) + dependsOn(copyJavadocDocs) + dependsOn(copyHtmlDocs) } } - private fun TaskContainer.registerCopyJavadoc(allowInternalJavadoc: Boolean) { - val inputs = composeJavadocInputs(allowInternalJavadoc) + private fun TaskContainer.registerCopyJavadoc() { + val inputs = composeJavadocInputs() - register(copyJavadoc, Copy::class.java) { + register(copyJavadocDocs, Copy::class.java) { inputs.forEach { from(it) } into(javadocOutputFolder) } } - private fun TaskContainer.composeJavadocInputs(allowInternalJavadoc: Boolean): List { + private fun TaskContainer.composeJavadocInputs(): List { val inputs = mutableListOf() - if (allowInternalJavadoc) { - inputs.add(javadocTask()) - } else { - inputs.add(javadocTask(ExcludeInternalDoclet.taskName)) - } + inputs.add(dokkaJavadocTask()!!) inputs.addAll(includedInputs) return inputs } @@ -196,9 +170,9 @@ class UpdateGitHubPages : Plugin { private fun TaskContainer.registerCopyDokka() { val inputs = composeDokkaInputs() - register(copyDokka, Copy::class.java) { + register(copyHtmlDocs, Copy::class.java) { inputs.forEach { from(it) } - into(dokkaOutputFolder) + into(htmlOutputFolder) } } @@ -226,7 +200,7 @@ class UpdateGitHubPages : Plugin { } private fun cleanup() { - val folders = listOf(dokkaOutputFolder, javadocOutputFolder) + val folders = listOf(htmlOutputFolder, javadocOutputFolder) folders.forEach { it.toFile().deleteRecursively() } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/UpdateGitHubPagesExtension.kt b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/UpdateGitHubPagesExtension.kt index 90eebc2..a849a83 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/UpdateGitHubPagesExtension.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/UpdateGitHubPagesExtension.kt @@ -38,65 +38,41 @@ import org.gradle.kotlin.dsl.property * Configures the `updateGitHubPages` extension. */ @Suppress("unused") -fun Project.updateGitHubPages(excludeInternalDocletVersion: String, - action: UpdateGitHubPagesExtension.() -> Unit) { +fun Project.updateGitHubPages( + action: UpdateGitHubPagesExtension.() -> Unit +) { apply() val extension = extensions.getByType(UpdateGitHubPagesExtension::class) - extension.excludeInternalDocletVersion = excludeInternalDocletVersion extension.action() } /** * The extension for configuring the [UpdateGitHubPages] plugin. + * + * @property rootFolder The root folder of the repository to which the updated `Project` belongs. + * @property includeInputs The external inputs, which output should be included + * into the GitHub Pages update. The values are interpreted according to + * [Copy.from][org.gradle.api.tasks.Copy.from] specification. + * This property is optional. */ -class UpdateGitHubPagesExtension -private constructor( - - /** - * Tells whether the types marked `@Internal` should be included into - * the doc generation. - */ - val allowInternalJavadoc: Property, - - /** - * The root folder of the repository to which the updated `Project` belongs. - */ +class UpdateGitHubPagesExtension private constructor( var rootFolder: Property, - - /** - * The external inputs, which output should be included into - * the GitHub Pages update. - * - * The values are interpreted according to - * [org.gradle.api.tasks.Copy.from] specification. - * - * This property is optional. - */ var includeInputs: SetProperty ) { - - /** - * The version of the - * [ExcludeInternalDoclet][io.spine.gradle.javadoc.ExcludeInternalDoclet] - * used when updating documentation at GitHub Pages. - * - * This value is used when adding dependency on the doclet when the plugin tasks - * are registered. Since the doclet dependency is required, its value passed as - * a parameter for the extension, rather than a property. - */ - internal lateinit var excludeInternalDocletVersion: String - internal companion object { - /** The name of the extension. */ + /** + * The name of the extension. + */ const val name = "updateGitHubPages" - /** Creates a new extension and adds it to the passed project. */ + /** + * Creates a new extension and adds it to the passed project. + */ fun createIn(project: Project): UpdateGitHubPagesExtension { val factory = project.objects val result = UpdateGitHubPagesExtension( - allowInternalJavadoc = factory.property(Boolean::class), rootFolder = factory.property(File::class), includeInputs = factory.setProperty(Any::class.java) ) @@ -105,27 +81,15 @@ private constructor( } } - /** - * Returns `true` if the `@Internal`-annotated code should be included into the - * generated documentation, `false` otherwise. - */ - fun allowInternalJavadoc(): Boolean { - return allowInternalJavadoc.get() - } - /** * Returns the local root folder of the repository, to which the handled Gradle * Project belongs. */ - fun rootFolder(): File { - return rootFolder.get() - } + fun rootFolder(): File = rootFolder.get() /** * Returns the external inputs, which results should be included into the * GitHub Pages update. */ - fun includedInputs(): Set { - return includeInputs.get() - } + fun includedInputs(): Set = includeInputs.get() } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/java/Linters.kt b/buildSrc/src/main/kotlin/io/spine/gradle/java/Linters.kt new file mode 100644 index 0000000..fc00555 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/gradle/java/Linters.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2025, 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.java + +import net.ltgt.gradle.errorprone.errorprone +import org.gradle.api.Project +import org.gradle.api.tasks.compile.JavaCompile +import org.gradle.kotlin.dsl.invoke +import org.gradle.kotlin.dsl.named + +/** + * Disables Java linters in this [Project]. + * + * In particular, the following linters will be disabled: + * + * 1. CheckStyle. + * 2. PMD. + * 3. ErrorProne. + * + * Apply this configuration for modules that have original Flogger sources, + * which have not been migrated to Kotlin yet. They produce a lot of + * errors/warnings failing the build. + * + * Our own sources are mostly in Kotlin (as for `spine-logging` repo), + * so this action seems quite safe. + */ +// TODO:2023-09-22:yevhenii.nadtochii: Remove this piece of configuration. +// See issue: https://github.com/SpineEventEngine/logging/issues/56 +fun Project.disableLinters() { + tasks { + named("checkstyleMain") { enabled = false } + named("pmdMain") { enabled = false } + named("compileJava") { + options.errorprone.isEnabled.set(false) + } + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/javadoc/ExcludeInternalDoclet.kt b/buildSrc/src/main/kotlin/io/spine/gradle/javadoc/ExcludeInternalDoclet.kt index 541504c..c42c65c 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/javadoc/ExcludeInternalDoclet.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/javadoc/ExcludeInternalDoclet.kt @@ -26,8 +26,7 @@ package io.spine.gradle.javadoc -import io.spine.dependency.local.ArtifactVersion -import io.spine.dependency.local.Spine +import io.spine.dependency.local.ToolBase import io.spine.gradle.javadoc.ExcludeInternalDoclet.Companion.taskName import io.spine.gradle.sourceSets import org.gradle.api.Project @@ -39,12 +38,9 @@ import org.gradle.external.javadoc.StandardJavadocDocletOptions * The doclet which removes Javadoc for `@Internal` things in the Java code. */ @Suppress("ConstPropertyName") -class ExcludeInternalDoclet( - @Deprecated("`Spine.ArtifactVersion.javadocTools` is used instead.") - val version: String = ArtifactVersion.javadocTools -) { +class ExcludeInternalDoclet { - private val dependency = Spine.javadocFilter + private val dependency = ToolBase.JavadocFilter.artifact companion object { diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/javadoc/JavadocConfig.kt b/buildSrc/src/main/kotlin/io/spine/gradle/javadoc/JavadocConfig.kt index d2e4c90..9616e99 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/javadoc/JavadocConfig.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/javadoc/JavadocConfig.kt @@ -26,11 +26,24 @@ package io.spine.gradle.javadoc +import io.spine.gradle.javadoc.JavadocConfig.tags import java.io.File import org.gradle.api.JavaVersion import org.gradle.api.Project +import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.javadoc.Javadoc import org.gradle.external.javadoc.StandardJavadocDocletOptions +import productionModules + +/** + * Finds a [Javadoc] Gradle task by the passed name. + */ +fun TaskContainer.javadocTask(named: String) = this.getByName(named) as Javadoc + +/** + * Finds a default [Javadoc] Gradle task. + */ +fun TaskContainer.javadocTask() = this.getByName("javadoc") as Javadoc /** * Javadoc processing settings. @@ -58,17 +71,25 @@ object JavadocConfig { fun applyTo(project: Project) { val javadocTask = project.tasks.javadocTask() + if (!isProductionModule(project)) { + javadocTask.enabled = false + return + } discardJavaModulesInLinks(javadocTask) val docletOptions = javadocTask.options as StandardJavadocDocletOptions configureDoclet(docletOptions) } + private fun isProductionModule(project: Project) = project.run { + rootProject.productionModules.contains(this) + } + /** - * Discards using of Java 9 modules in URL links generated by javadoc for our codebase. + * Discards using of Java 9 modules in URL links generated by Javadoc for our codebase. * * This fixes navigation to classes through the search results. * - * The issue appeared after migration to Java 11. When javadoc is generated for a project + * The issue appeared after migration to Java 11. When Javadoc is generated for a project * that does not declare Java 9 modules, search results contain broken links with appended * `undefined` prefix to the URL. This `undefined` was meant to be a name of a Java 9 module. * @@ -78,9 +99,9 @@ object JavadocConfig { // We ask `Javadoc` task to modify "search.js" and override a method, responsible for // the formation of URL prefixes. We can't specify the option "--no-module-directories", - // because it leads to discarding of all module prefixes in generated links. That means, - // links to the types from the standard library would not work, as they are declared - // within modules since Java 9. + // because it leads to discarding of all module prefixes in generated links. + // That means links to the types from the standard library would not work, + // as they are declared within modules since Java 9. val discardModulePrefix = """ diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/kotlin/KotlinConfig.kt b/buildSrc/src/main/kotlin/io/spine/gradle/kotlin/KotlinConfig.kt index 65cdebd..4539aa9 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/kotlin/KotlinConfig.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/kotlin/KotlinConfig.kt @@ -27,7 +27,7 @@ package io.spine.gradle.kotlin import org.gradle.jvm.toolchain.JavaLanguageVersion -import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions +import org.jetbrains.kotlin.gradle.dsl.KotlinCommonCompilerOptions import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension /** @@ -52,12 +52,14 @@ fun KotlinJvmProjectExtension.applyJvmToolchain(version: String) = * Opts-in to experimental features that we use in our codebase. */ @Suppress("unused") -fun KotlinJvmCompilerOptions.setFreeCompilerArgs() { +fun KotlinCommonCompilerOptions.setFreeCompilerArgs() { freeCompilerArgs.addAll( listOf( "-Xskip-prerelease-check", "-Xjvm-default=all", "-Xinline-classes", + "-Xexpect-actual-classes", + "-Xcontext-parameters", "-opt-in=" + "kotlin.contracts.ExperimentalContracts," + "kotlin.io.path.ExperimentalPathApi," + diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/protobuf/ProtoTaskExtensions.kt b/buildSrc/src/main/kotlin/io/spine/gradle/protobuf/ProtoTaskExtensions.kt deleted file mode 100644 index 85ef9fc..0000000 --- a/buildSrc/src/main/kotlin/io/spine/gradle/protobuf/ProtoTaskExtensions.kt +++ /dev/null @@ -1,418 +0,0 @@ -/* - * Copyright 2025, 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.protobuf - -import com.google.protobuf.gradle.GenerateProtoTask -import com.google.protobuf.gradle.ProtobufExtension -import io.spine.gradle.sourceSets -import java.io.File -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths -import java.nio.file.StandardOpenOption.TRUNCATE_EXISTING -import org.gradle.api.Project -import org.gradle.api.file.SourceDirectorySet -import org.gradle.api.tasks.SourceSet -import org.gradle.kotlin.dsl.get -import org.gradle.kotlin.dsl.getByType -import org.gradle.plugins.ide.idea.GenerateIdeaModule -import org.gradle.plugins.ide.idea.model.IdeaModel -import org.gradle.plugins.ide.idea.model.IdeaModule -import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask -import titleCaseFirstChar - -/** - * Obtains the path of the `generated` directory under the project root directory. - */ -private val Project.generatedDir: Path - get() = projectDir.resolve("generated").toPath() - -/** - * Obtains the `generated` directory for the source set of the task. - * - * If [language] is specified returns the subdirectory for this language. - */ -private fun GenerateProtoTask.generatedDir(language: String = ""): File { - val path = "${project.generatedDir}/${sourceSet.name}/$language" - return File(path) -} - -/** - * Configures a [GenerateProtoTask] for the code which cannot use Spine Model Compiler - * (e.g., Spine Base or Spine Validation modules). - * - * The task configuration consists of the following steps: - * - * 1. Adding `"kotlin"` to the list of involved `protoc` builtins. - * - * 2. Turning on the generation of a descriptor set file for each source set. - * These files are placed under the `build/descriptors` directory. - * - * 3. Removing source code generated for `com.google` package for both Java and Kotlin. - * This is done at the final steps of the code generation. - * - * 4. Making `processResource` tasks depend on corresponding `generateProto` tasks. - * If the source set of the configured task isn't `main`, appropriate infix for - * the task names is used. - * - * The usage of this extension in a module build file would be: - * ``` - * protobuf { - * generateProtoTasks.all().configureEach { - * setup() - * } - * } - * ``` - * Using the same code under `subprojects` in a root build file does not seem to work because - * test descriptor set files are not copied to resources. Performing this configuration from - * a module build script solves the issue. - * - * IMPORTANT: In addition to calling `setup`, a submodule must contain a descriptor set reference - * file (`desc.ref`) files placed under `resources`. The descriptor reference file must contain - * a reference to the descriptor set file generated by the corresponding `GenerateProtoTask`. - * - * For example, for the `test` source set, the reference would be `known_types_test.desc`, and - * for the `main` source set, the reference would be `known_types_main.desc`. - * - * See `io.spine.code.proto.DescriptorReference` and `io.spine.code.proto.FileDescriptors` classes - * under the `base` project for more details. - */ -@Suppress("unused") -fun GenerateProtoTask.setup() { - builtins.maybeCreate("kotlin") - setupDescriptorSetFileCreation() - - doLast { - copyGeneratedFiles() - } - - excludeProtocOutput() - - setupKotlinCompile() - dependOnProcessResourcesTask() - makeDirsForIdeaModule() -} - -/** - * Tell `protoc` to generate descriptor set files under the project build dir. - * - * The name of the descriptor set file to be generated - * is made to be unique via the project's Maven coordinates. - * - * As the last step of this task, writes a `desc.ref` file - * for the contextual source set, pointing to the generated descriptor set file. - * This is needed to allow other Spine libraries to locate and load the generated - * descriptor set files properly. - * - * Such a job is usually performed by Spine McJava plugin; - * however, it is not possible to use this plugin (or its code) - * in this repository due to cyclic dependencies. - */ -@Suppress( - "TooGenericExceptionCaught" /* Handling all file-writing failures in the same way.*/ -) -fun GenerateProtoTask.setupDescriptorSetFileCreation() { - // Tell `protoc` generate a descriptor set file. - // The name of the generated file reflects the Maven coordinates of the project. - val ssn = sourceSet.name - generateDescriptorSet = true - val buildDir = project.layout.buildDirectory.asFile.get().path - val descriptorsDir = "$buildDir/descriptors/${ssn}" - val descriptorName = project.descriptorSetName(sourceSet) - with(descriptorSetOptions) { - path = "$descriptorsDir/$descriptorName" - includeImports = true - includeSourceInfo = true - } - - // Add the descriptor set file into the resources. - project.sourceSets.named(ssn) { - resources.srcDirs(descriptorsDir) - } - - // Create a `desc.ref` in the same resource folder, - // with the name of the descriptor set file created above. - this.doLast { - val descRefFile = File(descriptorsDir, "desc.ref") - descRefFile.createNewFile() - try { - Files.write(descRefFile.toPath(), setOf(descriptorName), TRUNCATE_EXISTING) - } catch (e: Exception) { - project.logger.error("Error writing `${descRefFile.absolutePath}`.", e) - throw e - } - } -} - -/** - * Returns a name of the descriptor file for the given [sourceSet], - * reflecting the Maven coordinates of Gradle artifact, and the source set - * for which the descriptor set name is to be generated. - * - * The returned value is just a file name and does not contain a file path. - */ -private fun Project.descriptorSetName(sourceSet: SourceSet) = - arrayOf( - group.toString(), - name, - sourceSet.name, - version.toString() - ).joinToString(separator = "_", postfix = ".desc") - -/** - * Copies files from the [outputBaseDir][GenerateProtoTask.outputBaseDir] into - * a subdirectory of [generatedDir][Project.generatedDir] for - * the current [sourceSet][GenerateProtoTask.sourceSet]. - * - * Also removes sources belonging to the `com.google` package in the target directory. - */ -private fun GenerateProtoTask.copyGeneratedFiles() { - project.copy { - from(outputBaseDir) - into(generatedDir()) - } - deleteComGoogle("java") - deleteComGoogle("kotlin") -} - -/** - * Remove the code generated for Google Protobuf library types. - * - * Java code for the `com.google` package was generated because we wanted - * to have descriptors for all the types, including those from Google Protobuf library. - * We want all the descriptors so that they are included into the resources used by - * the `io.spine.type.KnownTypes` class. - * - * Now, as we have the descriptors _and_ excessive Java or Kotlin code, we delete it to avoid - * classes that duplicate those coming from Protobuf library JARs. - */ -private fun GenerateProtoTask.deleteComGoogle(language: String) { - val comDirectory = generatedDir(language).resolve("com") - val googlePackage = comDirectory.resolve("google") - - project.delete(googlePackage) - - // If the `com` directory becomes empty, delete it too. - if (comDirectory.exists() && comDirectory.isDirectory && comDirectory.list()!!.isEmpty()) { - project.delete(comDirectory) - } -} - -/** - * Exclude [GenerateProtoTask.outputBaseDir] from Java source set directories to avoid - * duplicated source code files. - */ -fun GenerateProtoTask.excludeProtocOutput() { - val protocOutputDir = File(outputBaseDir).parentFile - val java: SourceDirectorySet = sourceSet.java - - // Filter out directories belonging to `build/generated/source/proto`. - val newSourceDirectories = java.sourceDirectories - .filter { !it.residesIn(protocOutputDir) } - .toSet() - // Make sure we start from scratch. - // Not doing this failed the following, real, assignment sometimes. - java.setSrcDirs(listOf()) - java.srcDirs(newSourceDirectories) - - // Add copied files to the Java source set. - java.srcDir(generatedDir("java")) - java.srcDir(generatedDir("kotlin")) -} - -/** - * Make sure Kotlin compilation explicitly depends on this `GenerateProtoTask` to avoid racing. - */ -fun GenerateProtoTask.setupKotlinCompile() { - val kotlinCompile = project.kotlinCompilationTaskFor(sourceSet) - kotlinCompile?.dependsOn(this) -} - -/** - * Make the tasks `processResources` depend on `generateProto` tasks explicitly so that: - * 1) Descriptor set files get into resources, avoiding the racing conditions - * during the build. - * - * 2) We don't have the warning "Execution optimizations have been disabled..." issued - * by Gradle during the build because Protobuf Gradle Plugin does not set - * dependencies between `generateProto` and `processResources` tasks. - */ -fun GenerateProtoTask.dependOnProcessResourcesTask() { - val processResources = processResourceTaskName(sourceSet.name) - project.tasks[processResources].dependsOn(this) -} - -/** - * Obtains the name of the `processResource` task for the given source set name. - */ -private fun processResourceTaskName(sourceSetName: String): String { - val infix = - if (sourceSetName == "main") "" - else sourceSetName.titleCaseFirstChar() - return "process${infix}Resources" -} - -private fun Project.kotlinCompilationTaskFor(sourceSet: SourceSet): KotlinCompilationTask<*>? { - val taskName = sourceSet.getCompileTaskName("Kotlin") - return tasks.named(taskName, KotlinCompilationTask::class.java).orNull -} - -private fun File.residesIn(directory: File): Boolean = - canonicalFile.startsWith(directory.absolutePath) - -/** - * Ensures that generated directories for Java and Kotlin are created before [GenerateIdeaModule]. - * - * This works as advised by `Utils.groovy` from Protobuf Gradle plugin: - * ``` - * This is required because the IntelliJ IDEA plugin does not allow adding source directories - * that do not exist. The IntelliJ IDEA config files should be valid from the start even if - * a user runs './gradlew idea' before running './gradlew generateProto'. - * ``` - */ -fun GenerateProtoTask.makeDirsForIdeaModule() { - project.plugins.withId("idea") { - val javaDir = generatedDir("java") - val kotlinDir = generatedDir("kotlin") - project.tasks.withType(GenerateIdeaModule::class.java).forEach { - it.doFirst { - javaDir.mkdirs() - kotlinDir.mkdirs() - } - } - } -} - -/** - * Prints diagnostic output of `sourceDirs` and `generatedSourceDirs` of an [IdeaModule]. - * - * To get a handle on [IdeaModule] please use the following code: - * - * ```kotlin - * val module = project.extensions.findByType(IdeaModel::class.java)!!.module - * ``` - */ -@Suppress("unused") // To be used when debugging build scripts. -fun IdeaModule.printSourceDirectories() { - println("**** [IDEA] Source directories:") - sourceDirs.forEach { println(it) } - println() - println("**** [IDEA] Generated source directories:") - generatedSourceDirs.forEach { println(it) } - println() - println("**** [IDEA] Excluded directories:") - excludeDirs.forEach { println(it) } -} - -/** - * Obtains the directory where the Protobuf Gradle Plugin should place the generated code. - * - * The directory is fixed to be `$buildDir/generated/source/proto` and cannot be - * changed by the settings of the plugin. Even though [ProtobufExtension] has a property - * [generatedFilesBaseDir][ProtobufExtension.getGeneratedFilesBaseDir], which is supposed - * to be used for this purpose, it is declared with `@PackageScope` and thus cannot be - * accessed from outside the plugin. The Protobuf Gradle Plugin (at v0.9.2) does not - * modify the value of the property either. - */ -val Project.generatedSourceProtoDir: Path - get() = layout.buildDirectory.dir("generated/source/proto").get().asFile.toPath() - -/** - * Ensures that the sources generated by Protobuf Gradle Plugin - * are not included in the IDEA project. - * - * IDEA should only see the sources generated by ProtoData as - * we define in [GenerateProtoTask.excludeProtocOutput]. - */ -@Suppress("unused") -fun Project.configureIdea() { - - fun filterSources(sources: Set, excludeDir: File): Set = - sources.filter { !it.residesIn(excludeDir) }.toSet() - - pluginManager.withPlugin("idea") { - val idea = extensions.getByType() - with(idea.module) { - val protocOutput = file(generatedSourceProtoDir) - val protocTargets = protocTargets() - excludeWithNested(protocOutput.toPath(), protocTargets) - sourceDirs = filterSources(sourceDirs, protocOutput) - testSources.filter { !it.residesIn(protocOutput) } - generatedSourceDirs = generatedDir.resolve(protocTargets) - .map { it.toFile() } - .toSet() - } - } -} - -/** - * Lists target directories for Protobuf code generation. - * - * The directory names are in the following format: - * - * `/` - */ -private fun Project.protocTargets(): List { - val protobufTasks = tasks.withType(GenerateProtoTask::class.java) - val codegenTargets = sequence { - protobufTasks.forEach { task -> - val sourceSet = task.sourceSet.name - val builtins = task.builtins.map { builtin -> builtin.name } - val plugins = task.plugins.map { plugin -> plugin.name } - val combined = builtins + plugins - combined.forEach { subdir -> - yield(Paths.get(sourceSet, subdir)) - } - } - } - return codegenTargets.toList() -} - -private fun Path.resolve(subdirs: Iterable): List = - subdirs.map { - resolve(it) - } - -/** - * Excludes the given directory and its subdirectories from - * being seen as ones with the source code. - * - * The primary use of this extension is to exclude `build/generated/source/proto` and its - * subdirectories to avoid duplication of types in the generated code with those in - * produced by ProtoData under the `$projectDir/generated/` directory. - */ -private fun IdeaModule.excludeWithNested(directory: Path, subdirs: Iterable) { - excludeDirs.add(directory.toFile()) - directory.resolve(subdirs).forEach { - excludeDirs.add(it.toFile()) - } -} - -@Suppress("unused") // To be used when debugging build scripts. -private fun printExcluded(dir: Any) { - println(" [IDEA] Excluding directory: $dir") -} diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/publish/CheckVersionIncrement.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/CheckVersionIncrement.kt index 9e38068..9b4ba30 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/publish/CheckVersionIncrement.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/publish/CheckVersionIncrement.kt @@ -28,8 +28,9 @@ package io.spine.gradle.publish import com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES import com.fasterxml.jackson.dataformat.xml.XmlMapper -import io.spine.gradle.Repository +import io.spine.gradle.repo.Repository import java.io.FileNotFoundException +import java.net.URI import java.net.URL import org.gradle.api.DefaultTask import org.gradle.api.GradleException @@ -58,10 +59,11 @@ open class CheckVersionIncrement : DefaultTask() { @TaskAction fun fetchAndCheck() { val artifact = "${project.artifactPath()}/${MavenMetadata.FILE_NAME}" - checkInRepo(repository.snapshots, artifact) + val snapshots = repository.target(snapshots = true) + checkInRepo(snapshots, artifact) - if (repository.releases != repository.snapshots) { - checkInRepo(repository.releases, artifact) + if (!repository.hasOneTarget()) { + checkInRepo(repository.target(snapshots = false), artifact) } } @@ -74,16 +76,16 @@ open class CheckVersionIncrement : DefaultTask() { """ The version `$version` is already published to the Maven repository `$repoUrl`. Try incrementing the library version. - All available versions are: ${versions?.joinToString(separator = ", ")}. - - To disable this check, run Gradle with `-x $name`. + All available versions are: ${versions?.joinToString(separator = ", ")}. + + To disable this check, run Gradle with `-x $name`. """.trimIndent() ) } } private fun fetch(repository: String, artifact: String): MavenMetadata? { - val url = URL("$repository/$artifact") + val url = URI.create("$repository/$artifact").toURL() return MavenMetadata.fetchAndParse(url) } @@ -135,7 +137,7 @@ private data class MavenMetadata(var versioning: Versioning = Versioning()) { return try { val metadata = mapper.readValue(url, MavenMetadata::class.java) metadata - } catch (ignored: FileNotFoundException) { + } catch (_: FileNotFoundException) { null } } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/publish/CloudArtifactRegistry.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/CloudArtifactRegistry.kt index 1cfa7c2..67716d8 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/publish/CloudArtifactRegistry.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/publish/CloudArtifactRegistry.kt @@ -28,8 +28,8 @@ package io.spine.gradle.publish import com.google.auth.oauth2.GoogleCredentials import com.google.cloud.artifactregistry.auth.DefaultCredentialProvider -import io.spine.gradle.Credentials -import io.spine.gradle.Repository +import io.spine.gradle.repo.Credentials +import io.spine.gradle.repo.Repository import java.io.IOException import org.gradle.api.Project @@ -51,13 +51,15 @@ import org.gradle.api.Project * Ordering said hooks is a non-trivial operation and the result is usually quite fragile. * Thus, we choose to do this small piece of configuration manually. */ +@Suppress("ConstPropertyName") // https://bit.ly/kotlin-prop-names internal object CloudArtifactRegistry { private const val spineRepoLocation = "https://europe-maven.pkg.dev/spine-event-engine" val repository = Repository( - releases = "${spineRepoLocation}/releases", - snapshots = "${spineRepoLocation}/snapshots", + name = "CloudArtifactRegistry", + releases = "$spineRepoLocation/releases", + snapshots = "$spineRepoLocation/snapshots", credentialValues = this::fetchGoogleCredentials ) diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/publish/CloudRepo.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/CloudRepo.kt index 7db15aa..624f5cb 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/publish/CloudRepo.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/publish/CloudRepo.kt @@ -26,15 +26,16 @@ package io.spine.gradle.publish -import io.spine.gradle.Repository +import io.spine.gradle.repo.Repository /** * CloudRepo Maven repository. * * There is a special treatment for this repository. Usually, fetching and publishing of artifacts * is performed via the same URL. But it is not true for CloudRepo. Fetching is performed via - * the public repository, and publishing via the private one. Their URLs differ in `/public` infix. + * the public repository and publishing via the private one. Their URLs differ in `/public` infix. */ +@Deprecated(message = "Please use `PublishingRepos.cloudArtifactRegistry` instead.") internal object CloudRepo { private const val name = "CloudRepo" diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/publish/CustomPublicationHandler.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/CustomPublicationHandler.kt new file mode 100644 index 0000000..152455d --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/gradle/publish/CustomPublicationHandler.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2025, 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.spine.gradle.repo.Repository +import org.gradle.api.Project +import org.gradle.api.publish.maven.MavenPublication + +/** + * A handler for custom publications, which are declared under the [publications] + * section of a module. + * + * Such publications should be treated differently than [StandardJavaPublicationHandler], + * which is created for a module. Instead, since the publications are already declared, + * this class only [assigns Maven coordinates][copyProjectAttributes]. + * + * A module which declares custom publications must be specified in + * the [SpinePublishing.modulesWithCustomPublishing] property. + * + * If a module with [publications] declared locally is not specified as one with custom publishing, + * it may cause a name clash between an artifact produced by + * the [standard][org.gradle.api.publish.maven.MavenPublication] publication, and custom ones. + * To have both standard and custom publications, please specify custom artifact IDs or + * classifiers for each custom publication. + * + * @see StandardJavaPublicationHandler + */ +internal class CustomPublicationHandler private constructor( + project: Project, + destinations: Set +) : PublicationHandler(project, destinations) { + + override fun handlePublications() { + project.publications.forEach { + (it as MavenPublication).copyProjectAttributes() + } + } + + companion object : HandlerFactory() { + override fun create( + project: Project, + destinations: Set, + vararg params: Any + ): CustomPublicationHandler = CustomPublicationHandler(project, destinations) + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/publish/GitHubPackages.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/GitHubPackages.kt index 1072139..df326b8 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/publish/GitHubPackages.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/publish/GitHubPackages.kt @@ -26,8 +26,8 @@ package io.spine.gradle.publish -import io.spine.gradle.Credentials -import io.spine.gradle.Repository +import io.spine.gradle.repo.Credentials +import io.spine.gradle.repo.Repository import io.spine.gradle.buildDirectory import net.lingala.zip4j.ZipFile import org.gradle.api.Project @@ -42,12 +42,12 @@ internal object GitHubPackages { */ fun repository(repoName: String): Repository { val githubActor: String = actor() + val url = "https://maven.pkg.github.com/SpineEventEngine/$repoName" return Repository( - name = "GitHub Packages", - releases = "https://maven.pkg.github.com/SpineEventEngine/$repoName", - snapshots = "https://maven.pkg.github.com/SpineEventEngine/$repoName", - credentialValues = { project -> project.credentialsWithToken(githubActor) } - ) + name = "GitHub-Packages", + releases = url, + snapshots = url + ) { project -> project.credentialsWithToken(githubActor) } } private fun actor(): String { diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/publish/JarDsl.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/JarDsl.kt index 1371184..1bf0c0b 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/publish/JarDsl.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/publish/JarDsl.kt @@ -26,37 +26,6 @@ package io.spine.gradle.publish -/** - * A DSL element of [SpinePublishing] extension which configures publishing of - * [dokkaKotlinJar] artifact. - * - * This artifact contains Dokka-generated documentation. By default, it is not published. - * - * Take a look at the [SpinePublishing.dokkaJar] for a usage example. - * - * @see [artifacts] - */ -class DokkaJar { - /** - * Enables publishing `JAR`s with Dokka-generated documentation for all published modules. - */ - @Suppress("unused") - @Deprecated("Please use `kotlin` and `java` flags instead.") - var enabled = false - - /** - * Controls whether [dokkaKotlinJar] artifact should be published. - * The default value is `true`. - */ - var kotlin = true - - /** - * Controls whether [dokkaJavaJar] artifact should be published. - * The default value is `false`. - */ - var java = false -} - /** * A DSL element of [SpinePublishing] extension which allows enabling publishing * of [testJar] artifact. @@ -80,88 +49,25 @@ class TestJar { var enabled = false } -/** - * A DSL element of [SpinePublishing] extension which allows disabling publishing - * of [protoJar] artifact. - * - * This artifact contains all the `.proto` definitions from `sourceSets.main.proto`. By default, - * it is published. - * - * Take a look on [SpinePublishing.protoJar] for a usage example. - * - * @see [artifacts] - */ -class ProtoJar { - - /** - * Set of modules, for which a proto JAR will not be published. - */ - var exclusions: Set = emptySet() - - /** - * Disables proto JAR publishing for all published modules. - */ - var disabled = false -} - /** * Flags for turning optional JAR artifacts in a project. + * + * @property sourcesJar Tells whether [sourcesJar] artifact should be published. + * Default value is `true`. + * @property publishTestJar Tells whether [testJar] artifact should be published. */ internal data class JarFlags( - - /** - * Tells whether [sourcesJar] artifact should be published. - * - * Default value is `true`. - */ val sourcesJar: Boolean = true, - - /** - * Tells whether [javadocJar] artifact should be published. - * - * Default value is `true`. - */ - val javadocJar: Boolean = true, - - /** - * Tells whether [protoJar] artifact should be published. - */ - val publishProtoJar: Boolean, - - /** - * Tells whether [testJar] artifact should be published. - */ val publishTestJar: Boolean, - - /** - * Tells whether [dokkaKotlinJar] artifact should be published. - */ - val publishDokkaKotlinJar: Boolean, - - /** - * Tells whether [dokkaJavaJar] artifact should be published. - */ - val publishDokkaJavaJar: Boolean ) { internal companion object { /** * Creates an instance of [JarFlags] for the project with the given name, * taking the setup parameters from JAR DSL elements. */ - fun create( - projectName: String, - protoJar: ProtoJar, - testJar: TestJar, - dokkaJar: DokkaJar - ): JarFlags { - val addProtoJar = (protoJar.exclusions.contains(projectName) || protoJar.disabled).not() + fun create(projectName: String, testJar: TestJar): JarFlags { val addTestJar = testJar.inclusions.contains(projectName) || testJar.enabled - return JarFlags( - sourcesJar = true, - javadocJar = true, - addProtoJar, addTestJar, - dokkaJar.kotlin, dokkaJar.java - ) + return JarFlags(sourcesJar = true, addTestJar) } } } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/publish/ProtoExts.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/ProtoExts.kt index 874b7f9..a32e969 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/publish/ProtoExts.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/publish/ProtoExts.kt @@ -35,11 +35,15 @@ import org.gradle.api.file.SourceDirectorySet import org.gradle.api.tasks.bundling.Jar /** - * Tells whether there are any Proto sources in "main" source set. + * Tells whether there are any Proto sources in the "main" source set. */ -internal fun Project.hasProto(): Boolean { +fun Project.hasProto(): Boolean { val protoSources = protoSources() - val result = protoSources.any { it.exists() } + val result = protoSources.any { + it.exists() + && it.isDirectory + && it.listFiles()?.isNotEmpty() ?: false + } return result } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublicationHandler.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublicationHandler.kt new file mode 100644 index 0000000..5b3bbe1 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublicationHandler.kt @@ -0,0 +1,259 @@ +/* + * Copyright 2025, 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 LicenseSettings +import io.spine.gradle.isSnapshot +import io.spine.gradle.repo.Repository +import io.spine.gradle.report.pom.InceptionYear +import org.gradle.api.Project +import org.gradle.api.artifacts.dsl.RepositoryHandler +import org.gradle.api.invocation.BuildInvocationDetails +import org.gradle.api.publish.maven.MavenPublication +import org.gradle.kotlin.dsl.apply +import org.gradle.kotlin.dsl.support.serviceOf + +/** + * The name of the Maven Publishing Gradle plugin. + */ +private const val MAVEN_PUBLISH = "maven-publish" + +/** + * Abstract base for handlers of publications in a project + * with [spinePublishing] settings declared. + * + * @param project The project to which the handler is applied. + * @param destinations The repositories for publishing artifacts of this project. + * In a multi-module project the destinations can be re-defined by + * specifying custom values in + * the [`spinePublishing`][io.spine.gradle.publish.SpinePublishing.destinations] + * extension applied to the subproject. + */ +sealed class PublicationHandler( + protected val project: Project, + protected var destinations: Set +) { + /** + * Remembers if the [apply] function was called by this handler. + */ + private var applied: Boolean = false + + /** + * Overwrites the [destinations] property with the given set. + */ + fun publishTo(alternativeDestinations: Set) { + if (alternativeDestinations.isEmpty()) { + project.logger.info( + "The project ${project.path} is not going to be published because" + + " the publication handler `${this@PublicationHandler}`" + + " got an empty set of new `destinations`." + ) + } + destinations = alternativeDestinations + } + + /** + * Configures the publication of the associated [project]. + */ + fun apply() { + synchronized(project) { + if (applied) { + return + } + project.run { + // We apply the `maven-publish` plugin for modules with standard + // publishing automatically because they don't need custom DSL + // in their `build.gradle.kts` files. + // All the job is done by the `SpinePublishing` extension and + // `StandardPublicationHandler` instance associated with this project. + if (!hasCustomPublishing) { + apply(plugin = MAVEN_PUBLISH) + } + // And we do not apply the plugin for modules with custom publishing + // because they will need the `maven-publish` DSL to tune the publishing. + // Therefore, we only arrange the execution of our code when the plugin + // is applied. + pluginManager.withPlugin(MAVEN_PUBLISH) { + handlePublications() + registerDestinations() + configurePublishTask(destinations) + applied = true + } + } + } + } + + /** + * Either handles publications already declared in the associated [project] + * or creates new ones. + */ + abstract fun handlePublications() + + /** + * Goes through the [destinations] and registers each as a repository for publishing + * in the given Gradle project. + */ + private fun registerDestinations() { + val repositories = project.publishingExtension.repositories + destinations.forEach { destination -> + repositories.register(project, destination) + } + } + + /** + * Copies the attributes of Gradle [Project] to this [MavenPublication]. + * + * The following project attributes are copied: + * * [group][Project.getGroup]; + * * [version][Project.getVersion]; + * * [description][Project.getDescription]. + * + * Also, this function adds the [artifactPrefix][SpinePublishing.artifactPrefix] to + * the [artifactId][MavenPublication.setArtifactId] of this publication, + * if the prefix is not added yet. + * + * Finally, the Apache Software License 2.0 is set as the only license + * under which the published artifact is distributed. + */ + protected fun MavenPublication.copyProjectAttributes() { + groupId = project.group.toString() + val prefix = project.spinePublishing.artifactPrefix + if (!artifactId.startsWith(prefix)) { + artifactId = prefix + artifactId + } + version = project.version.toString() + pom.description.set(project.description) + pom.inceptionYear.set(InceptionYear.value) + pom.licenses { + license { + name.set(LicenseSettings.name) + url.set(LicenseSettings.url) + distribution.set(LicenseSettings.url) + } + } + pom.scm { + DocumentationSettings.run { + url.set(repoUrl(project)) + connection.set(connectionUrl(project)) + developerConnection.set(developerConnectionUrl(project)) + } + } + } + + /** + * The abstract base for factories producing instances of classes + * derived from [io.spine.gradle.publish.PublicationHandler]. + * + * The factory maintains associations between a path of the project to + * its publication handler. + * + * If the handler already exists, its settings are updated when + * the [serving] factory method is called. + * + * Otherwise, a new handler is created and associated with the project. + * + * @param H The type of the publication handlers produced by this repository. + * @see serving + */ + abstract class HandlerFactory { + + /** + * Maps a project path suffixed with build start time to the associated publication handler. + * + * The suffix after the project path is needed to create a new handler + * for each build. We do not use Guava or other cache expecting the small amount + * of memory consumption of each publication handler. + */ + private val handlers = mutableMapOf() + + /** + * Computes the key for a publication handler taking the [project] and + * its build start time. + */ + private fun createKey(project: Project): String { + val buildService = project.gradle.serviceOf() + val buildStartedMillis = buildService.buildStartedTime + val localTime = java.time.Instant.ofEpochMilli(buildStartedMillis) + val key = "${project.path}-at-$localTime" + return key + } + + /** + * Obtains an instance of [PublicationHandler] for the given project. + * + * If the handler for the given [project] was already created, the handler + * gets new [destinations], [overwriting][publishTo] previously specified. + * + * @return the handler for the given project which would handle publishing to + * the specified [destinations]. + */ + fun serving(project: Project, destinations: Set, vararg params: Any): H { + synchronized(handlers) { + val key = createKey(project) + var handler = handlers[key] + if (handler == null) { + handler = create(project, destinations, *params) + handlers[key] = handler + } else { + handler.publishTo(destinations) + } + return handler + } + } + + /** + * Creates a new publication handler for the given project. + * + * @param project The project to which the handler applies. + * @param destinations The repositories for publishing artifacts of this project. + * @param params Optional parameters to be passed as constructor parameters for + * classes of the type [H]. + */ + protected abstract fun create( + project: Project, + destinations: Set, + vararg params: Any + ): H + } +} + +/** + * Adds a Maven repository to the project specifying credentials, if they are + * [available][Repository.credentials] from the root project. + */ +private fun RepositoryHandler.register(project: Project, repository: Repository) { + val isSnapshot = project.version.toString().isSnapshot() + val credentials = repository.credentials(project.rootProject) + maven { + name = repository.name(isSnapshot) + url = project.uri(repository.target(isSnapshot)) + credentials { + username = credentials?.username + password = credentials?.password + } + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/publish/Publications.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/Publications.kt deleted file mode 100644 index c9acef8..0000000 --- a/buildSrc/src/main/kotlin/io/spine/gradle/publish/Publications.kt +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright 2025, 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.spine.gradle.Repository -import io.spine.gradle.isSnapshot -import org.gradle.api.Project -import org.gradle.api.artifacts.dsl.RepositoryHandler -import org.gradle.api.publish.maven.MavenPublication -import org.gradle.api.tasks.TaskProvider -import org.gradle.api.tasks.bundling.Jar -import org.gradle.kotlin.dsl.apply -import org.gradle.kotlin.dsl.create - -/** - * The name of the Maven Publishing Gradle plugin. - */ -private const val MAVEN_PUBLISH = "maven-publish" - -/** - * Abstract base for handlers of publications in a project - * with [spinePublishing] settings declared. - */ -internal sealed class PublicationHandler( - protected val project: Project, - private val destinations: Set -) { - - fun apply() = with(project) { - if (!hasCustomPublishing) { - apply(plugin = MAVEN_PUBLISH) - } - - pluginManager.withPlugin(MAVEN_PUBLISH) { - handlePublications() - registerDestinations() - configurePublishTask(destinations) - } - } - - /** - * Either handles publications already declared in the given project, - * or creates new ones. - */ - abstract fun handlePublications() - - /** - * Goes through the [destinations] and registers each as a repository for publishing - * in the given Gradle project. - */ - private fun registerDestinations() { - val repositories = project.publishingExtension.repositories - destinations.forEach { destination -> - repositories.register(project, destination) - } - } - - /** - * Copies the attributes of Gradle [Project] to this [MavenPublication]. - * - * The following project attributes are copied: - * * [group][Project.getGroup]; - * * [version][Project.getVersion]; - * * [description][Project.getDescription]. - * - * Also, this function adds the [artifactPrefix][SpinePublishing.artifactPrefix] to - * the [artifactId][MavenPublication.setArtifactId] of this publication, - * if the prefix is not added yet. - * - * Finally, the Apache Software License 2.0 is set as the only license - * under which the published artifact is distributed. - */ - protected fun MavenPublication.copyProjectAttributes() { - groupId = project.group.toString() - val prefix = project.spinePublishing.artifactPrefix - if (!artifactId.startsWith(prefix)) { - artifactId = prefix + artifactId - } - version = project.version.toString() - pom.description.set(project.description) - - pom.licenses { - license { - name.set("The Apache Software License, Version 2.0") - url.set("https://www.apache.org/licenses/LICENSE-2.0.txt") - } - } - } -} - -/** - * Adds a Maven repository to the project specifying credentials, if they are - * [available][Repository.credentials] from the root project. - */ -private fun RepositoryHandler.register(project: Project, repository: Repository) { - val isSnapshot = project.version.toString().isSnapshot() - val target = if (isSnapshot) repository.snapshots else repository.releases - val credentials = repository.credentials(project.rootProject) - maven { - url = project.uri(target) - credentials { - username = credentials?.username - password = credentials?.password - } - } -} - -/** - * A publication for a typical Java project. - * - * In Gradle, to publish something, one should create a publication. - * A publication has a name and consists of one or more artifacts plus information about - * those artifacts โ€“ the metadata. - * - * An instance of this class represents [MavenPublication] named "mavenJava". It is generally - * accepted that a publication with this name contains a Java project published to one or - * more Maven repositories. - * - * By default, only a jar with the compilation output of `main` source set and its - * metadata files are published. Other artifacts are specified through the - * [constructor parameter][jarFlags]. Please, take a look on [specifyArtifacts] for additional info. - * - * @param jarFlags - * flags for additional JARs published along with the compilation output. - * @param destinations - * Maven repositories to which the produced artifacts will be sent. - * @see - * Maven Publish Plugin | Publications - */ -internal class StandardJavaPublicationHandler( - project: Project, - private val jarFlags: JarFlags, - destinations: Set, -) : PublicationHandler(project, destinations) { - - /** - * Creates a new "mavenJava" [MavenPublication] in the given project. - */ - override fun handlePublications() { - val jars = project.artifacts(jarFlags) - val publications = project.publications - publications.create("mavenJava") { - copyProjectAttributes() - specifyArtifacts(jars) - } - } - - /** - * Specifies which artifacts this [MavenPublication] will contain. - * - * A typical Maven publication contains: - * - * 1. Jar archives. For example, compilation output, sources, javadoc, etc. - * 2. Maven metadata file that has the ".pom" extension. - * 3. Gradle's metadata file that has the ".module" extension. - * - * Metadata files contain information about a publication itself, its artifacts, and their - * dependencies. Presence of ".pom" file is mandatory for publication to be consumed by - * `mvn` build tool itself or other build tools that understand Maven notation (Gradle, Ivy). - * The presence of ".module" is optional, but useful when a publication is consumed by Gradle. - * - * @see Maven โ€“ POM Reference - * @see - * Understanding Gradle Module Metadata - */ - private fun MavenPublication.specifyArtifacts(jars: Set>) { - - /* "java" component provides a jar with compilation output of "main" source set. - It is NOT defined as another `Jar` task intentionally. Doing that will leave the - publication without correct ".pom" and ".module" metadata files generated. - */ - val javaComponent = project.components.findByName("java") - javaComponent?.let { - from(it) - } - - /* Other artifacts are represented by `Jar` tasks. Those artifacts don't bring any other - metadata in comparison with `Component` (such as dependencies notation). - */ - jars.forEach { - artifact(it) - } - } -} - -/** - * A handler for custom publications, which are declared under the [publications] - * section of a module. - * - * Such publications should be treated differently than [StandardJavaPublicationHandler], - * which is created for a module. Instead, since the publications are already declared, - * this class only [assigns maven coordinates][copyProjectAttributes]. - * - * A module which declares custom publications must be specified in - * the [SpinePublishing.modulesWithCustomPublishing] property. - * - * If a module with [publications] declared locally is not specified as one with custom publishing, - * it may cause a name clash between an artifact produced by the [standard][MavenPublication] - * publication, and custom ones. To have both standard and custom publications, - * please specify custom artifact IDs or classifiers for each custom publication. - */ -internal class CustomPublicationHandler(project: Project, destinations: Set) : - PublicationHandler(project, destinations) { - - override fun handlePublications() { - project.publications.forEach { - (it as MavenPublication).copyProjectAttributes() - } - } -} diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublishingExts.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublishingExts.kt index 62c639b..efe51d6 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublishingExts.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublishingExts.kt @@ -26,8 +26,9 @@ package io.spine.gradle.publish -import dokkaKotlinJar -import io.spine.gradle.Repository +import htmlDocsJar +import io.spine.gradle.isSnapshot +import io.spine.gradle.repo.Repository import io.spine.gradle.sourceSets import java.util.* import org.gradle.api.InvalidUserDataException @@ -35,6 +36,7 @@ import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.publish.PublicationContainer import org.gradle.api.publish.PublishingExtension +import org.gradle.api.publish.maven.MavenPublication import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.bundling.Jar @@ -57,6 +59,13 @@ internal val Project.publishingExtension: PublishingExtension internal val Project.publications: PublicationContainer get() = publishingExtension.publications +/** + * Obtains an instance, if available, of [SpinePublishing] extension + * applied to this project. + */ +internal val Project.localSpinePublishing: SpinePublishing? + get() = extensions.findByType() + /** * Obtains [SpinePublishing] extension from this [Project]. * @@ -65,7 +74,7 @@ internal val Project.publications: PublicationContainer */ internal val Project.spinePublishing: SpinePublishing get() { - val local = this.extensions.findByType() + val local = localSpinePublishing if (local != null) { return local } @@ -78,9 +87,16 @@ internal val Project.spinePublishing: SpinePublishing /** * Tells if this project has custom publishing. + * + * For a multi-module project this is checked by presence of this project + * in the list of [SpinePublishing.modulesWithCustomPublishing] of the root project. + * + * In a single-module project, the value of the [SpinePublishing.customPublishing] + * property is returned. */ internal val Project.hasCustomPublishing: Boolean - get() = spinePublishing.modulesWithCustomPublishing.contains(name) + get() = rootProject.spinePublishing.modulesWithCustomPublishing.contains(name) + || spinePublishing.customPublishing private const val PUBLISH_TASK = "publish" @@ -93,7 +109,7 @@ private const val PUBLISH_TASK = "publish" * Please note, task execution would not copy publications to the local Maven cache. * * @see - * Tasks | Maven Publish Plugin + * Tasks | The Maven Publish Plugin */ internal val TaskContainer.publish: TaskProvider get() = named(PUBLISH_TASK) @@ -140,14 +156,45 @@ private fun TaskContainer.getOrCreatePublishTask(): TaskProvider = register(PUBLISH_TASK) } +@Suppress( + /* Several types of exceptions may be thrown, + and Kotlin does not have a multi-catch support yet. */ + "TooGenericExceptionCaught" +) private fun TaskContainer.registerCheckCredentialsTask( - destinations: Set -): TaskProvider = - register("checkCredentials") { - doLast { - destinations.forEach { it.ensureCredentials(project) } + destinations: Set, +): TaskProvider { + val checkCredentials = "checkCredentials" + try { + // The result of this call is ignored intentionally. + // + // We expect this line to fail with the exception + // in case the task with this name is NOT registered. + // + // Otherwise, we need to replace the existing task + // to avoid checking the credentials + // for some previously asked `destinations`. + named(checkCredentials) + val toConfigure = replace(checkCredentials) + toConfigure.doLastCredentialsCheck(destinations) + return named(checkCredentials) + } catch (_: Exception) { + return register(checkCredentials) { doLastCredentialsCheck(destinations) } + } +} + +private fun Task.doLastCredentialsCheck(destinations: Set) { + doLast { + if (logger.isDebugEnabled) { + val isSnapshot = project.version.toString().isSnapshot() + val destinationsStr = destinations.joinToString(", ") { it.target(isSnapshot) } + logger.debug( + "Project '${project.name}': checking the credentials for repos: $destinationsStr." + ) } + destinations.forEach { it.ensureCredentials(project) } } +} private fun Repository.ensureCredentials(project: Project) { val credentials = credentials(project) @@ -175,8 +222,8 @@ fun TaskContainer.excludeGoogleProtoFromArtifacts() { * Locates or creates `sourcesJar` task in this [Project]. * * The output of this task is a `jar` archive. The archive contains sources from `main` source set. - * The task makes sure that sources from the directories below will be included into - * a resulted archive: + * The task makes sure that sources from the directories below will be included + * in the resulting archive: * * - Kotlin * - Java @@ -185,7 +232,7 @@ fun TaskContainer.excludeGoogleProtoFromArtifacts() { * Java and Kotlin sources are default to `main` source set since it is created by `java` plugin. * For Proto sources to be included โ€“ [special treatment][protoSources] is needed. */ -internal fun Project.sourcesJar(): TaskProvider = tasks.getOrCreate("sourcesJar") { +fun Project.sourcesJar(): TaskProvider = tasks.getOrCreate("sourcesJar") { dependOnGenerateProto() archiveClassifier.set("sources") from(sourceSets["main"].allSource) // Puts Java and Kotlin sources. @@ -199,7 +246,7 @@ internal fun Project.sourcesJar(): TaskProvider = tasks.getOrCreate("source * The output of this task is a `jar` archive. The archive contains only * [Proto sources][protoSources] from `main` source set. */ -internal fun Project.protoJar(): TaskProvider = tasks.getOrCreate("protoJar") { +fun Project.protoJar(): TaskProvider = tasks.getOrCreate("protoJar") { dependOnGenerateProto() archiveClassifier.set("proto") from(protoSources()) @@ -220,14 +267,14 @@ internal fun Project.testJar(): TaskProvider = tasks.getOrCreate("testJar") * Locates or creates `javadocJar` task in this [Project]. * * The output of this task is a `jar` archive. The archive contains Javadoc, - * generated upon Java sources from `main` source set. If javadoc for Kotlin is also needed, - * apply Dokka plugin. It tunes `javadoc` task to generate docs upon Kotlin sources as well. + * generated upon Java sources from `main` source set. If Javadoc for Kotlin is also needed, + * apply the Dokka plugin. It tunes `javadoc` task to generate docs upon Kotlin sources as well. */ fun Project.javadocJar(): TaskProvider = tasks.getOrCreate("javadocJar") { archiveClassifier.set("javadoc") - val javadocFiles = layout.buildDirectory.files("/docs/javadoc") + val javadocFiles = layout.buildDirectory.dir("dokka/javadoc") from(javadocFiles) - dependsOn("javadoc") + dependsOn("dokkaGeneratePublicationJavadoc") } internal fun TaskContainer.getOrCreate(name: String, init: Jar.() -> Unit): TaskProvider = @@ -254,12 +301,12 @@ internal fun Project.artifacts(jarFlags: JarFlags): Set> { tasks.add(sourcesJar()) } - if (jarFlags.javadocJar) { - tasks.add(javadocJar()) - } + tasks.add(javadocJar()) + tasks.add(htmlDocsJar()) + // We don't want to have an empty "proto.jar" when a project doesn't have any Proto files. - if (hasProto() && jarFlags.publishProtoJar) { + if (hasProto()) { tasks.add(protoJar()) } @@ -269,10 +316,22 @@ internal fun Project.artifacts(jarFlags: JarFlags): Set> { tasks.add(testJar()) } - if (jarFlags.publishDokkaKotlinJar) { - tasks.add(dokkaKotlinJar()) - } - return tasks } +/** + * Adds the source code and documentation JARs to the publication. + */ +@Suppress("unused") +fun MavenPublication.addSourceAndDocJars(project: Project) { + val tasks = mutableSetOf>() + tasks.add(project.sourcesJar()) + tasks.add(project.javadocJar()) + tasks.add(project.htmlDocsJar()) + if (project.hasProto()) { + tasks.add(project.protoJar()) + } + tasks.forEach { + artifact(it) + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublishingRepos.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublishingRepos.kt index f383e68..eea6dc1 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublishingRepos.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublishingRepos.kt @@ -26,7 +26,7 @@ package io.spine.gradle.publish -import io.spine.gradle.Repository +import io.spine.gradle.repo.Repository /** * Repositories to which we may publish. diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/publish/ShadowJarExts.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/ShadowJarExts.kt new file mode 100644 index 0000000..87157fd --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/gradle/publish/ShadowJarExts.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2025, 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 com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + +/** + * Calls [ShadowJar.mergeServiceFiles] for the files we use in the Spine SDK. + */ +fun ShadowJar.handleMergingServiceFiles() { + ServiceFiles.all.forEach { + append(it) + } +} + +@Suppress("ConstPropertyName") +private object ServiceFiles { + + /** + * Files containing references to descriptor set files. + */ + private const val descriptorSetReferences = "desc.ref" + + private const val servicesDir = "META-INF/services" + /** + * Providers of custom Protobuf options introduced by the libraries. + */ + private const val optionProviders = "$servicesDir/io.spine.option.OptionsProvider" + + /** + * KSP symbol processor provider. + */ + private const val kspSymbolProcessorProviders = + "$servicesDir/com.google.devtools.ksp.KspSymbolProcessorProvider" + + /** + * Message routing setup classes generated by the Compiler for JVM. + */ + private const val routeSetupPackage = "io.spine.server.route.setup" + private const val routeSetupPrefix = "$servicesDir/$routeSetupPackage" + private const val commandRoutingSetupClasses = "$routeSetupPrefix.CommandRoutingSetup" + private const val eventRoutingSetupClasses = "$routeSetupPrefix.EventRoutingSetup" + private const val stateRoutingSetupClasses = "$routeSetupPrefix.StateRoutingSetup" + + val all = arrayOf( + descriptorSetReferences, + optionProviders, + kspSymbolProcessorProviders, + commandRoutingSetupClasses, + eventRoutingSetupClasses, + stateRoutingSetupClasses + ) +} diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/publish/SpinePublishing.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/SpinePublishing.kt index 2f74536..480175f 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/publish/SpinePublishing.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/publish/SpinePublishing.kt @@ -28,9 +28,7 @@ package io.spine.gradle.publish -import dokkaJavaJar -import dokkaKotlinJar -import io.spine.gradle.Repository +import io.spine.gradle.repo.Repository import org.gradle.api.Project import org.gradle.api.publish.maven.plugins.MavenPublishPlugin import org.gradle.kotlin.dsl.apply @@ -44,75 +42,108 @@ import org.gradle.kotlin.dsl.findByType * * The extension can be configured for single- and multi-module projects. * + * ## Using in a multi-module project + * * When used with a multi-module project, the extension should be opened in a root project's * build file. The published modules are specified explicitly by their names: * - * ``` + * ```kotlin * spinePublishing { * modules = setOf( * "subprojectA", * "subprojectB", * ) - * destinations = setOf( - * PublishingRepos.cloudRepo, - * PublishingRepos.cloudArtifactRegistry, + * destinations = PublishingRepos.run { setOf( + * cloudArtifactRegistry, + * gitHub("") // The name of the GitHub repository of the project. + * )} + * } + * ``` + * + * ### Filtering out test-only modules + * + * Sometimes a functional or an integration test requires a significant amount of + * configuration code which is better understood when isolated into a separate module. + * Conventionally, we use the `-tests` suffix for naming such modules. + * + * In order to avoid publishing of such a test-only module, we use the following extensions + * for the Gradle [Project] class: [productionModules], [productionModuleNames]. + * So the above code for specifying the modules to publish could be rewritten as follows: + * + * ```kotlin + * spinePublishing { + * modules = productionModuleNames.toSet() + * } + * ``` + * This code works for most of the projects. + * + * ### Arranging custom publishing for a module + * ```kotlin + * + * 1. Modify the list of standardly published modules in the root project like this: + * + * ```kotlin + * spinePublishing { + * modules = productionModuleNames + * .minus("my-custom-module") + * .toSet() + * + * modulesWithCustomPublishing = setOf( + * "my-custom-module" * ) + * + * // ... * } * ``` + * 2. Arrange the custom publishing in the `my-custom-module` project. + * + * ## Using in a single-module project * * When used with a single-module project, the extension should be opened in a project's build file. * Only destinations should be specified: * - * ``` + * ```kotlin * spinePublishing { - * destinations = setOf( - * PublishingRepos.cloudRepo, - * PublishingRepos.cloudArtifactRegistry, - * ) + * destinations = PublishingRepos.run { setOf( + * cloudArtifactRegistry, + * gitHub("") + * )} * } * ``` * - * It is worth to mention, that publishing of a module can be configured only from a single place. + * ## Publishing modules + * + * It is worth mentioning that publishing of a module can be configured only from a single place. * For example, declaring `subprojectA` as published in a root project and opening * `spinePublishing` extension within `subprojectA` itself would lead to an exception. * - * In Gradle, in order to publish something somewhere one should create a publication. In each + * In Gradle, in order to publish something somewhere, one should create a publication. In each * of published modules, the extension will create a [publication][StandardJavaPublicationHandler] - * named "mavenJava". All artifacts, published by this extension belong to this publication. + * named "mavenJava". All artifacts published by this extension belong to this publication. * - * By default, along with the compilation output of "main" source set, the extension publishes + * ## Published artifacts + * + * By default, along with the compilation output of the `main` source set, the extension publishes * the following artifacts: * - * 1. [sourcesJar] โ€“ sources from "main" source set. Includes "hand-made" Java, - * Kotlin and Proto files. In order to include the generated code into this artifact, a module - * should specify those files as a part of "main" source set. - * - * Here's an example of how to do that: - * - * ``` - * sourceSets { - * val generatedDir by extra("$projectDir/generated") - * val generatedSpineDir by extra("$generatedDir/main/java") - * main { - * java.srcDir(generatedSpineDir) - * } - * } - * ``` - * 2. [protoJar] โ€“ only Proto sources from "main" source set. It's published only if - * Proto files are actually present in the source set. Publication of this artifact is optional - * and can be disabled via [SpinePublishing.protoJar]. - * 3. [javadocJar] - javadoc, generated upon Java sources from "main" source set. - * If javadoc for Kotlin is also needed, apply Dokka plugin. It tunes `javadoc` task to generate - * docs upon Kotlin sources as well. - * 4. [dokkaKotlinJar] - documentation generated by Dokka for Kotlin and Java sources + * 1. [sourcesJar] โ€” sources from the `main` source set. Includes handcrafted and generated + * code in Java, Kotlin, and `.proto` files. + * + * 2. [protoJar] โ€“ only `.proto` sources from the `main` source set. It is published only if + * Proto files are actually present in the source set. + * + * 3. [javadocJar] โ€” Javadoc, generated upon Java sources from the `main` source set. + * If Javadoc for Kotlin is also needed, apply the Dokka plugin. + * It tunes the `javadoc` task to generate docs upon Kotlin sources as well. + * + * 4. [htmlDocsJar] โ€” documentation generated by Dokka for Kotlin and Java sources * using the Kotlin API mode. - * 5. [dokkaJavaJar] - documentation generated by Dokka for Kotlin and Java sources - * * using the Java API mode. * * Additionally, [testJar] artifact can be published. This artifact contains compilation output - * of "test" source set. Use [SpinePublishing.testJar] to enable its publishing. + * of the `test` source set. Use [SpinePublishing.testJar] to enable its publishing. * * @see [artifacts] + * @see SpinePublishing */ fun Project.spinePublishing(block: SpinePublishing.() -> Unit) { apply() @@ -127,11 +158,19 @@ fun Project.spinePublishing(block: SpinePublishing.() -> Unit) { } /** - * A Gradle extension for setting up publishing of spine modules using `maven-publish` plugin. + * A Gradle extension for setting up publishing of modules of Spine SDK modules + * using `maven-publish` plugin. + * + * ### Implementation Note * - * @param project - * a project in which the extension is opened. By default, this project will be - * published as long as a [set][modules] of modules to publish is not specified explicitly. + * This extension is overloaded with responsibilities. + * It basically does what an extension AND a Gradle plugin would normally do. + * + * We [should introduce a plugin class](https://github.com/SpineEventEngine/config/issues/562) + * and move the code related to creating tasks or setting dependencies between them into the plugin. + * + * @param project The project in which the extension is opened. By default, this project will be + * published as long as a [set][modules] of modules to publish is not specified explicitly. * * @see spinePublishing */ @@ -145,9 +184,7 @@ open class SpinePublishing(private val project: Project) { const val DEFAULT_PREFIX = "spine-" } - private val protoJar = ProtoJar() private val testJar = TestJar() - private val dokkaJar = DokkaJar() /** * Set of modules to be published. @@ -163,18 +200,27 @@ open class SpinePublishing(private val project: Project) { var modules: Set = emptySet() /** - * Controls whether the published module needs standard publications. + * Controls whether the [module][project] needs standard publications. + * + * Default value is `false`. * - * If `true`, the module should configure publications on its own. - * Otherwise, the extension will configure standard [ones][StandardJavaPublicationHandler]. + * In a single module [project], settings this property to `true` it tells + * that the project configures the publication in a specific way and + * [CustomPublicationHandler] should be used. + * Otherwise, the extension will configure the + * [standard publication][StandardJavaPublicationHandler]. * - * This property is analogue of [modulesWithCustomPublishing] for projects, + * This property is an analogue of [modulesWithCustomPublishing] in + * [multi-module][Project.getSubprojects] projects, * for which [spinePublishing] is configured individually. * - * Setting of this property and having a non-empty [modules] will lead - * to an exception. + * Setting of this property to `true` and having a non-empty [modules] property + * in the project to which the extension is applied will lead to [IllegalStateException]. * - * Default value is `false`. + * Settings this property to `true` in a subproject serves only the documentation purposes. + * This subproject still must be listed in the [modulesWithCustomPublishing] property in + * the extension of the [rootProject][Project.getRootProject], so that its publication + * can be configured in a specific way. */ var customPublishing = false @@ -191,62 +237,23 @@ open class SpinePublishing(private val project: Project) { * Usually, Spine-related projects are published to one or more repositories, * declared in [PublishingRepos]: * - * ``` - * destinations = setOf( - * PublishingRepos.cloudRepo, - * PublishingRepos.cloudArtifactRegistry, - * PublishingRepos.gitHub("base"), - * ) + * ```kotlin + * destinations = PublishingRepos.run { setOf( + * cloudArtifactRegistry, + * gitHub("") // The name of the GitHub repository of the project. + * )} * ``` * - * Empty by default. + * If the property is not initialized, the destinations will be taken from + * the parent project. */ - var destinations: Set = emptySet() + lateinit var destinations: Set /** * A prefix to be added before the name of each artifact. */ var artifactPrefix: String = DEFAULT_PREFIX - /** - * Allows disabling publishing of [protoJar] artifact, containing all Proto sources - * from `sourceSets.main.proto`. - * - * Here's an example of how to disable it for some of the published modules: - * - * ``` - * spinePublishing { - * modules = setOf( - * "subprojectA", - * "subprojectB", - * ) - * protoJar { - * exclusions = setOf( - * "subprojectB", - * ) - * } - * } - * ``` - * - * For all modules, or when the extension is configured within a published module itself: - * - * ``` - * spinePublishing { - * protoJar { - * disabled = true - * } - * } - * ``` - * - * The resulting artifact is available under "proto" classifier. - * For example, in Gradle 7+, one could depend on it like this: - * - * ``` - * implementation("io.spine:spine-client:$version@proto") - * ``` - */ - fun protoJar(block: ProtoJar.() -> Unit) = protoJar.run(block) - /** * Allows enabling publishing of [testJar] artifact, containing compilation output * of "test" source set. @@ -277,8 +284,8 @@ open class SpinePublishing(private val project: Project) { * } * ``` * - * The resulting artifact is available under "test" classifier. For example, - * in Gradle 7+, one could depend on it like this: + * The resulting artifact is available under the "test" classifier. + * For example, in Gradle 7+, one could depend on it like this: * * ``` * implementation("io.spine:spine-client:$version@test") @@ -286,34 +293,6 @@ open class SpinePublishing(private val project: Project) { */ fun testJar(block: TestJar.() -> Unit) = testJar.run(block) - /** - * Configures publishing of [dokkaKotlinJar] and [dokkaJavaJar] artifacts, - * containing Dokka-generated documentation. - * - * By default, publishing of the [dokkaKotlinJar] artifact is enabled, and [dokkaJavaJar] - * is disabled. - * - * Remember that the Dokka Gradle plugin should be applied to publish this artifact as it is - * produced by the `dokkaHtml` task. It can be done by using the - * [io.spine.dependency.build.Dokka] dependency object or by applying the - * `buildSrc/src/main/kotlin/dokka-for-kotlin` or - * `buildSrc/src/main/kotlin/dokka-for-java` script plugins. - * - * Here's an example of how to use this option: - * - * ``` - * spinePublishing { - * dokkaJar { - * kotlin = false - * java = true - * } - * } - * ``` - * - * The resulting artifact is available under "dokka" classifier. - */ - fun dokkaJar(block: DokkaJar.() -> Unit) = dokkaJar.run(block) - /** * Called to notify the extension that its configuration is completed. * @@ -321,14 +300,13 @@ open class SpinePublishing(private val project: Project) { * `maven-publish` plugin for each published module. */ internal fun configured() { - ensureProtoJarExclusionsArePublished() ensureTestJarInclusionsArePublished() ensureModulesNotDuplicated() ensureCustomPublishingNotMisused() val projectsToPublish = projectsToPublish() projectsToPublish.forEach { project -> - val jarFlags = JarFlags.create(project.name, protoJar, testJar, dokkaJar) + val jarFlags = JarFlags.create(project.name, testJar) project.setUpPublishing(jarFlags) } } @@ -367,21 +345,22 @@ open class SpinePublishing(private val project: Project) { * * We selected to use [Project.afterEvaluate] so that we can configure publishing of multiple * modules from a root project. When we do this, we configure publishing for a module, - * build file of which has not been even evaluated yet. + * a build file of which has not been even evaluated yet. * * The simplest example here is specifying of `version` and `group` for Maven coordinates. - * Let's suppose, they are declared in a module's build file. It is a common practice. - * But publishing of the module is configured from a root project's build file. By the time, - * when we need to specify them, we just don't know them. As a result, we have to use - * [Project.afterEvaluate] in order to guarantee that a module will be configured by the time - * we configure publishing for it. + * Let's suppose they are declared in a module's build file. It is a common practice. + * But publishing of the module is configured from a root project's build file. + * By the time when we need to specify them, we just don't know them. + * As the result, we have to use [Project.afterEvaluate] in order to guarantee that + * the module will be configured by the time we configure publishing for it. */ private fun Project.setUpPublishing(jarFlags: JarFlags) { val customPublishing = modulesWithCustomPublishing.contains(name) || customPublishing + val destinations = project.publishTo() val handler = if (customPublishing) { - CustomPublicationHandler(project, destinations) + CustomPublicationHandler.serving(project, destinations) } else { - StandardJavaPublicationHandler(project, jarFlags, destinations) + StandardJavaPublicationHandler.serving(project, destinations, jarFlags) } afterEvaluate { handler.apply() @@ -389,27 +368,31 @@ open class SpinePublishing(private val project: Project) { } /** - * Obtains an artifact ID for the given project. + * Obtains the set of repositories for publishing. * - * It consists of a project's name and [prefix][artifactPrefix]: - * ``. + * If there is a local instance of [io.spine.gradle.publish.SpinePublishing] extension, + * the [destinations] are obtained from this instance. + * Otherwise, the function attempts to obtain it from a [parent project][Project.getParent]. + * If there is no a parent project, an empty set is returned. + * + * The normal execution should end up at the root project of a multi-module project + * if there are no custom destinations specified by the local extension. */ - fun artifactId(project: Project): String = "$artifactPrefix${project.name}" + private fun Project.publishTo(): Set { + val ext = localSpinePublishing + if (ext != null && ext::destinations.isInitialized) { + return destinations + } + return parent?.publishTo() ?: emptySet() + } /** - * Ensures that all modules, marked as excluded from [protoJar] publishing, - * are actually published. + * Obtains an artifact ID for the given project. * - * It makes no sense to tell a module don't publish [protoJar] artifact, if the module is not - * published at all. + * It consists of a project's name and [prefix][artifactPrefix]: + * ``. */ - private fun ensureProtoJarExclusionsArePublished() { - val nonPublishedExclusions = protoJar.exclusions.minus(modules) - if (nonPublishedExclusions.isNotEmpty()) { - throw IllegalStateException("One or more modules are marked as `excluded from proto " + - "JAR publication`, but they are not even published: $nonPublishedExclusions") - } - } + fun artifactId(project: Project): String = "$artifactPrefix${project.name}" /** * Ensures that all modules, marked as included into [testJar] publishing, @@ -431,7 +414,7 @@ open class SpinePublishing(private val project: Project) { /** * Ensures that publishing of a module is configured only from a single place. * - * We allow configuration of publishing from two places - a root project and module itself. + * We allow configuration of publishing from two places - a root project and the module itself. * Here we verify that publishing of a module is not configured in both places simultaneously. */ private fun ensureModulesNotDuplicated() { diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/publish/StandardJavaPublicationHandler.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/StandardJavaPublicationHandler.kt new file mode 100644 index 0000000..17648f8 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/gradle/publish/StandardJavaPublicationHandler.kt @@ -0,0 +1,133 @@ +/* + * Copyright 2025, 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.spine.gradle.repo.Repository +import org.gradle.api.Project +import org.gradle.api.publish.maven.MavenPublication +import org.gradle.api.tasks.TaskProvider +import org.gradle.api.tasks.bundling.Jar +import org.gradle.kotlin.dsl.create + +/** + * A publication for a typical Java project. + * + * In Gradle, to publish something, one should create a publication. + * A publication has a name and consists of one or more artifacts plus information about + * those artifacts โ€“ the metadata. + * + * An instance of this class represents + * [MavenPublication][org.gradle.api.publish.maven.MavenPublication] + * named [`"mavenJava"`][PUBLICATION_NAME]. + * It is generally accepted that a publication with this name contains a Java project + * published to one or more Maven repositories. + * + * By default, only a jar with the compilation output of `main` source set and its + * metadata files are published. Other artifacts are specified through the + * [constructor parameter][jarFlags]. + * Please take a look on [specifyArtifacts] for additional info. + * + * @param jarFlags The flags for additional JARs published along with the compilation output. + * @param destinations Maven repositories to which the produced artifacts will be sent. + * @see + * The Maven Publish Plugin | Publications + * @see CustomPublicationHandler + */ +class StandardJavaPublicationHandler private constructor( + project: Project, + private val jarFlags: JarFlags, + destinations: Set, +) : PublicationHandler(project, destinations) { + + companion object : HandlerFactory() { + + /** + * The name of the publication created by [StandardJavaPublicationHandler]. + */ + const val PUBLICATION_NAME = "mavenJava" + + override fun create( + project: Project, + destinations: Set, + vararg params: Any + ): StandardJavaPublicationHandler { + return StandardJavaPublicationHandler(project, params[0] as JarFlags, destinations) + } + } + + /** + * Creates a new `"mavenJava"` [MavenPublication][org.gradle.api.publish.maven.MavenPublication] + * in the [project] associated with this publication handler. + */ + override fun handlePublications() { + val jars = project.artifacts(jarFlags) + val publications = project.publications + publications.create(PUBLICATION_NAME) { + copyProjectAttributes() + specifyArtifacts(jars) + } + } + + /** + * Specifies which artifacts this [MavenPublication] will contain. + * + * A typical Maven publication contains: + * + * 1. Jar archives. For example, compilation output, sources, javadoc, etc. + * 2. Maven metadata file that has the ".pom" extension. + * 3. Gradle's metadata file that has the ".module" extension. + * + * Metadata files contain information about a publication itself, its artifacts, and their + * dependencies. Presence of ".pom" file is mandatory for publication to be consumed by + * `mvn` build tool itself or other build tools that understand Maven notation (Gradle, Ivy). + * The presence of ".module" is optional, but useful when a publication is consumed by Gradle. + * + * @see Maven โ€“ POM Reference + * @see + * Understanding Gradle Module Metadata + */ + private fun MavenPublication.specifyArtifacts(jars: Set>) { + + /* + "java" component provides a jar with compilation output of "main" source set. + It is NOT defined as another `Jar` task intentionally. Doing that will leave the + publication without correct ".pom" and ".module" metadata files generated. + */ + val javaComponent = project.components.findByName("java") + javaComponent?.let { + from(it) + } + + /* + Other artifacts are represented by `Jar` tasks. Those artifacts do not bring any other + metadata in comparison with `Component` (such as the `dependencies` notation). + */ + jars.forEach { + artifact(it) + } + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/dokka/TaskContainerExtensions.kt b/buildSrc/src/main/kotlin/io/spine/gradle/repo/Credentials.kt similarity index 83% rename from buildSrc/src/main/kotlin/io/spine/gradle/dokka/TaskContainerExtensions.kt rename to buildSrc/src/main/kotlin/io/spine/gradle/repo/Credentials.kt index 02deead..1624b38 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/dokka/TaskContainerExtensions.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/repo/Credentials.kt @@ -24,13 +24,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package io.spine.gradle.dokka - -import org.gradle.api.tasks.TaskContainer -import org.jetbrains.dokka.gradle.DokkaTask +package io.spine.gradle.repo /** - * Finds the `dokkaHtml` Gradle task. + * Password credentials for a Maven repository. */ -@Suppress("unused") -fun TaskContainer.dokkaHtmlTask() = this.getByName("dokkaHtml") as DokkaTask +data class Credentials( + val username: String?, + val password: String? +) diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/RepoSlug.kt b/buildSrc/src/main/kotlin/io/spine/gradle/repo/RepoSlug.kt similarity index 88% rename from buildSrc/src/main/kotlin/io/spine/gradle/RepoSlug.kt rename to buildSrc/src/main/kotlin/io/spine/gradle/repo/RepoSlug.kt index 71fbac3..d4a6bfd 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/RepoSlug.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/repo/RepoSlug.kt @@ -24,13 +24,14 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package io.spine.gradle +package io.spine.gradle.repo import org.gradle.api.GradleException /** * A name of a repository. */ +@Suppress("unused") class RepoSlug(val value: String) { companion object { @@ -44,12 +45,12 @@ class RepoSlug(val value: String) { /** * Reads `REPO_SLUG` environment variable and returns its value. * - * In case it is not set, a [GradleException] is thrown. + * In case it is not set, a [org.gradle.api.GradleException] is thrown. */ fun fromVar(): RepoSlug { val envValue = System.getenv(environmentVariable) if (envValue.isNullOrEmpty()) { - throw GradleException("`REPO_SLUG` environment variable is not set.") + throw GradleException("`$environmentVariable` environment variable is not set.") } return RepoSlug(envValue) } @@ -61,6 +62,6 @@ class RepoSlug(val value: String) { * Returns the GitHub URL to the project repository. */ fun gitHost(): String { - return "git@github.com-publish:${value}.git" + return "git@github-publish:${value}.git" } } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/repo/Repositories.kt b/buildSrc/src/main/kotlin/io/spine/gradle/repo/Repositories.kt new file mode 100644 index 0000000..de91e33 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/gradle/repo/Repositories.kt @@ -0,0 +1,178 @@ +/* + * Copyright 2025, 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. + */ + +@file:Suppress("TooManyFunctions") // Deprecated functions will be kept for a while. + +package io.spine.gradle.repo + +import io.spine.gradle.publish.PublishingRepos +import java.net.URI +import org.gradle.api.artifacts.dsl.RepositoryHandler +import org.gradle.api.artifacts.repositories.MavenArtifactRepository +import org.gradle.kotlin.dsl.maven + +/** + * Registers the standard set of Maven repositories. + * + * To be used in `buildscript` clauses when a fully-qualified call must be made. + */ +@Suppress("unused") +@Deprecated( + message = "Please use `standardSpineSdkRepositories()`.", + replaceWith = ReplaceWith("standardSpineSdkRepositories()") +) +fun doApplyStandard(repositories: RepositoryHandler) = repositories.standardToSpineSdk() + +/** + * A scrambled version of PAT generated with the only "read:packages" scope. + * + * The scrambling around PAT is necessary because GitHub analyzes commits for the presence + * of tokens and invalidates them. + * + * @see + * How to make GitHub packages to the public + */ +private object Pat { + private const val shade = "_phg->8YlN->MFRA->gxIk->HVkm->eO6g->FqHJ->z8MS->H4zC->ZEPq" + private const val separator = "->" + private val chunks: Int = shade.split(separator).size - 1 + + fun credentials(): Credentials { + val pass = shade.replace(separator, "").splitAndReverse(chunks, "") + return Credentials("public", pass) + } + + /** + * Splits this string to the chunks, reverses each chunk, and joins them + * back to a string using the [separator]. + */ + private fun String.splitAndReverse(numChunks: Int, separator: String): String { + check(length / numChunks >= 2) { + "The number of chunks is too big. Must be <= ${length / 2}." + } + val chunks = chunked(length / numChunks) + val reversedChunks = chunks.map { chunk -> chunk.reversed() } + return reversedChunks.joinToString(separator) + } +} + +/** + * Adds a read-only view to all artifacts of the SpineEventEngine + * GitHub organization. + */ +fun RepositoryHandler.spineArtifacts(): MavenArtifactRepository = maven { + url = URI("https://maven.pkg.github.com/SpineEventEngine/*") + includeSpineOnly() + val pat = Pat.credentials() + credentials { + username = pat.username + password = pat.password + } +} + +val RepositoryHandler.intellijReleases: MavenArtifactRepository + get() = maven("https://www.jetbrains.com/intellij-repository/releases") + +val RepositoryHandler.intellijDependencies: MavenArtifactRepository + get() = maven("https://packages.jetbrains.team/maven/p/ij/intellij-dependencies") { + content { + includeGroupByRegex("com\\.jetbrains\\.intellij.*") + includeGroupByRegex("org\\.jetbrains\\.intellij.*") + includeGroupByRegex("com\\.intellij.*") + } + } + +/** + * Applies repositories commonly used by Spine Event Engine projects. + */ +fun RepositoryHandler.standardToSpineSdk() { + spineArtifacts() + + @Suppress("DEPRECATION") // Still use `CloudRepo` for earlier versions. + val spineRepos = listOf( + Repos.spine, + Repos.spineSnapshots, + Repos.artifactRegistry, + Repos.artifactRegistrySnapshots + ) + + spineRepos + .map { URI(it) } + .forEach { + maven { + url = it + includeSpineOnly() + } + } + + intellijReleases + intellijDependencies + + maven { + url = URI(Repos.sonatypeSnapshots) + } + + mavenCentral() + gradlePluginPortal() + mavenLocal().includeSpineOnly() +} + +@Deprecated( + message = "Please use `standardToSpineSdk() instead.", + replaceWith = ReplaceWith("standardToSpineSdk()") +) +fun RepositoryHandler.applyStandard() = this.standardToSpineSdk() + +/** + * Defines names of additional repositories commonly used in the Spine SDK projects. + * + * @see [applyStandard] + */ +@Suppress( + "DEPRECATION" /* Still need to use `CloudRepo` for older versions. */, + "ConstPropertyName" // https://bit.ly/kotlin-prop-names +) +private object Repos { + @Deprecated(message = "Please use `cloudArtifactRegistry.releases` instead.") + val spine = io.spine.gradle.publish.CloudRepo.published.target(snapshots = false) + + @Deprecated(message = "Please use `artifactRegistry.snapshots` instead.") + val spineSnapshots = io.spine.gradle.publish.CloudRepo.published.target(snapshots = true) + + val artifactRegistry = PublishingRepos.cloudArtifactRegistry.target(snapshots = false) + val artifactRegistrySnapshots = PublishingRepos.cloudArtifactRegistry.target(snapshots = true) + + const val sonatypeSnapshots = "https://oss.sonatype.org/content/repositories/snapshots" +} + +/** + * Narrows down the search for this repository to Spine-related artifact groups. + */ +private fun MavenArtifactRepository.includeSpineOnly() { + content { + includeGroupByRegex("io\\.spine.*") + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/repo/Repository.kt b/buildSrc/src/main/kotlin/io/spine/gradle/repo/Repository.kt new file mode 100644 index 0000000..a586ffd --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/gradle/repo/Repository.kt @@ -0,0 +1,138 @@ +/* + * Copyright 2025, 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.repo + +import java.io.File +import java.util.Properties +import org.gradle.api.Project + +/** + * A Maven repository. + * + * @param name The human-readable name which is also used in the publishing task names + * for identifying the target repository. + * The name must match the [regex]. + * @param releases The URL for publishing release versions of artifacts. + * @param snapshots The URL for publishing [snapshot][io.spine.gradle.isSnapshot] versions. + * @param credentialsFile The path to the file which contains the credentials for the registry. + * @param credentialValues The function to obtain an instance of [Credentials] from + * a Gradle [Project], if [credentialsFile] is not specified. + */ +data class Repository( + private val name: String, + private val releases: String, + private val snapshots: String, + private val credentialsFile: String? = null, + private val credentialValues: ((Project) -> Credentials?)? = null +) { + + companion object { + val regex = Regex("[A-Za-z0-9_\\-.]+") + } + + init { + require(regex.matches(name)) { + "The repository name `$name` does not match the regex `$regex`." + } + } + + /** + * Obtains the name of the repository. + * + * The name will be primarily used in the publishing tasks. + * + * @param snapshots If `true` this repository is used for publishing snapshots, + * and the suffix `-snapshots` will be added to the value of the [name] property. + * Otherwise, the function returns just [name]. + */ + fun name(snapshots: Boolean): String = name + if (snapshots) "-snapshots" else "" + + /** + * Obtains the target URL of the repository for publishing. + */ + fun target(snapshots: Boolean): String = if (snapshots) this.snapshots else releases + + /** + * Tells if release and snapshot versions are published to the same destination + * of this repository. + */ + fun hasOneTarget() = snapshots == releases + + /** + * Obtains the publishing password credentials to this repository. + * + * If the credentials are represented by a `.properties` file, reads the file and parses + * the credentials. The file must have properties `user.name` and `user.password`, which store + * the username and the password for the Maven repository auth. + */ + fun credentials(project: Project): Credentials? = when { + credentialValues != null -> credentialValues.invoke(project) + credentialsFile != null -> credsFromFile(credentialsFile, project) + else -> throw IllegalArgumentException( + "Credentials file or a supplier function should be passed." + ) + } + + private fun credsFromFile(fileName: String, project: Project): Credentials? { + val file = project.rootProject.file(fileName) + if (file.exists().not()) { + return null + } + + val log = project.logger + log.info("Using credentials from `$fileName`.") + val creds = file.parseCredentials() + log.info("Publishing build as `${creds.username}`.") + return creds + } + + private fun File.parseCredentials(): Credentials { + val properties = Properties().apply { load(inputStream()) } + val username = properties.getProperty("user.name") + val password = properties.getProperty("user.password") + return Credentials(username, password) + } + + override fun equals(other: Any?): Boolean = when { + this === other -> true + other !is Repository -> false + else -> name == other.name && + releases == other.releases && + snapshots == other.snapshots +} + + override fun hashCode(): Int { + var result = name.hashCode() + result = 31 * result + releases.hashCode() + result = 31 * result + snapshots.hashCode() + return result + } + + override fun toString(): String { + return name + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/report/coverage/CodebaseFilter.kt b/buildSrc/src/main/kotlin/io/spine/gradle/report/coverage/CodebaseFilter.kt index a5b9e72..efdf605 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/report/coverage/CodebaseFilter.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/report/coverage/CodebaseFilter.kt @@ -29,7 +29,6 @@ package io.spine.gradle.report.coverage import com.google.errorprone.annotations.CanIgnoreReturnValue import io.spine.gradle.report.coverage.FileFilter.generatedOnly import java.io.File -import kotlin.streams.toList import org.gradle.api.Project import org.gradle.api.file.ConfigurableFileTree import org.gradle.api.file.FileTree diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/report/coverage/JacocoConfig.kt b/buildSrc/src/main/kotlin/io/spine/gradle/report/coverage/JacocoConfig.kt index de5d00b..65bf2ee 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/report/coverage/JacocoConfig.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/report/coverage/JacocoConfig.kt @@ -28,7 +28,7 @@ package io.spine.gradle.report.coverage import io.spine.dependency.test.Jacoco import io.spine.gradle.applyPlugin -import io.spine.gradle.findTask +import io.spine.gradle.getTask import io.spine.gradle.report.coverage.TaskName.check import io.spine.gradle.report.coverage.TaskName.copyReports import io.spine.gradle.report.coverage.TaskName.jacocoRootReport @@ -144,7 +144,7 @@ class JacocoConfig( private fun registerRootReport( tasks: TaskContainer, - copyReports: TaskProvider? + copyReports: TaskProvider ): TaskProvider { val allSourceSets = Projects(projects).sourceSets() val mainJavaSrcDirs = allSourceSets.mainJavaSrcDirs() @@ -181,7 +181,7 @@ class JacocoConfig( private fun registerCopy(tasks: TaskContainer): TaskProvider { val everyExecData = mutableListOf() projects.forEach { project -> - val jacocoTestReport = project.findTask(jacocoTestReport.name) + val jacocoTestReport = project.getTask(jacocoTestReport.name) val executionData = jacocoTestReport.executionData everyExecData.add(executionData) } @@ -194,7 +194,7 @@ class JacocoConfig( rename { "${UUID.randomUUID()}.exec" } - dependsOn(projects.map { it.findTask(jacocoTestReport.name) }) + dependsOn(projects.map { it.getTask(jacocoTestReport.name) }) } return copyReports } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt b/buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt index 9b7a57f..5f18b95 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt @@ -30,7 +30,7 @@ import com.github.jk1.license.LicenseReportExtension import com.github.jk1.license.LicenseReportExtension.ALL import com.github.jk1.license.LicenseReportPlugin import io.spine.gradle.applyPlugin -import io.spine.gradle.findTask +import io.spine.gradle.getTask import java.io.File import org.gradle.api.Project import org.gradle.api.Task @@ -98,7 +98,7 @@ object LicenseReporter { } /** - * Tells to merge all per-project reports which were previously [generated][generateReportIn] + * Tells to merge all per-project reports that were previously [generated][generateReportIn] * for each of the subprojects of the root Gradle project. * * The merge result is placed according to [Paths]. @@ -109,10 +109,10 @@ object LicenseReporter { val rootProject = project.rootProject val mergeTask = rootProject.tasks.register(mergeTaskName) { val consolidationTask = this - val assembleTask = project.findTask("assemble") + val assembleTask = project.getTask("assemble") val sourceProjects: Iterable = sourceProjects(rootProject) sourceProjects.forEach { - val perProjectTask = it.findTask(projectTaskName) + val perProjectTask = it.getTask(projectTaskName) consolidationTask.dependsOn(perProjectTask) perProjectTask.dependsOn(assembleTask) } @@ -121,7 +121,7 @@ object LicenseReporter { } dependsOn(assembleTask) } - project.findTask("build") + project.getTask("build") .finalizedBy(mergeTask) } @@ -151,11 +151,18 @@ object LicenseReporter { sourceProjects: Iterable, rootProject: Project ) { - val paths = sourceProjects.map { - val buildDir = it.layout.buildDirectory.asFile.get() - "$buildDir/${Paths.relativePath}/${Paths.outputFilename}" - } - println("Merging the license reports from the all projects.") + val paths = sourceProjects + .map { + val buildDir = it.layout.buildDirectory.asFile.get() + "$buildDir/${Paths.relativePath}/${Paths.outputFilename}" + }.filter { + val exists = File(it).exists() + if (!exists) { + rootProject.logger.debug("License report file not found: $it") + } + exists + } + println("Merging the license reports from all projects.") val mergedContent = paths.joinToString("\n\n\n") { (File(it)).readText() } val output = File("${rootProject.rootDir}/${Paths.outputFilename}") output.writeText(mergedContent) diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/report/license/ModuleDataExtensions.kt b/buildSrc/src/main/kotlin/io/spine/gradle/report/license/ModuleDataExtensions.kt index 0aca30f..91247e2 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/report/license/ModuleDataExtensions.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/report/license/ModuleDataExtensions.kt @@ -91,7 +91,6 @@ private fun MarkdownDocument.print( return this } - /** * Prints the URL to the project which provides the dependency. * diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/report/license/Template.kt b/buildSrc/src/main/kotlin/io/spine/gradle/report/license/Template.kt index adda37b..15bd24d 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/report/license/Template.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/report/license/Template.kt @@ -28,7 +28,7 @@ package io.spine.gradle.report.license import io.spine.docs.MarkdownDocument import io.spine.gradle.artifactId -import java.util.* +import java.util.Date import org.gradle.api.Project /** @@ -40,32 +40,33 @@ internal class Template( ) { private companion object { + @Suppress("ConstPropertyName") private const val longBreak = "\n\n" } - internal fun writeHeader() { + internal fun writeHeader() = with(project) { out.nl() - .h1( - "Dependencies of " + - "`${project.group}:${project.artifactId}:${project.version}`" - ) - .nl() + .h1("Dependencies of `$group:$artifactId:$version`") + .nl() } internal fun writeFooter() { + val currentTime = Date() out.text(longBreak) .text("The dependencies distributed under several licenses, ") .text("are used according their commercial-use-friendly license.") .text(longBreak) .text("This report was generated on ") - .bold("${Date()}") + .bold("$currentTime") .text(" using ") + .nl() .link( "Gradle-License-Report plugin", "https://github.com/jk1/Gradle-License-Report" ) .text(" by Evgeny Naumenko, ") .text("licensed under ") + .nl() .link( "Apache 2.0 License", "https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE" diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/DependencyWriter.kt b/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/DependencyWriter.kt index eda2493..79d00c6 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/DependencyWriter.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/DependencyWriter.kt @@ -121,11 +121,8 @@ fun Project.dependencies(): SortedSet { private fun Project.depsFromAllConfigurations(): Set { val result = mutableSetOf() this.configurations.forEach { configuration -> - if (configuration.isCanBeResolved) { - // Force resolution of the configuration. - configuration.resolvedConfiguration - } - configuration.dependencies.filter { it.isExternal() } + configuration.dependencies + .filter { it.isExternal() } .forEach { dependency -> val forcedVersion = configuration.forcedVersionOf(dependency) val moduleDependency = diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/InceptionYear.kt b/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/InceptionYear.kt index cb25b3d..ed94b29 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/InceptionYear.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/InceptionYear.kt @@ -35,7 +35,10 @@ import org.gradle.kotlin.dsl.withGroovyBuilder */ internal object InceptionYear { - private const val SPINE_INCEPTION_YEAR = "2015" + /** + * The year of the inception of Spine. + */ + const val value = "2015" /** * Returns a string containing the inception year of Spine in a `pom.xml` format. @@ -44,7 +47,7 @@ internal object InceptionYear { val writer = StringWriter() val xml = MarkupBuilder(writer) xml.withGroovyBuilder { - "inceptionYear" { xml.text(SPINE_INCEPTION_YEAR) } + "inceptionYear" { xml.text(value) } } return writer.toString() } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomFormatting.kt b/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomFormatting.kt index ba673bb..15059ea 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomFormatting.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomFormatting.kt @@ -92,7 +92,7 @@ internal object PomFormatting { * Writes the specified lines using the specified [destination], dividing them * by platform-specific line separator. * - * The written lines are also padded with platform's line separator from both sides + * Each written line is followed by two platform-specific line separators. */ internal fun writeBlocks(destination: StringWriter, vararg lines: String) { lines.iterator().forEach { diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomGenerator.kt b/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomGenerator.kt index 9144e2f..6e986e1 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomGenerator.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomGenerator.kt @@ -57,7 +57,8 @@ import org.gradle.api.plugins.BasePlugin * * By default, those values are taken from the `project` object, which may or may not include * them. If the project does not have these values, and they are not specified in the `ext` - * block, the resulting `pom.xml` file is going to contain empty blocks, e.g. ``. + * block, the resulting `pom.xml` file is going to contain empty blocks, + * e.g., ``. */ @Suppress("unused") object PomGenerator { @@ -68,8 +69,11 @@ object PomGenerator { fun applyTo(project: Project) { /** - * In some cases, the `base` plugin, which is by default is added by e.g. `java`, - * is not yet added. `base` plugin defines the `build` task. This generator needs it. + * In some cases, the `base` plugin, which by default is added by e.g. `java`, + * is not yet added. + * + * The `base` plugin defines the `build` task. + * This generator needs it. */ project.apply { plugin(BasePlugin::class.java) diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomXmlWriter.kt b/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomXmlWriter.kt index 5312219..a0b1ade 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomXmlWriter.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomXmlWriter.kt @@ -35,9 +35,9 @@ import java.io.StringWriter /** * Writes the dependencies of a Gradle project and its subprojects as a `pom.xml` file. * - * The resulting file is not usable for `maven` build tasks, but serves rather as a description - * of the first-level dependencies for each project/subproject. Their transitive dependencies - * are not included into the result. + * The resulting file is not usable for `maven` build tasks but serves as a description + * of the first-level dependencies for each project or subproject. + * Their transitive dependencies are not included in the result. */ internal class PomXmlWriter internal constructor( @@ -51,12 +51,10 @@ internal constructor( *

If a file with the specified location exists, its contents will be substituted * with a new `pom.xml`. * - * @param file a file to write `pom.xml` contents to + * @param file a file to write `pom.xml` contents to. */ fun writeTo(file: File) { - val fileWriter = FileWriter(file) val out = StringWriter() - writeStart(out) writeBlocks( out, @@ -67,8 +65,9 @@ internal constructor( ) PomFormatting.writeEnd(out) - fileWriter.write(out.toString()) - fileWriter.close() + FileWriter(file).use { + it.write(out.toString()) + } } /** @@ -83,4 +82,3 @@ internal constructor( return destination.toString() } } - diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/testing/Tasks.kt b/buildSrc/src/main/kotlin/io/spine/gradle/testing/Tasks.kt index 971c4b4..30ac810 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/testing/Tasks.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/testing/Tasks.kt @@ -29,6 +29,7 @@ package io.spine.gradle.testing import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.testing.Test import org.gradle.kotlin.dsl.register +import org.gradle.kotlin.dsl.withType /** * Registers [slowTest][SlowTest] and [fastTest][FastTest] tasks in this [TaskContainer]. @@ -45,10 +46,10 @@ import org.gradle.kotlin.dsl.register */ @Suppress("unused") fun TaskContainer.registerTestTasks() { - withType(Test::class.java).configureEach { + withType().configureEach { filter { - // There could be cases with no matching tests. E.g. tests could be based on Kotest, - // which has custom task types and names. + // There could be cases with no matching tests. + // E.g., tests could be based on Kotest, which has custom task types and names. isFailOnNoMatchingTests = false includeTestsMatching("*Test") includeTestsMatching("*Spec") diff --git a/buildSrc/src/main/kotlin/jacoco-kmm-jvm.gradle.kts b/buildSrc/src/main/kotlin/jacoco-kmm-jvm.gradle.kts new file mode 100644 index 0000000..de7f1bf --- /dev/null +++ b/buildSrc/src/main/kotlin/jacoco-kmm-jvm.gradle.kts @@ -0,0 +1,72 @@ +/* + * Copyright 2025, 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. + */ + +import java.io.File +import org.gradle.kotlin.dsl.getValue +import org.gradle.kotlin.dsl.getting +import org.gradle.kotlin.dsl.jacoco +import org.gradle.testing.jacoco.tasks.JacocoReport + +plugins { + jacoco +} + +/** + * Configures [JacocoReport] task to run in a Kotlin KMM project for `commonMain` and `jvmMain` + * source sets. + * + * This script plugin must be applied using the following construct at the end of + * a `build.gradle.kts` file of a module: + * + * ```kotlin + * apply(plugin="jacoco-kmm-jvm") + * ``` + * Please do not apply this script plugin in the `plugins {}` block because `jacocoTestReport` + * task is not yet available at this stage. + */ +@Suppress("unused") +private val about = "" + +/** + * Configure the Jacoco task with custom input a KMM project + * to which this convention plugin is applied. + */ +@Suppress("unused") +val jacocoTestReport: JacocoReport by tasks.getting(JacocoReport::class) { + val buildDir = project.layout.buildDirectory.get().asFile.absolutePath + val classFiles = File("${buildDir}/classes/kotlin/jvm/") + .walkBottomUp() + .toSet() + classDirectories.setFrom(classFiles) + + val coverageSourceDirs = arrayOf( + "src/commonMain", + "src/jvmMain" + ) + sourceDirectories.setFrom(files(coverageSourceDirs)) + + executionData.setFrom(files("${buildDir}/jacoco/jvmTest.exec")) +} diff --git a/buildSrc/src/main/kotlin/jvm-module.gradle.kts b/buildSrc/src/main/kotlin/jvm-module.gradle.kts index 5b1577e..11696c1 100644 --- a/buildSrc/src/main/kotlin/jvm-module.gradle.kts +++ b/buildSrc/src/main/kotlin/jvm-module.gradle.kts @@ -24,65 +24,57 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import BuildSettings.javaVersion +import io.spine.dependency.boms.BomsPlugin import io.spine.dependency.build.CheckerFramework import io.spine.dependency.build.Dokka import io.spine.dependency.build.ErrorProne +import io.spine.dependency.build.JSpecify import io.spine.dependency.lib.Guava -import io.spine.dependency.lib.JavaX import io.spine.dependency.lib.Protobuf -import io.spine.dependency.local.Logging import io.spine.dependency.local.Reflect -import io.spine.dependency.local.TestLib -import io.spine.dependency.test.JUnit import io.spine.dependency.test.Jacoco -import io.spine.dependency.test.Kotest import io.spine.gradle.checkstyle.CheckStyleConfig import io.spine.gradle.github.pages.updateGitHubPages import io.spine.gradle.javac.configureErrorProne import io.spine.gradle.javac.configureJavac import io.spine.gradle.javadoc.JavadocConfig -import io.spine.gradle.kotlin.applyJvmToolchain import io.spine.gradle.kotlin.setFreeCompilerArgs import io.spine.gradle.report.license.LicenseReporter -import io.spine.gradle.testing.configureLogging -import io.spine.gradle.testing.registerTestTasks plugins { `java-library` id("net.ltgt.errorprone") id("pmd-settings") id("project-report") - id("dokka-for-java") kotlin("jvm") - id("io.kotest") - id("org.jetbrains.kotlinx.kover") id("detekt-code-analysis") - id("dokka-for-kotlin") + id("dokka-setup") + id("org.jetbrains.kotlinx.kover") + id("module-testing") } - +apply() LicenseReporter.generateReportIn(project) JavadocConfig.applyTo(project) CheckStyleConfig.applyTo(project) project.run { - configureJava(javaVersion) - configureKotlin(javaVersion) + configureJava() + configureKotlin() addDependencies() forceConfigurations() val generatedDir = "$projectDir/generated" setTaskDependencies(generatedDir) - setupTests() configureGitHubPages() } typealias Module = Project -fun Module.configureJava(javaVersion: JavaLanguageVersion) { +fun Module.configureJava() { java { - toolchain.languageVersion.set(javaVersion) + sourceCompatibility = BuildSettings.javaVersionCompat + targetCompatibility = BuildSettings.javaVersionCompat } tasks { @@ -93,9 +85,8 @@ fun Module.configureJava(javaVersion: JavaLanguageVersion) { } } -fun Module.configureKotlin(javaVersion: JavaLanguageVersion) { +fun Module.configureKotlin() { kotlin { - applyJvmToolchain(javaVersion.asInt()) explicitApi() compilerOptions { jvmTarget.set(BuildSettings.jvmTarget) @@ -105,12 +96,11 @@ fun Module.configureKotlin(javaVersion: JavaLanguageVersion) { kover { useJacoco(version = Jacoco.version) - } - - koverReport { - defaults { - xml { - onCheck = true + reports { + total { + xml { + onCheck = true + } } } } @@ -130,21 +120,8 @@ fun Module.addDependencies() = dependencies { api(Guava.lib) compileOnlyApi(CheckerFramework.annotations) - compileOnlyApi(JavaX.annotations) + api(JSpecify.annotations) ErrorProne.annotations.forEach { compileOnlyApi(it) } - - implementation(Logging.lib) - - testImplementation(Guava.testLib) - testImplementation(JUnit.runner) - testImplementation(JUnit.pioneer) - JUnit.api.forEach { testImplementation(it) } - - testImplementation(TestLib.lib) - testImplementation(Kotest.frameworkEngine) - testImplementation(Kotest.datatest) - testImplementation(Kotest.runnerJUnit5Jvm) - testImplementation(JUnit.runner) } fun Module.forceConfigurations() { @@ -154,8 +131,6 @@ fun Module.forceConfigurations() { all { resolutionStrategy { force( - JUnit.bom, - JUnit.runner, Dokka.BasePlugin.lib, Reflect.lib, ) @@ -164,18 +139,6 @@ fun Module.forceConfigurations() { } } -fun Module.setupTests() { - tasks { - registerTestTasks() - test.configure { - useJUnitPlatform { - includeEngines("junit-jupiter") - } - configureLogging() - } - } -} - fun Module.setTaskDependencies(generatedDir: String) { tasks { val cleanGenerated by registering(Delete::class) { @@ -196,9 +159,7 @@ fun Module.setTaskDependencies(generatedDir: String) { } fun Module.configureGitHubPages() { - val docletVersion = project.version.toString() - updateGitHubPages(docletVersion) { - allowInternalJavadoc.set(true) + updateGitHubPages { rootFolder.set(rootDir) } } diff --git a/buildSrc/src/main/kotlin/kmp-module.gradle.kts b/buildSrc/src/main/kotlin/kmp-module.gradle.kts new file mode 100644 index 0000000..4f7ec63 --- /dev/null +++ b/buildSrc/src/main/kotlin/kmp-module.gradle.kts @@ -0,0 +1,187 @@ +/* + * Copyright 2025, 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. + */ + +import io.spine.dependency.boms.BomsPlugin +import io.spine.dependency.local.Reflect +import io.spine.dependency.local.TestLib +import io.spine.dependency.test.JUnit +import io.spine.dependency.test.Jacoco +import io.spine.dependency.test.Kotest +import io.spine.gradle.checkstyle.CheckStyleConfig +import io.spine.gradle.javac.configureJavac +import io.spine.gradle.kotlin.setFreeCompilerArgs +import io.spine.gradle.publish.IncrementGuard +import io.spine.gradle.report.license.LicenseReporter + +/** + * Configures this [Project] as a Kotlin Multiplatform module. + * + * By its nature, this script plugin is similar to `jvm-module`. It performs + * the basic module configuration. + * + * `jvm-module` is based on a mix of Java and Kotlin Gradle plugins. It allows + * usage of Kotlin and Java in a single module that is built for JVM. + * Whereas `kmp-module` is based on a Kotlin Multiplatform plugin. This plugin + * supports different compilation targets within a single module: JVM, IOS, + * Desktop, JS, etc. Also, it allows having some common sources in Kotlin + * that can be shared with target-specific code. They are located in + * `commonMain` and `commonTest` source sets. Each concrete target implicitly + * depends on them. + * + * As for now, this script configures only JVM target, but other targets + * will be added further. + * + * ### JVM target + * + * Sources for this target are placed in `jvmMain` and `jvmTest` directories. + * Java is allowed to be used in `jvm` sources, but Kotlin is a preference. + * Use Java only as a fall-back option where Kotlin is insufficient. + * Due to this, Java linters are not even configured by `kmp-module`. + * + * @see Kotlin Multiplatform docs + */ +@Suppress("unused") +val about = "" + +plugins { + kotlin("multiplatform") + id("detekt-code-analysis") + id("org.jetbrains.kotlinx.kover") + `project-report` +} +apply() +apply() + +project.forceConfigurations() + +fun Project.forceConfigurations() { + with(configurations) { + forceVersions() + all { + resolutionStrategy { + force( + Reflect.lib + ) + } + } + } +} + +/** + * Configures Kotlin Multiplatform plugin. + * + * Please note, this extension DOES NOT configure Kotlin for JVM. + * It configures KMP, in which Kotlin for JVM is only one of + * possible targets. + */ +@Suppress("UNUSED_VARIABLE") // Avoid warnings for source set vars. +kotlin { + // Enables explicit API mode for any Kotlin sources within the module. + explicitApi() + + compilerOptions { + setFreeCompilerArgs() + } + + // Enables and configures JVM target. + jvm { + compilerOptions { + jvmTarget.set(BuildSettings.jvmTarget) + } + } + + // Dependencies are specified per-target. + // Please note, common sources are implicitly available in all targets. + @Suppress("unused") // source set `val`s are used implicitly. + sourceSets { + val commonTest by getting { + dependencies { + implementation(kotlin("test-common")) + implementation(kotlin("test-annotations-common")) + implementation(Kotest.assertions) + implementation(Kotest.frameworkEngine) + implementation(Kotest.datatest) + } + } + val jvmTest by getting { + dependencies { + implementation(dependencies.enforcedPlatform(JUnit.bom)) + implementation(TestLib.lib) + implementation(JUnit.Jupiter.engine) + implementation(Kotest.runnerJUnit5Jvm) + } + } + } +} + +java { + sourceCompatibility = BuildSettings.javaVersionCompat + targetCompatibility = BuildSettings.javaVersionCompat +} + +/** + * Performs the standard task's configuration. + * + * Here's no difference with `jvm-module`, which does the same. + * + * Kotlin here is configured for both common and JVM-specific sources. + * Java is for JVM only. + * + * Also, Kotlin and Java share the same test executor (JUnit), so tests + * configuration is for both. + */ +tasks { + withType().configureEach { + configureJavac() + } +} + +/** + * Overrides the default location of Kotlin sources. + * + * The default configuration of Detekt assumes presence of Kotlin sources + * in `src/main/kotlin`, which is not the case for KMP. + */ +detekt { + source.setFrom( + "src/commonMain", + "src/jvmMain" + ) +} + +kover { + useJacoco(version = Jacoco.version) + reports { + total { + xml { + onCheck = true + } + } + } +} + +LicenseReporter.generateReportIn(project) +CheckStyleConfig.applyTo(project) diff --git a/buildSrc/src/main/kotlin/kmp-publish.gradle.kts b/buildSrc/src/main/kotlin/kmp-publish.gradle.kts new file mode 100644 index 0000000..e4c3aa1 --- /dev/null +++ b/buildSrc/src/main/kotlin/kmp-publish.gradle.kts @@ -0,0 +1,75 @@ +/* + * Copyright 2025, 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. + */ + +import org.gradle.api.publish.maven.MavenPublication +import org.gradle.kotlin.dsl.`maven-publish` +import org.gradle.kotlin.dsl.named + +/** + * Configures publications for `kmp-module`. + * + * As for now, [spinePublishing][io.spine.gradle.publish.spinePublishing] + * doesn't support Kotlin Multiplatform modules. So, their publications are + * configured by this script plugin. Other publishing-related configuration + * is still performed by the extension. + * + * To publish a KMP module, one still needs to open and configure + * `spinePublishing` extension. Make sure `spinePublishing.customPublishing` + * property is set to `true`, and this script plugin is applied. + * + * For example: + * + * ``` + * plugins { + * `kmp-module` + * `kmp-publish` + * } + * + * spinePublishing { + * destinations = setOf(...) + * customPublishing = true + * } + * ``` + */ +@Suppress("unused") +val about = "" + +plugins { + `maven-publish` + id("dokka-setup") +} + +publishing.publications { + named("kotlinMultiplatform") { + // Although, the "common artifact" can't be used independently + // of target artifacts, it is published with documentation. + artifact(project.htmlDocsJar()) + } + named("jvm") { + // Includes Kotlin (JVM + common) and Java documentation. + artifact(project.htmlDocsJar()) + } +} diff --git a/buildSrc/src/main/kotlin/module-testing.gradle.kts b/buildSrc/src/main/kotlin/module-testing.gradle.kts new file mode 100644 index 0000000..ee3fe61 --- /dev/null +++ b/buildSrc/src/main/kotlin/module-testing.gradle.kts @@ -0,0 +1,120 @@ +/* + * Copyright 2025, 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. + */ + +import io.spine.dependency.lib.Guava +import io.spine.dependency.local.TestLib +import io.spine.dependency.test.JUnit +import io.spine.dependency.test.JUnit.Jupiter +import io.spine.dependency.test.Kotest +import io.spine.dependency.test.Truth +import io.spine.gradle.testing.configureLogging +import io.spine.gradle.testing.registerTestTasks + +/** + * This convention plugin applies test dependencies and configures test-related tasks. + * + * The version of the [JUnit] platform must be applied via the [BomsPlugin][io.spine.dependency.boms.BomsPlugin]: + * + * ```kotlin + * apply() + * ``` + */ +@Suppress("unused") +private val about = "" + +plugins { + `java-library` +} + +project.run { + setupTests() + forceTestDependencies() +} + +dependencies { + forceJunitPlatform() + + testImplementation(Jupiter.api) + testImplementation(Jupiter.params) + testImplementation(JUnit.pioneer) + + testImplementation(Guava.testLib) + + testImplementation(TestLib.lib) + testImplementation(Kotest.assertions) + + testRuntimeOnly(Jupiter.engine) +} + +/** + * Forces the version of [JUnit] platform and its dependencies via [JUnit.bom]. + */ +private fun DependencyHandlerScope.forceJunitPlatform() { + testImplementation(enforcedPlatform(JUnit.bom)) +} + +typealias Module = Project + +/** + * Configure this module to run JUnit-based tests. + */ +fun Module.setupTests() { + tasks { + registerTestTasks() + test.configure { + useJUnitPlatform { + includeEngines("junit-jupiter") + } + configureLogging() + } + } +} + +/** + * Forces the versions of task dependencies that are used _in addition_ to + * the forced JUnit platform. + */ +@Suppress( + /* We're OK with incubating API for configurations. It does not seem to change recently. */ + "UnstableApiUsage" +) +fun Module.forceTestDependencies() { + configurations { + all { + resolutionStrategy { + forceTestDependencies() + } + } + } +} + +private fun ResolutionStrategy.forceTestDependencies() { + force( + Guava.testLib, + Truth.libs, + Kotest.assertions, + ) +} diff --git a/buildSrc/src/main/kotlin/module.gradle.kts b/buildSrc/src/main/kotlin/module.gradle.kts new file mode 100644 index 0000000..b24b5b2 --- /dev/null +++ b/buildSrc/src/main/kotlin/module.gradle.kts @@ -0,0 +1,31 @@ +/* + * Copyright 2025, 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. + */ + +// This is a template file for an actual script which should be +// defined by a project to which `config` is applied. +// +// The reason for having this file is that it is referenced as +// a plugin in `uber-jar-module.gradle.kts` diff --git a/buildSrc/src/main/kotlin/pmd-settings.gradle.kts b/buildSrc/src/main/kotlin/pmd-settings.gradle.kts index 0373ee0..938a764 100644 --- a/buildSrc/src/main/kotlin/pmd-settings.gradle.kts +++ b/buildSrc/src/main/kotlin/pmd-settings.gradle.kts @@ -42,7 +42,7 @@ pmd { ruleSets = listOf() // Load PMD settings. - val pmdSettings = file("$rootDir/config/quality/pmd.xml") + val pmdSettings = file("$rootDir/buildSrc/quality/pmd.xml") val textResource: TextResource = resources.text.fromFile(pmdSettings) ruleSetConfig = textResource diff --git a/buildSrc/src/main/kotlin/test-module.gradle.kts b/buildSrc/src/main/kotlin/test-module.gradle.kts new file mode 100644 index 0000000..d64de09 --- /dev/null +++ b/buildSrc/src/main/kotlin/test-module.gradle.kts @@ -0,0 +1,57 @@ +/* + * 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. + */ + +import io.spine.dependency.local.Base +import io.spine.dependency.local.Validation +import io.spine.gradle.report.license.LicenseReporter + +plugins { + java + `java-test-fixtures` + id("module-testing") +} +LicenseReporter.generateReportIn(project) + +dependencies { + arrayOf( + Base.lib, + Validation.runtime + ).forEach { + testFixturesImplementation(it)?.because( + """ + We do not apply CoreJvm Compiler Gradle plugin which adds + the `implementation` dependency on Validation runtime automatically + (see `Project.configureValidation()` function in `CompilerConfigPlugin.kt`). + + In a test module we use vanilla `protoc` (via ProtoTap) and then run codegen + using the Spine Compiler `Pipeline` and the plugins of the module under the test. + + Because of this we need to add the dependencies above explicitly for the + generated code of test fixtures to compile. + """.trimIndent() + ) + } +} diff --git a/buildSrc/src/main/kotlin/uber-jar-module.gradle.kts b/buildSrc/src/main/kotlin/uber-jar-module.gradle.kts new file mode 100644 index 0000000..f3dda52 --- /dev/null +++ b/buildSrc/src/main/kotlin/uber-jar-module.gradle.kts @@ -0,0 +1,202 @@ +/* + * Copyright 2025, 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. + */ + +@file:Suppress("UnstableApiUsage") // `configurations` block. + +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import io.spine.gradle.publish.IncrementGuard +import io.spine.gradle.publish.SpinePublishing +import io.spine.gradle.publish.spinePublishing +import io.spine.gradle.report.license.LicenseReporter + +plugins { + id("module") + `maven-publish` + id("com.gradleup.shadow") + id("write-manifest") + `project-report` + idea +} +apply() +LicenseReporter.generateReportIn(project) + +spinePublishing { + artifactPrefix = "" + destinations = rootProject.the().destinations + customPublishing = true +} + +/** The ID of the far JAR artifact. */ +private val projectArtifact = project.name.replace(":", "") + +publishing { + val groupName = project.group.toString() + val versionName = project.version.toString() + + publications { + create("fatJar", MavenPublication::class) { + groupId = groupName + artifactId = projectArtifact + version = versionName + artifact(tasks.shadowJar) + } + } +} + +/** + * Declare dependency explicitly to address the Gradle error. + */ +@Suppress("unused") +val publishFatJarPublicationToMavenLocal: Task by tasks.getting { + dependsOn(tasks.shadowJar) +} + +// Disable the `jar` task to free up the name of the resulting archive. +tasks.jar { + enabled = false +} + +tasks.publish { + dependsOn(tasks.shadowJar) +} + +tasks.shadowJar { + excludeFiles() + setZip64(true) /* The archive has way too many items. So using the Zip64 mode. */ + archiveClassifier.set("") /** To prevent Gradle setting something like `osx-x86_64`. */ +} + +/** + * Exclude unwanted directories. + */ +@Suppress("LongMethod") +private fun ShadowJar.excludeFiles() { + exclude( + /* + Exclude IntelliJ Platform images and other resources associated with IntelliJ UI. + We do not call the UI, so they won't be used. + */ + "actions/**", + "chooser/**", + "codeStyle/**", + "codeStylePreview/**", + "codeWithMe/**", + "darcula/**", + "debugger/**", + "diff/**", + "duplicates/**", + "expui/**", + "extensions/**", + "fileTemplates/**", + "fileTypes/**", + "general/**", + "graph/**", + "gutter/**", + "hierarchy/**", + "icons/**", + "ide/**", + "idea/**", + "inlayProviders/**", + "inspectionDescriptions/**", + "inspectionReport/**", + "intentionDescriptions/**", + "javadoc/**", + "javaee/**", + "json/**", + "liveTemplates/**", + "mac/**", + "modules/**", + "nodes/**", + "objectBrowser/**", + "plugins/**", + "postfixTemplates/**", + "preferences/**", + "process/**", + "providers/**", + "runConfigurations/**", + "scope/**", + "search/**", + "toolbar/**", + "toolbarDecorator/**", + "toolwindows/**", + "vcs/**", + "webreferences/**", + "welcome/**", + "windows/**", + "xml/**", + + /* + Exclude `https://github.com/JetBrains/pty4j`. + We don't need the terminal. + */ + "resources/com/pti4j/**", + + /* Exclude the IntelliJ fork of + `http://www.sparetimelabs.com/purejavacomm/purejavacomm.php`. + It is the part of the IDEA's terminal implementation. + */ + "purejavacomm/**", + + /* Exclude IDEA project templates. */ + "resources/projectTemplates/**", + + /* + Exclude dynamic libraries. Should the tool users need them, + they would add them explicitly. + */ + "bin/**", + + /* + Exclude Google Protobuf definitions to avoid duplicates. + */ + "google/**", + "src/google/**", + + /** + * Exclude Spine Protobuf definitions to avoid duplications. + */ + "spine/**", + + /** + * Exclude Kotlin runtime because it will be provided. + */ + "kotlin/**", + "kotlinx/**", + + /** + * Exclude native libraries related to debugging. + */ + "win32-x86/**", + "win32-x86-64/**", + + /** + * Exclude the Windows process management (WinP) libraries. + * See: `https://github.com/jenkinsci/winp`. + */ + "winp.dll", + "winp.x64.dll", + ) +} diff --git a/change/build.gradle.kts b/change/build.gradle.kts index d5e48d5..952e20d 100644 --- a/change/build.gradle.kts +++ b/change/build.gradle.kts @@ -25,15 +25,13 @@ */ import io.spine.dependency.local.Base -import io.spine.dependency.local.Spine import io.spine.dependency.local.Time import io.spine.dependency.local.Validation import io.spine.gradle.publish.IncrementGuard -import io.spine.protodata.gradle.plugin.LaunchProtoData plugins { protobuf - id(mcJava.pluginId) + id(coreJvmCompiler.pluginId) `detekt-code-analysis` } diff --git a/change/src/main/java/io/spine/change/package-info.java b/change/src/main/java/io/spine/change/package-info.java index 5a45c63..7b33823 100644 --- a/change/src/main/java/io/spine/change/package-info.java +++ b/change/src/main/java/io/spine/change/package-info.java @@ -29,9 +29,9 @@ */ @CheckReturnValue -@ParametersAreNonnullByDefault +@NullMarked package io.spine.change; import com.google.errorprone.annotations.CheckReturnValue; -import javax.annotation.ParametersAreNonnullByDefault; +import org.jspecify.annotations.NullMarked; diff --git a/change/src/test/java/io/spine/change/ChangesTest.java b/change/src/test/java/io/spine/change/ChangesTest.java index 500f8f4..4fb1688 100644 --- a/change/src/test/java/io/spine/change/ChangesTest.java +++ b/change/src/test/java/io/spine/change/ChangesTest.java @@ -31,7 +31,7 @@ import com.google.protobuf.Timestamp; import io.spine.base.Time; import io.spine.testing.UtilityClassTest; -import io.spine.time.testing.Past; +import io.spine.testing.time.Past; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; diff --git a/config b/config index 99fb7ce..9a4fbe2 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit 99fb7ce679e6c71713fd6c21c24e0cd19304b118 +Subproject commit 9a4fbe2bcae9a7b5f0d20159d41a8756d236e146 diff --git a/dependencies.md b/dependencies.md index da43e70..edc85cd 100644 --- a/dependencies.md +++ b/dependencies.md @@ -1,25 +1,25 @@ -# Dependencies of `io.spine:spine-change:2.0.0-SNAPSHOT.200` +# Dependencies of `io.spine:spine-change:2.0.0-SNAPSHOT.205` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. * **Project URL:** [http://findbugs.sourceforge.net/](http://findbugs.sourceforge.net/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.code.gson. **Name** : gson. **Version** : 2.10.1. - * **Project URL:** [https://github.com/google/gson/gson](https://github.com/google/gson/gson) +1. **Group** : com.google.code.gson. **Name** : gson. **Version** : 2.13.0. + * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) 1. **Group** : com.google.errorprone. **Name** : error_prone_annotations. **Version** : 2.36.0. * **Project URL:** [https://errorprone.info/error_prone_annotations](https://errorprone.info/error_prone_annotations) * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : failureaccess. **Version** : 1.0.1. +1. **Group** : com.google.guava. **Name** : failureaccess. **Version** : 1.0.3. * **Project URL:** [https://github.com/google/guava/](https://github.com/google/guava/) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.3-jre. +1. **Group** : com.google.guava. **Name** : guava. **Version** : 33.5.0-jre. * **Project URL:** [https://github.com/google/guava](https://github.com/google/guava) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -30,90 +30,122 @@ * **Project URL:** [https://github.com/google/j2objc/](https://github.com/google/j2objc/) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.25.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 4.33.1. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.25.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 4.33.1. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.25.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 4.33.1. + * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : org.checkerframework. **Name** : checker-qual. **Version** : 3.40.0. - * **Project URL:** [https://checkerframework.org/](https://checkerframework.org/) - * **License:** [The MIT License](http://opensource.org/licenses/MIT) - 1. **Group** : org.jetbrains. **Name** : annotations. **Version** : 26.0.2. * **Project URL:** [https://github.com/JetBrains/java-annotations](https://github.com/JetBrains/java-annotations) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-reflect. **Version** : 2.1.20. +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-bom. **Version** : 2.2.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib. **Version** : 2.1.20. +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-reflect. **Version** : 2.2.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-jdk7. **Version** : 2.1.20. +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib. **Version** : 2.2.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-jdk8. **Version** : 2.1.20. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) +1. **Group** : org.jetbrains.kotlinx. **Name** : atomicfu. **Version** : 0.29.0. + * **Project URL:** [https://github.com/Kotlin/kotlinx.atomicfu](https://github.com/Kotlin/kotlinx.atomicfu) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.ow2.asm. **Name** : asm. **Version** : 9.6. - * **Project URL:** [http://asm.ow2.io/](http://asm.ow2.io/) - * **License:** [BSD-3-Clause](https://asm.ow2.io/license.html) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) +1. **Group** : org.jetbrains.kotlinx. **Name** : atomicfu-jvm. **Version** : 0.29.0. + * **Project URL:** [https://github.com/Kotlin/kotlinx.atomicfu](https://github.com/Kotlin/kotlinx.atomicfu) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -## Compile, tests, and tooling -1. **Group** : com.beust. **Name** : jcommander. **Version** : 1.48. - * **Project URL:** [http://beust.com/jcommander](http://beust.com/jcommander) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) +1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-bom. **Version** : 1.10.2. + * **Project URL:** [https://github.com/Kotlin/kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.15.3. +1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-datetime. **Version** : 0.7.1. + * **Project URL:** [https://github.com/Kotlin/kotlinx-datetime](https://github.com/Kotlin/kotlinx-datetime) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-datetime-jvm. **Version** : 0.7.1. + * **Project URL:** [https://github.com/Kotlin/kotlinx-datetime](https://github.com/Kotlin/kotlinx-datetime) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.jspecify. **Name** : jspecify. **Version** : 1.0.0. + * **Project URL:** [http://jspecify.org/](http://jspecify.org/) + * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +## Compile, tests, and tooling +1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. * **Project URL:** [https://github.com/FasterXML/jackson-bom](https://github.com/FasterXML/jackson-bom) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-annotations. **Version** : 2.15.3. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-annotations. **Version** : 2.20. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.15.3. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.20.0. * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.15.3. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.20.0. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-xml. **Version** : 2.15.3. +1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-xml. **Version** : 2.20.0. * **Project URL:** [https://github.com/FasterXML/jackson-dataformat-xml](https://github.com/FasterXML/jackson-dataformat-xml) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-yaml. **Version** : 2.15.3. +1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-yaml. **Version** : 2.20.0. * **Project URL:** [https://github.com/FasterXML/jackson-dataformats-text](https://github.com/FasterXML/jackson-dataformats-text) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-kotlin. **Version** : 2.15.3. +1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-guava. **Version** : 2.20.0. + * **Project URL:** [https://github.com/FasterXML/jackson-datatypes-collections](https://github.com/FasterXML/jackson-datatypes-collections) + * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-jdk8. **Version** : 2.20.0. + * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8](https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) + * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-jsr310. **Version** : 2.20.0. + * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jsr310](https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jsr310) + * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-kotlin. **Version** : 2.20.0. * **Project URL:** [https://github.com/FasterXML/jackson-module-kotlin](https://github.com/FasterXML/jackson-module-kotlin) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.woodstox. **Name** : woodstox-core. **Version** : 6.5.1. +1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-parameter-names. **Version** : 2.20.0. + * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names](https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) + * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.fasterxml.woodstox. **Name** : woodstox-core. **Version** : 7.1.1. * **Project URL:** [https://github.com/FasterXML/woodstox](https://github.com/FasterXML/woodstox) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) +1. **Group** : com.github.ben-manes.caffeine. **Name** : caffeine. **Version** : 2.9.3. + * **Project URL:** [https://github.com/ben-manes/caffeine](https://github.com/ben-manes/caffeine) + * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + 1. **Group** : com.github.ben-manes.caffeine. **Name** : caffeine. **Version** : 3.0.5. * **Project URL:** [https://github.com/ben-manes/caffeine](https://github.com/ben-manes/caffeine) * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -126,11 +158,15 @@ * **Project URL:** [https://www.github.com/KevinStern/software-and-algorithms](https://www.github.com/KevinStern/software-and-algorithms) * **License:** [MIT License](http://www.opensource.org/licenses/mit-license.php) +1. **Group** : com.github.oowekyala.ooxml. **Name** : nice-xml-messages. **Version** : 3.1. + * **Project URL:** [https://github.com/oowekyala/nice-xml-messages](https://github.com/oowekyala/nice-xml-messages) + * **License:** [MIT License](https://github.com/oowekyala/nice-xml-messages/tree/master/LICENSE) + 1. **Group** : com.google.android. **Name** : annotations. **Version** : 4.1.1.4. * **Project URL:** [http://source.android.com/](http://source.android.com/) * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : com.google.api.grpc. **Name** : proto-google-common-protos. **Version** : 2.22.0. +1. **Group** : com.google.api.grpc. **Name** : proto-google-common-protos. **Version** : 2.59.2. * **Project URL:** [https://github.com/googleapis/sdk-platform-java](https://github.com/googleapis/sdk-platform-java) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -150,31 +186,27 @@ * **Project URL:** [http://findbugs.sourceforge.net/](http://findbugs.sourceforge.net/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.code.gson. **Name** : gson. **Version** : 2.10.1. - * **Project URL:** [https://github.com/google/gson/gson](https://github.com/google/gson/gson) +1. **Group** : com.google.code.gson. **Name** : gson. **Version** : 2.13.0. + * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing. **Version** : 2.1.20-1.0.31. +1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing. **Version** : 2.3.0. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-aa-embeddable. **Version** : 2.1.20-1.0.31. +1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-aa-embeddable. **Version** : 2.3.0. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-api. **Version** : 2.1.20-1.0.31. +1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-api. **Version** : 2.3.0. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-cmdline. **Version** : 2.1.20-1.0.31. +1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-common-deps. **Version** : 2.3.0. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-common-deps. **Version** : 2.1.20-1.0.31. - * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-gradle-plugin. **Version** : 2.1.20-1.0.31. +1. **Group** : com.google.devtools.ksp. **Name** : symbol-processing-gradle-plugin. **Version** : 2.3.0. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -213,15 +245,19 @@ 1. **Group** : com.google.googlejavaformat. **Name** : google-java-format. **Version** : 1.19.1. * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : failureaccess. **Version** : 1.0.1. +1. **Group** : com.google.gradle. **Name** : osdetector-gradle-plugin. **Version** : 1.7.3. + * **Project URL:** [https://github.com/google/osdetector-gradle-plugin](https://github.com/google/osdetector-gradle-plugin) + * **License:** [Apache License 2.0](http://opensource.org/licenses/Apache-2.0) + +1. **Group** : com.google.guava. **Name** : failureaccess. **Version** : 1.0.3. * **Project URL:** [https://github.com/google/guava/](https://github.com/google/guava/) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.3-jre. +1. **Group** : com.google.guava. **Name** : guava. **Version** : 33.5.0-jre. * **Project URL:** [https://github.com/google/guava](https://github.com/google/guava) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava-testlib. **Version** : 32.1.3-jre. +1. **Group** : com.google.guava. **Name** : guava-testlib. **Version** : 33.5.0-jre. * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) 1. **Group** : com.google.guava. **Name** : listenablefuture. **Version** : 9999.0-empty-to-avoid-conflict-with-guava. @@ -231,15 +267,20 @@ * **Project URL:** [https://github.com/google/j2objc/](https://github.com/google/j2objc/) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.25.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-gradle-plugin. **Version** : 0.9.5. + * **Project URL:** [https://github.com/google/protobuf-gradle-plugin](https://github.com/google/protobuf-gradle-plugin) + * **License:** [BSD 3-Clause](http://opensource.org/licenses/BSD-3-Clause) + +1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 4.33.1. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.25.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 4.33.1. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.25.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 4.33.1. + * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) 1. **Group** : com.google.protobuf. **Name** : protoc. **Version** : 3.19.6. @@ -247,23 +288,31 @@ * **License:** [3-Clause BSD License](https://opensource.org/licenses/BSD-3-Clause) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.truth. **Name** : truth. **Version** : 1.1.5. +1. **Group** : com.google.truth. **Name** : truth. **Version** : 1.4.4. * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.truth.extensions. **Name** : truth-java8-extension. **Version** : 1.1.5. +1. **Group** : com.google.truth.extensions. **Name** : truth-java8-extension. **Version** : 1.4.4. * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.truth.extensions. **Name** : truth-liteproto-extension. **Version** : 1.1.5. +1. **Group** : com.google.truth.extensions. **Name** : truth-liteproto-extension. **Version** : 1.4.4. * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.truth.extensions. **Name** : truth-proto-extension. **Version** : 1.1.5. +1. **Group** : com.google.truth.extensions. **Name** : truth-proto-extension. **Version** : 1.4.4. * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) +1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format. **Version** : 2.75.0. + * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) + * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) + +1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.75.0. + * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) + * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) + 1. **Group** : com.puppycrawl.tools. **Name** : checkstyle. **Version** : 10.12.1. * **Project URL:** [https://checkstyle.org/](https://checkstyle.org/) * **License:** [LGPL-2.1+](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt) -1. **Group** : com.sksamuel.aedile. **Name** : aedile-core. **Version** : 1.3.1. +1. **Group** : com.sksamuel.aedile. **Name** : aedile-core. **Version** : 2.1.2. * **Project URL:** [http://www.github.com/sksamuel/aedile](http://www.github.com/sksamuel/aedile) * **License:** [The Apache 2.0 License](https://opensource.org/licenses/Apache-2.0) @@ -275,15 +324,15 @@ * **Project URL:** [http://github.com/square/javapoet/](http://github.com/square/javapoet/) * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.squareup. **Name** : kotlinpoet. **Version** : 2.0.0. +1. **Group** : com.squareup. **Name** : kotlinpoet. **Version** : 2.2.0. * **Project URL:** [https://github.com/square/kotlinpoet](https://github.com/square/kotlinpoet) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.squareup. **Name** : kotlinpoet-jvm. **Version** : 2.0.0. +1. **Group** : com.squareup. **Name** : kotlinpoet-jvm. **Version** : 2.2.0. * **Project URL:** [https://github.com/square/kotlinpoet](https://github.com/square/kotlinpoet) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.squareup. **Name** : kotlinpoet-ksp. **Version** : 2.0.0. +1. **Group** : com.squareup. **Name** : kotlinpoet-ksp. **Version** : 2.2.0. * **Project URL:** [https://github.com/square/kotlinpoet](https://github.com/square/kotlinpoet) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -426,82 +475,78 @@ * **Project URL:** [https://detekt.dev](https://detekt.dev) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.59.0. +1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.76.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-context. **Version** : 1.59.0. +1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.76.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.59.0. +1. **Group** : io.grpc. **Name** : grpc-context. **Version** : 1.76.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-inprocess. **Version** : 1.59.0. +1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.76.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.59.0. +1. **Group** : io.grpc. **Name** : grpc-inprocess. **Version** : 1.76.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-protobuf-lite. **Version** : 1.59.0. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) +1. **Group** : io.grpc. **Name** : grpc-kotlin-stub. **Version** : 1.4.1. + * **Project URL:** [https://github.com/grpc/grpc-kotlin](https://github.com/grpc/grpc-kotlin) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.59.0. +1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.76.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-util. **Version** : 1.59.0. +1. **Group** : io.grpc. **Name** : grpc-protobuf-lite. **Version** : 1.76.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : protoc-gen-grpc-java. **Version** : 1.59.0. +1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.76.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.kotest. **Name** : kotest-assertions-api. **Version** : 5.9.1. +1. **Group** : io.kotest. **Name** : kotest-assertions-core. **Version** : 6.0.4. * **Project URL:** [https://github.com/kotest/kotest](https://github.com/kotest/kotest) * **License:** [Apache-2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.kotest. **Name** : kotest-assertions-api-jvm. **Version** : 5.9.1. +1. **Group** : io.kotest. **Name** : kotest-assertions-core-jvm. **Version** : 6.0.4. * **Project URL:** [https://github.com/kotest/kotest](https://github.com/kotest/kotest) * **License:** [Apache-2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.kotest. **Name** : kotest-assertions-core. **Version** : 5.9.1. +1. **Group** : io.kotest. **Name** : kotest-assertions-shared. **Version** : 6.0.4. * **Project URL:** [https://github.com/kotest/kotest](https://github.com/kotest/kotest) * **License:** [Apache-2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.kotest. **Name** : kotest-assertions-core-jvm. **Version** : 5.9.1. +1. **Group** : io.kotest. **Name** : kotest-assertions-shared-jvm. **Version** : 6.0.4. * **Project URL:** [https://github.com/kotest/kotest](https://github.com/kotest/kotest) * **License:** [Apache-2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.kotest. **Name** : kotest-assertions-shared. **Version** : 5.9.1. +1. **Group** : io.kotest. **Name** : kotest-common. **Version** : 6.0.4. * **Project URL:** [https://github.com/kotest/kotest](https://github.com/kotest/kotest) * **License:** [Apache-2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.kotest. **Name** : kotest-assertions-shared-jvm. **Version** : 5.9.1. +1. **Group** : io.kotest. **Name** : kotest-common-jvm. **Version** : 6.0.4. * **Project URL:** [https://github.com/kotest/kotest](https://github.com/kotest/kotest) * **License:** [Apache-2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.kotest. **Name** : kotest-common. **Version** : 5.9.1. - * **Project URL:** [https://github.com/kotest/kotest](https://github.com/kotest/kotest) - * **License:** [Apache-2.0](https://opensource.org/licenses/Apache-2.0) +1. **Group** : io.opentelemetry. **Name** : opentelemetry-api. **Version** : 1.41.0. + * **Project URL:** [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) + * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : io.kotest. **Name** : kotest-common-jvm. **Version** : 5.9.1. - * **Project URL:** [https://github.com/kotest/kotest](https://github.com/kotest/kotest) - * **License:** [Apache-2.0](https://opensource.org/licenses/Apache-2.0) +1. **Group** : io.opentelemetry. **Name** : opentelemetry-context. **Version** : 1.41.0. + * **Project URL:** [https://github.com/open-telemetry/opentelemetry-java](https://github.com/open-telemetry/opentelemetry-java) + * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : io.perfmark. **Name** : perfmark-api. **Version** : 0.26.0. +1. **Group** : io.perfmark. **Name** : perfmark-api. **Version** : 0.27.0. * **Project URL:** [https://github.com/perfmark/perfmark](https://github.com/perfmark/perfmark) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : it.unimi.dsi. **Name** : fastutil-core. **Version** : 8.5.12. - * **Project URL:** [http://fastutil.di.unimi.it/](http://fastutil.di.unimi.it/) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html) - 1. **Group** : javax.annotation. **Name** : javax.annotation-api. **Version** : 1.3.2. * **Project URL:** [http://jcp.org/en/jsr/detail?id=250](http://jcp.org/en/jsr/detail?id=250) * **License:** [CDDL + GPLv2 with classpath exception](https://github.com/javaee/javax.annotation/blob/master/LICENSE) @@ -510,35 +555,42 @@ * **Project URL:** [http://code.google.com/p/atinject/](http://code.google.com/p/atinject/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : junit. **Name** : junit. **Version** : 4.13.1. +1. **Group** : junit. **Name** : junit. **Version** : 4.13.2. * **Project URL:** [http://junit.org](http://junit.org) * **License:** [Eclipse Public License 1.0](http://www.eclipse.org/legal/epl-v10.html) +1. **Group** : kr.motd.maven. **Name** : os-maven-plugin. **Version** : 1.7.1. + * **Project URL:** [https://github.com/trustin/os-maven-plugin/](https://github.com/trustin/os-maven-plugin/) + * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + 1. **Group** : net.sf.saxon. **Name** : Saxon-HE. **Version** : 12.2. * **Project URL:** [http://www.saxonica.com/](http://www.saxonica.com/) * **License:** [Mozilla Public License Version 2.0](http://www.mozilla.org/MPL/2.0/) -1. **Group** : net.sourceforge.pmd. **Name** : pmd-core. **Version** : 6.55.0. +1. **Group** : net.sf.saxon. **Name** : Saxon-HE. **Version** : 12.5. + * **Project URL:** [http://www.saxonica.com/](http://www.saxonica.com/) + * **License:** [Mozilla Public License Version 2.0](http://www.mozilla.org/MPL/2.0/) + +1. **Group** : net.sourceforge.pmd. **Name** : pmd-ant. **Version** : 7.12.0. * **License:** [BSD-style](http://pmd.sourceforge.net/license.html) -1. **Group** : net.sourceforge.pmd. **Name** : pmd-java. **Version** : 6.55.0. +1. **Group** : net.sourceforge.pmd. **Name** : pmd-core. **Version** : 7.12.0. * **License:** [BSD-style](http://pmd.sourceforge.net/license.html) -1. **Group** : net.sourceforge.saxon. **Name** : saxon. **Version** : 9.1.0.8. - * **Project URL:** [http://saxon.sourceforge.net/](http://saxon.sourceforge.net/) - * **License:** [Mozilla Public License Version 1.0](http://www.mozilla.org/MPL/MPL-1.0.txt) +1. **Group** : net.sourceforge.pmd. **Name** : pmd-java. **Version** : 7.12.0. + * **License:** [BSD-style](http://pmd.sourceforge.net/license.html) 1. **Group** : org.antlr. **Name** : antlr4-runtime. **Version** : 4.11.1. * **Project URL:** [https://www.antlr.org/](https://www.antlr.org/) * **License:** [BSD-3-Clause](https://www.antlr.org/license.html) -1. **Group** : org.antlr. **Name** : antlr4-runtime. **Version** : 4.7.2. +1. **Group** : org.antlr. **Name** : antlr4-runtime. **Version** : 4.9.3. * **Project URL:** [http://www.antlr.org](http://www.antlr.org) * **License:** [The BSD License](http://www.antlr.org/license.html) -1. **Group** : org.apache.commons. **Name** : commons-lang3. **Version** : 3.8.1. - * **Project URL:** [http://commons.apache.org/proper/commons-lang/](http://commons.apache.org/proper/commons-lang/) - * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) +1. **Group** : org.apache.commons. **Name** : commons-lang3. **Version** : 3.17.0. + * **Project URL:** [https://commons.apache.org/proper/commons-lang/](https://commons.apache.org/proper/commons-lang/) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) 1. **Group** : org.apache.httpcomponents.client5. **Name** : httpclient5. **Version** : 5.1.3. * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -553,6 +605,23 @@ * **Project URL:** [https://github.com/apiguardian-team/apiguardian](https://github.com/apiguardian-team/apiguardian) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) +1. **Group** : org.bouncycastle. **Name** : bcpg-jdk18on. **Version** : 1.80. + * **Project URL:** [https://www.bouncycastle.org/download/bouncy-castle-java/](https://www.bouncycastle.org/download/bouncy-castle-java/) + * **License:** [Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) + * **License:** [Bouncy Castle Licence](https://www.bouncycastle.org/licence.html) + +1. **Group** : org.bouncycastle. **Name** : bcpkix-jdk18on. **Version** : 1.80. + * **Project URL:** [https://www.bouncycastle.org/download/bouncy-castle-java/](https://www.bouncycastle.org/download/bouncy-castle-java/) + * **License:** [Bouncy Castle Licence](https://www.bouncycastle.org/licence.html) + +1. **Group** : org.bouncycastle. **Name** : bcprov-jdk18on. **Version** : 1.80. + * **Project URL:** [https://www.bouncycastle.org/download/bouncy-castle-java/](https://www.bouncycastle.org/download/bouncy-castle-java/) + * **License:** [Bouncy Castle Licence](https://www.bouncycastle.org/licence.html) + +1. **Group** : org.bouncycastle. **Name** : bcutil-jdk18on. **Version** : 1.80. + * **Project URL:** [https://www.bouncycastle.org/download/bouncy-castle-java/](https://www.bouncycastle.org/download/bouncy-castle-java/) + * **License:** [Bouncy Castle Licence](https://www.bouncycastle.org/licence.html) + 1. **Group** : org.checkerframework. **Name** : checker-compat-qual. **Version** : 2.5.3. * **Project URL:** [https://checkerframework.org](https://checkerframework.org) * **License:** [GNU General Public License, version 2 (GPL2), with the classpath exception](http://www.gnu.org/software/classpath/license.html) @@ -566,33 +635,37 @@ * **License:** [MIT license](http://www.opensource.org/licenses/mit-license.php) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.codehaus.woodstox. **Name** : stax2-api. **Version** : 4.2.1. +1. **Group** : org.codehaus.woodstox. **Name** : stax2-api. **Version** : 4.2.2. * **Project URL:** [http://github.com/FasterXML/stax2-api](http://github.com/FasterXML/stax2-api) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The BSD License](http://www.opensource.org/licenses/bsd-license.php) + * **License:** [The BSD 2-Clause License](http://www.opensource.org/licenses/bsd-license.php) 1. **Group** : org.freemarker. **Name** : freemarker. **Version** : 2.3.32. * **Project URL:** [https://freemarker.apache.org/](https://freemarker.apache.org/) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.hamcrest. **Name** : hamcrest. **Version** : 2.2. +1. **Group** : org.functionaljava. **Name** : functionaljava. **Version** : 4.8. + * **Project URL:** [http://functionaljava.org/](http://functionaljava.org/) + * **License:** [The BSD3 License](https://github.com/functionaljava/functionaljava/blob/master/etc/LICENCE) + +1. **Group** : org.hamcrest. **Name** : hamcrest. **Version** : 3.0. * **Project URL:** [http://hamcrest.org/JavaHamcrest/](http://hamcrest.org/JavaHamcrest/) - * **License:** [BSD License 3](http://opensource.org/licenses/BSD-3-Clause) + * **License:** [BSD-3-Clause](https://raw.githubusercontent.com/hamcrest/JavaHamcrest/master/LICENSE) -1. **Group** : org.hamcrest. **Name** : hamcrest-core. **Version** : 2.2. +1. **Group** : org.hamcrest. **Name** : hamcrest-core. **Version** : 3.0. * **Project URL:** [http://hamcrest.org/JavaHamcrest/](http://hamcrest.org/JavaHamcrest/) - * **License:** [BSD License 3](http://opensource.org/licenses/BSD-3-Clause) + * **License:** [BSD-3-Clause](https://raw.githubusercontent.com/hamcrest/JavaHamcrest/master/LICENSE) -1. **Group** : org.jacoco. **Name** : org.jacoco.agent. **Version** : 0.8.12. +1. **Group** : org.jacoco. **Name** : org.jacoco.agent. **Version** : 0.8.13. * **License:** [EPL-2.0](https://www.eclipse.org/legal/epl-2.0/) -1. **Group** : org.jacoco. **Name** : org.jacoco.ant. **Version** : 0.8.12. +1. **Group** : org.jacoco. **Name** : org.jacoco.ant. **Version** : 0.8.13. * **License:** [EPL-2.0](https://www.eclipse.org/legal/epl-2.0/) -1. **Group** : org.jacoco. **Name** : org.jacoco.core. **Version** : 0.8.12. +1. **Group** : org.jacoco. **Name** : org.jacoco.core. **Version** : 0.8.13. * **License:** [EPL-2.0](https://www.eclipse.org/legal/epl-2.0/) -1. **Group** : org.jacoco. **Name** : org.jacoco.report. **Version** : 0.8.12. +1. **Group** : org.jacoco. **Name** : org.jacoco.report. **Version** : 0.8.13. * **License:** [EPL-2.0](https://www.eclipse.org/legal/epl-2.0/) 1. **Group** : org.javassist. **Name** : javassist. **Version** : 3.28.0-GA. @@ -601,11 +674,11 @@ * **License:** [LGPL 2.1](http://www.gnu.org/licenses/lgpl-2.1.html) * **License:** [MPL 1.1](http://www.mozilla.org/MPL/MPL-1.1.html) -1. **Group** : org.jboss.forge.roaster. **Name** : roaster-api. **Version** : 2.28.0.Final. +1. **Group** : org.jboss.forge.roaster. **Name** : roaster-api. **Version** : 2.29.0.Final. * **License:** [Eclipse Public License version 1.0](http://www.eclipse.org/legal/epl-v10.html) * **License:** [Public Domain](http://repository.jboss.org/licenses/cc0-1.0.txt) -1. **Group** : org.jboss.forge.roaster. **Name** : roaster-jdt. **Version** : 2.28.0.Final. +1. **Group** : org.jboss.forge.roaster. **Name** : roaster-jdt. **Version** : 2.29.0.Final. * **License:** [Eclipse Public License version 1.0](http://www.eclipse.org/legal/epl-v10.html) * **License:** [Public Domain](http://repository.jboss.org/licenses/cc0-1.0.txt) @@ -617,43 +690,39 @@ * **Project URL:** [https://github.com/JetBrains/java-annotations](https://github.com/JetBrains/java-annotations) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains. **Name** : markdown. **Version** : 0.5.2. +1. **Group** : org.jetbrains. **Name** : markdown. **Version** : 0.7.3. * **Project URL:** [https://github.com/JetBrains/markdown](https://github.com/JetBrains/markdown) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains. **Name** : markdown-jvm. **Version** : 0.5.2. +1. **Group** : org.jetbrains. **Name** : markdown-jvm. **Version** : 0.7.3. * **Project URL:** [https://github.com/JetBrains/markdown](https://github.com/JetBrains/markdown) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.dokka. **Name** : analysis-kotlin-descriptors. **Version** : 1.9.20. - * **Project URL:** [https://github.com/Kotlin/dokka](https://github.com/Kotlin/dokka) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - -1. **Group** : org.jetbrains.dokka. **Name** : analysis-markdown. **Version** : 1.9.20. +1. **Group** : org.jetbrains.dokka. **Name** : analysis-kotlin-symbols. **Version** : 2.1.0. * **Project URL:** [https://github.com/Kotlin/dokka](https://github.com/Kotlin/dokka) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.dokka. **Name** : dokka-base. **Version** : 1.9.20. +1. **Group** : org.jetbrains.dokka. **Name** : analysis-markdown. **Version** : 2.1.0. * **Project URL:** [https://github.com/Kotlin/dokka](https://github.com/Kotlin/dokka) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.dokka. **Name** : dokka-core. **Version** : 1.9.20. +1. **Group** : org.jetbrains.dokka. **Name** : dokka-base. **Version** : 2.1.0. * **Project URL:** [https://github.com/Kotlin/dokka](https://github.com/Kotlin/dokka) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.dokka. **Name** : gfm-plugin. **Version** : 1.9.20. +1. **Group** : org.jetbrains.dokka. **Name** : dokka-core. **Version** : 2.1.0. * **Project URL:** [https://github.com/Kotlin/dokka](https://github.com/Kotlin/dokka) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.dokka. **Name** : javadoc-plugin. **Version** : 1.9.20. +1. **Group** : org.jetbrains.dokka. **Name** : javadoc-plugin. **Version** : 2.1.0. * **Project URL:** [https://github.com/Kotlin/dokka](https://github.com/Kotlin/dokka) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.dokka. **Name** : jekyll-plugin. **Version** : 1.9.20. +1. **Group** : org.jetbrains.dokka. **Name** : kotlin-as-java-plugin. **Version** : 2.1.0. * **Project URL:** [https://github.com/Kotlin/dokka](https://github.com/Kotlin/dokka) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.dokka. **Name** : kotlin-as-java-plugin. **Version** : 1.9.20. +1. **Group** : org.jetbrains.dokka. **Name** : templating-plugin. **Version** : 2.1.0. * **Project URL:** [https://github.com/Kotlin/dokka](https://github.com/Kotlin/dokka) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -661,105 +730,149 @@ * **Project URL:** [https://github.com/JetBrains/intellij-deps-trove4j](https://github.com/JetBrains/intellij-deps-trove4j) * **License:** [GNU LESSER GENERAL PUBLIC LICENSE 2.1](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-build-tools-api. **Version** : 2.1.20. +1. **Group** : org.jetbrains.kotlin. **Name** : abi-tools. **Version** : 2.2.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-build-tools-impl. **Version** : 2.1.20. +1. **Group** : org.jetbrains.kotlin. **Name** : abi-tools-api. **Version** : 2.2.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-bom. **Version** : 2.2.21. + * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-build-tools-api. **Version** : 2.2.21. + * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-build-tools-impl. **Version** : 2.2.21. + * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) 1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-compiler-embeddable. **Version** : 2.0.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-compiler-embeddable. **Version** : 2.1.20. +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-compiler-embeddable. **Version** : 2.2.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-compiler-runner. **Version** : 2.1.20. +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-compiler-runner. **Version** : 2.2.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-daemon-client. **Version** : 2.1.20. +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-daemon-client. **Version** : 2.2.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) 1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-daemon-embeddable. **Version** : 2.0.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-daemon-embeddable. **Version** : 2.1.20. +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-daemon-embeddable. **Version** : 2.2.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-klib-commonizer-embeddable. **Version** : 2.1.20. +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-klib-commonizer-embeddable. **Version** : 2.2.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-reflect. **Version** : 2.1.20. +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-metadata-jvm. **Version** : 2.2.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-reflect. **Version** : 2.2.21. + * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) 1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-script-runtime. **Version** : 2.0.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-script-runtime. **Version** : 2.1.20. +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-script-runtime. **Version** : 2.2.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-scripting-common. **Version** : 2.1.20. +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-scripting-common. **Version** : 2.2.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-scripting-compiler-embeddable. **Version** : 2.1.20. +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-scripting-compiler-embeddable. **Version** : 2.2.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-scripting-compiler-impl-embeddable. **Version** : 2.1.20. +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-scripting-compiler-impl-embeddable. **Version** : 2.2.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-scripting-jvm. **Version** : 2.1.20. +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-scripting-jvm. **Version** : 2.2.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib. **Version** : 2.1.20. +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib. **Version** : 2.2.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-common. **Version** : 2.1.20. +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-common. **Version** : 2.2.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-jdk7. **Version** : 2.1.20. +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-jdk7. **Version** : 2.2.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-jdk8. **Version** : 2.1.20. +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-jdk8. **Version** : 2.2.21. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.jetbrains.kotlin. **Name** : swift-export-embeddable. **Version** : 2.2.21. + * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) + * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) 1. **Group** : org.jetbrains.kotlinx. **Name** : atomicfu. **Version** : 0.23.1. * **Project URL:** [https://github.com/Kotlin/kotlinx.atomicfu](https://github.com/Kotlin/kotlinx.atomicfu) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-bom. **Version** : 1.10.1. +1. **Group** : org.jetbrains.kotlinx. **Name** : atomicfu. **Version** : 0.29.0. + * **Project URL:** [https://github.com/Kotlin/kotlinx.atomicfu](https://github.com/Kotlin/kotlinx.atomicfu) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.jetbrains.kotlinx. **Name** : atomicfu-jvm. **Version** : 0.29.0. + * **Project URL:** [https://github.com/Kotlin/kotlinx.atomicfu](https://github.com/Kotlin/kotlinx.atomicfu) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-bom. **Version** : 1.10.2. * **Project URL:** [https://github.com/Kotlin/kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-core. **Version** : 1.10.1. +1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-core. **Version** : 1.10.2. * **Project URL:** [https://github.com/Kotlin/kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-core-jvm. **Version** : 1.10.1. +1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-core-jvm. **Version** : 1.10.2. * **Project URL:** [https://github.com/Kotlin/kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-jdk8. **Version** : 1.10.1. +1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-jdk8. **Version** : 1.10.2. * **Project URL:** [https://github.com/Kotlin/kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-test. **Version** : 1.10.2. + * **Project URL:** [https://github.com/Kotlin/kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-test-jvm. **Version** : 1.10.2. + * **Project URL:** [https://github.com/Kotlin/kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-datetime. **Version** : 0.7.1. + * **Project URL:** [https://github.com/Kotlin/kotlinx-datetime](https://github.com/Kotlin/kotlinx-datetime) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-datetime-jvm. **Version** : 0.7.1. + * **Project URL:** [https://github.com/Kotlin/kotlinx-datetime](https://github.com/Kotlin/kotlinx-datetime) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) 1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-html-jvm. **Version** : 0.8.1. * **Project URL:** [https://github.com/Kotlin/kotlinx.html](https://github.com/Kotlin/kotlinx.html) @@ -769,7 +882,7 @@ * **Project URL:** [https://github.com/Kotlin/kotlinx.html](https://github.com/Kotlin/kotlinx.html) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-serialization-bom. **Version** : 1.6.3. +1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-serialization-bom. **Version** : 1.7.3. * **Project URL:** [https://github.com/Kotlin/kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -777,7 +890,7 @@ * **Project URL:** [https://github.com/Kotlin/kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-serialization-core. **Version** : 1.6.3. +1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-serialization-core. **Version** : 1.7.3. * **Project URL:** [https://github.com/Kotlin/kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -785,7 +898,7 @@ * **Project URL:** [https://github.com/Kotlin/kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-serialization-core-jvm. **Version** : 1.6.3. +1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-serialization-core-jvm. **Version** : 1.7.3. * **Project URL:** [https://github.com/Kotlin/kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -793,18 +906,10 @@ * **Project URL:** [https://github.com/Kotlin/kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-serialization-json. **Version** : 1.6.3. - * **Project URL:** [https://github.com/Kotlin/kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-serialization-json-jvm. **Version** : 1.4.1. * **Project URL:** [https://github.com/Kotlin/kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-serialization-json-jvm. **Version** : 1.6.3. - * **Project URL:** [https://github.com/Kotlin/kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : org.jsoup. **Name** : jsoup. **Version** : 1.16.1. * **Project URL:** [https://jsoup.org/](https://jsoup.org/) * **License:** [The MIT License](https://jsoup.org/license) @@ -813,28 +918,36 @@ * **Project URL:** [http://jspecify.org/](http://jspecify.org/) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.junit. **Name** : junit-bom. **Version** : 5.10.0. - * **Project URL:** [https://junit.org/junit5/](https://junit.org/junit5/) +1. **Group** : org.junit. **Name** : junit-bom. **Version** : 6.0.0. + * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 5.10.0. - * **Project URL:** [https://junit.org/junit5/](https://junit.org/junit5/) +1. **Group** : org.junit-pioneer. **Name** : junit-pioneer. **Version** : 2.3.0. + * **Project URL:** [https://junit-pioneer.org/](https://junit-pioneer.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-engine. **Version** : 5.10.0. - * **Project URL:** [https://junit.org/junit5/](https://junit.org/junit5/) +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 6.0.0. + * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-params. **Version** : 5.10.0. - * **Project URL:** [https://junit.org/junit5/](https://junit.org/junit5/) +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-engine. **Version** : 6.0.0. + * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-commons. **Version** : 1.10.0. - * **Project URL:** [https://junit.org/junit5/](https://junit.org/junit5/) +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-params. **Version** : 6.0.0. + * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) -1. **Group** : org.junit.platform. **Name** : junit-platform-engine. **Version** : 1.10.0. - * **Project URL:** [https://junit.org/junit5/](https://junit.org/junit5/) +1. **Group** : org.junit.platform. **Name** : junit-platform-commons. **Version** : 6.0.0. + * **Project URL:** [https://junit.org/](https://junit.org/) + * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) + +1. **Group** : org.junit.platform. **Name** : junit-platform-engine. **Version** : 6.0.0. + * **Project URL:** [https://junit.org/](https://junit.org/) + * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) + +1. **Group** : org.junit.platform. **Name** : junit-platform-launcher. **Version** : 6.0.0. + * **Project URL:** [https://junit.org/](https://junit.org/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) 1. **Group** : org.opentest4j. **Name** : opentest4j. **Version** : 1.3.0. @@ -860,11 +973,19 @@ * **Project URL:** [https://github.com/hrldcpr/pcollections](https://github.com/hrldcpr/pcollections) * **License:** [The MIT License](https://opensource.org/licenses/mit-license.php) +1. **Group** : org.pcollections. **Name** : pcollections. **Version** : 4.0.2. + * **Project URL:** [https://github.com/hrldcpr/pcollections](https://github.com/hrldcpr/pcollections) + * **License:** [The MIT License](https://opensource.org/licenses/mit-license.php) + 1. **Group** : org.reflections. **Name** : reflections. **Version** : 0.10.2. * **Project URL:** [http://github.com/ronmamo/reflections](http://github.com/ronmamo/reflections) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [WTFPL](http://www.wtfpl.net/) +1. **Group** : org.slf4j. **Name** : jul-to-slf4j. **Version** : 1.7.36. + * **Project URL:** [http://www.slf4j.org](http://www.slf4j.org) + * **License:** [MIT License](http://www.opensource.org/licenses/mit-license.php) + 1. **Group** : org.snakeyaml. **Name** : snakeyaml-engine. **Version** : 2.7. * **Project URL:** [https://bitbucket.org/snakeyaml/snakeyaml-engine](https://bitbucket.org/snakeyaml/snakeyaml-engine) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -873,11 +994,17 @@ * **Project URL:** [https://github.com/xmlresolver/xmlresolver](https://github.com/xmlresolver/xmlresolver) * **License:** [Apache License version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : org.yaml. **Name** : snakeyaml. **Version** : 2.1. +1. **Group** : org.xmlresolver. **Name** : xmlresolver. **Version** : 5.2.2. + * **Project URL:** [https://github.com/xmlresolver/xmlresolver](https://github.com/xmlresolver/xmlresolver) + * **License:** [Apache License version 2.0](https://www.apache.org/licenses/LICENSE-2.0) + +1. **Group** : org.yaml. **Name** : snakeyaml. **Version** : 2.4. * **Project URL:** [https://bitbucket.org/snakeyaml/snakeyaml](https://bitbucket.org/snakeyaml/snakeyaml) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Mar 27 10:27:34 WET 2025** 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 +This report was generated on **Mon Dec 22 19:25:55 WET 2025** using +[Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under +[Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 4c1c2c4..93bcb69 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,28 +1,25 @@ -# -# 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. -# +# Allow Gradle to auto-detect installed JDKs. +org.gradle.java.installations.auto-detect=true -# Dokka plugin eats more memory than usual. Therefore all builds should have enough. -org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m +# Optional: Allow Gradle to download JDKs if needed. +org.gradle.java.installations.auto-download=true + +# Use parallel builds for better performance. +org.gradle.parallel=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 + +# suppress inspection "UnusedProperty" +# The below property enables generation of XML reports for tests. +# If this flag is false, it causes `KotlinTestReport` task to replace these reports with one +# consolidated HTML report. +# See: https://github.com/JetBrains/kotlin/blob/9fd05632f0d7f074b6544527e73eb0fbb2fb1ef2/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/testing/internal/KotlinTestReport.kt#L20 +# See: https://youtrack.jetbrains.com/issue/KT-32608 +kotlin.tests.individualTaskReports=true + +# Enables the Dokka migration mode from v1 to v2. +# For details please see: +# https://kotlinlang.org/docs/dokka-migration.html#enable-migration-helpers +org.jetbrains.dokka.experimental.gradle.pluginMode=V2EnabledWithHelpers diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 9bbc975..f8e1ee3 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 37f853b..23449a2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 965e398..adff685 100755 --- a/gradlew +++ b/gradlew @@ -1,13 +1,13 @@ #!/bin/sh # -# Copyright ยฉ 2015-2021 the original authors. +# Copyright ยฉ 2015 the original authors. # # 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 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -210,8 +210,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 9d21a21..c4bdd3a 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,10 @@ goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/pom.xml b/pom.xml index 89126b0..72cc3ad 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ all modules and does not describe the project structure per-subproject. --> io.spine spine-change -2.0.0-SNAPSHOT.200 +2.0.0-SNAPSHOT.205 2015 @@ -26,37 +26,73 @@ all modules and does not describe the project structure per-subproject. io.spine spine-base - 2.0.0-SNAPSHOT.307 + 2.0.0-SNAPSHOT.383 compile - io.spine.validation - spine-validation-java-runtime - 2.0.0-SNAPSHOT.305 + io.spine + spine-validation-jvm-runtime + 2.0.0-SNAPSHOT.383 compile org.jetbrains.kotlin kotlin-stdlib - 2.1.20 + 2.2.21 compile + + com.google.guava + guava-testlib + 33.5.0-jre + test + + + io.kotest + kotest-assertions-core + 6.0.4 + test + io.spine.tools spine-testlib - 2.0.0-SNAPSHOT.185 + 2.0.0-SNAPSHOT.211 test io.spine.tools spine-time-testlib - 2.0.0-SNAPSHOT.200 + 2.0.0-SNAPSHOT.220 + test + + + org.junit + junit-bom + 6.0.0 + test + + + org.junit-pioneer + junit-pioneer + 2.3.0 + test + + + org.junit.jupiter + junit-jupiter-api + null test org.junit.jupiter junit-jupiter-engine - 5.10.0 + null + test + + + org.junit.jupiter + junit-jupiter-params + null test @@ -68,28 +104,18 @@ all modules and does not describe the project structure per-subproject. com.google.devtools.ksp symbol-processing - 2.1.20-1.0.31 + 2.3.0 com.google.devtools.ksp symbol-processing-api - 2.1.20-1.0.31 - - - com.google.devtools.ksp - symbol-processing-cmdline - 2.1.20-1.0.31 + 2.3.0 com.google.errorprone error_prone_core 2.36.0 - - com.google.errorprone - javac - 9+181-r4173-1 - com.google.protobuf protoc @@ -111,109 +137,99 @@ all modules and does not describe the project structure per-subproject. 1.23.8 - io.grpc - protoc-gen-grpc-java - 1.59.0 + io.spine.tools + compiler-cli-all + 2.0.0-SNAPSHOT.035 - io.spine.protodata - protodata-fat-cli - 0.93.4 + io.spine.tools + compiler-protoc-plugin + 2.0.0-SNAPSHOT.035 - io.spine.protodata - protodata-protoc - 0.93.4 + io.spine.tools + core-jvm-gradle-plugins + 2.0.0-SNAPSHOT.042 io.spine.tools - spine-dokka-extensions - 2.0.0-SNAPSHOT.6 + core-jvm-routing + 2.0.0-SNAPSHOT.042 io.spine.tools - spine-mc-java-plugins - 2.0.0-SNAPSHOT.306 + spine-dokka-extensions + 2.0.0-SNAPSHOT.7 io.spine.tools - spine-mc-java-routing - 2.0.0-SNAPSHOT.306 + validation-java-bundle + 2.0.0-SNAPSHOT.383 - io.spine.validation - spine-validation-java-bundle - 2.0.0-SNAPSHOT.302 + net.sourceforge.pmd + pmd-ant + 7.12.0 net.sourceforge.pmd pmd-java - 6.55.0 + 7.12.0 org.jacoco org.jacoco.agent - 0.8.12 + 0.8.13 org.jacoco org.jacoco.ant - 0.8.12 + 0.8.13 org.jetbrains.dokka - analysis-kotlin-descriptors - 1.9.20 + all-modules-page-plugin + 2.1.0 org.jetbrains.dokka - dokka-base - 1.9.20 + analysis-kotlin-symbols + 2.1.0 org.jetbrains.dokka - dokka-core - 1.9.20 + dokka-base + 2.1.0 org.jetbrains.dokka - gfm-plugin - 1.9.20 + dokka-core + 2.1.0 org.jetbrains.dokka javadoc-plugin - 1.9.20 - - - org.jetbrains.dokka - jekyll-plugin - 1.9.20 + 2.1.0 org.jetbrains.dokka - kotlin-as-java-plugin - 1.9.20 + templating-plugin + 2.1.0 org.jetbrains.kotlin kotlin-build-tools-impl - 2.1.20 + 2.2.21 org.jetbrains.kotlin kotlin-compiler-embeddable - 2.1.20 - - - org.jetbrains.kotlin - kotlin-klib-commonizer-embeddable - 2.1.20 + 2.2.21 org.jetbrains.kotlin kotlin-scripting-compiler-embeddable - 2.1.20 + 2.2.21 diff --git a/version.gradle.kts b/version.gradle.kts index 4f3d2b7..3f3af8d 100644 --- a/version.gradle.kts +++ b/version.gradle.kts @@ -29,4 +29,4 @@ * * For dependencies on Spine modules please see [io.spine.dependency.local.Spine]. */ -val versionToPublish by extra("2.0.0-SNAPSHOT.200") +val versionToPublish by extra("2.0.0-SNAPSHOT.205")