From 299374ef947fca7f35529d1422c979900f368f36 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 25 Apr 2024 21:22:52 +0100 Subject: [PATCH 01/39] Add `config` --- .codecov.yml | 29 + .gitattributes | 61 ++ .github/keys/cloudrepo.properties.gpg | Bin 0 -> 786 bytes .github/keys/deploy_key_rsa.gpg | Bin 0 -> 2206 bytes .github/keys/firebase-sa.json.gpg | Bin 0 -> 1715 bytes .github/keys/gcs-auth-key.json.gpg | Bin 0 -> 1723 bytes ...gradle-plugin-portal.secret.properties.gpg | 2 + .github/keys/maven-publisher.json.gpg | Bin 0 -> 1727 bytes .github/workflows/build-on-ubuntu.yml | 37 + .github/workflows/build-on-windows.yml | 36 + .github/workflows/ensure-reports-updated.yml | 25 + .../workflows/gradle-wrapper-validation.yml | 19 + .github/workflows/increment-guard.yml | 29 + .github/workflows/publish.yml | 63 ++ .gitignore | 108 ++- .gitmodules | 3 + .idea/codeStyles/Project.xml | 103 ++ .idea/codeStyles/codeStyleConfig.xml | 5 + .idea/copyright/TeamDev_Open_Source.xml | 6 + .idea/copyright/profiles_settings.xml | 7 + .idea/dictionaries/common.xml | 69 ++ .idea/inspectionProfiles/Project_Default.xml | 911 ++++++++++++++++++ .idea/kotlinc.xml | 13 + .idea/live-templates/README.md | 27 + .idea/live-templates/Spine.xml | 58 ++ .idea/live-templates/User.xml | 11 + .idea/misc.xml | 44 + CODE_OF_CONDUCT.md | 128 +++ CONTRIBUTING.md | 37 + LICENSE.md | 201 ++++ buildSrc/aus.weis | Bin 0 -> 236 bytes buildSrc/build.gradle.kts | 242 +++++ buildSrc/src/main/kotlin/BuildExtensions.kt | 178 ++++ buildSrc/src/main/kotlin/BuildSettings.kt | 36 + .../src/main/kotlin/DependencyResolution.kt | 164 ++++ buildSrc/src/main/kotlin/DokkaExts.kt | 216 +++++ .../main/kotlin/compile-protobuf.gradle.kts | 45 + .../src/main/kotlin/config-tester.gradle.kts | 57 ++ .../kotlin/detekt-code-analysis.gradle.kts | 85 ++ .../src/main/kotlin/dokka-for-java.gradle.kts | 43 + .../main/kotlin/dokka-for-kotlin.gradle.kts | 42 + .../internal/dependency/AnimalSniffer.kt | 34 + .../spine/internal/dependency/ApacheHttp.kt | 34 + .../io/spine/internal/dependency/AppEngine.kt | 39 + .../io/spine/internal/dependency/Asm.kt | 34 + .../io/spine/internal/dependency/AssertK.kt | 39 + .../io/spine/internal/dependency/Auto.kt | 49 + .../spine/internal/dependency/BouncyCastle.kt | 33 + .../spine/internal/dependency/CheckStyle.kt | 46 + .../internal/dependency/CheckerFramework.kt | 39 + .../spine/internal/dependency/CommonsCli.kt | 39 + .../spine/internal/dependency/CommonsCodec.kt | 34 + .../internal/dependency/CommonsLogging.kt | 38 + .../spine/internal/dependency/Coroutines.kt | 41 + .../io/spine/internal/dependency/Dokka.kt | 84 ++ .../spine/internal/dependency/ErrorProne.kt | 59 ++ .../io/spine/internal/dependency/FindBugs.kt | 41 + .../io/spine/internal/dependency/Firebase.kt | 34 + .../io/spine/internal/dependency/Flogger.kt | 42 + .../spine/internal/dependency/GoogleApis.kt | 59 ++ .../spine/internal/dependency/GoogleCloud.kt | 43 + .../spine/internal/dependency/GradleDoctor.kt | 38 + .../io/spine/internal/dependency/Grpc.kt | 50 + .../spine/internal/dependency/GrpcKotlin.kt | 43 + .../io/spine/internal/dependency/Gson.kt | 39 + .../io/spine/internal/dependency/Guava.kt | 43 + .../io/spine/internal/dependency/Hamcrest.kt | 40 + .../spine/internal/dependency/HttpClient.kt | 42 + .../io/spine/internal/dependency/IntelliJ.kt | 89 ++ .../io/spine/internal/dependency/J2ObjC.kt | 51 + .../io/spine/internal/dependency/JUnit.kt | 64 ++ .../io/spine/internal/dependency/Jackson.kt | 63 ++ .../internal/dependency/JavaDiffUtils.kt | 42 + .../io/spine/internal/dependency/JavaJwt.kt | 46 + .../io/spine/internal/dependency/JavaPoet.kt | 34 + .../io/spine/internal/dependency/JavaX.kt | 36 + .../io/spine/internal/dependency/Klaxon.kt | 38 + .../io/spine/internal/dependency/Kotest.kt | 60 ++ .../io/spine/internal/dependency/Kotlin.kt | 66 ++ .../spine/internal/dependency/KotlinSemver.kt | 34 + .../io/spine/internal/dependency/KotlinX.kt | 41 + .../io/spine/internal/dependency/Kover.kt | 35 + .../io/spine/internal/dependency/Netty.kt | 38 + .../io/spine/internal/dependency/Okio.kt | 38 + .../spine/internal/dependency/OpenTest4J.kt | 38 + .../spine/internal/dependency/OsDetector.kt | 36 + .../io/spine/internal/dependency/Plexus.kt | 48 + .../io/spine/internal/dependency/Pmd.kt | 42 + .../io/spine/internal/dependency/ProtoData.kt | 170 ++++ .../io/spine/internal/dependency/Protobuf.kt | 60 ++ .../io/spine/internal/dependency/Roaster.kt | 44 + .../io/spine/internal/dependency/Slf4J.kt | 47 + .../io/spine/internal/dependency/Spine.kt | 220 +++++ .../spine/internal/dependency/TestKitTruth.kt | 41 + .../io/spine/internal/dependency/Truth.kt | 38 + .../spine/internal/dependency/Validation.kt | 64 ++ .../kotlin/io/spine/internal/gradle/Build.kt | 32 + .../kotlin/io/spine/internal/gradle/Clean.kt | 48 + .../io/spine/internal/gradle/ConfigTester.kt | 363 +++++++ .../internal/gradle/ProjectExtensions.kt | 93 ++ .../io/spine/internal/gradle/RepoSlug.kt | 66 ++ .../io/spine/internal/gradle/Repositories.kt | 370 +++++++ .../io/spine/internal/gradle/RunBuild.kt | 37 + .../io/spine/internal/gradle/RunGradle.kt | 188 ++++ .../io/spine/internal/gradle/Runtime.kt | 99 ++ .../spine/internal/gradle/StringExtensions.kt | 35 + .../io/spine/internal/gradle/TaskName.kt | 59 ++ .../io/spine/internal/gradle/VersionWriter.kt | 149 +++ .../io/spine/internal/gradle/base/Tasks.kt | 87 ++ .../gradle/checkstyle/CheckStyleConfig.kt | 73 ++ .../spine/internal/gradle/dart/DartContext.kt | 45 + .../internal/gradle/dart/DartEnvironment.kt | 170 ++++ .../internal/gradle/dart/DartExtension.kt | 187 ++++ .../gradle/dart/plugin/DartPlugins.kt | 92 ++ .../internal/gradle/dart/plugin/Protobuf.kt | 52 + .../spine/internal/gradle/dart/task/Build.kt | 155 +++ .../internal/gradle/dart/task/DartTasks.kt | 115 +++ .../gradle/dart/task/IntegrationTest.kt | 92 ++ .../internal/gradle/dart/task/Publish.kt | 176 ++++ .../spine/internal/gradle/fs/LazyTempPath.kt | 109 +++ .../io/spine/internal/gradle/git/Branch.kt | 43 + .../spine/internal/gradle/git/Repository.kt | 164 ++++ .../io/spine/internal/gradle/git/UserInfo.kt | 42 + .../gradle/github/pages/AuthorEmail.kt | 56 ++ .../github/pages/RepositoryExtensions.kt | 58 ++ .../internal/gradle/github/pages/SshKey.kt | 104 ++ .../internal/gradle/github/pages/TaskName.kt | 47 + .../internal/gradle/github/pages/Update.kt | 167 ++++ .../gradle/github/pages/UpdateGitHubPages.kt | 234 +++++ .../pages/UpdateGitHubPagesExtension.kt | 131 +++ .../io/spine/internal/gradle/java/Tasks.kt | 45 + .../spine/internal/gradle/javac/ErrorProne.kt | 88 ++ .../io/spine/internal/gradle/javac/Javac.kt | 74 ++ .../spine/internal/gradle/javadoc/Encoding.kt | 32 + .../gradle/javadoc/ExcludeInternalDoclet.kt | 118 +++ .../internal/gradle/javadoc/JavadocConfig.kt | 139 +++ .../internal/gradle/javadoc/JavadocTag.kt | 37 + .../gradle/javadoc/TaskContainerExtensions.kt | 40 + .../internal/gradle/javascript/JsContext.kt | 61 ++ .../gradle/javascript/JsEnvironment.kt | 255 +++++ .../internal/gradle/javascript/JsExtension.kt | 194 ++++ .../internal/gradle/javascript/plugin/Idea.kt | 64 ++ .../gradle/javascript/plugin/JsPlugins.kt | 92 ++ .../internal/gradle/javascript/plugin/McJs.kt | 63 ++ .../gradle/javascript/plugin/Protobuf.kt | 88 ++ .../gradle/javascript/task/Assemble.kt | 205 ++++ .../internal/gradle/javascript/task/Check.kt | 197 ++++ .../internal/gradle/javascript/task/Clean.kt | 124 +++ .../gradle/javascript/task/IntegrationTest.kt | 128 +++ .../gradle/javascript/task/JsTasks.kt | 118 +++ .../gradle/javascript/task/LicenseReport.kt | 89 ++ .../gradle/javascript/task/Publish.kt | 194 ++++ .../gradle/javascript/task/Webpack.kt | 108 +++ .../internal/gradle/kotlin/KotlinConfig.kt | 69 ++ .../gradle/protobuf/ProtoTaskExtensions.kt | 342 +++++++ .../gradle/publish/CheckVersionIncrement.kt | 129 +++ .../gradle/publish/CloudArtifactRegistry.kt | 78 ++ .../internal/gradle/publish/CloudRepo.kt | 68 ++ .../internal/gradle/publish/GitHubPackages.kt | 104 ++ .../internal/gradle/publish/IncrementGuard.kt | 108 +++ .../spine/internal/gradle/publish/JarDsl.kt | 167 ++++ .../internal/gradle/publish/ProtoExts.kt | 89 ++ .../internal/gradle/publish/Publications.kt | 224 +++++ .../internal/gradle/publish/PublishingExts.kt | 264 +++++ .../gradle/publish/PublishingRepos.kt | 44 + .../gradle/publish/SpinePublishing.kt | 428 ++++++++ .../gradle/report/coverage/CodebaseFilter.kt | 113 +++ .../gradle/report/coverage/FileExtension.kt | 49 + .../gradle/report/coverage/FileExtensions.kt | 129 +++ .../gradle/report/coverage/FileFilter.kt | 50 + .../gradle/report/coverage/JacocoConfig.kt | 223 +++++ .../gradle/report/coverage/PathMarker.kt | 70 ++ .../gradle/report/coverage/TaskName.kt | 39 + .../gradle/report/license/Configuration.kt | 51 + .../gradle/report/license/LicenseReporter.kt | 156 +++ .../report/license/MarkdownReportRenderer.kt | 62 ++ .../report/license/ModuleDataExtensions.kt | 169 ++++ .../internal/gradle/report/license/Paths.kt | 49 + .../report/license/ProjectDependencies.kt | 70 ++ .../internal/gradle/report/license/Tasks.kt | 40 + .../gradle/report/license/Template.kt | 75 ++ .../gradle/report/pom/DependencyScope.kt | 45 + .../gradle/report/pom/DependencyWriter.kt | 185 ++++ .../gradle/report/pom/InceptionYear.kt | 51 + .../gradle/report/pom/MarkupExtensions.kt | 38 + .../gradle/report/pom/ModuleDependency.kt | 92 ++ .../gradle/report/pom/PomFormatting.kt | 111 +++ .../gradle/report/pom/PomGenerator.kt | 94 ++ .../gradle/report/pom/PomXmlWriter.kt | 86 ++ .../gradle/report/pom/ProjectMetadata.kt | 103 ++ .../gradle/report/pom/ScopedDependency.kt | 179 ++++ .../gradle/report/pom/SpineLicense.kt | 59 ++ .../spine/internal/gradle/testing/Logging.kt | 88 ++ .../internal/gradle/testing/Multiproject.kt | 75 ++ .../io/spine/internal/gradle/testing/Tasks.kt | 102 ++ .../spine/internal/markup/MarkdownDocument.kt | 119 +++ .../main/kotlin/jacoco-kotlin-jvm.gradle.kts | 70 ++ .../src/main/kotlin/jvm-module.gradle.kts | 216 +++++ .../src/main/kotlin/pmd-settings.gradle.kts | 56 ++ .../src/main/kotlin/write-manifest.gradle.kts | 164 ++++ .../main/resources/dokka/assets/logo-icon.svg | 17 + .../resources/dokka/styles/custom-styles.css | 52 + config | 1 + gradle.properties | 5 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 61624 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 244 +++++ gradlew.bat | 92 ++ settings.gradle.kts | 51 + 209 files changed, 18259 insertions(+), 11 deletions(-) create mode 100644 .codecov.yml create mode 100644 .gitattributes create mode 100644 .github/keys/cloudrepo.properties.gpg create mode 100644 .github/keys/deploy_key_rsa.gpg create mode 100644 .github/keys/firebase-sa.json.gpg create mode 100644 .github/keys/gcs-auth-key.json.gpg create mode 100644 .github/keys/gradle-plugin-portal.secret.properties.gpg create mode 100644 .github/keys/maven-publisher.json.gpg create mode 100644 .github/workflows/build-on-ubuntu.yml create mode 100644 .github/workflows/build-on-windows.yml create mode 100644 .github/workflows/ensure-reports-updated.yml create mode 100644 .github/workflows/gradle-wrapper-validation.yml create mode 100644 .github/workflows/increment-guard.yml create mode 100644 .github/workflows/publish.yml create mode 100644 .gitmodules create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/copyright/TeamDev_Open_Source.xml create mode 100644 .idea/copyright/profiles_settings.xml create mode 100644 .idea/dictionaries/common.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/kotlinc.xml create mode 100644 .idea/live-templates/README.md create mode 100644 .idea/live-templates/Spine.xml create mode 100644 .idea/live-templates/User.xml create mode 100644 .idea/misc.xml create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE.md create mode 100644 buildSrc/aus.weis create mode 100644 buildSrc/build.gradle.kts create mode 100644 buildSrc/src/main/kotlin/BuildExtensions.kt create mode 100644 buildSrc/src/main/kotlin/BuildSettings.kt create mode 100644 buildSrc/src/main/kotlin/DependencyResolution.kt create mode 100644 buildSrc/src/main/kotlin/DokkaExts.kt create mode 100644 buildSrc/src/main/kotlin/compile-protobuf.gradle.kts create mode 100644 buildSrc/src/main/kotlin/config-tester.gradle.kts create mode 100644 buildSrc/src/main/kotlin/detekt-code-analysis.gradle.kts create mode 100644 buildSrc/src/main/kotlin/dokka-for-java.gradle.kts create mode 100644 buildSrc/src/main/kotlin/dokka-for-kotlin.gradle.kts create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/AnimalSniffer.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/ApacheHttp.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/AppEngine.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Asm.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/AssertK.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Auto.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/BouncyCastle.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/CheckStyle.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/CheckerFramework.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/CommonsCli.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/CommonsCodec.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/CommonsLogging.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Coroutines.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Dokka.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/ErrorProne.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/FindBugs.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Firebase.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Flogger.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/GoogleApis.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/GoogleCloud.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/GradleDoctor.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Grpc.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/GrpcKotlin.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Gson.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Guava.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Hamcrest.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/HttpClient.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/IntelliJ.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/J2ObjC.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/JUnit.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Jackson.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/JavaDiffUtils.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/JavaJwt.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/JavaPoet.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/JavaX.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Klaxon.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Kotest.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Kotlin.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/KotlinSemver.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/KotlinX.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Kover.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Netty.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Okio.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/OpenTest4J.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/OsDetector.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Plexus.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Pmd.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/ProtoData.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Protobuf.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Roaster.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Slf4J.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Spine.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/TestKitTruth.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Truth.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/dependency/Validation.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/Build.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/Clean.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/ConfigTester.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/ProjectExtensions.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/RepoSlug.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/Repositories.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/RunBuild.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/RunGradle.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/Runtime.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/StringExtensions.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/TaskName.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/VersionWriter.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/base/Tasks.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/checkstyle/CheckStyleConfig.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/DartContext.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/DartEnvironment.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/DartExtension.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/plugin/DartPlugins.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/plugin/Protobuf.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/task/Build.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/task/DartTasks.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/task/IntegrationTest.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/task/Publish.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/fs/LazyTempPath.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/git/Branch.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/git/Repository.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/git/UserInfo.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/AuthorEmail.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/RepositoryExtensions.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/SshKey.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/TaskName.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/Update.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/UpdateGitHubPages.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/UpdateGitHubPagesExtension.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/java/Tasks.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/javac/ErrorProne.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/javac/Javac.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/javadoc/Encoding.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/javadoc/ExcludeInternalDoclet.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/javadoc/JavadocConfig.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/javadoc/JavadocTag.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/javadoc/TaskContainerExtensions.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/JsContext.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/JsEnvironment.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/JsExtension.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/plugin/Idea.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/plugin/JsPlugins.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/plugin/McJs.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/plugin/Protobuf.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/Assemble.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/Check.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/Clean.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/IntegrationTest.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/JsTasks.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/LicenseReport.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/Publish.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/Webpack.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/kotlin/KotlinConfig.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/protobuf/ProtoTaskExtensions.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/CheckVersionIncrement.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/CloudArtifactRegistry.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/CloudRepo.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/GitHubPackages.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/IncrementGuard.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/JarDsl.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/ProtoExts.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/Publications.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/PublishingExts.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/PublishingRepos.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/SpinePublishing.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/CodebaseFilter.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/FileExtension.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/FileExtensions.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/FileFilter.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/JacocoConfig.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/PathMarker.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/TaskName.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/Configuration.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/LicenseReporter.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/MarkdownReportRenderer.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/ModuleDataExtensions.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/Paths.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/ProjectDependencies.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/Tasks.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/Template.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/DependencyScope.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/DependencyWriter.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/InceptionYear.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/MarkupExtensions.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/ModuleDependency.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/PomFormatting.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/PomGenerator.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/PomXmlWriter.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/ProjectMetadata.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/ScopedDependency.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/SpineLicense.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/testing/Logging.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/testing/Multiproject.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/gradle/testing/Tasks.kt create mode 100644 buildSrc/src/main/kotlin/io/spine/internal/markup/MarkdownDocument.kt create mode 100644 buildSrc/src/main/kotlin/jacoco-kotlin-jvm.gradle.kts create mode 100644 buildSrc/src/main/kotlin/jvm-module.gradle.kts create mode 100644 buildSrc/src/main/kotlin/pmd-settings.gradle.kts create mode 100644 buildSrc/src/main/kotlin/write-manifest.gradle.kts create mode 100644 buildSrc/src/main/resources/dokka/assets/logo-icon.svg create mode 100644 buildSrc/src/main/resources/dokka/styles/custom-styles.css create mode 160000 config create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle.kts diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 0000000..b5739f8 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,29 @@ +# +# See default configuration here: https://github.com/codecov/support/blob/master/codecov.yml +# +# For more options see: https://gist.github.com/stevepeak/53bee7b2c326b24a9b4a +# +# Codecov documentation is available here: https://codecov.io/docs + +coverage: + ignore: + - "**/generated/**/*" + - "**/examples/**/*" + - "**/test/**/*" + status: + # https://docs.codecov.com/docs/github-checks#yaml-configuration-for-github-checks-and-codecov + patch: false + # https://docs.codecov.com/docs/commit-status + project: + default: + target: auto + threshold: 0.05% + base: auto + paths: + - "src" + if_ci_failed: error + informational: false + only_pulls: true + +comment: + layout: "header, diff, changes, uncovered" diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..810ea7f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,61 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. + +# Common formats +*.html text +*.xml text +*.css text +*.scss text +*.js text +*.properties text +*.rtf text +*.yaml text +*.yml text +*.md text + +LICENSE text + +# SQL scripts +*.sql text + +# Java sources +*.java text + +# Python sources +*.py text + +# Gradle build files +*.gradle text + +# Google protocol buffers +*.proto text + +# Miscellaneous +*.rb text + +# Declare files that will always have CRLF line endings on checkout. +*.bat text eol=crlf + +# Declare files that will always have LF line endings on checkout. +*.sh text eol=lf +gradlew text eol=lf +pull text eol=lf + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary +*.gif binary +*.swf binary +*.jar binary +*.desc binary + +*.scpt binary +*.scssc binary + +# Encrypted files +*.enc binary +*.gpg binary +*.weis binary diff --git a/.github/keys/cloudrepo.properties.gpg b/.github/keys/cloudrepo.properties.gpg new file mode 100644 index 0000000000000000000000000000000000000000..1084e34e5e383b9fe954f4d61069b03e0f9b79eb GIT binary patch literal 786 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 literal 0 HcmV?d00001 diff --git a/.github/keys/firebase-sa.json.gpg b/.github/keys/firebase-sa.json.gpg new file mode 100644 index 0000000000000000000000000000000000000000..4925f29ba1fa2fd3b5c0b5f3526920b8f27ef39c GIT binary patch literal 1715 zcmV;k22A;k4Fm}T0<=-=EWVF`)%4Qp0pu|OIHTps*-w&5{=JAP@??hEGr0ZD!q*dS zbQFSQ>BPMa4hvpLlh>y$-M$Q>@IR6=f55CTxrY)5p{`dpfZBM6&og6@U{p1*@qx!B zb;`qQ@!`LzTdUu0ax8NMCCWpO0KMJVy{?Zn#RAurfnze9Llh+82@NXTioYpgqjtGjoXurxhcU(*iryflir z=-hBxPHnFcmqB)*!{RG{A^|}U%RN$k{V~f15?3WEw;fI*aaw>qXW(WhvvT0)?#6pC) z!D>UwCaOOWxak|=7}oqbg+j6?uU{wU>+aROZnv8%B4SHBiSj^|aR6eiFEh_kdSKG` z0`*{ufe3+LKJFy+y?BgI(wL&n065sf2(T1@M?A>f8!7u#c9EQB$6;{llq&~+%ECM7 z3A$*P8jHS8oPZFyuQJPOj60V`m_ga=KG#>R$k+*FM6xl7fR{J9JG5tN$1vas^-(WZ zgDj4(C8*U(zeofrN&X!T&_X9y=ApU~-H%>K1g6WU$R zR~VQ-WB%dh=a;XXDk+O8!v$uDZ-rPLx?v;#jkyMey;R=CIVey2x>ry>r@+>!dkqfV z+{MPSo;DqFk=<(2<8ZQ!!;J+OAJRK%A=3_kRnvU!V#Uc<(rtZ+|Tkxx@3`Yvvz@ zhl}k*6dc!B*dL8o3Am;y20zVD&z@(mooyDK?W)u`PwiDWnVL_}^>ZMs<9JV!a#z)x zAhliv9(?YzR!<}dSXNM~DY4G|BWc@gCmiG=Zjwu&>QlQy$HZbNJy%DlF9c@eMMNA9 zc1pCUHk<<(+D`PQup$5mVq<=@nR`OB?`I(!#tDJ6z{ay1Ca`EWbNN)kE|`Qc;&&S( zR!P}PEL~zq@*NA0r4a2BCqA#XYb=%^blfP0B?JOEtj6N%iJa+kSLel%bL2NoE6DT+ z_a}bF=b=llVJw^=3@FS3qj#=a>&2*rwGQKDpyCmo09FAolAO(PmpHne$WT^X!{uN< z*gMfnw-o`tUYCn^Ev_b9%8Aj6x2vV2OlA%i@632^ii<#{uH!0%GjD%{Sd17KgS=H? zp8#0ni))O|ZCOuCzlQ;?(oG)%qR-DBguEVh&PQ(z(l?7s2uMOCW<+MVrIcL5JzAO9 zMVlFD_|mJ9wO?o7hD9dd|2Kvpj1||WJjUvu&|Txs6M;d57C@$aMXKj z6hzrbAm`?sfg+l8VDd;Hak(eg;NvJ{X6DsOpb#ulN3qaJS4(QxMWu36b{vB3Z?F`p z&7jtwVG!^BEvU#u1Q4715TOnmJk=O>r@1nBqfs2#p}5+oJ>~}w40gr`Yr5VQh$8P4kSwCj6qHJA99db!Mp(~ivkbu J&K@T6|6*T6Ot}C6 literal 0 HcmV?d00001 diff --git a/.github/keys/gcs-auth-key.json.gpg b/.github/keys/gcs-auth-key.json.gpg new file mode 100644 index 0000000000000000000000000000000000000000..5f37d1d47655b8d6d9571cb5885b815bd0e0bee4 GIT binary patch literal 1723 zcmV;s21NOc4Fm}T0z)rQ2TF;UbC$b6u zGX}d{dkZIt>g3MalINVnQEygmK zTF|?lim047V~UTj{f1>~=8?$M1BN%j{bk1p%b94P(YYFNUZFy#4;5d^G(mxT5esY4 zt_@hd5|`3aoBSaxF_&Mhb}C;l*5&2*2=9h=WQH`~(Te~gYEBh~wv*phc*uQ;nSKb& z$6m;ntQT8qCWpEea34-7e=K5IbD@7r1qLgrb7UH+oH{UvlryUss4`gp=|~@UlRC?6 zz&%(i)YBSs%bN!^Pz(=-5P_plJi}_!^kYm)4%mmFxNFM9nJ_s}b1gY(Gcj|B4?>9b z)~wxAGGIuQ^4xx6j)aa-%GlY9vHlq@+CO@#Ug&CZ|1*N4E)lcSX6K+k%?4;=i9-f(J0mT zVLF^7pJ;nW0%W>k(O4c+cPm&Jzz8nk_07)?7pb&Hkb^4WJLCjz`)%>@En#Y`n8<5A zZIvOd*)!!R6M`1qiVVjKL z8)y#rmx%iHSE2SlTI@jKiTSqadtwnp9|6n1we93)W0|kVX|=?zGzHTxq{lA++j1sBobgM;IjKqe>h++RF9sM_$H!P&F%%(EN^ zmZTF2JO-|UNR7B&P-g7byB`D1l{5YmsT}4r%;cZm4pIK4z!?DQn)T6T2Cr18`UIjG zf}tvPir`r!74@4hSB^?{P$?TLXYFs`>?wb+@V~tXG{UdSqG@EiQU3wBpwe?CEyZ3w z$jB?|69W#1iDYTvg{xI!Mqi)OI#U4Ab3wRb+5eevJzxzR{ZS=BYnFCB0hqdb$?AH#lCnouJP>`LZje z_Gc9&0=}==={Y^Xq44$oLW4(I2zc%c{pak87EePP=OeqUt$MDuOd#XyPZxOov%WMZti&z9llzW%T4$~6fs3F>Nl7zg%xppQn3TT-Kr1yC`rHt-LE;kB zmCbHn;{Z`&+4F>rdA3a#ZX57^F5Eop63XW?fkYTj7w9kkjh|`HN+|m5*&)|Brw_PW z8D1K{+f*U;_1j_*)7_1LDQal$heCTnO&F67jLu{7I}}+nsP;a>C)`xuUBo{B#&;W+ zDTZ2RUU{a_YYM7Lf8|@(jwY_CIVyvUyT1pfF$NZ&C*9cjg;IDA8%I@8z;;SS$gAgxja7Kk zYcfPG%xE7^9Q{!r{3j&{#?1X%PUAV%Dy!z*||JTIayRg<>JTY*Nw}rH%pp#j8(iVoV%9ia_9n3u0ZoAFw9A1TqDR+I-W>=-R3Zk3*8jp)BNv2LJe%t*hV) zI&j0kCcaZY7AL&Huk~pMWR{2l2xMVmF*Cl;;T2HjMvNg{dWsb=nJQJ%={-1=RHD_1 RhvZn3bTZrD&dElfdPS-lSQ!8S literal 0 HcmV?d00001 diff --git a/.github/keys/gradle-plugin-portal.secret.properties.gpg b/.github/keys/gradle-plugin-portal.secret.properties.gpg new file mode 100644 index 0000000..8ec9a8a --- /dev/null +++ b/.github/keys/gradle-plugin-portal.secret.properties.gpg @@ -0,0 +1,2 @@ +  2.y:ҢF`J1$9 D~ze!yuGBU2K0! q =˂p@zeqB>R#qj?1m굘U^2*kJ 3wq84}. +2M< \ No newline at end of file diff --git a/.github/keys/maven-publisher.json.gpg b/.github/keys/maven-publisher.json.gpg new file mode 100644 index 0000000000000000000000000000000000000000..f5655f93a39e1a35034704e02b10d554ff1e5426 GIT binary patch literal 1727 zcmV;w20;0Y4Fm}T0@N@N)U}psko3~(0Ru*)JMg!Gl>%H?y4K|Pnt)-((2c3(@*z-K ziB=F(^93WDNggfYHYkz>URB&0&`0r#flCF^f{YqF9?d(rJRG&52}Ad=XgYy^>JUv5 zoq$@fTW?Idp5GEJyUl2Xh%VN)$oJ#AI*?CUtG_!9-oJ<{&Wn;mCa8JJ89rNNp^rJi z;MQ2qVEf;3&BO}aMdmaOsAWM_Rvay7G^zd8lbn^qSQ68e{%gD(qNiG0cMIUUI=y;o zGak(buexDF6`h!&8vU_B9gKp3?P=FY3hol$0D0CgJEs5MtE@Y$tE%=#j{i#!|MaV# zk3FEFDEn&I?0JWPh9C4+oJ)Zg@25XqEKK-1T6Xu~RSvF1RTw)!@=_PgcUnK7m?vwf z9K2z@c{aE9jL4K3jNLl4$7|^U_3y&>(t1Q;CF+s8l`a_)i}Og7LgH!P4X@svcHQw; zAZL+R?8sSOoJSyR*w2MaCTDIx?Qv^~(5}{X5fkr9_+4%v6=f2OmNTK}2UFyu`nq;6 zch+5Q`p865bl1*+0)=u;xe-qjrsX~D4SMCi#)}b}SAD2auP&JVvRskuI)8Dlg2nHY z<`Upf-*xe7)u&jeL{rR}>Mk?JzL}PJ>;n%^^cSaV!7WzZ5~)j z-+!c1Z(Z`Qok@XwOB3a9f_N)2KIG#uFJ<2mz!x!U2f8g=Jd_Ct0@M||4 z=g~)0tphTe8U9tp+2mBzg1!e#PkCp@)G0na{NW6qh73Fn9eQWsQCmakz!6GHKBJAM zF!5%Q7+PWk!C?9ZspDfM2?4(d^Fy=zP?Ob7o7tVFy0SBojp@2v%gd|3B&W@72rC{s zF3uI2^7BVf4Y_0$x1!5BQLctnyd(>$77;|s@Z26Dlp9>!aI12XJQ- z&lmf?k3xuHK<<_;-fHFBsn~CORzh4vOGpKThAzAq61eaR9E7-i`SM*E{tTNvN(BNxvK+d%js&xAcu2wS$tF+o<1l=eI3`U zMZL}M=ru+9^hMEs;{#Q-nkF3=G_y$)jD==?bNK{T@`c0j!Jfp&)maszIWUNwo$ax`DlOoG?7Xn0np zAO@*Eo0pW;C=Kj#zgHZX`8-nCs`8j&9)N?Iv}^;%`-zf)(S}ZBJD)5t{Q&fu+%xaT zbhzM5s;)Tmje4VJ7b$OeP$JQ9p|s30lFaEFoxniy0^66b(pdXi%#Jv;YVP?Fu+y~I z|ATEg52+6Q=}bx#=Lsv8KR)x9ZZ3z%_J!AEIU946L)T#HauL8%@mvQ6os&v2*g#rQ)P%)j(++Y+tUW@r=Tcv$#&9f|KwHI)UZC6seo7j3 zFs#oN+z&~1A9Vio(j;=t)pdLMFm>QuQraeC>&mfeOY87K^XHS#LEzy`Y&1wX~h zaS4Uyh$6X$q)>2)SiC*(6=C58v25uXgT#tu{y1`Z-oQ>UAq{E@sf5M*sr~BNlT~x| z_w%tlKgGLbDctFsdL=_TOHg!iMmxHe3Nw%1i`73ao;xm;=}>yQhm{8v7OR(%C8U)f V%p6JR31Gm#dC{I=Pt!o&)nmxOV0-`o literal 0 HcmV?d00001 diff --git a/.github/workflows/build-on-ubuntu.yml b/.github/workflows/build-on-ubuntu.yml new file mode 100644 index 0000000..55bb482 --- /dev/null +++ b/.github/workflows/build-on-ubuntu.yml @@ -0,0 +1,37 @@ +name: Build under Ubuntu + +on: push + +jobs: + build: + name: Build under Ubuntu + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + submodules: 'true' + + - uses: actions/setup-java@v3 + with: + java-version: 11 + distribution: zulu + cache: gradle + + - name: Build project and run tests + shell: bash + run: ./gradlew build --stacktrace + + # See: https://github.com/marketplace/actions/junit-report-action + - name: Publish Test Report + uses: mikepenz/action-junit-report@v4.0.3 + if: always() # always run even if the previous step fails + with: + report_paths: '**/build/test-results/**/TEST-*.xml' + require_tests: true # will fail workflow if test reports not found + + - name: Upload code coverage report + uses: codecov/codecov-action@v3 + with: + fail_ci_if_error: false + verbose: true diff --git a/.github/workflows/build-on-windows.yml b/.github/workflows/build-on-windows.yml new file mode 100644 index 0000000..ff947a6 --- /dev/null +++ b/.github/workflows/build-on-windows.yml @@ -0,0 +1,36 @@ +name: Build under Windows + +on: pull_request + +jobs: + build: + name: Build under Windows + runs-on: windows-latest + + steps: + - uses: actions/checkout@v3 + with: + submodules: 'true' + + - uses: actions/setup-java@v3 + with: + java-version: 11 + distribution: zulu + cache: gradle + + # See: https://github.com/al-cheb/configure-pagefile-action + - name: Configure Pagefile + uses: al-cheb/configure-pagefile-action@v1.3 + + - name: Build project and run tests + shell: cmd + # For the reason on `--no-daemon` see https://github.com/actions/cache/issues/454 + run: gradlew.bat build --stacktrace --no-daemon + + # See: https://github.com/marketplace/actions/junit-report-action + - name: Publish Test Report + uses: mikepenz/action-junit-report@v4.0.3 + if: always() # always run even if the previous step fails + with: + report_paths: '**/build/test-results/**/TEST-*.xml' + require_tests: true # will fail workflow if test reports not found diff --git a/.github/workflows/ensure-reports-updated.yml b/.github/workflows/ensure-reports-updated.yml new file mode 100644 index 0000000..f16798e --- /dev/null +++ b/.github/workflows/ensure-reports-updated.yml @@ -0,0 +1,25 @@ +# Ensures that the license report files were modified in this PR. + +name: Ensure license reports updated + +on: + pull_request: + branches: + - '**' + +jobs: + build: + name: Ensure license reports updated + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + # Configure the checkout of all branches, so that it is possible to run the comparison. + fetch-depth: 0 + # Check out the `config` submodule to fetch the required script file. + submodules: true + + - name: Check that both `pom.xml` and license report files are modified + shell: bash + run: chmod +x ./config/scripts/ensure-reports-updated.sh && ./config/scripts/ensure-reports-updated.sh diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml new file mode 100644 index 0000000..47e2938 --- /dev/null +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -0,0 +1,19 @@ +name: Validate Gradle Wrapper +on: + push: + branches: + - main + pull_request: + branches: + - '**' + +jobs: + validation: + name: Validation + runs-on: ubuntu-latest + steps: + - name: Checkout latest code + uses: actions/checkout@v3 + + - name: Validate Gradle Wrapper + uses: gradle/wrapper-validation-action@v1 diff --git a/.github/workflows/increment-guard.yml b/.github/workflows/increment-guard.yml new file mode 100644 index 0000000..b33fd74 --- /dev/null +++ b/.github/workflows/increment-guard.yml @@ -0,0 +1,29 @@ +# Ensures that the current lib version is not yet published but executing the Gradle +# `checkVersionIncrement` task. + +name: Check version increment + +on: + push: + branches: + - '**' + +jobs: + build: + name: Check version increment + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + submodules: 'true' + + - uses: actions/setup-java@v3 + with: + java-version: 11 + distribution: zulu + cache: gradle + + - name: Check version is not yet published + shell: bash + run: ./gradlew checkVersionIncrement --stacktrace diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..cadc62c --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,63 @@ +name: Publish + +on: + push: + branches: [master] + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + submodules: 'true' + + - uses: actions/setup-java@v3 + with: + java-version: 11 + distribution: zulu + cache: gradle + + - name: Decrypt CloudRepo credentials + run: ./config/scripts/decrypt.sh "$CLOUDREPO_CREDENTIALS_KEY" ./.github/keys/cloudrepo.properties.gpg ./cloudrepo.properties + env: + CLOUDREPO_CREDENTIALS_KEY: ${{ secrets.CLOUDREPO_CREDENTIALS_KEY }} + + - name: Decrypt Git SSH credentials + run: ./config/scripts/decrypt.sh "$GIT_CREDENTIALS_KEY" ./.github/keys/deploy_key_rsa.gpg ./deploy_key_rsa + env: + GIT_CREDENTIALS_KEY: ${{ secrets.GIT_CREDENTIALS_KEY }} + + # Make sure the SSH key is not "too visible". SSH agent will not accept it otherwise. + - name: Set file system permissions + run: chmod 400 ./deploy_key_rsa && chmod +x ./config/scripts/register-ssh-key.sh + + - name: Decrypt GCS credentials + run: ./config/scripts/decrypt.sh "$GCS_CREDENTIALS_KEY" ./.github/keys/gcs-auth-key.json.gpg ./gcs-auth-key.json + env: + GCS_CREDENTIALS_KEY: ${{ secrets.GCS_CREDENTIALS_KEY }} + + - name: Decrypt GCAR credentials + run: ./config/scripts/decrypt.sh "$MAVEN_PUBLISHER_KEY" ./.github/keys/maven-publisher.json.gpg ./maven-publisher.json + env: + MAVEN_PUBLISHER_KEY: ${{ secrets.MAVEN_PUBLISHER_KEY }} + + - name: Decrypt Git SSH credentials + run: ./config/scripts/decrypt.sh "$GRADLE_PORTAL_CREDENTIALS_KEY" ./.github/keys/gradle-plugin-portal.secret.properties.gpg ./gradle-plugin-portal.secret.properties + env: + GRADLE_PORTAL_CREDENTIALS_KEY: ${{ secrets.GRADLE_PORTAL_CREDENTIALS_KEY }} + + - name: Append Gradle properties + run: cat ./gradle-plugin-portal.secret.properties >> ./gradle.properties + + - name: Publish artifacts to Maven + # Since we're in the `master` branch already, this means that tests of a PR passed. + # So, no need to run the tests again when publishing. + run: ./gradlew publish -x test --stacktrace + env: + 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 + GOOGLE_APPLICATION_CREDENTIALS: ./maven-publisher.json + NPM_TOKEN: ${{ secrets.NPM_SECRET }} diff --git a/.gitignore b/.gitignore index 524f096..2acc1af 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,98 @@ -# Compiled class file -*.class +# +# 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 +# +# 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. +# -# Log file -*.log +# +# This file is used for two purposes: +# 1. ignoring files in the `config` project. +# 2. ignoring files in the projects that import `config` as a sub-module. +# +# Therefore, instructions below are superset of instructions required for all the projects. + +# IntelliJ IDEA modules and interim config files. +*.iml +.idea/*.xml +.idea/.name +.idea/artifacts +.idea/libraries +.idea/modules +.idea/shelf + +# Do not ignore the following IDEA settings +!.idea/misc.xml +!.idea/kotlinc.xml +!.idea/codeStyleSettings.xml +!.idea/codeStyles/ +!.idea/copyright/ + +# Gradle interim configs +**/.gradle/** + +# Temp directory for Gradle TestKit runners +**/.gradle-test-kit/** + +# Generated source code +**/generated/** + +# Gradle build files +**/build/** + +# Build files produced by the IDE +**/out/** + +# Ignore Gradle GUI config +gradle-app.setting -# BlueJ files -*.ctxt +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar -# Mobile Tools for Java (J2ME) -.mtj.tmp/ +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +# 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 +credentials.properties +cloudrepo.properties +deploy_key_rsa +gcs-auth-key.json + +# Log files +*.log # Package Files # -*.jar *.war -*.nar *.ear *.zip *.tar.gz @@ -21,4 +100,11 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* -replay_pid* + +.packages +pubspec.lock + +# Ignore the `tmp` directory used for building dependant repositories. +/tmp + +.gradle-test-kit/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..94e8664 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "config"] + path = config + url = https://github.com/SpineEventEngine/config diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..809943c --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,103 @@ + + + + diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..0f7bc51 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + diff --git a/.idea/copyright/TeamDev_Open_Source.xml b/.idea/copyright/TeamDev_Open_Source.xml new file mode 100644 index 0000000..14d7385 --- /dev/null +++ b/.idea/copyright/TeamDev_Open_Source.xml @@ -0,0 +1,6 @@ + + + + diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..0b8f9a1 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/.idea/dictionaries/common.xml b/.idea/dictionaries/common.xml new file mode 100644 index 0000000..e62952a --- /dev/null +++ b/.idea/dictionaries/common.xml @@ -0,0 +1,69 @@ + + + + afghani + arraybuffer + aspx + bytebuffer + callees + closeables + cqrs + dartdocs + dataset + datastore + datastores + deserialized + dirham + enrichable + enrichments + escaper + flushables + googleapis + gradle + grpc + handshaker + hohpe + idempotency + lempira + liskov + melnik + memoized + memoizes + memoizing + mergeable + mikhaylov + millisecs + multitenancy + multitenant + nullable + onclose + oneof + onmessage + onopen + parameterizing + plugable + processmanager + procman + proto's + protodata + protos + sfixed + stderr + stringifier + stringifiers + substituter + switchman + testutil + threeten + tuples + unicast + unregister + unregistering + unregisters + unregistration + websocket + workflows + yevsyukov + + + diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..35bbdff --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,911 @@ + + + + diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..9bfa224 --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/.idea/live-templates/README.md b/.idea/live-templates/README.md new file mode 100644 index 0000000..66713b3 --- /dev/null +++ b/.idea/live-templates/README.md @@ -0,0 +1,27 @@ +### Live Templates + +This directory contains two live template groups: + +1. `Spine.xml`: shortcuts for the repeated patterns used in the framework. +2. `User.xml`: a single shortcut to generate TODO comments. + +### Instlallation + +Live templates are not picked up by IDEA automatically. They should be added manually. +In order to add these templates, perform the following steps: + +1. Copy `*.xml` files from this directory to `templates` directory in the IntelliJ IDEA + [settings folder][settings_folder]. +2. Restart IntelliJ IDEA: `File -> Invalidate Caches -> Just restart`. +3. Go to `Preferences -> Editor -> Live Templates`. +4. Verify `User` and `Spine` template groups are present. + +[settings_folder]: https://www.jetbrains.com/help/idea/directories-used-by-the-ide-to-store-settings-caches-plugins-and-logs.html#config-directory + +### Configuring `User.todo` template + +1. Open the corresponding template: `Preferences -> Editor -> Live Templates -> User.todo`. +2. Click on `Edit variables`. +3. Set `USER` variable to your domain email address without `@teamdev.com` ending. For example, + for `jack.sparrow@teamdev.com` use the follwoing expression `"jack.sparrow"`. +4. Verify that the template generates expected comments: `// TODO:2022-11-03:jack.sparrow: <...>`. diff --git a/.idea/live-templates/Spine.xml b/.idea/live-templates/Spine.xml new file mode 100644 index 0000000..369b72d --- /dev/null +++ b/.idea/live-templates/Spine.xml @@ -0,0 +1,58 @@ + + + + + + + + + + diff --git a/.idea/live-templates/User.xml b/.idea/live-templates/User.xml new file mode 100644 index 0000000..cc15650 --- /dev/null +++ b/.idea/live-templates/User.xml @@ -0,0 +1,11 @@ + + + diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..a3bc764 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..0a1b5f2 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +developers@spine.io. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..2185ef6 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,37 @@ +How to contribute +================== +Thank you for wanting to contribute to Spine. The following links will help you get started: + * [Wiki home][wiki-home] — the home of the framework developer's documentation. + * [Getting started with Spine in Java][quick-start] — this guide will walk you through + a minimal client-server “Hello World!” application in Java. + * [Introduction][docs-intro] — this section of the Spine Documentation will help you understand + the foundation of the framework. + +Pull requests +------------- +The work on an improvement starts with creating an issue that describes a bug or a feature. The issue will be used for communications on the proposed improvements. +If code changes are going to be introduced, the issue should also have a link to the corresponding Pull Request. + +Code contributions should: + * Be accompanied by tests. + * Be licensed under the Apache v2.0 license with the appropriate copyright header for each file. + * Formatted according to the code style. See [Wiki home][wiki-home] for the links to + style guides of the programming languages used in the framework. + +Contributor License Agreement +----------------------------- +Contributions to the code of Spine Event Engine framework and its libraries must be accompanied by +Contributor License Agreement (CLA). + + * If you are an individual writing original source code and you're sure you own + the intellectual property, then you'll need to sign an individual CLA. + + * If you work for a company which wants you to contribute your work, + then an authorized person from your company will need to sign a corporate CLA. + +Please [contact us][legal-email] for arranging the paper formalities. + +[wiki-home]: https://github.com/SpineEventEngine/SpineEventEngine.github.io/wiki +[quick-start]: https://spine.io/docs/quick-start +[docs-intro]: https://spine.io/docs/introduction +[legal-email]: mailto:legal@teamdev.com diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..b880baa --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright {yyyy} {name of copyright owner} + +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 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/buildSrc/aus.weis b/buildSrc/aus.weis new file mode 100644 index 0000000000000000000000000000000000000000..fa72e08d3e65db39172fc760ab7fc7bdf0e3dbc9 GIT binary patch literal 236 zcmWIWW@h1H;ACK6SYCNJXcK$Pc@rR81Bf{pWEe{Fvs3f*N-9c1LpT|jqmJ)Lhz8=) z3T_5QmT!y<3@jo*h4m@=g;U?kClzPz2wioe_w2qp3(FOmPrBp|^0TJgy`h~dXvt$Q zUT!GNy?jeV^}F5Lwh6%j-s~L6mIZh-GRZOHa)<=b76t~OeGE$)K`bO^u|k}M=A-~` RRyL4AMj-SD((ND)0|0UwIyC?Q literal 0 HcmV?d00001 diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 0000000..d5e76bf --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,242 @@ +/* + * 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 + * + * 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. + */ + +/** + * This script uses two declarations of the constant [licenseReportVersion] because + * currently there is no way to define a constant _before_ a build script of `buildSrc`. + * We cannot use imports or do something else before the `buildscript` or `plugin` clauses. + */ + +plugins { + java + groovy + `kotlin-dsl` + + // https://github.com/jk1/Gradle-License-Report/releases + id("com.github.jk1.dependency-license-report").version("2.1") + + // https://github.com/johnrengelman/shadow/releases + id("com.github.johnrengelman.shadow").version("7.1.2") +} + +repositories { + mavenLocal() + gradlePluginPortal() + mavenCentral() +} + +/** + * The version of Jackson used by `buildSrc`. + * + * Please keep this value in sync. with `io.spine.internal.dependency.Jackson.version`. + * It's not a requirement, but would be good in terms of consistency. + */ +val jacksonVersion = "2.15.3" + +/** + * The version of Google Artifact Registry used by `buildSrc`. + * + * The version `2.1.5` is the latest before `2.2.0`, which introduces breaking changes. + * + * @see + * Google Artifact Registry at Maven + */ +val googleAuthToolVersion = "2.1.5" + +val licenseReportVersion = "2.1" + +val grGitVersion = "4.1.1" + +/** + * The version of the Kotlin Gradle plugin and Kotlin binaries used by the build process. + * + * This version may change from the [version of Kotlin][io.spine.internal.dependency.Kotlin.version] + * used by the project. + */ +val kotlinVersion = "1.8.22" + +/** + * The version of Guava used in `buildSrc`. + * + * Always use the same version as the one specified in [io.spine.internal.dependency.Guava]. + * Otherwise, when testing Gradle plugins, clashes may occur. + */ +val guavaVersion = "32.1.3-jre" + +/** + * The version of ErrorProne Gradle plugin. + * + * Please keep in sync. with [io.spine.internal.dependency.ErrorProne.GradlePlugin.version]. + * + * @see + * Error Prone Gradle Plugin Releases + */ +val errorPronePluginVersion = "3.1.0" + +/** + * The version of Protobuf Gradle Plugin. + * + * Please keep in sync. with [io.spine.internal.dependency.Protobuf.GradlePlugin.version]. + * + * @see + * Protobuf Gradle Plugins Releases + */ +val protobufPluginVersion = "0.9.4" + +/** + * The version of Dokka Gradle Plugins. + * + * Please keep in sync with [io.spine.internal.dependency.Dokka.version]. + * + * @see + * Dokka Releases + */ +val dokkaVersion = "1.9.10" + +/** + * The version of Detekt Gradle Plugin. + * + * @see Detekt Releases + */ +val detektVersion = "1.23.0" + +/** + * @see [io.spine.internal.dependency.Kotest] + */ +val kotestJvmPluginVersion = "0.4.10" + +/** + * @see [io.spine.internal.dependency.Kover] + */ +val koverVersion = "0.7.2" + +/** + * The version of the Shadow Plugin. + * + * `7.1.2` is the last version compatible with Gradle 7.x. Newer versions require Gradle v8.x. + * + * @see Shadow Plugin releases + */ +val shadowVersion = "7.1.2" + +configurations.all { + resolutionStrategy { + force( + "com.google.guava:guava:${guavaVersion}", + "com.google.protobuf:protobuf-gradle-plugin:$protobufPluginVersion", + + // Force Kotlin lib versions avoiding using those bundled with Gradle. + "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion", + "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion", + "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion" + ) + } +} + +val jvmVersion = JavaLanguageVersion.of(11) + +java { + toolchain.languageVersion.set(jvmVersion) +} + +tasks.withType { + kotlinOptions { + jvmTarget = jvmVersion.toString() + } +} + +dependencies { + api("com.github.jk1:gradle-license-report:$licenseReportVersion") + dependOnAuthCommon() + + listOf( + "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion", + "com.fasterxml.jackson.dataformat:jackson-dataformat-xml:$jacksonVersion", + "com.github.jk1:gradle-license-report:$licenseReportVersion", + "com.google.guava:guava:$guavaVersion", + "com.google.protobuf:protobuf-gradle-plugin:$protobufPluginVersion", + "gradle.plugin.com.github.johnrengelman:shadow:${shadowVersion}", + "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:$detektVersion", + "io.kotest:kotest-gradle-plugin:$kotestJvmPluginVersion", + // https://github.com/srikanth-lingala/zip4j + "net.lingala.zip4j:zip4j:2.10.0", + "net.ltgt.gradle:gradle-errorprone-plugin:${errorPronePluginVersion}", + "org.ajoberstar.grgit:grgit-core:${grGitVersion}", + "org.jetbrains.dokka:dokka-base:${dokkaVersion}", + "org.jetbrains.dokka:dokka-gradle-plugin:${dokkaVersion}", + "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion", + "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion", + "org.jetbrains.kotlinx:kover-gradle-plugin:$koverVersion" + ).forEach { + implementation(it) + } +} + +dependOnBuildSrcJar() + +/** + * Adds a dependency on a `buildSrc.jar`, iff: + * 1) the `src` folder is missing, and + * 2) `buildSrc.jar` is present in `buildSrc/` folder instead. + * + * This approach is used in the scope of integration testing. + */ +fun Project.dependOnBuildSrcJar() { + val srcFolder = this.rootDir.resolve("src") + val buildSrcJar = rootDir.resolve("buildSrc.jar") + if (!srcFolder.exists() && buildSrcJar.exists()) { + logger.info("Adding the pre-compiled 'buildSrc.jar' to 'implementation' dependencies.") + dependencies { + implementation(files("buildSrc.jar")) + } + } +} + +/** + * Includes the `implementation` dependency on `artifactregistry-auth-common`, + * with the version defined in [googleAuthToolVersion]. + * + * `artifactregistry-auth-common` has transitive dependency on Gson and Apache `commons-codec`. + * Gson from version `2.8.6` until `2.8.9` is vulnerable to Deserialization of Untrusted Data + * (https://devhub.checkmarx.com/cve-details/CVE-2022-25647/). + * + * Apache `commons-codec` before 1.13 is vulnerable to information exposure + * (https://devhub.checkmarx.com/cve-details/Cxeb68d52e-5509/). + * + * We use Gson `2.10.1` and we force it in `forceProductionDependencies()`. + * We use `commons-code` with version `1.16.0`, forcing it in `forceProductionDependencies()`. + * + * So, we should be safe with the current version `artifactregistry-auth-common` until + * we migrate to a later version. + */ +fun DependencyHandlerScope.dependOnAuthCommon() { + @Suppress("VulnerableLibrariesLocal", "RedundantSuppression") + implementation( + "com.google.cloud.artifactregistry:artifactregistry-auth-common:$googleAuthToolVersion" + ) { + exclude(group = "com.google.guava") + } +} diff --git a/buildSrc/src/main/kotlin/BuildExtensions.kt b/buildSrc/src/main/kotlin/BuildExtensions.kt new file mode 100644 index 0000000..9e51bca --- /dev/null +++ b/buildSrc/src/main/kotlin/BuildExtensions.kt @@ -0,0 +1,178 @@ +/* + * Copyright 2023, 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. + */ + +@file:Suppress("UnusedReceiverParameter", "unused", "TopLevelPropertyNaming", "ObjectPropertyName") + +import io.spine.internal.dependency.ErrorProne +import io.spine.internal.dependency.GradleDoctor +import io.spine.internal.dependency.Kotest +import io.spine.internal.dependency.Kover +import io.spine.internal.dependency.ProtoData +import io.spine.internal.dependency.Protobuf +import io.spine.internal.dependency.Spine +import io.spine.internal.gradle.standardToSpineSdk +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.kotlin.dsl.ScriptHandlerScope +import org.gradle.plugin.use.PluginDependenciesSpec +import org.gradle.plugin.use.PluginDependencySpec + +/** + * Applies [standard][standardToSpineSdk] repositories to this `buildscript`. + */ +fun ScriptHandlerScope.standardSpineSdkRepositories() { + repositories.standardToSpineSdk() +} + +/** + * Provides shortcuts to reference our dependency objects. + * + * Dependency objects cannot be used under `plugins` section because `io` is a value + * declared in auto-generated `org.gradle.kotlin.dsl.PluginAccessors.kt` file. + * It conflicts with our own declarations. + * + * In such cases, a shortcut to apply a plugin can be created: + * + * ``` + * val PluginDependenciesSpec.`gradle-doctor`: PluginDependencySpec + * get() = id(GradleDoctor.pluginId).version(GradleDoctor.version) + * ``` + * + * But for some plugins, it's impossible to apply them directly to a project. + * For example, when a plugin is not published to Gradle Portal, it can only be + * applied with buildscript's classpath. Thus, it's needed to leave some freedom + * upon how to apply them. In such cases, just a shortcut to a dependency object + * can be declared, without applying of the plugin in-place. + */ +private const val ABOUT_DEPENDENCY_EXTENSIONS = "" + +/** + * Shortcut to [Spine.McJava] dependency object. + * + * This plugin is not published to Gradle Portal and cannot be applied directly to a project. + * Firstly, it should be put to buildscript's classpath and then applied by ID only. + */ +val PluginDependenciesSpec.mcJava: Spine.McJava + get() = Spine.McJava + +/** + * Shortcut to [ProtoData] dependency object. + * + * This plugin is in Gradle Portal. But when used in pair with [mcJava], it cannot be applied + * directly to a project. It is so, because [mcJava] uses [protoData] as its dependency. + * And buildscript's classpath ends up with both of them. + */ +val PluginDependenciesSpec.protoData: ProtoData + get() = ProtoData + +/** + * Provides shortcuts for applying plugins from our dependency objects. + * + * Dependency objects cannot be used under `plugins` section because `io` is a value + * declared in auto-generated `org.gradle.kotlin.dsl.PluginAccessors.kt` file. + * It conflicts with our own declarations. + * + * Declaring of top-level shortcuts eliminates need in applying plugins + * using fully-qualified name of dependency objects. + * + * It is still possible to apply a plugin with a custom version, if needed. + * Just declare a version again on the returned [PluginDependencySpec]. + * + * For example: + * + * ``` + * plugins { + * protobuf version("0.8.19-custom") + * } + * ``` + */ +private const val ABOUT_PLUGIN_ACCESSORS = "" + +val PluginDependenciesSpec.errorprone: PluginDependencySpec + get() = id(ErrorProne.GradlePlugin.id) + +val PluginDependenciesSpec.protobuf: PluginDependencySpec + get() = id(Protobuf.GradlePlugin.id) + +val PluginDependenciesSpec.`gradle-doctor`: PluginDependencySpec + get() = id(GradleDoctor.pluginId).version(GradleDoctor.version) + +val PluginDependenciesSpec.kotest: PluginDependencySpec + get() = Kotest.MultiplatformGradlePlugin.let { + return id(it.id).version(it.version) + } + +val PluginDependenciesSpec.kover: PluginDependencySpec + get() = id(Kover.id).version(Kover.version) + +/** + * Configures the dependencies between third-party Gradle tasks + * and those defined via ProtoData and Spine Model Compiler. + * + * It is required in order to avoid warnings in build logs, detecting the undeclared + * usage of Spine-specific task output by other tasks, + * e.g. the output of `launchProtoData` is used by `compileKotlin`. + */ +@Suppress("unused") +fun Project.configureTaskDependencies() { + + /** + * Creates a dependency between the Gradle task of *this* name + * onto the task with `taskName`. + * + * If either of tasks does not exist in the enclosing `Project`, + * this method does nothing. + * + * This extension is kept local to `configureTaskDependencies` extension + * to prevent its direct usage from outside. + */ + fun String.dependOn(taskName: String) { + val whoDepends = this + val dependOntoTask: Task? = tasks.findByName(taskName) + dependOntoTask?.let { + tasks.findByName(whoDepends)?.dependsOn(it) + } + } + + afterEvaluate { + val launchProtoData = "launchProtoData" + val launchTestProtoData = "launchTestProtoData" + val generateProto = "generateProto" + val createVersionFile = "createVersionFile" + "compileKotlin".dependOn(launchProtoData) + "compileTestKotlin".dependOn(launchTestProtoData) + val sourcesJar = "sourcesJar" + sourcesJar.dependOn(generateProto) + sourcesJar.dependOn(launchProtoData) + sourcesJar.dependOn(createVersionFile) + sourcesJar.dependOn("prepareProtocConfigVersions") + val dokkaHtml = "dokkaHtml" + dokkaHtml.dependOn(generateProto) + dokkaHtml.dependOn(launchProtoData) + "dokkaJavadoc".dependOn(launchProtoData) + "publishPluginJar".dependOn(createVersionFile) + } +} diff --git a/buildSrc/src/main/kotlin/BuildSettings.kt b/buildSrc/src/main/kotlin/BuildSettings.kt new file mode 100644 index 0000000..1adbb92 --- /dev/null +++ b/buildSrc/src/main/kotlin/BuildSettings.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2023, 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. + */ + +import org.gradle.jvm.toolchain.JavaLanguageVersion + +/** + * This object provides high-level constants, like version of JVM, to be used + * throughout the project. + */ +object BuildSettings { + private const val JVM_VERSION = 11 + val javaVersion: JavaLanguageVersion = JavaLanguageVersion.of(JVM_VERSION) +} diff --git a/buildSrc/src/main/kotlin/DependencyResolution.kt b/buildSrc/src/main/kotlin/DependencyResolution.kt new file mode 100644 index 0000000..aa2eb71 --- /dev/null +++ b/buildSrc/src/main/kotlin/DependencyResolution.kt @@ -0,0 +1,164 @@ +/* + * Copyright 2023, 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. + */ + +import io.spine.internal.dependency.AnimalSniffer +import io.spine.internal.dependency.Asm +import io.spine.internal.dependency.AutoCommon +import io.spine.internal.dependency.AutoService +import io.spine.internal.dependency.AutoValue +import io.spine.internal.dependency.CheckerFramework +import io.spine.internal.dependency.CommonsCli +import io.spine.internal.dependency.CommonsCodec +import io.spine.internal.dependency.CommonsLogging +import io.spine.internal.dependency.Dokka +import io.spine.internal.dependency.ErrorProne +import io.spine.internal.dependency.FindBugs +import io.spine.internal.dependency.Gson +import io.spine.internal.dependency.Guava +import io.spine.internal.dependency.Hamcrest +import io.spine.internal.dependency.J2ObjC +import io.spine.internal.dependency.JUnit +import io.spine.internal.dependency.Jackson +import io.spine.internal.dependency.JavaDiffUtils +import io.spine.internal.dependency.Kotest +import io.spine.internal.dependency.Kotlin +import io.spine.internal.dependency.Okio +import io.spine.internal.dependency.OpenTest4J +import io.spine.internal.dependency.Plexus +import io.spine.internal.dependency.Protobuf +import io.spine.internal.dependency.Slf4J +import io.spine.internal.dependency.Truth +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.ConfigurationContainer +import org.gradle.api.artifacts.ResolutionStrategy + +/** + * The function to be used in `buildscript` when a fully-qualified call must be made. + */ +@Suppress("unused") +fun doForceVersions(configurations: ConfigurationContainer) { + configurations.forceVersions() +} + +/** + * Forces dependencies used in the project. + */ +fun NamedDomainObjectContainer.forceVersions() { + all { + resolutionStrategy { + failOnVersionConflict() + cacheChangingModulesFor(0, "seconds") + forceProductionDependencies() + forceTestDependencies() + forceTransitiveDependencies() + } + } +} + +private fun ResolutionStrategy.forceProductionDependencies() { + @Suppress("DEPRECATION") // Force versions of SLF4J and Kotlin libs. + force( + AnimalSniffer.lib, + AutoCommon.lib, + AutoService.annotations, + CheckerFramework.annotations, + Dokka.BasePlugin.lib, + ErrorProne.annotations, + ErrorProne.core, + FindBugs.annotations, + Gson.lib, + Guava.lib, + Kotlin.reflect, + Kotlin.stdLib, + Kotlin.stdLibCommon, + Kotlin.stdLibJdk7, + Kotlin.stdLibJdk8, + Protobuf.GradlePlugin.lib, + Protobuf.libs, + Slf4J.lib + ) +} + +private fun ResolutionStrategy.forceTestDependencies() { + force( + Guava.testLib, + JUnit.api, + JUnit.bom, + JUnit.Platform.commons, + JUnit.Platform.launcher, + JUnit.legacy, + Truth.libs, + Kotest.assertions, + ) +} + +/** + * Forces transitive dependencies of 3rd party components that we don't use directly. + */ +private fun ResolutionStrategy.forceTransitiveDependencies() { + force( + Asm.lib, + AutoValue.annotations, + CommonsCli.lib, + CommonsCodec.lib, + CommonsLogging.lib, + Gson.lib, + Hamcrest.core, + J2ObjC.annotations, + JUnit.Platform.engine, + JUnit.Platform.suiteApi, + JUnit.runner, + Jackson.annotations, + Jackson.bom, + Jackson.core, + Jackson.databind, + Jackson.dataformatXml, + Jackson.dataformatYaml, + Jackson.moduleKotlin, + JavaDiffUtils.lib, + Kotlin.jetbrainsAnnotations, + Okio.lib, + OpenTest4J.lib, + Plexus.utils, + ) +} + +@Suppress("unused") +fun NamedDomainObjectContainer.excludeProtobufLite() { + + fun excludeProtoLite(configurationName: String) { + named(configurationName).get().exclude( + mapOf( + "group" to "com.google.protobuf", + "module" to "protobuf-lite" + ) + ) + } + + excludeProtoLite("runtimeOnly") + excludeProtoLite("testRuntimeOnly") +} diff --git a/buildSrc/src/main/kotlin/DokkaExts.kt b/buildSrc/src/main/kotlin/DokkaExts.kt new file mode 100644 index 0000000..feb9eb0 --- /dev/null +++ b/buildSrc/src/main/kotlin/DokkaExts.kt @@ -0,0 +1,216 @@ +/* + * 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 + * + * 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. + */ + +import io.spine.internal.dependency.Dokka +import io.spine.internal.gradle.publish.getOrCreate +import java.io.File +import java.time.LocalDate +import org.gradle.api.Project +import org.gradle.api.artifacts.Dependency +import org.gradle.api.artifacts.dsl.DependencyHandler +import org.gradle.api.file.FileCollection +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider +import org.gradle.api.tasks.bundling.Jar +import org.gradle.configurationcache.extensions.capitalized +import org.gradle.kotlin.dsl.DependencyHandlerScope +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.DokkaBaseConfiguration +import org.jetbrains.dokka.gradle.DokkaTask +import org.jetbrains.dokka.gradle.GradleDokkaSourceSetBuilder + +/** + * To generate the documentation as seen from Java perspective, the `kotlin-as-java` + * plugin was added to the Dokka classpath. + * + * @see + * Dokka output formats + */ +fun DependencyHandlerScope.useDokkaForKotlinAsJava() { + dokkaPlugin(Dokka.KotlinAsJavaPlugin.lib) +} + +/** + * To exclude pieces of code annotated with `@Internal` from the documentation + * a custom plugin is added to the Dokka's classpath. + * + * @see + * Custom Dokka Plugins + */ +fun DependencyHandlerScope.useDokkaWithSpineExtensions() { + dokkaPlugin(Dokka.SpineExtensions.lib) +} + +private fun DependencyHandler.dokkaPlugin(dependencyNotation: Any): Dependency? = + add("dokkaPlugin", dependencyNotation) + +private fun Project.dokkaOutput(language: String): File = + buildDir.resolve("docs/dokka${language.capitalized()}") + +private fun Project.dokkaConfigFile(file: String): File { + val dokkaConfDir = project.rootDir.resolve("buildSrc/src/main/resources/dokka") + return dokkaConfDir.resolve(file) +} + +private fun DokkaTask.configureFor(language: String) { + dokkaSourceSets.configureEach { + /** + * Configures links to the external Java documentation. + */ + jdkVersion.set(BuildSettings.javaVersion.asInt()) + + skipEmptyPackages.set(true) + + documentedVisibilities.set( + setOf( + DokkaConfiguration.Visibility.PUBLIC, + DokkaConfiguration.Visibility.PROTECTED + ) + ) + } + + outputDirectory.set(project.dokkaOutput(language)) + + /** + * Dokka Base plugin allows to set a few properties to customize the output: + * + * - `customStyleSheets` property to which CSS files are passed overriding + * styles generated by Dokka; + * - `customAssets` property to provide resources. The image with the name + * "logo-icon.svg" is passed to override the default logo used by Dokka; + * - `separateInheritedMembers` when set to `true`, creates a separate tab in + * type-documentation for inherited members. + * + * @see + * Dokka modifying frontend assets + */ + pluginConfiguration { + customStyleSheets = listOf(project.dokkaConfigFile("styles/custom-styles.css")) + customAssets = listOf(project.dokkaConfigFile("assets/logo-icon.svg")) + separateInheritedMembers = true + footerMessage = "Copyright ${LocalDate.now().year}, TeamDev" + } +} + +/** + * Configures this [DokkaTask] to accept only Kotlin files. + */ +fun DokkaTask.configureForKotlin() { + configureFor("kotlin") +} + +/** + * Configures this [DokkaTask] to accept only Java files. + */ +fun DokkaTask.configureForJava() { + configureFor("java") +} + +/** + * Finds the `dokkaHtml` Gradle task. + */ +fun TaskContainer.dokkaHtmlTask(): DokkaTask? = this.findByName("dokkaHtml") as DokkaTask? + +/** + * Returns only Java source roots out of all present in the source set. + * + * It is a helper method for generating documentation by Dokka only for Java code. + * It is helpful when both Java and Kotlin source files are present in a source set. + * Dokka can properly generate documentation for either Kotlin or Java depending on + * the configuration, but not both. + */ +@Suppress("unused") +internal fun GradleDokkaSourceSetBuilder.onlyJavaSources(): FileCollection { + return sourceRoots.filter(File::isJavaSourceDirectory) +} + +private fun File.isJavaSourceDirectory(): Boolean { + return isDirectory && name == "java" +} + +/** + * Locates or creates `dokkaKotlinJar` task in this [Project]. + * + * The output of this task is a `jar` archive. The archive contains the Dokka output, generated upon + * Kotlin sources from `main` source set. Requires Dokka to be configured in the target project by + * applying `dokka-for-kotlin` plugin. + */ +fun Project.dokkaKotlinJar(): TaskProvider = tasks.getOrCreate("dokkaKotlinJar") { + archiveClassifier.set("dokka") + from(files(dokkaOutput("kotlin"))) + + tasks.dokkaHtmlTask()?.let{ dokkaTask -> + this@getOrCreate.dependsOn(dokkaTask) + } +} + +/** + * Tells if this task belongs to the execution graph which contains publishing tasks. + * + * The task `"publishToMavenLocal"` is excluded from the check because it is a part of + * the local testing workflow. + */ +fun DokkaTask.isInPublishingGraph(): Boolean = + project.gradle.taskGraph.allTasks.any { + with(it.name) { + startsWith("publish") && !startsWith("publishToMavenLocal") + } + } + +/** + * Locates or creates `dokkaJavaJar` task in this [Project]. + * + * The output of this task is a `jar` archive. The archive contains the Dokka output, generated upon + * Kotlin sources from `main` source set. Requires Dokka to be configured in the target project by + * applying `dokka-for-java` and/or `dokka-for-kotlin` script plugin. + */ +fun Project.dokkaJavaJar(): TaskProvider = tasks.getOrCreate("dokkaJavaJar") { + archiveClassifier.set("dokka-java") + from(files(dokkaOutput("java"))) + + tasks.dokkaHtmlTask()?.let{ dokkaTask -> + this@getOrCreate.dependsOn(dokkaTask) + } +} + +/** + * Disables Dokka and Javadoc tasks in this `Project`. + * + * This function could be useful to improve build speed when building subprojects containing + * test environments or integration test projects. + */ +@Suppress("unused") +fun Project.disableDocumentationTasks() { + gradle.taskGraph.whenReady { + tasks.forEach { task -> + val lowercaseName = task.name.toLowerCase() + if (lowercaseName.contains("dokka") || lowercaseName.contains("javadoc")) { + task.enabled = false + } + } + } +} diff --git a/buildSrc/src/main/kotlin/compile-protobuf.gradle.kts b/buildSrc/src/main/kotlin/compile-protobuf.gradle.kts new file mode 100644 index 0000000..baffe25 --- /dev/null +++ b/buildSrc/src/main/kotlin/compile-protobuf.gradle.kts @@ -0,0 +1,45 @@ +/* + * Copyright 2023, 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. + */ + +import io.spine.internal.dependency.Protobuf +import io.spine.internal.gradle.protobuf.setup + +plugins { + id("java-library") + id("com.google.protobuf") +} + + +// For generating test fixtures. See `src/test/proto`. +protobuf { + configurations.excludeProtobufLite() + protoc { + artifact = Protobuf.compiler + } + generateProtoTasks.all().configureEach { + setup() + } +} diff --git a/buildSrc/src/main/kotlin/config-tester.gradle.kts b/buildSrc/src/main/kotlin/config-tester.gradle.kts new file mode 100644 index 0000000..14a236c --- /dev/null +++ b/buildSrc/src/main/kotlin/config-tester.gradle.kts @@ -0,0 +1,57 @@ +/* + * Copyright 2023, 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. + */ + +import io.spine.internal.gradle.ConfigTester +import io.spine.internal.gradle.SpineRepos +import io.spine.internal.gradle.cleanFolder +import java.nio.file.Path +import java.nio.file.Paths + +// A reference to `config` to use along with the `ConfigTester`. +val config: Path = Paths.get("./") + +// A temp folder to use to check out the sources of other repositories with the `ConfigTester`. +val tempFolder = File("./tmp") + +// Creates a Gradle task which checks out and builds the selected Spine repositories +// with the local version of `config` and `config/buildSrc`. +ConfigTester(config, tasks, tempFolder) + .addRepo(SpineRepos.baseTypes) // Builds `base-types` at `master`. + .addRepo(SpineRepos.base) // Builds `base` at `master`. + .addRepo(SpineRepos.coreJava) // Builds `core-java` at `master`. + + // This is how one builds a specific branch of some repository: + // .addRepo(SpineRepos.coreJava, Branch("grpc-concurrency-fixes")) + + // Register the produced task under the selected name to invoke manually upon need. + .registerUnder("buildDependants") + +// Cleans the temp folder used to check out the sources from Git. +tasks.register("clean") { + doLast { + cleanFolder(tempFolder) + } +} diff --git a/buildSrc/src/main/kotlin/detekt-code-analysis.gradle.kts b/buildSrc/src/main/kotlin/detekt-code-analysis.gradle.kts new file mode 100644 index 0000000..89151fb --- /dev/null +++ b/buildSrc/src/main/kotlin/detekt-code-analysis.gradle.kts @@ -0,0 +1,85 @@ +/* + * Copyright 2023, 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. + */ + +import io.gitlab.arturbosch.detekt.Detekt + +/** + * This script-plugin sets up Kotlin code analyzing with Detekt. + * + * After applying, Detekt is configured to use `${rootDir}/config/quality/detekt-config.yml` file. + * Projects can append their own config files to override some parts of the default one or drop + * it at all in a favor of their own one. + * + * An example of appending a custom config file to the default one: + * + * ``` + * detekt { + * config.from("config/detekt-custom-config.yml") + * } + * ``` + * + * To totally substitute it, just overwrite the corresponding property: + * + * ``` + * detekt { + * config = files("config/detekt-custom-config.yml") + * } + * ``` + * + * Also, it's possible to suppress Detekt findings using [baseline](https://detekt.dev/docs/introduction/baseline/) + * file instead of suppressions in source code. + * + * An example of passing a baseline file: + * + * ``` + * detekt { + * baseline = file("config/detekt-baseline.yml") + * } + * ``` + */ +@Suppress("unused") +private val about = "" + +plugins { + id("io.gitlab.arturbosch.detekt") +} + +detekt { + buildUponDefaultConfig = true + config.from(files("${rootDir}/config/quality/detekt-config.yml")) +} + +tasks { + withType().configureEach { + reports { + html.required.set(true) // Only HTML report is generated. + xml.required.set(false) + txt.required.set(false) + sarif.required.set(false) + md.required.set(false) + } + } +} diff --git a/buildSrc/src/main/kotlin/dokka-for-java.gradle.kts b/buildSrc/src/main/kotlin/dokka-for-java.gradle.kts new file mode 100644 index 0000000..49ac6d3 --- /dev/null +++ b/buildSrc/src/main/kotlin/dokka-for-java.gradle.kts @@ -0,0 +1,43 @@ +/* + * 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 + * + * 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. + */ + +import org.jetbrains.dokka.gradle.DokkaTask + +plugins { + id("org.jetbrains.dokka") // Cannot use `Dokka` dependency object here yet. +} + +dependencies { + useDokkaForKotlinAsJava() + useDokkaWithSpineExtensions() +} + +tasks.withType().configureEach { + configureForJava() + onlyIf { + (it as DokkaTask).isInPublishingGraph() + } +} diff --git a/buildSrc/src/main/kotlin/dokka-for-kotlin.gradle.kts b/buildSrc/src/main/kotlin/dokka-for-kotlin.gradle.kts new file mode 100644 index 0000000..9ed226e --- /dev/null +++ b/buildSrc/src/main/kotlin/dokka-for-kotlin.gradle.kts @@ -0,0 +1,42 @@ +/* + * 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 + * + * 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. + */ + +import org.jetbrains.dokka.gradle.DokkaTask + +plugins { + id("org.jetbrains.dokka") // Cannot use `Dokka` dependency object here yet. +} + +dependencies { + useDokkaWithSpineExtensions() +} + +tasks.withType().configureEach { + configureForKotlin() + onlyIf { + (it as DokkaTask).isInPublishingGraph() + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/AnimalSniffer.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/AnimalSniffer.kt new file mode 100644 index 0000000..09b2403 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/AnimalSniffer.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +// https://www.mojohaus.org/animal-sniffer/animal-sniffer-maven-plugin/ +@Suppress("unused", "ConstPropertyName") +object AnimalSniffer { + private const val version = "1.21" + const val lib = "org.codehaus.mojo:animal-sniffer-annotations:${version}" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/ApacheHttp.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/ApacheHttp.kt new file mode 100644 index 0000000..1eff6a0 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/ApacheHttp.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +@Suppress("unused", "ConstPropertyName") +object ApacheHttp { + + // https://hc.apache.org/downloads.cgi + const val core = "org.apache.httpcomponents:httpcore:4.4.14" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/AppEngine.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/AppEngine.kt new file mode 100644 index 0000000..be15a8c --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/AppEngine.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +// https://cloud.google.com/java/docs/reference +@Suppress("unused", "ConstPropertyName") +object AppEngine { + private const val version = "1.9.82" + const val sdk = "com.google.appengine:appengine-api-1.0-sdk:${version}" + + object GradlePlugin { + private const val version = "2.2.0" + const val lib = "com.google.cloud.tools:appengine-gradle-plugin:${version}" + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Asm.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Asm.kt new file mode 100644 index 0000000..82550ef --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Asm.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +// https://asm.ow2.io/ +@Suppress("unused", "ConstPropertyName") +object Asm { + private const val version = "9.2" + const val lib = "org.ow2.asm:asm:$version" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/AssertK.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/AssertK.kt new file mode 100644 index 0000000..bfc176e --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/AssertK.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +/** + * Assertion library for tests in Kotlin + * + * [AssertK](https://github.com/willowtreeapps/assertk) + */ +@Deprecated("Please use Kotest assertions instead.") +@Suppress("unused", "ConstPropertyName") +object AssertK { + private const val version = "0.26.1" + const val libJvm = "com.willowtreeapps.assertk:assertk-jvm:${version}" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Auto.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Auto.kt new file mode 100644 index 0000000..2167439 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Auto.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2023, 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. + */ + +@file:Suppress("unused", "ConstPropertyName") + +package io.spine.internal.dependency + +// https://github.com/google/auto +object AutoCommon { + private const val version = "1.2.2" + const val lib = "com.google.auto:auto-common:${version}" +} + +// https://github.com/google/auto +object AutoService { + private const val version = "1.1.1" + const val annotations = "com.google.auto.service:auto-service-annotations:${version}" + @Suppress("unused") + const val processor = "com.google.auto.service:auto-service:${version}" +} + +// https://github.com/google/auto +object AutoValue { + private const val version = "1.10.2" + const val annotations = "com.google.auto.value:auto-value-annotations:${version}" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/BouncyCastle.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/BouncyCastle.kt new file mode 100644 index 0000000..445d57e --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/BouncyCastle.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +// https://www.bouncycastle.org/java.html +@Suppress("unused", "ConstPropertyName") +object BouncyCastle { + const val libPkcsJdk15 = "org.bouncycastle:bcpkix-jdk15on:1.68" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/CheckStyle.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/CheckStyle.kt new file mode 100644 index 0000000..d7c7988 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/CheckStyle.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +/** + * Dependencies on Checkstyle Java linter. + * + * @see Checkstyle + * @see [io.spine.internal.gradle.checkstyle.CheckStyleConfig] + */ +@Suppress("unused", "ConstPropertyName") +object CheckStyle { + /** + * The version to be used in the project. + * + * `10.12.1` is the last version in `10.12.0`, which does not introduce + * capability conflict over `google-collections` with Guava. + * + * @see Checkstyle + */ + const val version = "10.12.1" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/CheckerFramework.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/CheckerFramework.kt new file mode 100644 index 0000000..7a3993b --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/CheckerFramework.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +// https://checkerframework.org/ +@Suppress("unused", "ConstPropertyName") +object CheckerFramework { + private const val version = "3.40.0" + const val annotations = "org.checkerframework:checker-qual:${version}" + @Suppress("unused") + val dataflow = listOf( + "org.checkerframework:dataflow:${version}", + "org.checkerframework:javacutil:${version}" + ) +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/CommonsCli.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/CommonsCli.kt new file mode 100644 index 0000000..5d8c092 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/CommonsCli.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +/** + * Commons CLI is a transitive dependency which we don't use directly. + * We `force` it in [forceVersions]. + * + * [Commons CLI](https://commons.apache.org/proper/commons-cli/) + */ +@Suppress("unused", "ConstPropertyName") +object CommonsCli { + private const val version = "1.5.0" + const val lib = "commons-cli:commons-cli:${version}" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/CommonsCodec.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/CommonsCodec.kt new file mode 100644 index 0000000..641c9ee --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/CommonsCodec.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +// https://commons.apache.org/proper/commons-codec/changes-report.html +@Suppress("unused", "ConstPropertyName") +object CommonsCodec { + private const val version = "1.16.0" + const val lib = "commons-codec:commons-codec:$version" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/CommonsLogging.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/CommonsLogging.kt new file mode 100644 index 0000000..c63890f --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/CommonsLogging.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +/** + * [Commons Logging](https://commons.apache.org/proper/commons-logging/) is a transitive + * dependency which we don't use directly. This object is used for forcing the version. + */ +@Suppress("unused", "ConstPropertyName") +object CommonsLogging { + // https://commons.apache.org/proper/commons-logging/ + private const val version = "1.2" + const val lib = "commons-logging:commons-logging:${version}" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Coroutines.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Coroutines.kt new file mode 100644 index 0000000..3074c21 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Coroutines.kt @@ -0,0 +1,41 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +/** + * Kotlin Coroutines. + * + * @see GitHub projecet + */ +@Suppress("unused") +object Coroutines { + const val version = "1.6.4" + const val jdk8 = "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:$version" + const val core = "org.jetbrains.kotlinx:kotlinx-coroutines-core:$version" + const val bom = "org.jetbrains.kotlinx:kotlinx-coroutines-bom:$version" + const val coreJvm = "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:$version" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Dokka.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Dokka.kt new file mode 100644 index 0000000..6158c67 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Dokka.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +// https://github.com/Kotlin/dokka +@Suppress("unused", "ConstPropertyName") +object Dokka { + private const val group = "org.jetbrains.dokka" + + /** + * When changing the version, also change the version used in the + * `buildSrc/build.gradle.kts`. + */ + const val version = "1.9.10" + + object GradlePlugin { + const val id = "org.jetbrains.dokka" + + /** + * The version of this plugin is already specified in `buildSrc/build.gradle.kts` + * file. Thus, when applying the plugin to project's build files, only the [id] + * should be used. + */ + const val lib = "${group}:dokka-gradle-plugin:${version}" + } + + object BasePlugin { + const val lib = "${group}:dokka-base:${version}" + } + + const val analysis = "org.jetbrains.dokka:dokka-analysis:${version}" + + object CorePlugin { + const val lib = "${group}:dokka-core:${version}" + } + + /** + * To generate the documentation as seen from the Java perspective, please use this plugin. + * + * @see + * Dokka output formats + */ + object KotlinAsJavaPlugin { + const val lib = "${group}:kotlin-as-java-plugin:${version}" + } + + /** + * Custom Dokka plugins developed for Spine-specific needs like excluding by + * `@Internal` annotation. + * + * @see + * Custom Dokka Plugins + */ + object SpineExtensions { + private const val group = "io.spine.tools" + + const val version = "2.0.0-SNAPSHOT.4" + const val lib = "${group}:spine-dokka-extensions:${version}" + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/ErrorProne.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/ErrorProne.kt new file mode 100644 index 0000000..f04f863 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/ErrorProne.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +// https://errorprone.info/ +@Suppress("unused", "ConstPropertyName") +object ErrorProne { + // https://github.com/google/error-prone + private const val version = "2.23.0" + // https://github.com/tbroyer/gradle-errorprone-plugin/blob/v0.8/build.gradle.kts + private const val javacPluginVersion = "9+181-r4173-1" + + val annotations = listOf( + "com.google.errorprone:error_prone_annotations:${version}", + "com.google.errorprone:error_prone_type_annotations:${version}" + ) + const val core = "com.google.errorprone:error_prone_core:${version}" + const val checkApi = "com.google.errorprone:error_prone_check_api:${version}" + const val testHelpers = "com.google.errorprone:error_prone_test_helpers:${version}" + const val javacPlugin = "com.google.errorprone:javac:${javacPluginVersion}" + + // https://github.com/tbroyer/gradle-errorprone-plugin/releases + object GradlePlugin { + const val id = "net.ltgt.errorprone" + /** + * The version of this plugin is already specified in `buildSrc/build.gradle.kts` file. + * Thus, when applying the plugin to projects build files, only the [id] should be used. + * + * When the plugin is used as a library (e.g., in tools), its version and the library + * artifacts are of importance. + */ + const val version = "3.1.0" + const val lib = "net.ltgt.gradle:gradle-errorprone-plugin:${version}" + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/FindBugs.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/FindBugs.kt new file mode 100644 index 0000000..3c2a1af --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/FindBugs.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +/** + * The FindBugs project has been dead since 2017. It has a successor called SpotBugs, + * but we don't use it. We use ErrorProne for static analysis instead. + * The only reason for having this dependency is the annotations for null-checking + * introduced by JSR-305. These annotations are troublesome, + * but no alternatives are known for some of them so far. + * Please see [this issue](https://github.com/SpineEventEngine/base/issues/108) for more details. + */ +@Suppress("unused", "ConstPropertyName") +object FindBugs { + private const val version = "3.0.2" + const val annotations = "com.google.code.findbugs:jsr305:${version}" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Firebase.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Firebase.kt new file mode 100644 index 0000000..499c1ce --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Firebase.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +// https://firebase.google.com/docs/admin/setup#java +@Suppress("unused", "ConstPropertyName") +object Firebase { + private const val adminVersion = "8.1.0" + const val admin = "com.google.firebase:firebase-admin:${adminVersion}" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Flogger.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Flogger.kt new file mode 100644 index 0000000..cdfadb9 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Flogger.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +// https://github.com/google/flogger +@Deprecated("Please use Spine Logging library instead.") +@Suppress("unused", "ConstPropertyName") +object Flogger { + internal const val version = "0.7.4" + const val lib = "com.google.flogger:flogger:${version}" + + object Runtime { + const val systemBackend = "com.google.flogger:flogger-system-backend:${version}" + const val log4j2Backend = "com.google.flogger:flogger-log4j2-backend:${version}" + const val slf4JBackend = "com.google.flogger:flogger-slf4j-backend:${version}" + const val grpcContext = "com.google.flogger:flogger-grpc-context:${version}" + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/GoogleApis.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/GoogleApis.kt new file mode 100644 index 0000000..421915c --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/GoogleApis.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +/** + * Provides dependencies on [GoogleApis projects](https://github.com/googleapis/). + */ +@Suppress("unused", "ConstPropertyName") +object GoogleApis { + + // https://github.com/googleapis/google-api-java-client + const val client = "com.google.api-client:google-api-client:1.32.2" + + // https://github.com/googleapis/api-common-java + const val common = "com.google.api:api-common:2.1.1" + + // https://github.com/googleapis/java-common-protos + const val commonProtos = "com.google.api.grpc:proto-google-common-protos:2.7.0" + + // https://github.com/googleapis/gax-java + const val gax = "com.google.api:gax:2.7.1" + + // https://github.com/googleapis/java-iam + const val protoAim = "com.google.api.grpc:proto-google-iam-v1:1.2.0" + + // https://github.com/googleapis/google-oauth-java-client + const val oAuthClient = "com.google.oauth-client:google-oauth-client:1.32.1" + + // https://github.com/googleapis/google-auth-library-java + object AuthLibrary { + const val version = "1.3.0" + const val credentials = "com.google.auth:google-auth-library-credentials:${version}" + const val oAuth2Http = "com.google.auth:google-auth-library-oauth2-http:${version}" + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/GoogleCloud.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/GoogleCloud.kt new file mode 100644 index 0000000..5091364 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/GoogleCloud.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +@Suppress("unused", "ConstPropertyName") +object GoogleCloud { + + // https://github.com/googleapis/java-core + const val core = "com.google.cloud:google-cloud-core:2.3.3" + + // https://github.com/googleapis/java-pubsub/tree/main/proto-google-cloud-pubsub-v1 + const val pubSubGrpcApi = "com.google.api.grpc:proto-google-cloud-pubsub-v1:1.97.0" + + // https://github.com/googleapis/java-trace + const val trace = "com.google.cloud:google-cloud-trace:2.1.0" + + // https://github.com/googleapis/java-datastore + const val datastore = "com.google.cloud:google-cloud-datastore:2.2.1" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/GradleDoctor.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/GradleDoctor.kt new file mode 100644 index 0000000..707fac3 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/GradleDoctor.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +/** + * Helps optimize Gradle Builds by ensuring recommendations at build time. + * + * See [plugin site](https://runningcode.github.io/gradle-doctor) for features and usage. + */ +@Suppress("unused", "ConstPropertyName") +object GradleDoctor { + const val version = "0.8.1" + const val pluginId = "com.osacky.doctor" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Grpc.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Grpc.kt new file mode 100644 index 0000000..32d2176 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Grpc.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +// https://github.com/grpc/grpc-java +@Suppress("unused", "ConstPropertyName") +object Grpc { + @Suppress("MemberVisibilityCanBePrivate") + const val version = "1.59.0" + const val api = "io.grpc:grpc-api:${version}" + const val auth = "io.grpc:grpc-auth:${version}" + const val core = "io.grpc:grpc-core:${version}" + const val context = "io.grpc:grpc-context:${version}" + const val inProcess = "io.grpc:grpc-inprocess:${version}" + const val stub = "io.grpc:grpc-stub:${version}" + const val okHttp = "io.grpc:grpc-okhttp:${version}" + const val protobuf = "io.grpc:grpc-protobuf:${version}" + const val protobufLite = "io.grpc:grpc-protobuf-lite:${version}" + const val netty = "io.grpc:grpc-netty:${version}" + const val nettyShaded = "io.grpc:grpc-netty-shaded:${version}" + + object ProtocPlugin { + const val id = "grpc" + const val artifact = "io.grpc:protoc-gen-grpc-java:${version}" + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/GrpcKotlin.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/GrpcKotlin.kt new file mode 100644 index 0000000..a2fb7ef --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/GrpcKotlin.kt @@ -0,0 +1,43 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +/** + * gRPC-Kotlin/JVM. + * + * @see GitHub project + */ +@Suppress("unused") +object GrpcKotlin { + const val version = "1.3.0" + const val stub = "io.grpc:grpc-kotlin-stub:$version" + + object ProtocPlugin { + const val id = "grpckt" + const val artifact = "io.grpc:protoc-gen-grpc-kotlin:$version:jdk8@jar" + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Gson.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Gson.kt new file mode 100644 index 0000000..feb1008 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Gson.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +/** + * Gson is a transitive dependency, which we don't use directly. + * We `force` it in [DependencyResolution.forceConfiguration()]. + * + * [Gson](https://github.com/google/gson) + */ +@Suppress("unused", "ConstPropertyName") +object Gson { + private const val version = "2.10.1" + const val lib = "com.google.code.gson:gson:${version}" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Guava.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Guava.kt new file mode 100644 index 0000000..810a804 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Guava.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +/** + * The dependencies for Guava. + * + * When changing the version, also change the version used in the `build.gradle.kts`. We need + * to synchronize the version used in `buildSrc` and in Spine modules. Otherwise, when testing + * Gradle plugins, errors may occur due to version clashes. + * + * @see Guava at GitHub. + */ +@Suppress("unused", "ConstPropertyName") +object Guava { + private const val version = "32.1.3-jre" + const val lib = "com.google.guava:guava:${version}" + const val testLib = "com.google.guava:guava-testlib:${version}" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Hamcrest.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Hamcrest.kt new file mode 100644 index 0000000..297b969 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Hamcrest.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +/** + * The dependency on the Hamcrest, which is transitive for us. + * + * If you need assertions in Java, please use Google [Truth] instead. + * For Kotlin, please use [Kotest]. + */ +@Suppress("unused", "ConstPropertyName") +object Hamcrest { + // https://github.com/hamcrest/JavaHamcrest/releases + private const val version = "2.2" + const val core = "org.hamcrest:hamcrest-core:${version}" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/HttpClient.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/HttpClient.kt new file mode 100644 index 0000000..c481e16 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/HttpClient.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +/** + * Google implementations of [HTTP client](https://github.com/googleapis/google-http-java-client). + */ +@Suppress("unused", "ConstPropertyName") +object HttpClient { + // https://github.com/googleapis/google-http-java-client + const val version = "1.43.3" + const val google = "com.google.http-client:google-http-client:${version}" + const val jackson2 = "com.google.http-client:google-http-client-jackson2:${version}" + const val gson = "com.google.http-client:google-http-client-gson:${version}" + const val apache2 = "com.google.http-client:google-http-client-apache-v2:${version}" + + const val apache = "com.google.http-client:google-http-client-apache:2.1.2" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/IntelliJ.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/IntelliJ.kt new file mode 100644 index 0000000..1062c9b --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/IntelliJ.kt @@ -0,0 +1,89 @@ +/* + * Copyright 2023, 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. + */ + +@file:Suppress("ConstPropertyName") + +package io.spine.internal.dependency + +/** + * The components of the IntelliJ Platform. + * + * Make sure to add the `intellijReleases` and `jetBrainsCacheRedirector` + * repositories to your project. See `kotlin/Repositories.kt` for details. + */ +@Suppress("unused") +object IntelliJ { + + /** + * The version of the IntelliJ platform. + * + * This is the version used by Kotlin compiler `1.9.21`. + * Advance this version with caution because it may break the setup of + * IntelliJ platform standalone execution. + */ + const val version = "213.7172.53" + + object Platform { + private const val group = "com.jetbrains.intellij.platform" + const val core = "$group:core:$version" + const val util = "$group:util:$version" + const val coreImpl = "$group:core-impl:$version" + const val codeStyle = "$group:code-style:$version" + const val codeStyleImpl = "$group:code-style-impl:$version" + const val projectModel = "$group:project-model:$version" + const val projectModelImpl = "$group:project-model-impl:$version" + const val lang = "$group:lang:$version" + const val langImpl = "$group:lang-impl:$version" + const val ideImpl = "$group:ide-impl:$version" + const val ideCoreImpl = "$group:ide-core-impl:$version" + const val analysisImpl = "$group:analysis-impl:$version" + const val indexingImpl = "$group:indexing-impl:$version" + } + + object Jsp { + private const val group = "com.jetbrains.intellij.jsp" + @Suppress("MemberNameEqualsClassName") + const val jsp = "$group:jsp:$version" + } + + object Xml { + private const val group = "com.jetbrains.intellij.xml" + const val xmlPsiImpl = "$group:xml-psi-impl:$version" + } + + object JavaPsi { + private const val group = "com.jetbrains.intellij.java" + const val api = "$group:java-psi:$version" + const val impl = "$group:java-psi-impl:$version" + } + + object Java { + private const val group = "com.jetbrains.intellij.java" + @Suppress("MemberNameEqualsClassName") + const val java = "$group:java:$version" + const val impl = "$group:java-impl:$version" + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/J2ObjC.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/J2ObjC.kt new file mode 100644 index 0000000..54522bc --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/J2ObjC.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +/** + * [J2ObjC](https://developers.google.com/j2objc) is a transitive dependency, + * which we don't use directly. This object is used for forcing the version. + */ +@Suppress("unused", "ConstPropertyName") +object J2ObjC { + /** + * See [J2ObjC releases](https://github.com/google/j2objc/releases). + * + * `1.3` was the latest version available from Maven Central. + * Now `2.8` is the latest version available. + * As [HttpClient] + * [migrated](https://github.com/googleapis/google-http-java-client/releases/tag/v1.43.3) to v2, + * we set the latest v2 version as well. + * + * @see + * J2ObjC on Maven Central + */ + private const val version = "2.8" + const val annotations = "com.google.j2objc:j2objc-annotations:${version}" + @Deprecated("Please use `annotations` instead.", ReplaceWith("annotations")) + const val lib = annotations +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/JUnit.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/JUnit.kt new file mode 100644 index 0000000..122c657 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/JUnit.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +// https://junit.org/junit5/ +@Suppress("unused", "ConstPropertyName") +object JUnit { + const val version = "5.10.0" + private const val legacyVersion = "4.13.1" + + // https://github.com/apiguardian-team/apiguardian + private const val apiGuardianVersion = "1.1.2" + + // https://github.com/junit-pioneer/junit-pioneer + private const val pioneerVersion = "2.0.1" + + const val legacy = "junit:junit:${legacyVersion}" + + val api = listOf( + "org.apiguardian:apiguardian-api:${apiGuardianVersion}", + "org.junit.jupiter:junit-jupiter-api:${version}", + "org.junit.jupiter:junit-jupiter-params:${version}" + ) + const val bom = "org.junit:junit-bom:${version}" + + const val runner = "org.junit.jupiter:junit-jupiter-engine:${version}" + const val params = "org.junit.jupiter:junit-jupiter-params:${version}" + + const val pioneer = "org.junit-pioneer:junit-pioneer:${pioneerVersion}" + + object Platform { + // https://junit.org/junit5/ + const val version = "1.10.0" + internal const val group = "org.junit.platform" + const val commons = "$group:junit-platform-commons:$version" + const val launcher = "$group:junit-platform-launcher:$version" + const val engine = "$group:junit-platform-engine:$version" + const val suiteApi = "$group:junit-platform-suite-api:$version" + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Jackson.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Jackson.kt new file mode 100644 index 0000000..1ef088c --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Jackson.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +// https://github.com/FasterXML/jackson/wiki/Jackson-Releases +@Suppress("unused", "ConstPropertyName") +object Jackson { + const val version = "2.15.3" + private const val databindVersion = "2.15.3" + + private const val coreGroup = "com.fasterxml.jackson.core" + private const val dataformatGroup = "com.fasterxml.jackson.dataformat" + private const val moduleGroup = "com.fasterxml.jackson.module" + + // https://github.com/FasterXML/jackson-core + const val core = "$coreGroup:jackson-core:${version}" + // https://github.com/FasterXML/jackson-databind + const val databind = "$coreGroup:jackson-databind:${databindVersion}" + // https://github.com/FasterXML/jackson-annotations + const val annotations = "$coreGroup:jackson-annotations:${version}" + + // https://github.com/FasterXML/jackson-dataformat-xml/releases + const val dataformatXml = "$dataformatGroup:jackson-dataformat-xml:${version}" + // https://github.com/FasterXML/jackson-dataformats-text/releases + const val dataformatYaml = "$dataformatGroup:jackson-dataformat-yaml:${version}" + + // https://github.com/FasterXML/jackson-module-kotlin/releases + const val moduleKotlin = "$moduleGroup:jackson-module-kotlin:${version}" + + // https://github.com/FasterXML/jackson-bom + const val bom = "com.fasterxml.jackson:jackson-bom:${version}" + + // https://github.com/FasterXML/jackson-jr + object Junior { + const val version = Jackson.version + const val group = "com.fasterxml.jackson.jr" + const val objects = "$group:jackson-jr-objects:$version" + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/JavaDiffUtils.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/JavaDiffUtils.kt new file mode 100644 index 0000000..c04aedd --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/JavaDiffUtils.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +/** + * The dependency on the `java-diff-utils` library, which is transitive for us at the time + * of writing. + * + * It might become our dependency as a part of + * the [Spine Text](https://github.com/SpineEventEngine/text) library. + */ +@Suppress("unused", "ConstPropertyName") +object JavaDiffUtils { + + // https://github.com/java-diff-utils/java-diff-utils/releases + private const val version = "4.12" + const val lib = "io.github.java-diff-utils:java-diff-utils:${version}" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/JavaJwt.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/JavaJwt.kt new file mode 100644 index 0000000..a60e726 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/JavaJwt.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +/** + * A Java implementation of JSON Web Token (JWT) - RFC 7519. + * + * [Java JWT](https://github.com/auth0/java-jwt) + */ +@Suppress("unused", "ConstPropertyName") +object JavaJwt { + + /** + * The last version in the v3.x.x series. + * + * There's a v4.x.x series (e.g., https://github.com/auth0/java-jwt/releases/tag/4.4.0), but + * it introduces breaking changes. Consider upgrading to it when we're ready to migrate. + */ + private const val version = "3.19.4" + + const val lib = "com.auth0:java-jwt:${version}" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/JavaPoet.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/JavaPoet.kt new file mode 100644 index 0000000..44ddb2c --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/JavaPoet.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +// https://github.com/square/javapoet +@Suppress("unused", "ConstPropertyName") +object JavaPoet { + private const val version = "1.13.0" + const val lib = "com.squareup:javapoet:${version}" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/JavaX.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/JavaX.kt new file mode 100644 index 0000000..44cbbd5 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/JavaX.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +@Suppress("unused", "ConstPropertyName") +object JavaX { + // This artifact which used to be a part of J2EE moved under Eclipse EE4J project. + // https://github.com/eclipse-ee4j/common-annotations-api + const val annotations = "javax.annotation:javax.annotation-api:1.3.2" + + const val servletApi = "javax.servlet:javax.servlet-api:3.1.0" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Klaxon.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Klaxon.kt new file mode 100644 index 0000000..a2d81ce --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Klaxon.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +/** + * A JSON parser in Kotlin. + * + * [Klaxon](https://github.com/cbeust/klaxon) + */ +@Suppress("unused", "ConstPropertyName") +object Klaxon { + private const val version = "5.6" + const val lib = "com.beust:klaxon:${version}" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Kotest.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Kotest.kt new file mode 100644 index 0000000..f64ff9a --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Kotest.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2023, 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. + */ + +@file:Suppress("unused") + +package io.spine.internal.dependency + +/** + * Testing framework for Kotlin. + * + * @see Kotest site + */ +@Suppress("unused", "ConstPropertyName") +object Kotest { + const val version = "5.8.0" + const val group = "io.kotest" + const val assertions = "$group:kotest-assertions-core:$version" + const val runnerJUnit5 = "$group:kotest-runner-junit5:$version" + const val runnerJUnit5Jvm = "$group:kotest-runner-junit5-jvm:$version" + const val frameworkApi = "$group:kotest-framework-api:$version" + const val datatest = "$group:kotest-framework-datatest:$version" + const val frameworkEngine = "$group:kotest-framework-engine:$version" + + // https://plugins.gradle.org/plugin/io.kotest.multiplatform + object MultiplatformGradlePlugin { + const val version = Kotest.version + const val id = "io.kotest.multiplatform" + const val classpath = "$group:kotest-framework-multiplatform-plugin-gradle:$version" + } + + // https://github.com/kotest/kotest-gradle-plugin + object JvmGradlePlugin { + const val version = "0.4.10" + const val id = "io.kotest" + const val classpath = "$group:kotest-gradle-plugin:$version" + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Kotlin.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Kotlin.kt new file mode 100644 index 0000000..ec7f338 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Kotlin.kt @@ -0,0 +1,66 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +// https://github.com/JetBrains/kotlin +// https://github.com/Kotlin +@Suppress("unused", "ConstPropertyName") +object Kotlin { + + /** + * When changing the version, also change the version used in the `buildSrc/build.gradle.kts`. + */ + @Suppress("MemberVisibilityCanBePrivate") // used directly from the outside. + const val version = "1.9.23" + + /** + * The version of the JetBrains annotations library, which is a transitive + * dependency for us via Kotlin libraries. + * + * @see Java Annotations + */ + private const val annotationsVersion = "24.0.1" + + private const val group = "org.jetbrains.kotlin" + + const val stdLib = "$group:kotlin-stdlib:$version" + const val stdLibCommon = "$group:kotlin-stdlib-common:$version" + + @Deprecated("Please use `stdLib` instead.") + const val stdLibJdk7 = "$group:kotlin-stdlib-jdk7:$version" + + @Deprecated("Please use `stdLib` instead.") + const val stdLibJdk8 = "$group:kotlin-stdlib-jdk8:$version" + + const val reflect = "$group:kotlin-reflect:$version" + const val testJUnit5 = "$group:kotlin-test-junit5:$version" + + const val gradlePluginApi = "$group:kotlin-gradle-plugin-api:$version" + const val gradlePluginLib = "$group:kotlin-gradle-plugin:$version" + + const val jetbrainsAnnotations = "org.jetbrains:annotations:$annotationsVersion" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/KotlinSemver.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/KotlinSemver.kt new file mode 100644 index 0000000..c8e8029 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/KotlinSemver.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +// https://github.com/z4kn4fein/kotlin-semver +@Suppress("unused", "ConstPropertyName") +object KotlinSemver { + private const val version = "1.4.2" + const val lib = "io.github.z4kn4fein:semver:$version" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/KotlinX.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/KotlinX.kt new file mode 100644 index 0000000..f10eb45 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/KotlinX.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +@Suppress("unused", "ConstPropertyName") +object KotlinX { + + const val group = "org.jetbrains.kotlinx" + + object Coroutines { + + // https://github.com/Kotlin/kotlinx.coroutines + const val version = "1.7.3" + const val core = "$group:kotlinx-coroutines-core:$version" + const val jdk8 = "$group:kotlinx-coroutines-jdk8:$version" + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Kover.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Kover.kt new file mode 100644 index 0000000..30f81f5 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Kover.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +// https://github.com/Kotlin/kotlinx-kover +@Suppress("unused", "ConstPropertyName") +object Kover { + const val version = "0.7.4" + const val id = "org.jetbrains.kotlinx.kover" + const val classpath = "org.jetbrains.kotlinx:kover-gradle-plugin:$version" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Netty.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Netty.kt new file mode 100644 index 0000000..eefcd75 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Netty.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +@Suppress("unused", "ConstPropertyName") +object Netty { + // https://github.com/netty/netty/tags + private const val version = "4.1.100.Final" + const val common = "io.netty:netty-common:${version}" + const val buffer = "io.netty:netty-buffer:${version}" + const val transport = "io.netty:netty-transport:${version}" + const val handler = "io.netty:netty-handler:${version}" + const val codecHttp = "io.netty:netty-codec-http:${version}" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Okio.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Okio.kt new file mode 100644 index 0000000..4ec7cbf --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Okio.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +/** + * Okio is a transitive dependency, which we don't use directly. + * + * https://github.com/square/okio/tags + */ +@Suppress("unused", "ConstPropertyName") +object Okio { + private const val version = "3.6.0" + const val lib = "com.squareup.okio:okio:${version}" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/OpenTest4J.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/OpenTest4J.kt new file mode 100644 index 0000000..0b189f0 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/OpenTest4J.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +/** + * The dependency on the OpenTest4j library, which is transitive for us. + */ +@Suppress("unused", "ConstPropertyName") +object OpenTest4J { + + // https://github.com/ota4j-team/opentest4j/releases + private const val version = "1.3.0" + const val lib = "org.opentest4j:opentest4j:${version}" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/OsDetector.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/OsDetector.kt new file mode 100644 index 0000000..a11cec2 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/OsDetector.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +@Suppress("unused", "ConstPropertyName") +object OsDetector { + // https://github.com/google/osdetector-gradle-plugin + const val version = "1.7.3" + const val id = "com.google.osdetector" + const val lib = "com.google.gradle:osdetector-gradle-plugin:${version}" + const val classpath = lib +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Plexus.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Plexus.kt new file mode 100644 index 0000000..19305a1 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Plexus.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2023, 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. + */ + +@file:Suppress("MaxLineLength") + +package io.spine.internal.dependency + +/** + * Plexus Utils is a transitive dependency, which we don't use directly. + * + * [Plexus Utils](https://github.com/codehaus-plexus/plexus-utils) + */ +@Suppress("unused", "ConstPropertyName") +object Plexus { + + /** + * This is the last version in the 3.x series. + * + * There's a major update to 4.x. + * + * @see plexus-utils-4.0.0 + */ + private const val version = "4.0.0" + const val utils = "org.codehaus.plexus:plexus-utils:${version}" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Pmd.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Pmd.kt new file mode 100644 index 0000000..8d27f55 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Pmd.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2023, 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. + */ + +@file:Suppress("MaxLineLength") + +package io.spine.internal.dependency + +// https://github.com/pmd/pmd/releases +@Suppress("unused", "ConstPropertyName") +object Pmd { + /** + * This is the last version in the 6.x series. + * + * There's a major update to 7.x series. + * + * @see spine-base + */ + const val base = "2.0.0-SNAPSHOT.199" + + /** + * The version of [Spine.reflect]. + * + * @see spine-reflect + */ + const val reflect = "2.0.0-SNAPSHOT.183" + + /** + * The version of [Spine.Logging]. + * + * @see spine-logging + */ + const val logging = "2.0.0-SNAPSHOT.233" + + /** + * The version of [Spine.testlib]. + * + * @see spine-testlib + */ + const val testlib = "2.0.0-SNAPSHOT.184" + + /** + * The version of `core-java`. + * + * @see [Spine.CoreJava.client] + * @see [Spine.CoreJava.server] + * @see core-java + */ + const val core = "2.0.0-SNAPSHOT.176" + + /** + * The version of [Spine.modelCompiler]. + * + * @see spine-model-compiler + */ + const val mc = "2.0.0-SNAPSHOT.133" + + /** + * The version of [McJava]. + * + * @see spine-mc-java + */ + const val mcJava = "2.0.0-SNAPSHOT.205" + + /** + * The version of [Spine.baseTypes]. + * + * @see spine-base-types + */ + const val baseTypes = "2.0.0-SNAPSHOT.126" + + /** + * The version of [Spine.time]. + * + * @see spine-time + */ + const val time = "2.0.0-SNAPSHOT.135" + + /** + * The version of [Spine.change]. + * + * @see spine-change + */ + const val change = "2.0.0-SNAPSHOT.118" + + /** + * The version of [Spine.text]. + * + * @see spine-text + */ + const val text = "2.0.0-SNAPSHOT.6" + + /** + * The version of [Spine.toolBase]. + * + * @see spine-tool-base + */ + const val toolBase = "2.0.0-SNAPSHOT.208" + + /** + * The version of [Spine.javadocTools]. + * + * @see spine-javadoc-tools + */ + const val javadocTools = "2.0.0-SNAPSHOT.75" + } + + const val base = "$group:spine-base:${ArtifactVersion.base}" + + const val reflect = "$group:spine-reflect:${ArtifactVersion.reflect}" + const val baseTypes = "$group:spine-base-types:${ArtifactVersion.baseTypes}" + const val time = "$group:spine-time:${ArtifactVersion.time}" + const val change = "$group:spine-change:${ArtifactVersion.change}" + const val text = "$group:spine-text:${ArtifactVersion.text}" + + const val testlib = "$toolsGroup:spine-testlib:${ArtifactVersion.testlib}" + const val testUtilTime = "$toolsGroup:spine-testutil-time:${ArtifactVersion.time}" + const val psiJava = "$toolsGroup:spine-psi-java:${ArtifactVersion.toolBase}" + const val psiJavaBundle = "$toolsGroup:spine-psi-java-bundle:${ArtifactVersion.toolBase}" + const val toolBase = "$toolsGroup:spine-tool-base:${ArtifactVersion.toolBase}" + const val pluginBase = "$toolsGroup:spine-plugin-base:${ArtifactVersion.toolBase}" + const val pluginTestlib = "$toolsGroup:spine-plugin-testlib:${ArtifactVersion.toolBase}" + const val modelCompiler = "$toolsGroup:spine-model-compiler:${ArtifactVersion.mc}" + + /** + * Dependencies on the artifacts of the Spine Logging library. + * + * @see spine-logging + */ + object Logging { + const val version = ArtifactVersion.logging + const val lib = "$group:spine-logging:$version" + + const val log4j2Backend = "$group:spine-logging-log4j2-backend:$version" + const val stdContext = "$group:spine-logging-std-context:$version" + const val grpcContext = "$group:spine-logging-grpc-context:$version" + const val smokeTest = "$group:spine-logging-smoke-test:$version" + + // Transitive dependencies. + // Make `public` and use them to force a version in a particular repository, if needed. + internal const val julBackend = "$group:spine-logging-jul-backend:$version" + internal const val middleware = "$group:spine-logging-middleware:$version" + internal const val platformGenerator = "$group:spine-logging-platform-generator:$version" + internal const val jvmDefaultPlatform = "$group:spine-logging-jvm-default-platform:$version" + + @Deprecated( + message = "Please use `Logging.lib` instead.", + replaceWith = ReplaceWith("lib") + ) + const val floggerApi = "$group:spine-flogger-api:$version" + + @Deprecated( + message = "Please use `grpcContext` instead.", + replaceWith = ReplaceWith("grpcContext") + ) + const val floggerGrpcContext = "$group:spine-flogger-grpc-context:$version" + } + + /** + * Dependencies on Spine Model Compiler for Java. + * + * See [mc-java](https://github.com/SpineEventEngine/mc-java). + */ + @Suppress("MemberVisibilityCanBePrivate") // `pluginLib()` is used by subprojects. + object McJava { + const val version = ArtifactVersion.mcJava + const val pluginId = "io.spine.mc-java" + val pluginLib = pluginLib(version) + fun pluginLib(version: String): String = "$toolsGroup:spine-mc-java-plugins:$version:all" + } + + @Deprecated("Please use `javadocFilter` instead.", ReplaceWith("javadocFilter")) + const val javadocTools = "$toolsGroup::${ArtifactVersion.javadocTools}" + const val javadocFilter = "$toolsGroup:spine-javadoc-filter:${ArtifactVersion.javadocTools}" + + const val client = CoreJava.client // Added for brevity. + const val server = CoreJava.server // Added for brevity. + + /** + * Dependencies on `core-java` modules. + * + * See [`SpineEventEngine/core-java`](https://github.com/SpineEventEngine/core-java/). + */ + object CoreJava { + const val version = ArtifactVersion.core + const val core = "$group:spine-core:$version" + const val client = "$group:spine-client:$version" + const val server = "$group:spine-server:$version" + const val testUtilServer = "$toolsGroup:spine-testutil-server:$version" + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/TestKitTruth.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/TestKitTruth.kt new file mode 100644 index 0000000..a345b0f --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/TestKitTruth.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2023, 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. + */ + +@file:Suppress("MaxLineLength") + +package io.spine.internal.dependency + +/** + * Gradle TestKit extension for Google Truth. + * + * @see TestKit source code + * @see Usage description + */ +@Suppress("unused", "ConstPropertyName") +object TestKitTruth { + private const val version = "1.20.0" + const val lib = "com.autonomousapps:testkit-truth:$version" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Truth.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Truth.kt new file mode 100644 index 0000000..3135417 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Truth.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +// https://github.com/google/truth +@Suppress("unused", "ConstPropertyName") +object Truth { + private const val version = "1.1.5" + val libs = listOf( + "com.google.truth:truth:${version}", + "com.google.truth.extensions:truth-java8-extension:${version}", + "com.google.truth.extensions:truth-proto-extension:${version}" + ) +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Validation.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Validation.kt new file mode 100644 index 0000000..ded15d8 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Validation.kt @@ -0,0 +1,64 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.dependency + +/** + * Dependencies on Spine Validation SDK. + * + * See [`SpineEventEngine/validation`](https://github.com/SpineEventEngine/validation/). + */ +@Suppress("unused", "ConstPropertyName") +object Validation { + /** + * The version of the Validation library artifacts. + */ + const val version = "2.0.0-SNAPSHOT.132" + + /** + * The distinct version of the Validation library used by build tools during + * the transition from a previous version when breaking API changes are introduced. + * + * When Validation is used both for building the project and as a part of the project's + * transitional dependencies, this is the version used to build the project itself to + * avoid errors caused by incompatible API changes. + */ + const val dogfoodingVersion = "2.0.0-SNAPSHOT.132" + + const val group = "io.spine.validation" + private const val prefix = "spine-validation" + + const val runtime = "$group:$prefix-java-runtime:$version" + const val java = "$group:$prefix-java:$version" + + /** Obtains the artifact for the `java-bundle` artifact of the given version. */ + fun javaBundle(version: String) = "$group:$prefix-java-bundle:$version" + + val javaBundle = javaBundle(version) + + const val model = "$group:$prefix-model:$version" + const val config = "$group:$prefix-configuration:$version" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/Build.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/Build.kt new file mode 100644 index 0000000..f1c0331 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/Build.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle + +@Suppress("unused") +object Build { + val ci = "true".equals(System.getenv("CI")) +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/Clean.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/Clean.kt new file mode 100644 index 0000000..1f9b1cc --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/Clean.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle + +import java.io.File +import java.nio.file.Files +import java.nio.file.Path + +/** + * Cleans the folder and all of its content. + */ +fun cleanFolder(folder: File) { + if(!folder.exists()) { + return + } + if(!folder.isDirectory) { + throw IllegalArgumentException("A folder to clean " + + "must be supplied: `${folder.absolutePath}`.") + } + Files.walk(folder.toPath()) + .sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete) +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/ConfigTester.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/ConfigTester.kt new file mode 100644 index 0000000..65e00ad --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/ConfigTester.kt @@ -0,0 +1,363 @@ +/* + * Copyright 2023, 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. + */ + +@file:Suppress("unused") /* Some constants may be used throughout the Spine repos. */ + +package io.spine.internal.gradle + +import java.io.File +import java.net.URI +import java.nio.file.Files +import java.nio.file.Path +import org.ajoberstar.grgit.Grgit +import org.gradle.api.tasks.TaskContainer + +/** + * A tool to execute the Gradle `build` task in selected Git repositories + * with the local version of [config] contents. + * + * Checks out the content of selected repositories into the specified [tempFolder]. The folder + * is created if it does not exist. By default, uses `./tmp` as a temp folder. + * + * Replaces the `config` and `buildSrc` folders in the checked out repository by the local versions + * of code. If the repository-under-test already contains its own `buildSrc` or `config` folders, + * they are NOT overwritten, but rather renamed into `buildSrc-original` and `config-original` + * accordingly. This allows further tracing if the build fails. + * + * Uses Gradle's [tasks] container to register itself as a Gradle task. + * + * This tool uses `println`s to print out its state. This is done to simplify the configuration + * and dependencies. + * + * When running the Gradle build for each repository, a [RunBuild] task is used. Error and debug + * logs of each Gradle test build are written according to this task's implementation. + */ +class ConfigTester( + private val config: Path, + private val tasks: TaskContainer, + private val tempFolder: File = File("./tmp") +) { + + companion object { + + /** + * Gradle build timeout. + */ + private const val BUILD_TIMEOUT_MINUTES = 30L + } + + private val buildSrc: Path = config.resolve("buildSrc") + + /** + * Git repositories to test. + */ + private val repos: MutableList = ArrayList() + + /** + * Adds a Git [repo] into the test build by its URI. + * + * The `master` branch is used as the one to checkout. + */ + fun addRepo(repo: URI): ConfigTester { + repos.add(GitRepository(repo)) + return this + } + + /** + * Adds a test + */ + fun addRepo(repo: URI, branch: Branch): ConfigTester { + repos.add(GitRepository(repo, branch)) + return this + } + + fun registerUnder(taskName: String) { + val tasksPerRepo = repos.map { testWithConfig(it) } + + tasks.register(taskName) { + for (repoTaskName in tasksPerRepo) { + dependsOn(repoTaskName) + } + } + } + + private fun testWithConfig(gitRepo: GitRepository): String { + val runGradleName = runGradleTask(gitRepo) + doRegisterRunBuild(runGradleName, gitRepo) + + val executeBuildName = executeBuildTask(gitRepo) + doRegisterExecuteBuild(executeBuildName, gitRepo, runGradleName) + return executeBuildName + } + + private fun doRegisterExecuteBuild( + executeBuildName: String, + gitRepo: GitRepository, + runGradleName: String + ) { + tasks.register(executeBuildName) { + doLast { + println(" *** Testing `config` and `config/buildSrc` with `${gitRepo.name}`. ***") + val ignoredFolder = tempFolder.toPath() + gitRepo.checkout(tempFolder) + .replaceBuildSrc(buildSrc, ignoredFolder).replaceConfig(config, ignoredFolder) + } + finalizedBy(runGradleName) + } + } + + private fun doRegisterRunBuild( + runGradleName: String, + gitRepo: GitRepository, + ) { + tasks.register(runGradleName, RunBuild::class.java) { + doFirst { + println("`${gitRepo.name}`: starting Gradle build...") + } + doLast { + println("*** `${gitRepo.name}`: Gradle build completed. ***") + } + directory = gitRepo.prepareCheckout(tempFolder).absolutePath + maxDurationMins = BUILD_TIMEOUT_MINUTES + } + } + + private fun runGradleTask(repo: GitRepository): String { + return "run-gradle-${repo.name}" + } + + private fun executeBuildTask(repo: GitRepository): String { + return "execute-build-${repo.name}" + } +} + +/** + * A repository of source code hosted using Git. + */ +class GitRepository( + + /** + * URI pointing to the location of the repository. + */ + private val uri: URI, + + /** + * A branch to checkout. + * + * By default, points to `master`. + */ + private val branch: Branch = Branch("master"), +) { + /** + * The name of this repository. + */ + val name: String + + init { + name = repoName(uri) + } + + fun prepareCheckout(destinationFolder: File): File { + if (!destinationFolder.exists()) { + destinationFolder.mkdirs() + } + + val result = destinationFolder.toPath().resolve(name) + Files.createDirectories(result) + return result.toFile() + } + + /** + * Performs the checkout of the source code for this repository + * 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 + * the `destinationFolder/foobar` folder. + * + * If the supplied folder does not exist, it is created. + */ + fun checkout(destinationFolder: File): ClonedRepo { + val preparedFolder = prepareCheckout(destinationFolder).toPath() + println( + "Checking out the `$uri` repository at `${branch.name}` " + + "to `${preparedFolder.toAbsolutePath()}`." + ) + + Grgit.clone( + mapOf( + "dir" to preparedFolder, + "uri" to uri + ) + ).checkout( + mapOf( + "branch" to branch.name + ) + ) + return ClonedRepo(this, preparedFolder) + } + + private fun repoName(resourceLocation: URI): String { + var path = resourceLocation.path + if (path.endsWith('/')) { + path = path.substring(0, path.length - 1) + } + val fromLastSlash = path.lastIndexOf('/') + 1 + val repoName = path.substring(fromLastSlash) + return repoName + } + + /** + * Returns a new Git repository pointing to some particular Git [branch]. + */ + fun at(branch: Branch): GitRepository { + return GitRepository(uri, branch) + } +} + +/** + * The cloned Git repository. + */ +class ClonedRepo( + + /** + * Origin Git repository which is cloned. + */ + private val repo: GitRepository, + + /** + * The location into which the [repo] is cloned. + */ + private val location: Path +) { + + /** + * Replaces the `buildSrc` folder in this cloned repository by the contents + * of the folder defined by the [source]. + * + * [source] is expected to be another `buildSrc` folder. + * + * The original `buildSrc` folder, if it exists in this cloned repo, is renamed + * to `buildSrc-original`. + * + * Optionally, takes an [ignoredFolder] which will be excluded from the [source] paths + * when copying. + * + * + * Returns this instance of `ClonedRepo`, for call chaining. + */ + fun replaceBuildSrc(source: Path, ignoredFolder: Path?): ClonedRepo { + replaceFolder("buildSrc", source, ignoredFolder) + return this + } + + /** + * Replaces the `config` folder in this cloned repository by the contents + * of the folder defined by the [source]. + * + * [source] is expected to be another `config` folder. + * + * The original `config` folder, if it exists in this cloned repo, is renamed + * to `config-original`. + * + * Optionally, takes an [ignoredFolder] which will be excluded from the [source] paths + * when copying. + * + * Returns this instance of `ClonedRepo`, for call chaining. + */ + fun replaceConfig(source: Path, ignoredFolder: Path?): ClonedRepo { + replaceFolder("config", source, ignoredFolder) + return this + } + + private fun replaceFolder(folderName: String, source: Path, ignoredFolder: Path?) { + val folder = location.resolve(folderName) + val rawFolder = folder.toFile() + if (rawFolder.exists() && rawFolder.isDirectory) { + val toRenameInto = location.resolve(folderName + "-original") + println("Renaming ${folder.toAbsolutePath()} into ${toRenameInto.toAbsolutePath()}.") + rawFolder.renameTo(toRenameInto.toFile()) + } + println( + "Copying the files from ${source.toAbsolutePath()} " + + "into ${folder.toAbsolutePath()}." + ) + copyFolder(source, ignoredFolder, folder) + } + + @Suppress("TooGenericExceptionCaught") + private fun copyFolder(sourceFolder: Path, ignoredFolder: Path?, destinationFolder: Path) { + try { + copyRecursively(sourceFolder, ignoredFolder, destinationFolder) + } catch (e: Exception) { + throw IllegalStateException( + "Error copying folder `$sourceFolder` to `$destinationFolder`.", e + ) + } + } + + private fun copyRecursively(sourceFolder: Path, ignoredFolder: Path?, destinationFolder: Path) { + fun Path.isIgnored(): Boolean = ignoredFolder + ?.let { toAbsolutePath().startsWith(it.toAbsolutePath()) } + ?: false + + val flattenedTree = Files.walk(sourceFolder).filter { it.isIgnored().not() } + val filesToDestinations = flattenedTree.map { file -> + val destination = destinationFolder.resolve(sourceFolder.relativize(file)) + file to destination + } + + val directories = filesToDestinations.filter { Files.isDirectory(it.first) } + directories.forEach { Files.createDirectories(it.second) } + + val files = filesToDestinations.filter { Files.isDirectory(it.first).not() } + files.forEach { Files.copy(it.first, it.second) } + } +} + +/** + * Spine repositories at GitHub. + * + * The list is expected to grow over time. + */ +object SpineRepos { + + const val libsOrg: String = "https://github.com/SpineEventEngine/" + const val examplesOrg: String = "https://github.com/spine-examples/" + + val base: URI = library("base") + val baseTypes: URI = library("base-types") + val coreJava: URI = library("core-java") + val web: URI = library("web") + + private fun library(repo: String) = URI(libsOrg + repo) + private fun example(repo: String) = URI(examplesOrg + repo) +} + +/** + * A name of a Git branch. + */ +data class Branch(val name: String) diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/ProjectExtensions.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/ProjectExtensions.kt new file mode 100644 index 0000000..2ef4bd8 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/ProjectExtensions.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle + +import io.spine.internal.gradle.publish.SpinePublishing +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.tasks.SourceSetContainer +import org.gradle.kotlin.dsl.findByType +import org.gradle.kotlin.dsl.getByType + +/** + * This file contains extension methods and properties for the Gradle `Project`. + */ + +/** + * Obtains the Java plugin extension of the project. + */ +val Project.javaPluginExtension: JavaPluginExtension + get() = extensions.getByType() + +/** + * Obtains source set container of the Java project. + */ +val Project.sourceSets: SourceSetContainer + get() = javaPluginExtension.sourceSets + +/** + * Applies the specified Gradle plugin to this project by the plugin [class][cls]. + */ +fun Project.applyPlugin(cls: Class>) { + this.apply { + plugin(cls) + } +} + +/** + * Finds the task of type `T` in this project by the task name. + * + * The task must be present. Also, a caller is responsible for using the proper value of + * the generic parameter `T`. + */ +@Suppress("UNCHECKED_CAST") /* See the method docs. */ +fun Project.findTask(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 +} + +/** + * Obtains Maven artifact ID of this [Project]. + * + * The method checks if [SpinePublishing] extension is configured upon this project. If yes, + * returns [SpinePublishing.artifactId] for the project. Otherwise, a project's name is returned. + */ +val Project.artifactId: String + get() { + + // Publishing of a project can be configured either from the project itself or + // from its root project. This is why it is required to check both places. + + val spinePublishing = extensions.findByType() + ?: rootProject.extensions.findByType() + + val artifactId = spinePublishing?.artifactId(this) + return artifactId ?: name + } diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/RepoSlug.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/RepoSlug.kt new file mode 100644 index 0000000..b713a3b --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/RepoSlug.kt @@ -0,0 +1,66 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle + +import org.gradle.api.GradleException + +/** + * A name of a repository. + */ +class RepoSlug(val value: String) { + + companion object { + + /** + * The name of the environment variable containing the repository slug, for which + * the Gradle build is performed. + */ + private const val environmentVariable = "REPO_SLUG" + + /** + * Reads `REPO_SLUG` environment variable and returns its value. + * + * In case it is not set, a [GradleException] is thrown. + */ + fun fromVar(): RepoSlug { + val envValue = System.getenv(environmentVariable) + if (envValue.isNullOrEmpty()) { + throw GradleException("`REPO_SLUG` environment variable is not set.") + } + return RepoSlug(envValue) + } + } + + override fun toString(): String = value + + /** + * Returns the GitHub URL to the project repository. + */ + fun gitHost(): String { + return "git@github.com-publish:${value}.git" + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/Repositories.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/Repositories.kt new file mode 100644 index 0000000..6d04ae7 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/Repositories.kt @@ -0,0 +1,370 @@ +/* + * Copyright 2023, 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. + */ + +@file:Suppress("TooManyFunctions") // Deprecated functions will be kept for a while. + +package io.spine.internal.gradle + +import io.spine.internal.gradle.publish.CloudRepo +import io.spine.internal.gradle.publish.PublishingRepos +import io.spine.internal.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/internal/gradle/RunBuild.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/RunBuild.kt new file mode 100644 index 0000000..a39ffed --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/RunBuild.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle + +/** + * Runs the `build` task via Gradle Wrapper. + */ +open class RunBuild : RunGradle() { + + init { + task("build") + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/RunGradle.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/RunGradle.kt new file mode 100644 index 0000000..926b237 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/RunGradle.kt @@ -0,0 +1,188 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle + +import java.io.File +import java.io.FileOutputStream +import java.util.concurrent.TimeUnit +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.TaskAction +import org.gradle.internal.os.OperatingSystem + +/** + * A Gradle task which runs another Gradle build. + * + * Launches Gradle wrapper under a given [directory] with the specified [taskNames] names. + * The `clean` task is also run if current build includes a `clean` task. + * + * The build writes verbose log into `$directory/build/debug-out.txt`. + * The error output is written into `$directory/build/error-out.txt`. + */ +@Suppress("unused") +open class RunGradle : DefaultTask() { + + companion object { + + /** + * Default Gradle build timeout. + */ + private const val BUILD_TIMEOUT_MINUTES: Long = 10 + } + + /** + * Path to the directory which contains a Gradle wrapper script. + */ + @Internal + lateinit var directory: String + + /** + * The names of the tasks to be passed to the Gradle Wrapper script. + */ + private lateinit var taskNames: List + + /** + * For how many minutes to wait for the Gradle build to complete. + */ + @Internal + var maxDurationMins: Long = BUILD_TIMEOUT_MINUTES + + /** + * Names of Gradle properties to copy into the launched build. + * + * The properties are looked up in the root project. If a property is not found, it is ignored. + * + * See [Gradle doc](https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties) + * for more info about Gradle properties. + */ + @Internal + var includeGradleProperties: MutableSet = mutableSetOf() + + /** + * Specifies task names to be passed to the Gradle Wrapper script. + */ + fun task(vararg tasks: String) { + taskNames = tasks.asList() + } + + /** + * Sets the maximum time to wait until the build completion in minutes + * and specifies task names to be passed to the Gradle Wrapper script. + */ + fun task(maxDurationMins: Long, vararg tasks: String) { + taskNames = tasks.asList() + this.maxDurationMins = maxDurationMins + } + + @TaskAction + private fun execute() { + // Ensure build error output log. + // Since we're executing this task in another process, we redirect error output to + // the file under the `_out` directory. Using the `build` directory for this purpose + // proved to cause problems under Windows when executing the `clean` command, which + // fails because another process holds files. + val buildDir = File(directory, "_out") + if (!buildDir.exists()) { + buildDir.mkdir() + } + val errorOut = File(buildDir, "error-out.txt") + errorOut.truncate() + val debugOut = File(buildDir, "debug-out.txt") + debugOut.truncate() + + val command = buildCommand() + val process = startProcess(command, errorOut, debugOut) + + /* The timeout is set because of Gradle process execution under Windows. + See the following locations for details: + https://github.com/gradle/gradle/pull/8467#issuecomment-498374289 + https://github.com/gradle/gradle/issues/3987 + https://discuss.gradle.org/t/weirdness-in-gradle-exec-on-windows/13660/6 + */ + val completed = process.waitFor(maxDurationMins, TimeUnit.MINUTES) + val exitCode = process.exitValue() + if (!completed || exitCode != 0) { + val errorOutExists = errorOut.exists() + if (errorOutExists) { + logger.error(errorOut.readText()) + } + throw GradleException("Child build process FAILED." + + " Exit code: $exitCode." + + if (errorOutExists) " See $errorOut for details." + else " $errorOut file was not created." + ) + } + } + + private fun buildCommand(): List { + val script = buildScript() + val command = mutableListOf() + command.add("${project.rootDir}/$script") + val shouldClean = project.gradle + .taskGraph + .hasTask(":clean") + if (shouldClean) { + command.add("clean") + } + command.addAll(taskNames) + command.add("--console=plain") + command.add("--debug") + command.add("--stacktrace") + command.add("--no-daemon") + addProperties(command) + return command + } + + private fun addProperties(command: MutableList) { + val rootProject = project.rootProject + includeGradleProperties + .filter { rootProject.hasProperty(it) } + .map { name -> name to rootProject.property(name).toString() } + .forEach { (name, value) -> command.add("-P$name=$value") } + } + + private fun buildScript(): String { + val runsOnWindows = OperatingSystem.current().isWindows + return if (runsOnWindows) "gradlew.bat" else "gradlew" + } + + private fun startProcess(command: List, errorOut: File, debugOut: File) = + ProcessBuilder() + .command(command) + .directory(project.file(directory)) + .redirectError(errorOut) + .redirectOutput(debugOut) + .start() +} + +private fun File.truncate() { + val stream = FileOutputStream(this) + stream.use { + it.channel.truncate(0) + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/Runtime.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/Runtime.kt new file mode 100644 index 0000000..6594a2a --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/Runtime.kt @@ -0,0 +1,99 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle + +import java.io.File +import java.io.InputStream +import java.io.StringWriter +import java.lang.ProcessBuilder.Redirect.PIPE +import java.util.* + +/** + * Utilities for working with processes from Gradle code. + */ +@Suppress("unused") +private const val ABOUT = "" + +/** + * Executor of CLI commands. + * + * Uses the passed [workingFolder] as the directory in which the commands are executed. + */ +class Cli(private val workingFolder: File) { + + /** + * Executes the given terminal command and retrieves the command output. + * + *

{@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. + * + * @param command the command to execute + * @return the command line output + * @throws IllegalStateException upon an execution error + */ + fun execute(vararg command: String): String { + val outWriter = StringWriter() + val errWriter = StringWriter() + + val process = ProcessBuilder(*command).apply { + directory(workingFolder) + redirectOutput(PIPE) + redirectError(PIPE) + }.start() + + val exitCode = process.run { + inputStream!!.pourTo(outWriter) + errorStream!!.pourTo(errWriter) + waitFor() + } + + if (exitCode == 0) { + return outWriter.toString() + } else { + val commandLine = command.joinToString(" ") + val nl = System.lineSeparator() + val errorMsg = "Command `$commandLine` finished with exit code $exitCode:" + + "$nl$errWriter" + + "$nl$outWriter." + throw IllegalStateException(errorMsg) + } + } +} + +/** + * Asynchronously reads all lines from this [InputStream] and appends them + * to the passed [StringWriter]. + */ +fun InputStream.pourTo(dest: StringWriter) { + Thread { + val sc = Scanner(this) + while (sc.hasNextLine()) { + dest.append(sc.nextLine()) + } + }.start() +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/StringExtensions.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/StringExtensions.kt new file mode 100644 index 0000000..9d5253b --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/StringExtensions.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle + +/** + * Returns `true` if the version of a project contains `snapshot` (in any case), + * `false` otherwise. + */ +fun String.isSnapshot(): Boolean { + return contains("snapshot", ignoreCase = true) +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/TaskName.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/TaskName.kt new file mode 100644 index 0000000..24b587f --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/TaskName.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle + +import kotlin.reflect.KClass +import org.gradle.api.Task +import org.gradle.api.tasks.TaskContainer +import org.gradle.kotlin.dsl.named +import org.gradle.kotlin.dsl.register + +/** + * A name and a type of a Gradle task. + */ +internal class TaskName( + val value: String, + val clazz: KClass, +) { + companion object { + + fun of(name: String) = TaskName(name, Task::class) + + fun of(name: String, clazz: KClass) = TaskName(name, clazz) + } +} + +/** + * Locates [the task][TaskName] in this [TaskContainer]. + */ +internal fun TaskContainer.named(name: TaskName) = named(name.value, name.clazz) + +/** + * Registers [the task][TaskName] in this [TaskContainer]. + */ +internal fun TaskContainer.register(name: TaskName, init: T.() -> Unit) = + register(name.value, name.clazz, init) diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/VersionWriter.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/VersionWriter.kt new file mode 100644 index 0000000..3dc7ef2 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/VersionWriter.kt @@ -0,0 +1,149 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.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/internal/gradle/base/Tasks.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/base/Tasks.kt new file mode 100644 index 0000000..6fb9fbb --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/base/Tasks.kt @@ -0,0 +1,87 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.base + +import org.gradle.api.Task +import org.gradle.api.tasks.Delete +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider +import org.gradle.kotlin.dsl.named + +/** + * Locates `clean` task in this [TaskContainer]. + * + * The task deletes the build directory and everything in it, + * i.e. the path specified by the `Project.getBuildDir()` project property. + * + * @see + * Tasks | The Base Plugin + */ +val TaskContainer.clean: TaskProvider + get() = named("clean") + +/** + * Locates `check` task in this [TaskContainer]. + * + * This is a lifecycle task that performs no action itself. + * + * Plugins and build authors should attach their verification tasks, + * such as ones that run tests, to this lifecycle task using `check.dependsOn(myTask)`. + * + * @see + * Tasks | The Base Plugin + */ +val TaskContainer.check: TaskProvider + get() = named("check") + +/** + * Locates `assemble` task in this [TaskContainer]. + * + * This is a lifecycle task that performs no action itself. + * + * Plugins and build authors should attach their assembling tasks that produce distributions and + * other consumable artifacts to this lifecycle task using `assemble.dependsOn(myTask)`. + * + * @see + * Tasks | The Base Plugin + */ +val TaskContainer.assemble: TaskProvider + get() = named("assemble") + +/** + * Locates `build` task in this [TaskContainer]. + * + * Intended to build everything, including running all tests, producing the production artifacts + * and generating documentation. One will probably rarely attach concrete tasks directly + * to `build` as [assemble][io.spine.internal.gradle.base.assemble] and + * [check][io.spine.internal.gradle.base.check] are typically more appropriate. + * + * @see + * Tasks | The Base Plugin + */ +val TaskContainer.build: TaskProvider + get() = named("build") diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/checkstyle/CheckStyleConfig.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/checkstyle/CheckStyleConfig.kt new file mode 100644 index 0000000..42d1f62 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/checkstyle/CheckStyleConfig.kt @@ -0,0 +1,73 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.checkstyle + +import io.spine.internal.dependency.CheckStyle +import org.gradle.api.Project +import org.gradle.api.plugins.quality.Checkstyle +import org.gradle.api.plugins.quality.CheckstyleExtension +import org.gradle.api.plugins.quality.CheckstylePlugin +import org.gradle.kotlin.dsl.the + +/** + * Configures the Checkstyle plugin. + * + * Usage: + * ``` + * CheckStyleConfig.applyTo(project) + * ``` + * + * Please note, the checks of the `test` sources are disabled. + * + * Also, this type is named in double-camel-case to avoid re-declaration due to a clash + * with some Gradle-provided types. + */ +@Suppress("unused") +object CheckStyleConfig { + + /** + * Applies the configuration to the passed [project]. + */ + fun applyTo(project: Project) { + project.apply { + plugin(CheckstylePlugin::class.java) + } + + val configDir = project.rootDir.resolve("config/quality/") + + with(project.the()) { + toolVersion = CheckStyle.version + configDirectory.set(configDir) + } + + project.afterEvaluate { + // Disables checking the test sources. + val checkstyleTest = project.tasks.findByName("checkstyleTest") as Checkstyle + checkstyleTest.enabled = false + } + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/DartContext.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/DartContext.kt new file mode 100644 index 0000000..ed2eff7 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/DartContext.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.dart + +import org.gradle.api.Project +import org.gradle.api.tasks.Exec + +/** + * Provides access to the current [DartEnvironment] and shortcuts for running `pub` tool. + */ +open class DartContext(dartEnv: DartEnvironment, internal val project: Project) + : DartEnvironment by dartEnv +{ + /** + * Executes `pub` command in this [Exec] task. + * + * The Dart ecosystem uses packages to manage shared software such as libraries and tools. + * To get or publish Dart packages, the `pub` package manager is to be used. + */ + fun Exec.pub(vararg args: Any) = commandLine(pubExecutable, *args) +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/DartEnvironment.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/DartEnvironment.kt new file mode 100644 index 0000000..160e1ff --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/DartEnvironment.kt @@ -0,0 +1,170 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.dart + +import java.io.File +import org.apache.tools.ant.taskdefs.condition.Os + +/** + * Describes the environment in which Dart code is assembled and processed during the build. + * + * Consists of two parts describing: + * + * 1. The module itself. + * 2. Tools and their input/output files. + */ +interface DartEnvironment { + + /* + * A module itself + ******************/ + + /** + * Module's root catalog. + */ + val projectDir: File + + /** + * Module's name. + */ + val projectName: String + + /** + * A directory which all artifacts are generated into. + * + * Default value: "$projectDir/build". + */ + val buildDir: File + get() = projectDir.resolve("build") + + /** + * A directory where artifacts for further publishing would be prepared. + * + * Default value: "$buildDir/pub/publication/$projectName". + */ + val publicationDir: File + get() = buildDir + .resolve("pub") + .resolve("publication") + .resolve(projectName) + + /** + * A directory which contains integration test Dart sources. + * + * Default value: "$projectDir/integration-test". + */ + val integrationTestDir: File + get() = projectDir.resolve("integration-test") + + /* + * Tools and their input/output files + *************************************/ + + /** + * Name of an executable for running `pub` tool. + * + * Default value: + * + * 1. "pub.bat" for Windows. + * 2. "pub" for other Oss. + */ + val pubExecutable: String + get() = if (isWindows()) "pub.bat" else "pub" + + /** + * Dart module's metadata file. + * + * Every pub package needs some metadata so it can specify its dependencies. Pub packages that + * are shared with others also need to provide some other information so users can discover + * them. All of this metadata goes in the package’s `pubspec`. + * + * Default value: "$projectDir/pubspec.yaml". + * + * See [The pubspec file | Dart](https://dart.dev/tools/pub/pubspec) + */ + val pubSpec: File + get() = projectDir.resolve("pubspec.yaml") + + /** + * Module dependencies' index that maps resolved package names to location URIs. + * + * By default, pub creates a [packageConfig] file in the `.dart_tool/` directory for this. + * Before the [packageConfig], pub used to create this [packageIndex] file in the root + * directory. + * + * As for Dart 2.14, `pub` still updates the deprecated file for backwards compatibility. + * + * Default value: "$projectDir/.packages". + */ + val packageIndex: File + get() = projectDir.resolve(".packages") + + /** + * Module dependencies' index that maps resolved package names to location URIs. + * + * Default value: "$projectDir/.dart_tool/package_config.json". + */ + val packageConfig: File + get() = projectDir + .resolve(".dart_tool") + .resolve("package_config.json") +} + +/** + * Allows overriding [DartEnvironment]'s defaults. + * + * Please note, not all properties of the environment can be overridden. Properties that describe + * `pub` tool's input/output files can NOT be overridden because `pub` itself doesn't allow to + * specify them for its execution. + * + * The next properties could not be overridden: + * + * 1. [DartEnvironment.pubSpec]. + * 2. [DartEnvironment.packageIndex]. + * 3. [DartEnvironment.packageConfig]. + */ +class ConfigurableDartEnvironment(initialEnv: DartEnvironment) + : DartEnvironment by initialEnv +{ + /* + * A module itself + ******************/ + + override var projectDir = initialEnv.projectDir + override var projectName = initialEnv.projectName + override var buildDir = initialEnv.buildDir + override var publicationDir = initialEnv.publicationDir + override var integrationTestDir = initialEnv.integrationTestDir + + /* + * Tools and their input/output files + *************************************/ + + override var pubExecutable = initialEnv.pubExecutable +} + +internal fun isWindows(): Boolean = Os.isFamily(Os.FAMILY_WINDOWS) diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/DartExtension.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/DartExtension.kt new file mode 100644 index 0000000..d4bd4d8 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/DartExtension.kt @@ -0,0 +1,187 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.dart + +import io.spine.internal.gradle.dart.task.DartTasks +import io.spine.internal.gradle.dart.plugin.DartPlugins +import org.gradle.api.Project +import org.gradle.kotlin.dsl.create +import org.gradle.kotlin.dsl.findByType + +/** + * Configures [DartExtension] that facilitates configuration of Gradle tasks and plugins + * to build Dart projects. + * + * The whole structure of the extension looks as follows: + * + * ``` + * dart { + * environment { + * // ... + * } + * plugins { + * // ... + * } + * tasks { + * // ... + * } + * } + * ``` + * + * ### Environment + * + * One of the main features of this extension is [DartEnvironment]. Environment describes a module + * itself and used tools with their input/output files. + * + * The extension is shipped with a pre-configured environment. So, no pre-configuration is required. + * Most properties in [DartEnvironment] have calculated defaults right in the interface. + * Only two properties need explicit override. + * + * The extension defines them as follows: + * + * 1. [DartEnvironment.projectDir] –> `project.projectDir`. + * 2. [DartEnvironment.projectName] —> `project.name`. + * + * There are two ways to modify the environment: + * + * 1. Modify [DartEnvironment] interface directly. Go with this option when it is a global change + * that should affect all projects which use this extension. + * 2. Use [DartExtension.environment] scope — for temporary and custom overridings. + * + * An example of a property overriding: + * + * ``` + * dart { + * environment { + * integrationTestDir = projectDir.resolve("tests") + * } + * } + * ``` + * + * Please note, environment should be set up firstly to have the effect on the parts + * of the extension that use it. + * + * ### Tasks and Plugins + * + * The spirit of tasks configuration in this extension is extracting the code that defines and + * registers tasks into extension functions upon `DartTasks` in `buildSrc`. Those extensions should + * be named after a task it registers or a task group if several tasks are registered at once. + * Then this extension is called in a project's `build.gradle.kts`. + * + * `DartTasks` and `DartPlugins` scopes extend [DartContext] which provides access + * to the current [DartEnvironment] and shortcuts for running `pub` tool. + * + * Below is the simplest example of how to create a primitive `printPubVersion` task. + * + * Firstly, a corresponding extension function should be defined in `buildSrc`: + * + * ``` + * fun DartTasks.printPubVersion() = + * register("printPubVersion") { + * pub("--version") + * } + * ``` + * + * Secondly, in a project's `build.gradle.kts` this extension is called: + * + * ``` + * dart { + * tasks { + * printPubVersion() + * } + * } + * ``` + * + * An extension function is not restricted to register exactly one task. If several tasks can + * be grouped into a logical bunch, they should be registered together: + * + * ``` + * fun DartTasks.build() { + * assembleDart() + * testDart() + * generateCoverageReport() + * } + * + * private fun DartTasks.assembleDart() = ... + * + * private fun DartTasks.testDart() = ... + * + * private fun DartTasks.generateCoverageReport() = ... + * ``` + * + * This section is mostly dedicated to tasks. But tasks and plugins are configured + * in a very similar way. So, everything above is also applicable to plugins. More detailed + * guides can be found in docs to `DartTasks` and `DartPlugins`. + * + * @see [ConfigurableDartEnvironment] + * @see [DartTasks] + * @see [DartPlugins] + */ +fun Project.dart(configuration: DartExtension.() -> Unit) { + extensions.run { + configuration.invoke( + findByType() ?: create("dartExtension", project) + ) + } +} + +/** + * Scope for performing Dart-related configuration. + * + * @see [dart] + */ +open class DartExtension(project: Project) { + + private val environment = ConfigurableDartEnvironment( + object : DartEnvironment { + override val projectDir = project.projectDir + override val projectName = project.name + } + ) + + private val tasks = DartTasks(environment, project) + private val plugins = DartPlugins(environment, project) + + /** + * Overrides default values of [DartEnvironment]. + * + * Please note, environment should be set up firstly to have the effect on the parts + * of the extension that use it. + */ + fun environment(overridings: ConfigurableDartEnvironment.() -> Unit) = + environment.run(overridings) + + /** + * Configures [Dart-related plugins][DartPlugins]. + */ + fun plugins(configurations: DartPlugins.() -> Unit) = plugins.run(configurations) + + /** + * Configures [Dart-related tasks][DartTasks]. + */ + fun tasks(configurations: DartTasks.() -> Unit) = tasks.run(configurations) +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/plugin/DartPlugins.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/plugin/DartPlugins.kt new file mode 100644 index 0000000..43d8bb0 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/plugin/DartPlugins.kt @@ -0,0 +1,92 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.dart.plugin + +import io.spine.internal.gradle.dart.DartContext +import io.spine.internal.gradle.dart.DartEnvironment +import org.gradle.api.Project +import org.gradle.api.plugins.ExtensionContainer +import org.gradle.api.plugins.PluginContainer +import org.gradle.api.tasks.TaskContainer + +/** + * A scope for applying and configuring Dart-related plugins. + * + * The scope extends [DartContext] and provides shortcuts for key project's containers: + * + * 1. [plugins]. + * 2. [extensions]. + * 3. [tasks]. + * + * Let's imagine one wants to apply and configure `FooBar` plugin. To do that, several steps + * should be completed: + * + * 1. Declare the corresponding extension function upon [DartContext] named after the plugin. + * 2. Apply and configure the plugin inside that function. + * 3. Call the resulted extension in your `build.gradle.kts` file. + * + * Here's an example of `dart/plugin/FooBar.kt`: + * + * ``` + * fun DartPlugins.fooBar() { + * plugins.apply("com.fooBar") + * extensions.configure { + * // ... + * } + * } + * ``` + * + * And here's how to apply it in `build.gradle.kts`: + * + * ``` + * import io.spine.internal.gradle.dart.dart + * import io.spine.internal.gradle.dart.plugins.fooBar + * + * // ... + * + * dart { + * plugins { + * fooBar() + * } + * } + * ``` + */ +class DartPlugins(dartEnv: DartEnvironment, project: Project) : DartContext(dartEnv, project) { + + internal val plugins = project.plugins + internal val extensions = project.extensions + internal val tasks = project.tasks + + internal fun plugins(configurations: PluginContainer.() -> Unit) = + plugins.run(configurations) + + internal fun extensions(configurations: ExtensionContainer.() -> Unit) = + extensions.run(configurations) + + internal fun tasks(configurations: TaskContainer.() -> Unit) = + tasks.run(configurations) +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/plugin/Protobuf.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/plugin/Protobuf.kt new file mode 100644 index 0000000..65f6fb2 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/plugin/Protobuf.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.dart.plugin + +import com.google.protobuf.gradle.id +import com.google.protobuf.gradle.ProtobufExtension +import com.google.protobuf.gradle.remove +import io.spine.internal.dependency.Protobuf + +/** + * Applies `protobuf` plugin and configures `GenerateProtoTask` to work with a Dart module. + * + * @see DartPlugins + */ +fun DartPlugins.protobuf() { + + plugins.apply(Protobuf.GradlePlugin.id) + + val protobufExtension = project.extensions.getByType(ProtobufExtension::class.java) + protobufExtension.apply { + generateProtoTasks.all().configureEach { + apply { + plugins { id("dart") } + builtins { remove("java") } + } + } + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/task/Build.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/task/Build.kt new file mode 100644 index 0000000..55ec5ca --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/task/Build.kt @@ -0,0 +1,155 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.dart.task + +import io.spine.internal.gradle.TaskName +import io.spine.internal.gradle.base.assemble +import io.spine.internal.gradle.base.check +import io.spine.internal.gradle.base.clean +import io.spine.internal.gradle.named +import io.spine.internal.gradle.register +import org.gradle.api.tasks.Delete +import org.gradle.api.tasks.Exec +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider + +/** + * Registers tasks for building Dart projects. + * + * List of tasks to be created: + * + * 1. [TaskContainer.cleanPackageIndex]. + * 2. [TaskContainer.resolveDependencies]. + * 3. [TaskContainer.testDart]. + * + * An example of how to apply it in `build.gradle.kts`: + * + * ``` + * import io.spine.internal.gradle.dart.dart + * import io.spine.internal.gradle.dart.task.build + * + * // ... + * + * dart { + * tasks { + * build() + * } + * } + * ``` + * + * @param configuration any additional configuration related to the module's building. + */ +fun DartTasks.build(configuration: DartTasks.() -> Unit = {}) { + + cleanPackageIndex().also { + clean.configure { + dependsOn(it) + } + } + resolveDependencies().also { + assemble.configure { + dependsOn(it) + } + } + testDart().also { + check.configure { + dependsOn(it) + } + } + + configuration() +} + +private val resolveDependenciesName = TaskName.of("resolveDependencies", Exec::class) + +/** + * Locates `resolveDependencies` task in this [TaskContainer]. + * + * The task fetches dependencies declared via `pubspec.yaml` using `pub get` command. + */ +val TaskContainer.resolveDependencies: TaskProvider + get() = named(resolveDependenciesName) + +private fun DartTasks.resolveDependencies(): TaskProvider = + register(resolveDependenciesName) { + + description = "Fetches dependencies declared via `pubspec.yaml`." + group = DartTasks.Group.build + + mustRunAfter(cleanPackageIndex) + + inputs.file(pubSpec) + outputs.file(packageIndex) + + pub("get") + } + +private val cleanPackageIndexName = TaskName.of("cleanPackageIndex", Delete::class) + +/** + * Locates `cleanPackageIndex` task in this [TaskContainer]. + * + * The task deletes the resolved module dependencies' index. + * + * The standard configuration file that contains index is `package_config.json`. For backwards + * compatability `pub` still updates the deprecated `.packages` file. The task deletes both files. + */ +val TaskContainer.cleanPackageIndex: TaskProvider + get() = named(cleanPackageIndexName) + +private fun DartTasks.cleanPackageIndex(): TaskProvider = + register(cleanPackageIndexName) { + + description = "Deletes the resolved `.packages` and `package_config.json` files." + group = DartTasks.Group.build + + delete( + packageIndex, + packageConfig + ) + } + +private val testDartName = TaskName.of("testDart", Exec::class) + +/** + * Locates `testDart` task in this [TaskContainer]. + * + * The task runs Dart tests declared in the `./test` directory. + */ +val TaskContainer.testDart: TaskProvider + get() = named(testDartName) + +private fun DartTasks.testDart(): TaskProvider = + register(testDartName) { + + description = "Runs Dart tests declared in the `./test` directory." + group = DartTasks.Group.build + + dependsOn(resolveDependencies) + + pub("run", "test") + } diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/task/DartTasks.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/task/DartTasks.kt new file mode 100644 index 0000000..50574de --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/task/DartTasks.kt @@ -0,0 +1,115 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.dart.task + +import io.spine.internal.gradle.dart.DartContext +import io.spine.internal.gradle.dart.DartEnvironment +import org.gradle.api.Project +import org.gradle.api.tasks.TaskContainer + +/** + * A scope for registering and configuring Dart-related tasks. + * + * The scope provides: + * + * 1. Access to the current [DartContext]. + * 2. Project's [TaskContainer]. + * 3. Default task groups. + * + * Supposing, one needs to create a new task that would participate in building. Let the task name + * be `testDart`. To do that, several steps should be completed: + * + * 1. Define the task name and type using [TaskName][io.spine.internal.gradle.TaskName]. + * 2. Create a public typed reference for the task upon [TaskContainer]. It would facilitate + * referencing to the new task, so that external tasks could depend on it. This reference + * should be documented. + * 3. Implement an extension upon [DartTasks] to register the task. + * 4. Call the resulted extension from `build.gradle.kts`. + * + * Here's an example of `testDart()` extension: + * + * ``` + * import io.spine.internal.gradle.named + * import io.spine.internal.gradle.register + * import io.spine.internal.gradle.TaskName + * import org.gradle.api.Task + * import org.gradle.api.tasks.TaskContainer + * import org.gradle.api.tasks.Exec + * + * // ... + * + * private val testDartName = TaskName.of("testDart", Exec::class) + * + * /** + * * Locates `testDart` task in this [TaskContainer]. + * * + * * The task runs Dart tests declared in the `./test` directory. + * */ + * val TaskContainer.testDart: TaskProvider + * get() = named(testDartName) + * + * fun DartTasks.testDart() = + * register(testDartName) { + * + * description = "Runs Dart tests declared in the `./test` directory." + * group = DartTasks.Group.build + * + * // ... + * } + * ``` + * + * And here's how to apply it in `build.gradle.kts`: + * + * ``` + * import io.spine.internal.gradle.dart.dart + * import io.spine.internal.gradle.dart.task.testDart + * + * // ... + * + * dart { + * tasks { + * testDart() + * } + * } + * ``` + * + * Declaring typed references upon [TaskContainer] is optional. But it is highly encouraged + * to reference other tasks by such extensions instead of hard-typed string values. + */ +class DartTasks(dartEnv: DartEnvironment, project: Project) + : DartContext(dartEnv, project), TaskContainer by project.tasks +{ + /** + * Default task groups for tasks that participate in building a Dart module. + * + * @see [org.gradle.api.Task.getGroup] + */ + internal object Group { + const val build = "Dart/Build" + const val publish = "Dart/Publish" + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/task/IntegrationTest.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/task/IntegrationTest.kt new file mode 100644 index 0000000..69c691a --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/task/IntegrationTest.kt @@ -0,0 +1,92 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.dart.task + +import io.spine.internal.gradle.TaskName +import io.spine.internal.gradle.named +import io.spine.internal.gradle.register +import org.gradle.api.tasks.Exec +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider + +private val integrationTestName = TaskName.of("integrationTest", Exec::class) + +/** + * Locates `integrationTest` task in this [TaskContainer]. + * + * The task runs integration tests of the `spine-dart` library against a sample + * Spine-based application. The tests are run in Chrome browser because they use `WebFirebaseClient` + * which only works in web environment. + * + * A sample Spine-based application is run from the `test-app` module before integration + * tests start and is stopped as the tests complete. + */ +val TaskContainer.integrationTest: TaskProvider + get() = named(integrationTestName) + +/** + * Registers [TaskContainer.integrationTest] task. + * + * Please note, this task depends on [build] tasks. Therefore, building tasks should be applied in + * the first place. + * + * Here's an example of how to apply it in `build.gradle.kts`: + * + * ``` + * import io.spine.internal.gradle.dart.dart + * import io.spine.internal.gradle.task.build + * import io.spine.internal.gradle.task.integrationTest + * + * // ... + * + * dart { + * tasks { + * build() + * integrationTest() + * } + * } + * ``` + */ +@Suppress("unused") +fun DartTasks.integrationTest() = + register(integrationTestName) { + + dependsOn( + resolveDependencies, + ":test-app:appBeforeIntegrationTest" + ) + + pub( + "run", + "test", + integrationTestDir, + "-p", + "chrome" + ) + + finalizedBy(":test-app:appAfterIntegrationTest") + } diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/task/Publish.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/task/Publish.kt new file mode 100644 index 0000000..08b7b00 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/dart/task/Publish.kt @@ -0,0 +1,176 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.dart.task + +import io.spine.internal.gradle.TaskName +import io.spine.internal.gradle.base.assemble +import io.spine.internal.gradle.publish.publish +import io.spine.internal.gradle.named +import io.spine.internal.gradle.register +import org.gradle.api.tasks.Copy +import org.gradle.api.tasks.Exec +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider + +/** + * Registers tasks for publishing Dart projects. + * + * Please note, this task group depends on [build] tasks. Therefore, building tasks should + * be applied in the first place. + * + * List of tasks to be created: + * + * 1. [TaskContainer.stagePubPublication]. + * 2. [TaskContainer.activateLocally]. + * 3. [TaskContainer.publishToPub]. + * + * Usage example: + * + * ``` + * import io.spine.internal.gradle.dart.dart + * import io.spine.internal.gradle.dart.task.build + * import io.spine.internal.gradle.dart.task.publish + * + * // ... + * + * dart { + * tasks { + * build() + * publish() + * } + * } + * ``` + */ +fun DartTasks.publish() { + + stagePubPublication() + activateLocally() + + publishToPub().also { + publish.configure { + dependsOn(it) + } + } +} + +private val stagePubPublicationName = TaskName.of("stagePubPublication", Copy::class) + +/** + * Locates `stagePubPublication` in this [TaskContainer]. + * + * The task prepares the Dart package for Pub publication in the + * [publication directory][io.spine.internal.gradle.dart.DartEnvironment.publicationDir]. + */ +val TaskContainer.stagePubPublication: TaskProvider + get() = named(stagePubPublicationName) + +private fun DartTasks.stagePubPublication(): TaskProvider = + register(stagePubPublicationName) { + + description = "Prepares the Dart package for Pub publication." + group = DartTasks.Group.publish + + dependsOn(assemble) + + // Beside `.dart` sources itself, `pub` package manager conventions require: + // 1. README.md and CHANGELOG.md to build a page at `pub.dev/packages/;`. + // 2. `pubspec` file to fill out details about your package on the right side of your + // package’s page. + // 3. LICENSE file. + + from(project.projectDir) { + include("**/*.dart", "pubspec.yaml", "**/*.md") + exclude("proto/", "generated/", "build/", "**/.*") + } + from("${project.rootDir}/LICENSE") + into(publicationDir) + + doLast { + logger.debug("Pub publication is prepared in directory `{}`.", publicationDir) + } + } + +private val publishToPubName = TaskName.of("publishToPub", Exec::class) + +/** + * Locates `publishToPub` task in this [TaskContainer]. + * + * The task publishes the prepared publication to Pub using `pub publish` command. + */ +val TaskContainer.publishToPub: TaskProvider + get() = named(publishToPubName) + +private fun DartTasks.publishToPub(): TaskProvider = + register(publishToPubName) { + + description = "Publishes the prepared publication to Pub." + group = DartTasks.Group.publish + + dependsOn(stagePubPublication) + + val sayYes = "y".byteInputStream() + standardInput = sayYes + + workingDir(publicationDir) + + pub("publish", "--trace") + } + +private val activateLocallyName = TaskName.of("activateLocally", Exec::class) + +/** + * Locates `activateLocally` task in this [TaskContainer]. + * + * Makes this package available in the command line as an executable. + * + * The `dart run` command supports running a Dart program — located in a file, in the current + * package, or in one of the dependencies of the current package - from the command line. + * To run a program from an arbitrary location, the package should be "activated". + * + * See [dart pub global | Dart](https://dart.dev/tools/pub/cmd/pub-global) + */ +val TaskContainer.activateLocally: TaskProvider + get() = named(activateLocallyName) + +private fun DartTasks.activateLocally(): TaskProvider = + register(activateLocallyName) { + + description = "Activates this package locally." + group = DartTasks.Group.publish + + dependsOn(stagePubPublication) + + workingDir(publicationDir) + pub( + "global", + "activate", + "--source", + "path", + publicationDir, + "--trace" + ) + } diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/fs/LazyTempPath.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/fs/LazyTempPath.kt new file mode 100644 index 0000000..325d8e9 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/fs/LazyTempPath.kt @@ -0,0 +1,109 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.fs + +import java.io.File +import java.net.URI +import java.nio.file.FileSystem +import java.nio.file.Files.createTempDirectory +import java.nio.file.LinkOption +import java.nio.file.Path +import java.nio.file.WatchEvent +import java.nio.file.WatchKey +import java.nio.file.WatchService + +/** + * A path to a temporary folder, which is not created until it is really used. + * + * After the first usage, the instances of this type delegate all calls to the internally + * created instance of [Path] created with [createTempDirectory]. + */ +@Suppress("TooManyFunctions") +class LazyTempPath(private val prefix: String) : Path { + + private val delegate: Path by lazy { createTempDirectory(prefix) } + + override fun compareTo(other: Path): Int = delegate.compareTo(other) + + override fun iterator(): MutableIterator = delegate.iterator() + + override fun register( + watcher: WatchService, + events: Array>, + vararg modifiers: WatchEvent.Modifier? + ): WatchKey = delegate.register(watcher, events, *modifiers) + + override fun register(watcher: WatchService, vararg events: WatchEvent.Kind<*>?): WatchKey = + delegate.register(watcher, *events) + + override fun getFileSystem(): FileSystem = delegate.fileSystem + + override fun isAbsolute(): Boolean = delegate.isAbsolute + + override fun getRoot(): Path = delegate.root + + override fun getFileName(): Path = delegate.fileName + + override fun getParent(): Path = delegate.parent + + override fun getNameCount(): Int = delegate.nameCount + + override fun getName(index: Int): Path = delegate.getName(index) + + override fun subpath(beginIndex: Int, endIndex: Int): Path = + delegate.subpath(beginIndex, endIndex) + + override fun startsWith(other: Path): Boolean = delegate.startsWith(other) + + override fun startsWith(other: String): Boolean = delegate.startsWith(other) + + override fun endsWith(other: Path): Boolean = delegate.endsWith(other) + + override fun endsWith(other: String): Boolean = delegate.endsWith(other) + + override fun normalize(): Path = delegate.normalize() + + override fun resolve(other: Path): Path = delegate.resolve(other) + + override fun resolve(other: String): Path = delegate.resolve(other) + + override fun resolveSibling(other: Path): Path = delegate.resolveSibling(other) + + override fun resolveSibling(other: String): Path = delegate.resolveSibling(other) + + override fun relativize(other: Path): Path = delegate.relativize(other) + + override fun toUri(): URI = delegate.toUri() + + override fun toAbsolutePath(): Path = delegate.toAbsolutePath() + + override fun toRealPath(vararg options: LinkOption?): Path = delegate.toRealPath(*options) + + override fun toFile(): File = delegate.toFile() + + override fun toString(): String = delegate.toString() +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/git/Branch.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/git/Branch.kt new file mode 100644 index 0000000..63bae4e --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/git/Branch.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.git + +/** + * Branch names. + */ +object Branch { + + /** + * The default branch. + */ + const val master = "master" + + /** + * The branch used for publishing documentation to GitHub Pages. + */ + const val documentation = "gh-pages" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/git/Repository.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/git/Repository.kt new file mode 100644 index 0000000..229973d --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/git/Repository.kt @@ -0,0 +1,164 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.git + +import io.spine.internal.gradle.Cli +import io.spine.internal.gradle.fs.LazyTempPath + +/** + * Interacts with a real Git repository. + * + * Clones the repository with the provided SSH URL in a temporal folder. Provides + * functionality to configure a user, checkout branches, commit changes and push them + * to the remote repository. + * + * It is assumed that before using this class an appropriate SSH key that has + * sufficient rights to perform described above operations was registered + * in `ssh-agent`. + * + * 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. + */ +class Repository private constructor( + + /** + * The GitHub SSH URL to the underlying repository. + */ + 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 + +) : AutoCloseable { + + /** + * Path to the temporal folder for a clone of the underlying repository. + */ + val location = LazyTempPath("repoTemp") + + /** + * Clones the repository with [the SSH url][sshUrl] into the [temporal folder][location]. + */ + private fun clone() { + repoExecute("git", "clone", sshUrl, ".") + } + + /** + * Executes a command in the [location]. + */ + private fun repoExecute(vararg command: String): String = + Cli(location.toFile()).execute(*command) + + /** + * Checks out the branch by its name. + */ + fun checkout(branch: String) { + repoExecute("git", "checkout", branch) + + currentBranch = branch + } + + /** + * Configures the username and the email of the user. + * + * Overwrites `user.name` and `user.email` settings locally in [location] with + * values from [user]. These settings determine what ends up in author and + * committer fields of a commit. + */ + fun configureUser(user: UserInfo) { + repoExecute("git", "config", "user.name", user.name) + repoExecute("git", "config", "user.email", user.email) + + this.user = user + } + + /** + * Stages all changes and commits with the provided message. + */ + fun commitAllChanges(message: String) { + stageAllChanges() + commit(message) + } + + private fun stageAllChanges() { + repoExecute("git", "add", "--all") + } + + private fun commit(message: String) { + repoExecute( + "git", + "commit", + "--allow-empty", + "--message=${message}" + ) + } + + /** + * Pushes local repository to the remote. + */ + fun push() { + repoExecute("git", "push") + } + + override fun close() { + location.toFile().deleteRecursively() + } + + 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. + * + * @throws IllegalArgumentException if SSH URL is an empty string. + */ + fun of(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) + repo.clone() + repo.configureUser(user) + + if (branch != Branch.master) { + repo.checkout(branch) + } + + return repo + } + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/git/UserInfo.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/git/UserInfo.kt new file mode 100644 index 0000000..898eb5b --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/git/UserInfo.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.git + +/** + * Contains information about a Git user. + * + * Determines the author and committer fields of a commit. + * + * @constructor throws an [IllegalArgumentException] if the name or the email + * is an empty string. + */ +data class UserInfo(val name: String, val email: String) { + init { + require(name.isNotBlank()) { "Name cannot be an empty string." } + require(email.isNotBlank()) { "Email cannot be an empty string." } + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/AuthorEmail.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/AuthorEmail.kt new file mode 100644 index 0000000..0f48f00 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/AuthorEmail.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.github.pages + +/** + * An author of updates to GitHub pages. + */ +class AuthorEmail(val value: String) { + + companion object { + + /** + * The name of the environment variable that contains the email to use for authoring + * the commits to the GitHub Pages branch. + */ + @Suppress("MemberVisibilityCanBePrivate") // for documentation purposes. + const val environmentVariable = "FORMAL_GIT_HUB_PAGES_AUTHOR" + + /** + * Obtains the author from the system [environment variable][environmentVariable]. + */ + fun fromVar() : AuthorEmail { + val envValue = System.getenv(environmentVariable) + check(envValue != null && envValue.isNotBlank()) { + "Unable to obtain an author from `${environmentVariable}`." + } + return AuthorEmail(envValue) + } + } + + override fun toString(): String = value +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/RepositoryExtensions.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/RepositoryExtensions.kt new file mode 100644 index 0000000..4dacb62 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/RepositoryExtensions.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.github.pages + +import io.spine.internal.gradle.RepoSlug +import io.spine.internal.gradle.git.Branch +import io.spine.internal.gradle.git.Repository +import io.spine.internal.gradle.git.UserInfo + +/** + * Clones the current project repository with the branch dedicated to publishing + * documentation to GitHub Pages checked out. + * + * 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 + * 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 { + val host = RepoSlug.fromVar().gitHost() + + val username = "UpdateGitHubPages Plugin" + val userEmail = AuthorEmail.fromVar().toString() + val user = UserInfo(username, userEmail) + + val branch = Branch.documentation + + return of(host, user, branch) +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/SshKey.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/SshKey.kt new file mode 100644 index 0000000..7b1746a --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/SshKey.kt @@ -0,0 +1,104 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.github.pages + +import io.spine.internal.gradle.Cli +import java.io.File +import org.gradle.api.GradleException + +/** + * Registers SSH key for further operations with GitHub Pages. + */ +internal class SshKey(private val rootProjectFolder: File) { + /** + * Creates an SSH key with the credentials and registers it by invoking the + * `register-ssh-key.sh` script. + */ + fun register() { + val gitHubAccessKey = gitHubKey() + val sshConfigFile = sshConfigFile() + sshConfigFile.appendPublisher(gitHubAccessKey) + + execute( + "${rootProjectFolder.absolutePath}/config/scripts/register-ssh-key.sh", + gitHubAccessKey.absolutePath + ) + } + + /** + * Locates `deploy_key_rsa` in the [rootProjectFolder] and returns it as a [File]. + * + * A CI instance comes with an RSA key. However, of course, the default key has + * no privileges in Spine repositories. Thus, we add our own RSA key — + * `deploy_rsa_key`. It must have `write` rights in the associated repository. + * Also, we don't want that key to be used for anything else but GitHub Pages + * publishing. + * + * Thus, we configure the SSH agent to use the `deploy_rsa_key` only for specific + * references, namely in `github.com-publish`. + * + * @throws GradleException if `deploy_key_rsa` is not found. + */ + private fun gitHubKey(): File { + val gitHubAccessKey = File("${rootProjectFolder.absolutePath}/deploy_key_rsa") + + if (!gitHubAccessKey.exists()) { + throw GradleException( + "File $gitHubAccessKey does not exist. It should be encrypted" + + " in the repository and decrypted on CI." + ) + } + return gitHubAccessKey + } + + private fun sshConfigFile(): File { + val sshConfigFile = File("${System.getProperty("user.home")}/.ssh/config") + + if (!sshConfigFile.exists()) { + val parentDir = sshConfigFile.canonicalFile.parentFile + parentDir.mkdirs() + sshConfigFile.createNewFile() + } + + return sshConfigFile + } + + private fun File.appendPublisher(privateKey: File) { + val nl = System.lineSeparator() + this.appendText( + nl + + "Host github.com-publish" + nl + + "User git" + nl + + "IdentityFile ${privateKey.absolutePath}" + nl + ) + } + + /** + * Executes a command in the project [rootProjectFolder]. + */ + private fun execute(vararg command: String): String = Cli(rootProjectFolder).execute(*command) +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/TaskName.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/TaskName.kt new file mode 100644 index 0000000..98e4772 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/TaskName.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.github.pages + +object TaskName { + + /** + * The name of the task which updates the GitHub Pages. + */ + const val updateGitHubPages = "updateGitHubPages" + + /** + * The name of the helper task to gather the generated Javadoc before updating + * GitHub Pages. + */ + const val copyJavadoc = "copyJavadoc" + + /** + * The name of the helper task to gather Dokka-generated documentation before + * updating GitHub Pages. + */ + const val copyDokka = "copyDokka" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/Update.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/Update.kt new file mode 100644 index 0000000..f27858f --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/Update.kt @@ -0,0 +1,167 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.github.pages + +import io.spine.internal.gradle.git.Repository +import java.io.File +import java.nio.file.Path +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.FileCollection +import org.gradle.api.logging.Logger + +/** + * Performs the update of GitHub pages. + */ +fun Task.updateGhPages(project: Project) { + val plugin = project.plugins.getPlugin(UpdateGitHubPages::class.java) + + with(plugin) { + SshKey(rootFolder).register() + } + + val repository = Repository.forPublishingDocumentation() + + val updateJavadoc = with(plugin) { + UpdateJavadoc(project, javadocOutputFolder, repository, logger) + } + + val updateDokka = with(plugin) { + UpdateDokka(project, dokkaOutputFolder, repository, logger) + } + + repository.use { + updateJavadoc.run() + updateDokka.run() + repository.push() + } +} + +private abstract class UpdateDocumentation( + private val project: Project, + private val docsSourceFolder: Path, + private val repository: Repository, + private val logger: Logger +) { + + /** + * The folder under the repository's root(`/`) for storing documentation. + * + * The value should not contain any leading or trailing file separators. + * + * The absolute path to the project's documentation is made by appending its + * name to the end, making `/docsDestinationFolder/project.name`. + */ + protected abstract val docsDestinationFolder: String + + /** + * The name of the tool used to generate the documentation to update. + * + * This name will appear in logs as part of a message. + */ + protected abstract val toolName: String + + private val mostRecentFolder by lazy { + File("${repository.location}/${docsDestinationFolder}/${project.name}") + } + + private fun logDebug(message: () -> String) { + if (logger.isDebugEnabled) { + logger.debug(message()) + } + } + + fun run() { + val module = project.name + logDebug {"Update of the $toolName documentation for module `$module` started." } + + val documentation = replaceMostRecentDocs() + copyIntoVersionDir(documentation) + + val version = project.version + val updateMessage = + "Update `$toolName` documentation for module `$module` as for version $version" + repository.commitAllChanges(updateMessage) + + logDebug { "Update of the `$toolName` documentation for `$module` successfully finished." } + } + + private fun replaceMostRecentDocs(): ConfigurableFileCollection { + val generatedDocs = project.files(docsSourceFolder) + + logDebug { + "Replacing the most recent `$toolName` documentation in `${mostRecentFolder}`." + } + copyDocs(generatedDocs, mostRecentFolder) + + return generatedDocs + } + + private fun copyDocs(source: FileCollection, destination: File) { + destination.mkdir() + project.copy { + from(source) + into(destination) + } + } + + private fun copyIntoVersionDir(generatedDocs: ConfigurableFileCollection) { + val versionedDocDir = File("$mostRecentFolder/v/${project.version}") + + logDebug { + "Storing the new version of `$toolName` documentation in `${versionedDocDir}`." + } + copyDocs(generatedDocs, versionedDocDir) + } +} + +private class UpdateJavadoc( + project: Project, + docsSourceFolder: Path, + repository: Repository, + logger: Logger +) : UpdateDocumentation(project, docsSourceFolder, repository, logger) { + + override val docsDestinationFolder: String + get() = "reference" + override val toolName: String + get() = "Javadoc" +} + +private class UpdateDokka( + project: Project, + docsSourceFolder: Path, + repository: Repository, + logger: Logger +) : UpdateDocumentation(project, docsSourceFolder, repository, logger) { + + override val docsDestinationFolder: String + get() = "dokka-reference" + override val toolName: String + get() = "Dokka" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/UpdateGitHubPages.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/UpdateGitHubPages.kt new file mode 100644 index 0000000..ba8c290 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/UpdateGitHubPages.kt @@ -0,0 +1,234 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.github.pages + +import dokkaHtmlTask +import io.spine.internal.gradle.fs.LazyTempPath +import io.spine.internal.gradle.github.pages.TaskName.copyDokka +import io.spine.internal.gradle.github.pages.TaskName.copyJavadoc +import io.spine.internal.gradle.github.pages.TaskName.updateGitHubPages +import io.spine.internal.gradle.isSnapshot +import io.spine.internal.gradle.javadoc.ExcludeInternalDoclet +import io.spine.internal.gradle.javadoc.javadocTask +import java.io.File +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.tasks.Copy +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. + * + * 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) + * } + * ``` + * + * 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 + * it on CI upon publication. Also, the script uses the `FORMAL_GIT_HUB_PAGES_AUTHOR` + * environment variable to set the author email for the commits. The `gh-pages` + * branch itself should exist before the plugin is run. + * + * NOTE: when changing the value of "FORMAL_GIT_HUB_PAGES_AUTHOR", one also must change + * the SSH private (encrypted `deploy_key_rsa`) and the public + * ("GitHub Pages publisher" on GitHub) keys. + * + * Another requirement is an environment variable `REPO_SLUG`, which is set by the CI + * environment, such as `Publish` GitHub Actions workflow. It points to the repository + * for which the update is executed. E.g.: + * + * ``` + * REPO_SLUG: SpineEventEngine/base + * ``` + * + * @see UpdateGitHubPagesExtension for the extension which is used to configure + * this plugin + */ +class UpdateGitHubPages : Plugin { + + /** + * Root folder of the repository, to which this `Project` belongs. + */ + internal lateinit var rootFolder: File + + /** + * The external inputs to include into the publishing. + * + * The inputs are evaluated according to [Copy.from] specification. + */ + private lateinit var includedInputs: Set + + /** + * Path to the temp folder used to gather the Javadoc output 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. + */ + internal val dokkaOutputFolder = LazyTempPath("dokka") + + /** + * Applies the plugin to the specified [project]. + * + * If the project version says it is a snapshot, the plugin registers a no-op task. + * + * Even in such a case, the extension object is still created in the given project + * to allow customization of the parameters in its build script for later usage + * when the project version changes to non-snapshot. + */ + override fun apply(project: Project) { + val extension = UpdateGitHubPagesExtension.createIn(project) + project.afterEvaluate { + val projectVersion = project.version.toString() + if (projectVersion.isSnapshot()) { + registerNoOpTask() + } else { + registerTasks(extension) + } + } + } + + /** + * Registers `updateGitHubPages` task which performs no actual update, but prints + * the message telling the update is skipped, since the project is in + * its `SNAPSHOT` version. + */ + private fun Project.registerNoOpTask() { + tasks.register(updateGitHubPages) { + doLast { + val project = this@registerNoOpTask + println( + "GitHub Pages update will be skipped since this project is a snapshot: " + + "`${project.name}-${project.version}`." + ) + } + } + } + + 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.registerCopyDokka() + + val updatePagesTask = tasks.registerUpdateTask() + updatePagesTask.configure { + dependsOn(copyJavadoc) + dependsOn(copyDokka) + } + } + + private fun TaskContainer.registerCopyJavadoc(allowInternalJavadoc: Boolean) { + val inputs = composeJavadocInputs(allowInternalJavadoc) + + register(copyJavadoc, Copy::class.java) { + inputs.forEach { from(it) } + into(javadocOutputFolder) + } + } + + private fun TaskContainer.composeJavadocInputs(allowInternalJavadoc: Boolean): List { + val inputs = mutableListOf() + if (allowInternalJavadoc) { + inputs.add(javadocTask()) + } else { + inputs.add(javadocTask(ExcludeInternalDoclet.taskName)) + } + inputs.addAll(includedInputs) + return inputs + } + + private fun TaskContainer.registerCopyDokka() { + val inputs = composeDokkaInputs() + + register(copyDokka, Copy::class.java) { + inputs.forEach { from(it) } + into(dokkaOutputFolder) + } + } + + private fun TaskContainer.composeDokkaInputs(): List { + val inputs = mutableListOf() + + dokkaHtmlTask()?.let { + inputs.add(it) + } + inputs.addAll(includedInputs) + + return inputs + } + + private fun TaskContainer.registerUpdateTask(): TaskProvider { + return register(updateGitHubPages) { + doLast { + try { + updateGhPages(project) + } finally { + cleanup() + } + } + } + } + + private fun cleanup() { + val folders = listOf(dokkaOutputFolder, javadocOutputFolder) + folders.forEach { + it.toFile().deleteRecursively() + } + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/UpdateGitHubPagesExtension.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/UpdateGitHubPagesExtension.kt new file mode 100644 index 0000000..d523d5e --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/github/pages/UpdateGitHubPagesExtension.kt @@ -0,0 +1,131 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.github.pages + +import java.io.File +import org.gradle.api.Project +import org.gradle.api.provider.Property +import org.gradle.api.provider.SetProperty +import org.gradle.kotlin.dsl.apply +import org.gradle.kotlin.dsl.getByType +import org.gradle.kotlin.dsl.property + +/** + * Configures the `updateGitHubPages` extension. + */ +@Suppress("unused") +fun Project.updateGitHubPages(excludeInternalDocletVersion: String, + action: UpdateGitHubPagesExtension.() -> Unit) { + apply() + + val extension = extensions.getByType(UpdateGitHubPagesExtension::class) + extension.excludeInternalDocletVersion = excludeInternalDocletVersion + extension.action() +} + +/** + * The extension for configuring the [UpdateGitHubPages] plugin. + */ +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. + */ + 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.internal.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. */ + const val name = "updateGitHubPages" + + /** 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) + ) + project.extensions.add(result.javaClass, name, result) + return result + } + } + + /** + * 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() + } + + /** + * Returns the external inputs, which results should be included into the + * GitHub Pages update. + */ + fun includedInputs(): Set { + return includeInputs.get() + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/java/Tasks.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/java/Tasks.kt new file mode 100644 index 0000000..3e0aad9 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/java/Tasks.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.java + +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider +import org.gradle.api.tasks.testing.Test +import org.gradle.kotlin.dsl.named + +/** + * Locates `test` task in this [TaskContainer]. + * + * Runs the unit tests using JUnit or TestNG. + * + * Depends on `testClasses`, and all tasks which produce the test runtime classpath. + * + * @see + * Tasks | The Java Plugin + */ +val TaskContainer.test: TaskProvider + get() = named("test") diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/javac/ErrorProne.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javac/ErrorProne.kt new file mode 100644 index 0000000..1489c08 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javac/ErrorProne.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.javac + +import net.ltgt.gradle.errorprone.errorprone +import org.gradle.api.tasks.compile.JavaCompile +import org.gradle.process.CommandLineArgumentProvider + +/** + * Configures Error Prone for this `JavaCompile` task. + * + * Specifies the arguments for the compiler invocations. In particular, this configuration + * overrides a number of Error Prone defaults. See [ErrorProneConfig] for the details. + * + * Please note that while `ErrorProne` is a standalone Gradle plugin, + * it still has to be configured through `JavaCompile` task options. + * + * Here's an example of how to use it: + * + * ``` + * tasks { + * withType { + * configureErrorProne() + * } + * } + *``` + */ +@Suppress("unused") +fun JavaCompile.configureErrorProne() { + options.errorprone + .errorproneArgumentProviders + .add(ErrorProneConfig.ARGUMENTS) +} + +/** + * The knowledge that is required to set up `Error Prone`. + */ +private object ErrorProneConfig { + + /** + * Command line options for the `Error Prone` compiler. + */ + val ARGUMENTS = CommandLineArgumentProvider { + listOf( + + // Exclude generated sources from being analyzed by ErrorProne. + // Include all directories started from `generated`, such as `generated-proto`. + "-XepExcludedPaths:.*/generated.*/.*", + + // Turn the check off until ErrorProne can handle `@Nested` JUnit classes. + // See issue: https://github.com/google/error-prone/issues/956 + "-Xep:ClassCanBeStatic:OFF", + + // Turn off checks that report unused methods and method parameters. + // See issue: https://github.com/SpineEventEngine/config/issues/61 + "-Xep:UnusedMethod:OFF", + "-Xep:UnusedVariable:OFF", + + "-Xep:CheckReturnValue:OFF", + "-Xep:FloggerSplitLogStatement:OFF", + "-Xep:FloggerLogString:OFF" + ) + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/javac/Javac.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javac/Javac.kt new file mode 100644 index 0000000..de02687 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javac/Javac.kt @@ -0,0 +1,74 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.javac + +import org.gradle.api.tasks.compile.JavaCompile +import org.gradle.process.CommandLineArgumentProvider + +/** + * Configures the `javac` tool through this `JavaCompile` task. + * + * The following steps are performed: + * + * 1. Passes a couple of arguments to the compiler. See [JavacConfig] for more details; + * 2. Sets the UTF-8 encoding to be used when reading Java source files. + * + * Here's an example of how to use it: + * + *``` + * tasks { + * withType { + * configureJavac() + * } + * } + *``` + */ +@Suppress("unused") +fun JavaCompile.configureJavac() { + with(options) { + encoding = JavacConfig.SOURCE_FILES_ENCODING + compilerArgumentProviders.add(JavacConfig.COMMAND_LINE) + } +} + +/** + * The knowledge that is required to set up `javac`. + */ +private object JavacConfig { + const val SOURCE_FILES_ENCODING = "UTF-8" + val COMMAND_LINE = CommandLineArgumentProvider { + listOf( + + // Protobuf Compiler generates the code, which uses the deprecated `PARSER` field. + // See issue: https://github.com/SpineEventEngine/config/issues/173 + // "-Werror", + + "-Xlint:unchecked", + "-Xlint:deprecation", + ) + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/javadoc/Encoding.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javadoc/Encoding.kt new file mode 100644 index 0000000..bf8601b --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javadoc/Encoding.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.javadoc + +/** + * The encoding to use in Javadoc processing. + */ +data class Encoding(val name: String) diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/javadoc/ExcludeInternalDoclet.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javadoc/ExcludeInternalDoclet.kt new file mode 100644 index 0000000..4d5702b --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javadoc/ExcludeInternalDoclet.kt @@ -0,0 +1,118 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.javadoc + +import io.spine.internal.dependency.Spine +import io.spine.internal.gradle.javadoc.ExcludeInternalDoclet.Companion.taskName +import io.spine.internal.gradle.sourceSets +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.tasks.javadoc.Javadoc +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 = Spine.ArtifactVersion.javadocTools +) { + + private val dependency = Spine.javadocFilter + + companion object { + + /** + * The name of the custom configuration in scope of which the exclusion of + * `@Internal` types is performed. + */ + private const val configurationName = "excludeInternalDoclet" + + /** + * The fully-qualified class name of the doclet. + */ + const val className = "io.spine.tools.javadoc.ExcludeInternalDoclet" + + /** + * The name of the helper task which configures the Javadoc processing + * to exclude `@Internal` types. + */ + const val taskName = "noInternalJavadoc" + + private fun createConfiguration(project: Project): Configuration { + return project.configurations.create(configurationName) + } + } + + /** + * Creates a custom Javadoc task for the [project] which excludes the types + * annotated as `@Internal`. + * + * The task is registered under [taskName]. + */ + fun registerTaskIn(project: Project) { + val configuration = addTo(project) + project.appendCustomJavadocTask(configuration) + } + + /** + * Creates a configuration for the doclet in the given project and adds it to its dependencies. + * + * @return added configuration + */ + private fun addTo(project: Project): Configuration { + val configuration = createConfiguration(project) + project.dependencies.add(configuration.name, dependency) + return configuration + } +} + +private fun Project.appendCustomJavadocTask(excludeInternalDoclet: Configuration) { + val javadocTask = tasks.javadocTask() + tasks.register(taskName, Javadoc::class.java) { + + source = sourceSets.getByName("main").allJava.filter { + !it.absolutePath.contains("generated") + }.asFileTree + + classpath = javadocTask.classpath + + options { + encoding = JavadocConfig.encoding.name + + // Doclet fully qualified name. + doclet = ExcludeInternalDoclet.className + + // Path to the JAR containing the doclet. + docletpath = excludeInternalDoclet.files.toList() + } + + val docletOptions = options as StandardJavadocDocletOptions + JavadocConfig.registerCustomTags(docletOptions) + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/javadoc/JavadocConfig.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javadoc/JavadocConfig.kt new file mode 100644 index 0000000..debd78d --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javadoc/JavadocConfig.kt @@ -0,0 +1,139 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.javadoc + +import java.io.File +import org.gradle.api.JavaVersion +import org.gradle.api.Project +import org.gradle.api.tasks.javadoc.Javadoc +import org.gradle.external.javadoc.StandardJavadocDocletOptions + +/** + * Javadoc processing settings. + * + * This type is named with `Config` suffix to avoid its confusion with the standard `Javadoc` type. + */ +@Suppress("unused") +object JavadocConfig { + + /** + * Link to the documentation for Java 11 Standard Library API. + * + * Oracle JDK SE 11 is used for the reference. + */ + private const val standardLibraryAPI = "https://docs.oracle.com/en/java/javase/11/docs/api/" + + @Suppress("MemberVisibilityCanBePrivate") // opened to be visible from docs. + val tags = listOf( + JavadocTag("apiNote", "API Note"), + JavadocTag("implSpec", "Implementation Requirements"), + JavadocTag("implNote", "Implementation Note") + ) + + val encoding = Encoding("UTF-8") + + fun applyTo(project: Project) { + val javadocTask = project.tasks.javadocTask() + discardJavaModulesInLinks(javadocTask) + val docletOptions = javadocTask.options as StandardJavadocDocletOptions + configureDoclet(docletOptions) + } + + /** + * 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 + * 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. + * + * See: [Issue #334](https://github.com/SpineEventEngine/config/issues/334) + */ + private fun discardJavaModulesInLinks(javadoc: Javadoc) { + + // 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. + + val discardModulePrefix = """ + + getURLPrefix = function(ui) { + return ""; + }; + """.trimIndent() + + javadoc.doLast { + val destinationDir = javadoc.destinationDir!!.absolutePath + val searchScript = File("$destinationDir/search.js") + searchScript.appendText(discardModulePrefix) + } + } + + private fun configureDoclet(docletOptions: StandardJavadocDocletOptions) { + docletOptions.encoding = encoding.name + reduceParamWarnings(docletOptions) + registerCustomTags(docletOptions) + linkStandardLibraryAPI(docletOptions) + } + + /** + * Configures `javadoc` tool to avoid numerous warnings for missing `@param` tags. + * + * As suggested by Stephen Colebourne: + * [https://blog.joda.org/2014/02/turning-off-doclint-in-jdk-8-javadoc.html] + * + * See also: + * [https://github.com/GPars/GPars/blob/master/build.gradle#L268] + */ + private fun reduceParamWarnings(docletOptions: StandardJavadocDocletOptions) { + if (JavaVersion.current().isJava8Compatible) { + docletOptions.addStringOption("Xdoclint:none", "-quiet") + } + } + + /** + * Registers custom [tags] for the passed doclet options. + */ + fun registerCustomTags(docletOptions: StandardJavadocDocletOptions) { + docletOptions.tags = tags.map { it.toString() } + } + + /** + * Links the documentation for Java 11 Standard Library API. + * + * This documentation is used to be referenced to when navigating to the types from the + * standard library (`String`, `List`, etc.). + * + * OpenJDK SE 11 is used for the reference. + */ + private fun linkStandardLibraryAPI(docletOptions: StandardJavadocDocletOptions) { + docletOptions.addStringOption("link", standardLibraryAPI) + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/javadoc/JavadocTag.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javadoc/JavadocTag.kt new file mode 100644 index 0000000..8eb8b17 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javadoc/JavadocTag.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.javadoc + +/** + * The Javadoc tag. + */ +class JavadocTag(val name: String, val title: String) { + + override fun toString(): String { + return "${name}:a:${title}:" + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/javadoc/TaskContainerExtensions.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javadoc/TaskContainerExtensions.kt new file mode 100644 index 0000000..3897118 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javadoc/TaskContainerExtensions.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.javadoc + +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.javadoc.Javadoc + +/** + * 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 diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/JsContext.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/JsContext.kt new file mode 100644 index 0000000..75cd401 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/JsContext.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.javascript + +import java.io.File +import org.gradle.api.Project + +/** + * Provides access to the current [JsEnvironment] and shortcuts for running `npm` tool. + */ +open class JsContext(jsEnv: JsEnvironment, internal val project: Project) + : JsEnvironment by jsEnv +{ + /** + * Executes `npm` command in a separate process. + * + * [JsEnvironment.projectDir] is used as a working directory. + */ + fun npm(vararg args: String) = projectDir.npm(*args) + + /** + * Executes `npm` command in a separate process. + * + * This [File] is used as a working directory. + */ + fun File.npm(vararg args: String) = project.exec { + + workingDir(this@npm) + commandLine(npmExecutable) + args(*args) + + // Using private packages in a CI/CD workflow | npm Docs + // https://docs.npmjs.com/using-private-packages-in-a-ci-cd-workflow + + environment["NPM_TOKEN"] = npmAuthToken + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/JsEnvironment.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/JsEnvironment.kt new file mode 100644 index 0000000..bce4fa8 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/JsEnvironment.kt @@ -0,0 +1,255 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.javascript + +import java.io.File +import org.apache.tools.ant.taskdefs.condition.Os + +/** + * Describes the environment in which JavaScript code is assembled and processed during the build. + * + * Consists of three parts describing: + * + * 1. A module itself. + * 2. Tools and their input/output files. + * 3. Code generation. + */ +interface JsEnvironment { + + /* + * A module itself + ******************/ + + /** + * Module's root catalog. + */ + val projectDir: File + + /** + * Module's version. + */ + val moduleVersion: String + + /** + * Module's production sources directory. + * + * Default value: "$projectDir/main". + */ + val srcDir: File + get() = projectDir.resolve("main") + + /** + * Module's test sources directory. + * + * Default value: "$projectDir/test". + */ + val testSrcDir: File + get() = projectDir.resolve("test") + + /** + * A directory which all artifacts are generated into. + * + * Default value: "$projectDir/build". + */ + val buildDir: File + get() = projectDir.resolve("build") + + /** + * A directory where artifacts for further publishing would be prepared. + * + * Default value: "$buildDir/npm-publication". + */ + val publicationDir: File + get() = buildDir.resolve("npm-publication") + + /* + * Tools and their input/output files + *************************************/ + + /** + * Name of an executable for running `npm`. + * + * Default value: + * + * 1. "nmp.cmd" for Windows. + * 2. "npm" for other OSs. + */ + val npmExecutable: String + get() = if (isWindows()) "npm.cmd" else "npm" + + /** + * An access token that allows installation and/or publishing modules. + * + * During installation a token is required only if dependencies from private + * repositories are used. + * + * Default value is read from the environmental variable - `NPM_TOKEN`. + * "PUBLISHING_FORBIDDEN" stub value would be assigned in case `NPM_TOKEN` variable is not set. + * + * See [Creating and viewing access tokens | npm Docs](https://docs.npmjs.com/creating-and-viewing-access-tokens). + */ + val npmAuthToken: String + get() = System.getenv("NPM_TOKEN") ?: "PUBLISHING_FORBIDDEN" + + /** + * A directory where `npm` puts downloaded module's dependencies. + * + * Default value: "$projectDir/node_modules". + */ + val nodeModules: File + get() = projectDir.resolve("node_modules") + + /** + * Module's descriptor used by `npm`. + * + * Default value: "$projectDir/package.json". + */ + val packageJson: File + get() = projectDir.resolve("package.json") + + /** + * `npm` gets its configuration settings from the command line, environment variables, + * and `npmrc` file. + * + * Default value: "$projectDir/.npmrc". + * + * See [npmrc | npm Docs](https://docs.npmjs.com/cli/v8/configuring-npm/npmrc). + */ + val npmrc: File + get() = projectDir.resolve(".npmrc") + + /** + * A cache directory in which `nyc` tool outputs raw coverage report. + * + * Default value: "$projectDir/.nyc_output". + * + * See [istanbuljs/nyc](https://github.com/istanbuljs/nyc). + */ + val nycOutput: File + get() = projectDir.resolve(".nyc_output") + + /** + * A directory in which `webpack` would put a ready-to-use bundle. + * + * Default value: "$projectDir/dist" + * + * See [webpack - npm](https://www.npmjs.com/package/webpack). + */ + val webpackOutput: File + get() = projectDir.resolve("dist") + + /** + * A directory where bundled artifacts for further publishing would be prepared. + * + * Default value: "$publicationDir/dist". + */ + val webpackPublicationDir: File + get() = publicationDir.resolve("dist") + + /* + * Code generation + ******************/ + + /** + * Name of a directory that contains generated code. + * + * Default value: "proto". + */ + val genProtoDirName: String + get() = "proto" + + /** + * Directory with production Protobuf messages compiled into JavaScript. + * + * Default value: "$srcDir/$genProtoDirName". + */ + val genProtoMain: File + get() = srcDir.resolve(genProtoDirName) + + /** + * Directory with test Protobuf messages compiled into JavaScript. + * + * Default value: "$testSrcDir/$genProtoDirName". + */ + val genProtoTest: File + get() = testSrcDir.resolve(genProtoDirName) +} + +/** + * Allows overriding [JsEnvironment]'s defaults. + * + * All of declared properties can be split into two groups: + * + * 1. The ones that *define* something - can be overridden. + * 2. The ones that *describe* something - can NOT be overridden. + * + * Overriding a "defining" property affects the way `npm` tool works. + * In contrary, overriding a "describing" property does not affect the tool. + * Such properties just describe how the used tool works. + * + * Therefore, overriding of "describing" properties leads to inconsistency with expectations. + * + * The next properties could not be overridden: + * + * 1. [JsEnvironment.nodeModules]. + * 2. [JsEnvironment.packageJson]. + * 3. [JsEnvironment.npmrc]. + * 4. [JsEnvironment.nycOutput]. + */ +class ConfigurableJsEnvironment(initialEnvironment: JsEnvironment) + : JsEnvironment by initialEnvironment +{ + /* + * A module itself + ******************/ + + override var projectDir = initialEnvironment.projectDir + override var moduleVersion = initialEnvironment.moduleVersion + override var srcDir = initialEnvironment.srcDir + override var testSrcDir = initialEnvironment.testSrcDir + override var buildDir = initialEnvironment.buildDir + override var publicationDir = initialEnvironment.publicationDir + + /* + * Tools and their input/output files + *************************************/ + + override var npmExecutable = initialEnvironment.npmExecutable + override var npmAuthToken = initialEnvironment.npmAuthToken + override var webpackOutput = initialEnvironment.webpackOutput + override var webpackPublicationDir = initialEnvironment.webpackPublicationDir + + /* + * Code generation + ******************/ + + override var genProtoDirName = initialEnvironment.genProtoDirName + override var genProtoMain = initialEnvironment.genProtoMain + override var genProtoTest = initialEnvironment.genProtoTest +} + +internal fun isWindows(): Boolean = Os.isFamily(Os.FAMILY_WINDOWS) diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/JsExtension.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/JsExtension.kt new file mode 100644 index 0000000..22ebf52 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/JsExtension.kt @@ -0,0 +1,194 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.javascript + +import io.spine.internal.gradle.javascript.task.JsTasks +import io.spine.internal.gradle.javascript.plugin.JsPlugins +import org.gradle.api.Project +import org.gradle.kotlin.dsl.create +import org.gradle.kotlin.dsl.extra +import org.gradle.kotlin.dsl.findByType + +/** + * Configures [JsExtension] that facilitates configuration of Gradle tasks and plugins + * to build JavaScripts projects. + * + * The whole structure of the extension looks as follows: + * + * ``` + * javascript { + * environment { + * // ... + * } + * tasks { + * // ... + * } + * plugins { + * // ... + * } + * } + * ``` + * + * ### Environment + * + * One of the main features of this extension is [JsEnvironment]. Environment describes a module + * itself, used tools with their input/output files, code generation. + * + * The extension is shipped with a pre-configured environment. So, no pre-configuration is required. + * Most properties in [JsEnvironment] have calculated defaults right in the interface. + * Only two properties need explicit override. + * + * The extension defines them as follows: + * + * 1. [JsEnvironment.projectDir] –> `project.projectDir`. + * 2. [JsEnvironment.moduleVersion] —> `project.extra["versionToPublishJs"]`. + * + * There are two ways to modify the environment: + * + * 1. Update [JsEnvironment] directly. Go with this option when it is a global change + * that should affect all projects which use this extension. + * 2. Use [JsExtension.environment] scope — for temporary and custom overridings. + * + * An example of a property overriding: + * + * ``` + * javascript { + * environment { + * moduleVersion = "$moduleVersion-SPECIAL_EDITION" + * } + * } + * ``` + * + * Please note, environment should be set up firstly to have the effect on the parts + * of the extension that use it. + * + * ### Tasks and Plugins + * + * The spirit of tasks configuration in this extension is extracting the code that defines and + * registers tasks into extension functions upon `JsTasks` in `buildSrc`. Those extensions should be + * named after a task it registers or a task group if several tasks are registered at once. + * Then this extension is called in a project's `build.gradle.kts`. + * + * `JsTasks` and `JsPlugins` scopes extend [JsContext] which provides access + * to the current [JsEnvironment] and shortcuts for running `npm` tool. + * + * Below is the simplest example of how to create a primitive `printNpmVersion` task. + * + * Firstly, a corresponding extension function should be defined in `buildSrc`: + * + * ``` + * fun JsTasks.printNpmVersion() = + * register("printNpmVersion") { + * doLast { + * npm("--version") + * } + * } + * ``` + * + * Secondly, in a project's `build.gradle.kts` this extension is called: + * + * ``` + * javascript { + * tasks { + * printNpmVersion() + * } + * } + * ``` + * + * An extension function is not restricted to register exactly one task. If several tasks can + * be grouped into a logical bunch, they should be registered together: + * + * ``` + * fun JsTasks.build() { + * assembleJs() + * testJs() + * generateCoverageReport() + * } + * + * private fun JsTasks.assembleJs() = ... + * + * private fun JsTasks.testJs() = ... + * + * private fun JsTasks.generateCoverageReport() = ... + * ``` + * + * This section is mostly dedicated to tasks. But tasks and plugins are configured + * in a very similar way. So, everything above is also applicable to plugins. More detailed + * guides can be found in docs to `JsTasks` and `JsPlugins`. + * + * @see [ConfigurableJsEnvironment] + * @see [JsTasks] + * @see [JsPlugins] + */ +fun Project.javascript(configuration: JsExtension.() -> Unit) { + extensions.run { + configuration.invoke( + findByType() ?: create("jsExtension", project) + ) + } +} + +/** + * Scope for performing JavaScript-related configuration. + * + * @see [javascript] + */ +open class JsExtension(internal val project: Project) { + + private val configurableEnvironment = ConfigurableJsEnvironment( + object : JsEnvironment { + override val projectDir = project.projectDir + override val moduleVersion = project.extra["versionToPublishJs"].toString() + } + ) + + val environment: JsEnvironment = configurableEnvironment + val tasks: JsTasks = JsTasks(environment, project) + val plugins: JsPlugins = JsPlugins(environment, project) + + /** + * Overrides default values of [JsEnvironment]. + * + * Please note, environment should be set up firstly to have the effect on the parts + * of the extension that use it. + */ + fun environment(overridings: ConfigurableJsEnvironment.() -> Unit) = + configurableEnvironment.run(overridings) + + /** + * Configures [JavaScript-related tasks][JsTasks]. + */ + fun tasks(configurations: JsTasks.() -> Unit) = + tasks.run(configurations) + + /** + * Configures [JavaScript-related plugins][JsPlugins]. + */ + fun plugins(configurations: JsPlugins.() -> Unit) = + plugins.run(configurations) + +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/plugin/Idea.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/plugin/Idea.kt new file mode 100644 index 0000000..5e51155 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/plugin/Idea.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.javascript.plugin + +import org.gradle.kotlin.dsl.configure +import org.gradle.plugins.ide.idea.model.IdeaModel + +/** + * Applies and configures `idea` plugin to work with a JavaScript module. + * + * In particular, this method: + * + * 1. Specifies directories for production and test sources. + * 2. Excludes directories with generated code and build artifacts. + * + * @see JsPlugins + */ +fun JsPlugins.idea() { + + plugins { + apply("org.gradle.idea") + } + + extensions.configure { + + module { + sourceDirs.add(srcDir) + testSources.from(testSrcDir) + + excludeDirs.addAll( + listOf( + nodeModules, + nycOutput, + genProtoMain, + genProtoTest + ) + ) + } + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/plugin/JsPlugins.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/plugin/JsPlugins.kt new file mode 100644 index 0000000..60a088a --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/plugin/JsPlugins.kt @@ -0,0 +1,92 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.javascript.plugin + +import io.spine.internal.gradle.javascript.JsContext +import io.spine.internal.gradle.javascript.JsEnvironment +import org.gradle.api.Project +import org.gradle.api.plugins.ExtensionContainer +import org.gradle.api.plugins.PluginContainer +import org.gradle.api.tasks.TaskContainer + +/** + * A scope for applying and configuring JavaScript-related plugins. + * + * The scope extends [JsContext] and provides shortcuts for key project's containers: + * + * 1. [plugins]. + * 2. [extensions]. + * 3. [tasks]. + * + * Let's imagine one wants to apply and configure `FooBar` plugin. To do that, several steps + * should be completed: + * + * 1. Declare the corresponding extension function upon [JsPlugins] named after the plugin. + * 2. Apply and configure the plugin inside that function. + * 3. Call the resulted extension in your `build.gradle.kts` file. + * + * Here's an example of `javascript/plugin/FooBar.kt`: + * + * ``` + * fun JsPlugins.fooBar() { + * plugins.apply("com.fooBar") + * extensions.configure { + * // ... + * } + * } + * ``` + * + * And here's how to apply it in `build.gradle.kts`: + * + * ``` + * import io.spine.internal.gradle.javascript.javascript + * import io.spine.internal.gradle.javascript.plugins.fooBar + * + * // ... + * + * javascript { + * plugins { + * fooBar() + * } + * } + * ``` + */ +class JsPlugins(jsEnv: JsEnvironment, project: Project) : JsContext(jsEnv, project) { + + internal val plugins = project.plugins + internal val extensions = project.extensions + internal val tasks = project.tasks + + internal fun plugins(configurations: PluginContainer.() -> Unit) = + plugins.run(configurations) + + internal fun extensions(configurations: ExtensionContainer.() -> Unit) = + extensions.run(configurations) + + internal fun tasks(configurations: TaskContainer.() -> Unit) = + tasks.run(configurations) +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/plugin/McJs.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/plugin/McJs.kt new file mode 100644 index 0000000..61e59d5 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/plugin/McJs.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.javascript.plugin + +import org.gradle.api.Task +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider +import org.gradle.kotlin.dsl.withGroovyBuilder + +/** + * Applies `mc-js` plugin and specifies directories for generated code. + * + * @see JsPlugins + */ +fun JsPlugins.mcJs() { + + plugins { + apply("io.spine.mc-js") + } + + // Temporarily use GroovyInterop. + // Currently, it is not possible to obtain `McJsPlugin` on the classpath of `buildSrc`. + // See issue: https://github.com/SpineEventEngine/config/issues/298 + + project.withGroovyBuilder { + "protoJs" { + setProperty("generatedMainDir", genProtoMain) + setProperty("generatedTestDir", genProtoTest) + } + } +} + +/** + * Locates `generateJsonParsers` in this [TaskContainer]. + * + * The task generates JSON-parsing code for JavaScript messages compiled from Protobuf. + */ +val TaskContainer.generateJsonParsers: TaskProvider + get() = named("generateJsonParsers") diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/plugin/Protobuf.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/plugin/Protobuf.kt new file mode 100644 index 0000000..c50ddd9 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/plugin/Protobuf.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.javascript.plugin + +import com.google.protobuf.gradle.ProtobufExtension +import com.google.protobuf.gradle.id +import com.google.protobuf.gradle.remove +import io.spine.internal.dependency.Protobuf + +/** + * Applies and configures `protobuf` plugin to work with a JavaScript module. + * + * In particular, this method: + * + * 1. Specifies an executable for `protoc` compiler. + * 2. Configures `GenerateProtoTask`. + * + * @see JsPlugins + */ +fun JsPlugins.protobuf() { + + plugins { + apply(Protobuf.GradlePlugin.id) + } + + val protobufExt = project.extensions.getByType(ProtobufExtension::class.java) + protobufExt.apply { + + generatedFilesBaseDir = projectDir.path + + protoc { + artifact = Protobuf.compiler + } + + generateProtoTasks { + all().forEach { task -> + + task.builtins { + + // Do not use java builtin output in this project. + + remove("java") + + // For information on JavaScript code generation please see + // https://github.com/google/protobuf/blob/master/js/README.md + + id("js") { + option("import_style=commonjs") + outputSubDir = genProtoDirName + } + } + + val sourceSet = task.sourceSet.name + val testClassifier = if (sourceSet == "test") "_test" else "" + val artifact = "${project.group}_${project.name}_${moduleVersion}" + val descriptor = "$artifact$testClassifier.desc" + + task.generateDescriptorSet = true + task.descriptorSetOptions.path = + "${projectDir}/build/descriptors/${sourceSet}/${descriptor}" + } + } + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/Assemble.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/Assemble.kt new file mode 100644 index 0000000..4739307 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/Assemble.kt @@ -0,0 +1,205 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.javascript.task + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.node.ObjectNode +import com.google.protobuf.gradle.GenerateProtoTask +import io.spine.internal.gradle.base.assemble +import io.spine.internal.gradle.javascript.plugin.generateJsonParsers +import io.spine.internal.gradle.named +import io.spine.internal.gradle.register +import io.spine.internal.gradle.TaskName +import org.gradle.api.Task +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider +import org.gradle.kotlin.dsl.withType + +/** + * Registers tasks for assembling JavaScript artifacts. + * + * Please note, this task group depends on [mc-js][io.spine.internal.gradle.javascript.plugin.mcJs] + * and [protobuf][io.spine.internal.gradle.javascript.plugin.protobuf]` plugins. Therefore, + * these plugins should be applied in the first place. + * + * List of tasks to be created: + * + * 1. [TaskContainer.assembleJs]. + * 2. [TaskContainer.compileProtoToJs]. + * 3. [TaskContainer.installNodePackages]. + * 4. [TaskContainer.updatePackageVersion]. + * + * Here's an example of how to apply it in `build.gradle.kts`: + * + * ``` + * import io.spine.internal.gradle.javascript.javascript + * import io.spine.internal.gradle.javascript.task.assemble + * + * // ... + * + * javascript { + * tasks { + * assemble() + * } + * } + * ``` + * + * @param configuration any additional configuration related to the module's assembling. + */ +fun JsTasks.assemble(configuration: JsTasks.() -> Unit = {}) { + + installNodePackages() + + compileProtoToJs().also { + generateJsonParsers.configure { + dependsOn(it) + } + } + + updatePackageVersion() + + assembleJs().also { + assemble.configure { + dependsOn(it) + } + } + + configuration() +} + +private val assembleJsName = TaskName.of("assembleJs") + +/** + * Locates `assembleJs` task in this [TaskContainer]. + * + * It is a lifecycle task that produces consumable JavaScript artifacts. + */ +val TaskContainer.assembleJs: TaskProvider + get() = named(assembleJsName) + +private fun JsTasks.assembleJs() = + register(assembleJsName) { + + description = "Assembles JavaScript sources into consumable artifacts." + group = JsTasks.Group.assemble + + dependsOn( + installNodePackages, + compileProtoToJs, + updatePackageVersion, + generateJsonParsers + ) + } + +private val compileProtoToJsName = TaskName.of("compileProtoToJs") + +/** + * Locates `compileProtoToJs` task in this [TaskContainer]. + * + * The task is responsible for compiling Protobuf messages into JavaScript. It aggregates the tasks + * provided by `protobuf` plugin that perform actual compilation. + */ +val TaskContainer.compileProtoToJs: TaskProvider + get() = named(compileProtoToJsName) + +private fun JsTasks.compileProtoToJs() = + register(compileProtoToJsName) { + + description = "Compiles Protobuf messages into JavaScript." + group = JsTasks.Group.assemble + + withType() + .forEach { dependsOn(it) } + } + +private val installNodePackagesName = TaskName.of("installNodePackages") + +/** + * Locates `installNodePackages` task in this [TaskContainer]. + * + * The task installs Node packages which this module depends on using `npm install` command. + * + * The `npm install` command is executed with the vulnerability check disabled since + * it cannot fail the task execution despite on vulnerabilities found. + * + * To check installed Node packages for vulnerabilities execute + * [TaskContainer.auditNodePackages] task. + * + * See [npm-install | npm Docs](https://docs.npmjs.com/cli/v8/commands/npm-install). + */ +val TaskContainer.installNodePackages: TaskProvider + get() = named(installNodePackagesName) + +private fun JsTasks.installNodePackages() = + register(installNodePackagesName) { + + description = "Installs module`s Node dependencies." + group = JsTasks.Group.assemble + + inputs.file(packageJson) + outputs.dir(nodeModules) + + doLast { + npm("set", "audit", "false") + npm("install") + } + } + +private val updatePackageVersionName = TaskName.of("updatePackageVersion") + +/** + * Locates `updatePackageVersion` task in this [TaskContainer]. + * + * The task sets the module's version in `package.json` to the value of + * [moduleVersion][io.spine.internal.gradle.javascript.JsEnvironment.moduleVersion] + * specified in the current `JsEnvironment`. + */ +val TaskContainer.updatePackageVersion: TaskProvider + get() = named(updatePackageVersionName) + +private fun JsTasks.updatePackageVersion() = + register(updatePackageVersionName) { + + description = "Sets a module's version in `package.json`." + group = JsTasks.Group.assemble + + doLast { + val objectNode = ObjectMapper() + .readValue(packageJson, ObjectNode::class.java) + .put("version", moduleVersion) + + packageJson.writeText( + + // We are going to stick to JSON formatting used by `npm` itself. + // So that modifying the line with the version would ONLY affect a single line + // when comparing two files i.e. in Git. + + (objectNode.toPrettyString() + '\n') + .replace("\" : ", "\": ") + ) + } + } diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/Check.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/Check.kt new file mode 100644 index 0000000..c15f3e2 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/Check.kt @@ -0,0 +1,197 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.javascript.task + +import io.spine.internal.gradle.base.check +import io.spine.internal.gradle.java.test +import io.spine.internal.gradle.javascript.isWindows +import io.spine.internal.gradle.named +import io.spine.internal.gradle.register +import io.spine.internal.gradle.TaskName +import org.gradle.api.Task +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider + +/** + * Registers tasks for verifying a JavaScript module. + * + * Please note, this task group depends on [assemble] tasks. Therefore, assembling tasks should + * be applied in the first place. + * + * List of tasks to be created: + * + * 1. [TaskContainer.checkJs]. + * 2. [TaskContainer.auditNodePackages]. + * 3. [TaskContainer.testJs]. + * 4. [TaskContainer.coverageJs]. + * + * Here's an example of how to apply it in `build.gradle.kts`: + * + * ``` + * import io.spine.internal.gradle.javascript.javascript + * import io.spine.internal.gradle.javascript.task.assemble + * import io.spine.internal.gradle.javascript.task.check + * + * // ... + * + * javascript { + * tasks { + * assemble() + * check() + * } + * } + * ``` + * + * @param configuration any additional configuration related to the module's verification. + */ +fun JsTasks.check(configuration: JsTasks.() -> Unit = {}) { + + auditNodePackages() + coverageJs() + testJs() + + checkJs().also { + check.configure { + dependsOn(it) + } + } + + configuration() +} + +private val checkJsName = TaskName.of("checkJs") + +/** + * Locates `checkJs` task in this [TaskContainer]. + * + * The task runs tests, audits NPM modules and creates a test-coverage report. + */ +val TaskContainer.checkJs: TaskProvider + get() = named(checkJsName) + +private fun JsTasks.checkJs() = + register(checkJsName) { + + description = "Runs tests, audits NPM modules and creates a test-coverage report." + group = JsTasks.Group.check + + dependsOn( + auditNodePackages, + coverageJs, + testJs, + ) + } + +private val auditNodePackagesName = TaskName.of("auditNodePackages") + +/** + * Locates `auditNodePackages` task in this [TaskContainer]. + * + * The task audits the module dependencies using the `npm audit` command. + * + * The `audit` command submits a description of the dependencies configured in the module + * to a public registry and asks for a report of known vulnerabilities. If any are found, + * then the impact and appropriate remediation will be calculated. + * + * @see npm-audit | npm Docs + */ +val TaskContainer.auditNodePackages: TaskProvider + get() = named(auditNodePackagesName) + +private fun JsTasks.auditNodePackages() = + register(auditNodePackagesName) { + + description = "Audits the module's Node dependencies." + group = JsTasks.Group.check + + inputs.dir(nodeModules) + + doLast { + + // `critical` level is set as the minimum level of vulnerability for `npm audit` + // to exit with a non-zero code. + + npm("set", "audit-level", "critical") + + try { + npm("audit") + } catch (ignored: Exception) { + npm("audit", "--registry", "https://registry.npmjs.eu") + } + } + + dependsOn(installNodePackages) + } + +private val coverageJsName = TaskName.of("coverageJs") + +/** + * Locates `coverageJs` task in this [TaskContainer]. + * + * The task runs the JavaScript tests and collects the code coverage. + */ +val TaskContainer.coverageJs: TaskProvider + get() = named(coverageJsName) + +private fun JsTasks.coverageJs() = + register(coverageJsName) { + + description = "Runs the JavaScript tests and collects the code coverage." + group = JsTasks.Group.check + + outputs.dir(nycOutput) + + doLast { + npm("run", if (isWindows()) "coverage:win" else "coverage:unix") + } + + dependsOn(assembleJs) + } + +private val testJsName = TaskName.of("testJs") + +/** + * Locates `testJs` task in this [TaskContainer]. + * + * The task runs JavaScript tests. + */ +val TaskContainer.testJs: TaskProvider + get() = named(testJsName) + +private fun JsTasks.testJs() = + register(testJsName) { + + description = "Runs JavaScript tests." + group = JsTasks.Group.check + + doLast { + npm("run", "test") + } + + dependsOn(assembleJs) + mustRunAfter(test) + } diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/Clean.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/Clean.kt new file mode 100644 index 0000000..4680591 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/Clean.kt @@ -0,0 +1,124 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.javascript.task + +import io.spine.internal.gradle.base.clean +import io.spine.internal.gradle.named +import io.spine.internal.gradle.register +import io.spine.internal.gradle.TaskName +import org.gradle.api.tasks.Delete +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider + +/** + * Registers tasks for deleting output of JavaScript builds. + * + * Please note, this task group depends on [assemble] tasks. Therefore, assembling tasks should + * be applied in the first place. + * + * List of tasks to be created: + * + * 1. [TaskContainer.cleanJs]. + * 2. [TaskContainer.cleanGenerated]. + * + * Here's an example of how to apply it in `build.gradle.kts`: + * + * ``` + * import io.spine.internal.gradle.javascript.javascript + * import io.spine.internal.gradle.javascript.task.assemble + * import io.spine.internal.gradle.javascript.task.clean + * + * // ... + * + * javascript { + * tasks { + * assemble() + * clean() + * } + * } + * ``` + */ +fun JsTasks.clean() { + + cleanGenerated() + + cleanJs().also { + clean.configure { + dependsOn(it) + } + } +} + +private val cleanJsName = TaskName.of("cleanJs", Delete::class) + +/** + * Locates `cleanJs` task in this [TaskContainer]. + * + * The task deletes output of `assembleJs` task and output of its dependants. + */ +val TaskContainer.cleanJs: TaskProvider + get() = named(cleanJsName) + +private fun JsTasks.cleanJs() = + register(cleanJsName) { + + description = "Cleans output of `assembleJs` task and output of its dependants." + group = JsTasks.Group.clean + + delete( + assembleJs.map { it.outputs }, + compileProtoToJs.map { it.outputs }, + installNodePackages.map { it.outputs }, + ) + + dependsOn( + cleanGenerated + ) + } + +private val cleanGeneratedName = TaskName.of("cleanGenerated", Delete::class) + +/** + * Locates `cleanGenerated` task in this [TaskContainer]. + * + * The task deletes directories with generated code and reports. + */ +val TaskContainer.cleanGenerated: TaskProvider + get() = named(cleanGeneratedName) + +private fun JsTasks.cleanGenerated() = + register(cleanGeneratedName) { + + description = "Cleans generated code and reports." + group = JsTasks.Group.clean + + delete( + genProtoMain, + genProtoTest, + nycOutput, + ) + } diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/IntegrationTest.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/IntegrationTest.kt new file mode 100644 index 0000000..cf94995 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/IntegrationTest.kt @@ -0,0 +1,128 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.javascript.task + +import io.spine.internal.gradle.TaskName +import io.spine.internal.gradle.base.build +import io.spine.internal.gradle.named +import io.spine.internal.gradle.register +import org.gradle.api.Task +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider + +private val integrationTestName = TaskName.of("integrationTest") + +/** + * Locates `integrationTest` task in this [TaskContainer]. + * + * The task runs integration tests of the `spine-web` library against + * a sample Spine-based application. + * + * A sample Spine-based application is run from the `test-app` module before integration + * tests and is stopped as the tests complete. + * + * See also: `./integration-tests/README.MD` + */ +val TaskContainer.integrationTest: TaskProvider + get() = named(integrationTestName) + +/** + * Registers [TaskContainer.integrationTest] task. + * + * The task runs integration tests of the `spine-web` library against + * a sample Spine-based application. + * + * Please note, this task depends on [assemble] and `client-js:publishJsLocally` tasks. + * + * Here's an example of how to apply it in `build.gradle.kts`: + * + * ``` + * import io.spine.internal.gradle.javascript.javascript + * import io.spine.internal.gradle.javascript.task.integrationTest + * + * // ... + * + * javascript { + * tasks { + * assemble() + * integrationTest() + * } + * } + * ``` + */ +@Suppress("unused") +fun JsTasks.integrationTest() { + + linkSpineWebModule() + + register(integrationTestName) { + + // Find a way to run the same tests against `spine-web` in `client-js` module + // to recover coverage. + // See issue: https://github.com/SpineEventEngine/web/issues/96 + + description = "Runs integration tests of the `spine-web` library " + + "against the sample application." + group = JsTasks.Group.check + + dependsOn(build, linkSpineWebModule, ":test-app:appBeforeIntegrationTest") + + doLast { + npm("run", "test") + } + + finalizedBy(":test-app:appAfterIntegrationTest") + } +} + +private val linkSpineWebModuleName = TaskName.of("linkSpineWebModule") + +/** + * Locates `linkSpineWebModule` task in this [TaskContainer]. + * + * The task installs unpublished artifact of `spine-web` library as a module dependency. + * + * Creates a symbolic link from globally-installed `spine-web` library to `node_modules` of + * the current project. + * + * See also: [npm-link | npm Docs](https://docs.npmjs.com/cli/v8/commands/npm-link) + */ +val TaskContainer.linkSpineWebModule: TaskProvider + get() = named(linkSpineWebModuleName) + +private fun JsTasks.linkSpineWebModule() = + register(linkSpineWebModuleName) { + + description = "Install unpublished artifact of `spine-web` library as a module dependency." + group = JsTasks.Group.assemble + + dependsOn(":client-js:publishJsLocally") + + doLast { + npm("run", "installLinkedLib") + } + } diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/JsTasks.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/JsTasks.kt new file mode 100644 index 0000000..b13d996 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/JsTasks.kt @@ -0,0 +1,118 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.javascript.task + +import io.spine.internal.gradle.javascript.JsEnvironment +import io.spine.internal.gradle.javascript.JsContext +import org.gradle.api.Project +import org.gradle.api.tasks.TaskContainer + +/** + * A scope for registering and configuring JavaScript-related tasks. + * + * The scope provides: + * + * 1. Access to the current [JsContext]. + * 2. Project's [TaskContainer]. + * 3. Default task groups. + * + * Supposing, one needs to create a new task that would participate in building. Let the task name + * be `bundleJs`. To do that, several steps should be completed: + * + * 1. Define the task name and type using [TaskName][io.spine.internal.gradle.TaskName]. + * 2. Create a public typed reference for the task upon [TaskContainer]. It would facilitate + * referencing to the new task, so that external tasks could depend on it. This reference + * should be documented. + * 3. Implement an extension upon [JsTasks] to register the task. + * 4. Call the resulted extension from `build.gradle.kts`. + * + * Here's an example of `bundleJs()` extension: + * + * ``` + * import io.spine.internal.gradle.named + * import io.spine.internal.gradle.register + * import io.spine.internal.gradle.TaskName + * import org.gradle.api.Task + * import org.gradle.api.tasks.TaskContainer + * import org.gradle.api.tasks.Exec + * + * // ... + * + * private val bundleJsName = TaskName.of("bundleJs", Exec::class) + * + * /** + * * Locates `bundleJs` task in this [TaskContainer]. + * * + * * The task bundles JS sources using `webpack` tool. + * */ + * val TaskContainer.bundleJs: TaskProvider + * get() = named(bundleJsName) + * + * fun JsTasks.bundleJs() = + * register(bundleJsName) { + * + * description = "Bundles JS sources using `webpack` tool." + * group = JsTasks.Group.build + * + * // ... + * } + * ``` + * + * And here's how to apply it in `build.gradle.kts`: + * + * ``` + * import io.spine.internal.gradle.javascript.javascript + * import io.spine.internal.gradle.javascript.task.bundleJs + * + * // ... + * + * javascript { + * tasks { + * bundleJs() + * } + * } + * ``` + * + * Declaring typed references upon [TaskContainer] is optional. But it is highly encouraged + * to reference other tasks by such extensions instead of hard-typed string values. + */ +class JsTasks(jsEnv: JsEnvironment, project: Project) + : JsContext(jsEnv, project), TaskContainer by project.tasks +{ + /** + * Default task groups for tasks that participate in building a JavaScript module. + * + * @see [org.gradle.api.Task.getGroup] + */ + internal object Group { + const val assemble = "JavaScript/Assemble" + const val check = "JavaScript/Check" + const val clean = "JavaScript/Clean" + const val build = "JavaScript/Build" + const val publish = "JavaScript/Publish" + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/LicenseReport.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/LicenseReport.kt new file mode 100644 index 0000000..2a868d4 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/LicenseReport.kt @@ -0,0 +1,89 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.javascript.task + +import io.spine.internal.gradle.named +import io.spine.internal.gradle.register +import io.spine.internal.gradle.report.license.generateLicenseReport +import io.spine.internal.gradle.TaskName +import org.gradle.api.Task +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider + +/** + * Registers [npmLicenseReport] task for including NPM dependencies into license reports. + * + * The task depends on [generateLicenseReport]. + * + * Here's an example of how to apply it in `build.gradle.kts`: + * + * ``` + * import io.spine.internal.gradle.javascript.javascript + * import io.spine.internal.gradle.javascript.task.clean + * + * // ... + * + * javascript { + * tasks { + * licenseReport() + * } + * } + * ``` + */ +@Suppress("unused") +fun JsTasks.licenseReport() { + npmLicenseReport().also { + generateLicenseReport.configure { + finalizedBy(it) + } + } +} + +private val npmLicenseReportName = TaskName.of("npmLicenseReport") + +/** + * Locates `npmLicenseReport` task in this [TaskContainer]. + * + * The task generates the report on NPM dependencies and their licenses. + */ +val TaskContainer.npmLicenseReport: TaskProvider + get() = named(npmLicenseReportName) + +private fun JsTasks.npmLicenseReport() = + register(npmLicenseReportName) { + + description = "Generates the report on NPM dependencies and their licenses." + group = JsTasks.Group.build + + doLast { + + // The script below generates license report for NPM dependencies and appends it + // to the report for Java dependencies generated by `generateLicenseReport` task. + + npm("run", "license-report") + } + } diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/Publish.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/Publish.kt new file mode 100644 index 0000000..01a67f5 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/Publish.kt @@ -0,0 +1,194 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.javascript.task + +import io.spine.internal.gradle.publish.publish +import io.spine.internal.gradle.named +import io.spine.internal.gradle.register +import io.spine.internal.gradle.TaskName +import org.gradle.api.Task +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider + +/** + * Registers tasks for publishing a JavaScript module. + * + * Please note, this task group depends on [assemble] tasks. Therefore, assembling tasks should + * be applied in the first place. + * + * List of tasks to be created: + * + * 1. [TaskContainer.publishJs]. + * 2. [TaskContainer.publishJsLocally]. + * 3. [TaskContainer.prepareJsPublication]. + * + * Here's an example of how to apply it in `build.gradle.kts`: + * + * ``` + * import io.spine.internal.gradle.javascript.javascript + * import io.spine.internal.gradle.javascript.task.assemble + * import io.spine.internal.gradle.javascript.task.publish + * + * // ... + * + * javascript { + * tasks { + * assemble() + * publish() + * } + * } + * ``` + */ +fun JsTasks.publish() { + + transpileSources() + prepareJsPublication() + publishJsLocally() + + publishJs().also { + publish.configure { + dependsOn(it) + } + } +} + +private val transpileSourcesName = TaskName.of("transpileSources") + +/** + * Locates `transpileSources` task in this [TaskContainer]. + * + * The task transpiles JavaScript sources using Babel before their publishing. + */ +val TaskContainer.transpileSources: TaskProvider + get() = named(transpileSourcesName) + +private fun JsTasks.transpileSources() = + register(transpileSourcesName) { + + description = "Transpiles JavaScript sources using Babel before their publishing." + group = JsTasks.Group.publish + + doLast { + npm("run", "transpile-before-publish") + } + } + +private val prepareJsPublicationName = TaskName.of("prepareJsPublication") + +/** + * Locates `prepareJsPublication` task in this [TaskContainer]. + * + * This is a lifecycle task that prepares the NPM package in + * [publicationDirectory][io.spine.internal.gradle.javascript.JsEnvironment.publicationDir] + * of the current `JsEnvironment`. + */ +val TaskContainer.prepareJsPublication: TaskProvider + get() = named(prepareJsPublicationName) + +private fun JsTasks.prepareJsPublication() = + register(prepareJsPublicationName) { + + description = "Prepares the NPM package for publishing." + group = JsTasks.Group.publish + + // We need to copy two files into a destination directory without overwriting its content. + // Default `Copy` task is not used since it overwrites the content of a destination + // when copying there. + // See issue: https://github.com/gradle/gradle/issues/1012 + + doLast { + project.copy { + from( + packageJson, + npmrc + ) + + into(publicationDir) + } + } + + dependsOn( + assembleJs, + transpileSources + ) + } + +private val publishJsLocallyName = TaskName.of("publishJsLocally") + +/** + * Locates `publishJsLocally` task in this [TaskContainer]. + * + * The task publishes the prepared NPM package locally using `npm link`. + * + * @see npm-link | npm Docs + */ +val TaskContainer.publishJsLocally: TaskProvider + get() = named(publishJsLocallyName) + +private fun JsTasks.publishJsLocally() = + register(publishJsLocallyName) { + + description = "Publishes the NPM package locally with `npm link`." + group = JsTasks.Group.publish + + doLast { + publicationDir.npm("link") + } + + dependsOn(prepareJsPublication) + } + +private val publishJsName = TaskName.of("publishJs") + +/** + * Locates `publishJs` task in this [TaskContainer]. + * + * The task publishes the prepared NPM package from + * [publicationDirectory][io.spine.internal.gradle.javascript.JsEnvironment.publicationDir] + * using `npm publish`. + * + * Please note, in order to publish an NMP package, a valid + * [npmAuthToken][io.spine.internal.gradle.javascript.JsEnvironment.npmAuthToken] should be + * set. If no token is set, a default dummy value is quite enough for the local development. + * + * @see npm-publish | npm Docs + */ +val TaskContainer.publishJs: TaskProvider + get() = named(publishJsName) + +private fun JsTasks.publishJs() = + register(publishJsName) { + + description = "Publishes the NPM package with `npm publish`." + group = JsTasks.Group.publish + + doLast { + publicationDir.npm("publish") + } + + dependsOn(prepareJsPublication) + } diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/Webpack.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/Webpack.kt new file mode 100644 index 0000000..99da3f1 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/javascript/task/Webpack.kt @@ -0,0 +1,108 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.javascript.task + +import io.spine.internal.gradle.TaskName +import io.spine.internal.gradle.named +import io.spine.internal.gradle.register +import org.gradle.api.tasks.Copy +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider + +/** + * Configures `assembleJs` task and creates `copyBundledJs` task to work with `webpack` bundler. + * + * Please note, this task group depends on [assemble] and [publish] tasks. Therefore, those tasks + * should be applied in the first place. + * + * In particular, this method: + * + * 1. Extends `assembleJs` task to bundle sources during assembling. + * 2. Creates `copyBundledJs` task and binds it to `prepareJsPublication` task execution. + * + * Here's an example of how to apply it in `build.gradle.kts`: + * + * ``` + * import io.spine.internal.gradle.javascript.javascript + * import io.spine.internal.gradle.javascript.task.assemble + * import io.spine.internal.gradle.javascript.task.publish + * import io.spine.internal.gradle.javascript.task.webpack + * + * // ... + * + * javascript { + * tasks { + * assemble() + * publish() + * webpack() + * } + * } + * ``` + */ +@Suppress("unused") +fun JsTasks.webpack() { + + assembleJs.configure { + + outputs.dir(webpackOutput) + + doLast { + npm("run", "build") + npm("run", "build-dev") + } + } + + // Temporarily don't publish a bundle. + // See: https://github.com/SpineEventEngine/web/issues/61 + + copyBundledJs()/*.also { + prepareJsPublication.configure { + dependsOn(it) + } + }*/ +} + +private val copyBundledJsName = TaskName.of("copyBundledJs", Copy::class) + +/** + * Locates `copyBundledJs` task in this [TaskContainer]. + * + * The task copies bundled JavaScript sources to the publication directory. + */ +@Suppress("unused") +val TaskContainer.copyBundledJs: TaskProvider + get() = named(copyBundledJsName) + +private fun JsTasks.copyBundledJs() = + register(copyBundledJsName) { + + description = "Copies bundled JavaScript sources to the NPM publication directory." + group = JsTasks.Group.publish + + from(assembleJs.map { it.outputs }) + into(webpackPublicationDir) + } diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/kotlin/KotlinConfig.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/kotlin/KotlinConfig.kt new file mode 100644 index 0000000..b2964ce --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/kotlin/KotlinConfig.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.kotlin + +import org.gradle.jvm.toolchain.JavaLanguageVersion +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +/** + * Sets [Java toolchain](https://kotlinlang.org/docs/gradle.html#gradle-java-toolchains-support) + * to the specified version (e.g. 11 or 8). + */ +fun KotlinJvmProjectExtension.applyJvmToolchain(version: Int) { + jvmToolchain { + languageVersion.set(JavaLanguageVersion.of(version)) + } +} + +/** + * Sets [Java toolchain](https://kotlinlang.org/docs/gradle.html#gradle-java-toolchains-support) + * to the specified version (e.g. "11" or "8"). + */ +@Suppress("unused") +fun KotlinJvmProjectExtension.applyJvmToolchain(version: String) = + applyJvmToolchain(version.toInt()) + +/** + * Opts-in to experimental features that we use in our codebase. + */ +@Suppress("unused") +fun KotlinCompile.setFreeCompilerArgs() { + kotlinOptions { + freeCompilerArgs = listOf( + "-Xskip-prerelease-check", + "-Xjvm-default=all", + "-Xinline-classes", + "-opt-in=" + + "kotlin.contracts.ExperimentalContracts," + + "kotlin.io.path.ExperimentalPathApi," + + "kotlin.ExperimentalUnsignedTypes," + + "kotlin.ExperimentalStdlibApi," + + "kotlin.experimental.ExperimentalTypeInference", + ) + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/protobuf/ProtoTaskExtensions.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/protobuf/ProtoTaskExtensions.kt new file mode 100644 index 0000000..0c4c8b4 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/protobuf/ProtoTaskExtensions.kt @@ -0,0 +1,342 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.protobuf + +import com.google.protobuf.gradle.GenerateProtoTask +import io.spine.internal.gradle.sourceSets +import java.io.File +import java.nio.file.Files +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.configurationcache.extensions.capitalized +import org.gradle.kotlin.dsl.get +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.dsl.KotlinCompile + +/** + * Obtains the name of the `generated` directory under the project root directory. + */ +private val Project.generatedDir: String + get() = "${projectDir}/generated" + +/** + * 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 protobuf code generation task for the code which cannot use Spine Model Compiler + * (e.g. the `base` project). + * + * The task configuration consists of the following steps: + * + * 1. Adding `"kotlin"` to the list of involved `protoc` builtins. + * + * 2. Generation of descriptor set file is turned on 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 { + * for (task in all()) { + * task.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() + configureIdeaDirs() +} + +/** + * 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 per 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 in order 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.*/) +private fun GenerateProtoTask.setupDescriptorSetFileCreation() { + // Tell `protoc` generate descriptor set file. + // The name of the generated file reflects project's Maven coordinates. + val ssn = sourceSet.name + generateDescriptorSet = true + val descriptorsDir = "${project.buildDir}/descriptors/${ssn}" + val descriptorName = project.descriptorSetName(sourceSet) + with(descriptorSetOptions) { + path = "$descriptorsDir/$descriptorName" + includeImports = true + includeSourceInfo = true + } + + // Make the descriptor set file included 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. + */ +private 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() + 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. + */ +private fun GenerateProtoTask.setupKotlinCompile() { + val kotlinCompile = project.kotlinCompileFor(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. + */ +private 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.capitalized() + return "process${infix}Resources" +} + +/** + * Attempts to obtain the Kotlin compilation Gradle task for the given source set. + * + * Typically, the task is named by a pattern: `compileKotlin`, or just + * `compileKotlin` if the source set name is `"main"`. If the task does not fit this described + * pattern, this method will not find it. + */ +private fun Project.kotlinCompileFor(sourceSet: SourceSet): KotlinCompile<*>? { + val taskName = sourceSet.getCompileTaskName("Kotlin") + return tasks.findByName(taskName) as KotlinCompile<*>? +} + +private fun File.residesIn(directory: File): Boolean = + canonicalFile.startsWith(directory.absolutePath) + +private fun GenerateProtoTask.configureIdeaDirs() = project.plugins.withId("idea") { + val module = project.extensions.findByType(IdeaModel::class.java)!!.module + + // Make IDEA forget about sources under `outputBaseDir`. + val protocOutputDir = File(outputBaseDir).parentFile + module.generatedSourceDirs.removeIf { dir -> + dir.residesIn(protocOutputDir) + } + + module.sourceDirs.removeIf { dir -> + dir.residesIn(protocOutputDir) + } + + val javaDir = generatedDir("java") + val kotlinDir = generatedDir("kotlin") + + // 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'. + project.tasks.withType(GenerateIdeaModule::class.java).forEach { + it.doFirst { + javaDir.mkdirs() + kotlinDir.mkdirs() + } + } + + if (isTest) { + module.testSources.run { + from(javaDir) + from(kotlinDir) + } + } else { + module.sourceDirs.run { + add(javaDir) + add(kotlinDir) + } + } + + module.generatedSourceDirs.run { + add(javaDir) + add(kotlinDir) + } +} + +/** + * Prints diagnostic output of `sourceDirs` and `generatedSourceDirs` of an [IdeaModule]. + * + * The warning `"unused"` is suppressed because this function is not used in + * the production mode. + */ +@Suppress("unused") +private fun IdeaModule.printSourceDirectories() { + println("**** [IDEA] Source directories:") + sourceDirs.forEach { println(it) } + println() + println("**** [IDEA] Generated source directories:") + generatedSourceDirs.forEach { println(it) } + println() +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/CheckVersionIncrement.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/CheckVersionIncrement.kt new file mode 100644 index 0000000..a81cbdc --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/CheckVersionIncrement.kt @@ -0,0 +1,129 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.publish + +import com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES +import com.fasterxml.jackson.dataformat.xml.XmlMapper +import io.spine.internal.gradle.Repository +import java.io.FileNotFoundException +import java.net.URL +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.Project +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.TaskAction + +/** + * A task which verifies that the current version of the library has not been published to the given + * Maven repository yet. + */ +open class CheckVersionIncrement : DefaultTask() { + + /** + * The Maven repository in which to look for published artifacts. + * + * We check both the `releases` and `snapshots` repositories. Artifacts in either of these repos + * may not be overwritten. + */ + @Input + lateinit var repository: Repository + + @Input + val version: String = project.version as String + + @TaskAction + fun fetchAndCheck() { + val artifact = "${project.artifactPath()}/${MavenMetadata.FILE_NAME}" + checkInRepo(repository.snapshots, artifact) + + if (repository.releases != repository.snapshots) { + checkInRepo(repository.releases, artifact) + } + } + + private fun checkInRepo(repoUrl: String, artifact: String) { + val metadata = fetch(repoUrl, artifact) + val versions = metadata?.versioning?.versions + val versionExists = versions?.contains(version) ?: false + if (versionExists) { + throw GradleException(""" + Version `$version` is already published to maven repository `$repoUrl`. + Try incrementing the library version. + 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") + return MavenMetadata.fetchAndParse(url) + } + + private fun Project.artifactPath(): String { + val group = this.group as String + val name = "spine-${this.name}" + + val pathElements = ArrayList(group.split('.')) + pathElements.add(name) + val path = pathElements.joinToString(separator = "/") + return path + } +} + +private data class MavenMetadata(var versioning: Versioning = Versioning()) { + + companion object { + + const val FILE_NAME = "maven-metadata.xml" + + private val mapper = XmlMapper() + + init { + mapper.configure(FAIL_ON_UNKNOWN_PROPERTIES, false) + } + + /** + * Fetches the metadata for the repository and parses the document. + * + *

If the document could not be found, assumes that the module was never + * released and thus has no metadata. + */ + fun fetchAndParse(url: URL): MavenMetadata? { + return try { + val metadata = mapper.readValue(url, MavenMetadata::class.java) + metadata + } catch (ignored: FileNotFoundException) { + null + } + } + } +} + +private data class Versioning(var versions: List = listOf()) diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/CloudArtifactRegistry.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/CloudArtifactRegistry.kt new file mode 100644 index 0000000..4751d56 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/CloudArtifactRegistry.kt @@ -0,0 +1,78 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.publish + +import com.google.auth.oauth2.GoogleCredentials +import com.google.cloud.artifactregistry.auth.DefaultCredentialProvider +import io.spine.internal.gradle.Credentials +import io.spine.internal.gradle.Repository +import java.io.IOException +import org.gradle.api.Project + +/** + * The experimental Google Cloud Artifact Registry repository. + * + * In order to successfully publish into this repository, a service account key is needed. + * The published must create a service account, grant it the permission to write into + * Artifact Registry, and generate a JSON key. + * Then, the key must be placed somewhere on the file system and the environment variable + * `GOOGLE_APPLICATION_CREDENTIALS` must be set to point at the key file. + * Once these preconditions are met, publishing becomes possible. + * + * Google provides a Gradle plugin for configuring the publishing repository credentials + * automatically. We achieve the same goal by assembling the credentials manually. We do so + * in order to fit the Google Cloud Artifact Registry repository into the standard frame of + * the Maven [Repository]-s. Applying the plugin would take a substantial effort due to the fact + * that both our publishing scripts and the Google's plugin use `afterEvaluate { }` hooks. + * 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. + */ +internal object CloudArtifactRegistry { + + private const val spineRepoLocation = "https://europe-maven.pkg.dev/spine-event-engine" + + val repository = Repository( + releases = "${spineRepoLocation}/releases", + snapshots = "${spineRepoLocation}/snapshots", + credentialValues = this::fetchGoogleCredentials + ) + + private fun fetchGoogleCredentials(p: Project): Credentials? { + return try { + val googleCreds = DefaultCredentialProvider() + val creds = googleCreds.credential as GoogleCredentials + creds.refreshIfExpired() + Credentials("oauth2accesstoken", creds.accessToken.tokenValue) + } catch (e: IOException) { + p.logger.info("Unable to fetch credentials for Google Cloud Artifact Registry." + + " Reason: '${e.message}'." + + " The debug output may contain more details.") + null + } + } +} + diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/CloudRepo.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/CloudRepo.kt new file mode 100644 index 0000000..148ebce --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/CloudRepo.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.publish + +import io.spine.internal.gradle.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 + * public repository, and publishing via private one. Their URLs differ in `/public` infix. + */ +internal object CloudRepo { + + private const val name = "CloudRepo" + private const val credentialsFile = "cloudrepo.properties" + private const val publicUrl = "https://spine.mycloudrepo.io/public/repositories" + private val privateUrl = publicUrl.replace("/public", "") + + /** + * CloudRepo repository for fetching of artifacts. + * + * Use this instance to depend on artifacts from this repository. + */ + val published = Repository( + name = name, + releases = "$publicUrl/releases", + snapshots = "$publicUrl/snapshots", + credentialsFile = credentialsFile + ) + + /** + * CloudRepo repository for publishing of artifacts. + * + * Use this instance to push new artifacts to this repository. + */ + val destination = Repository( + name = name, + releases = "$privateUrl/releases", + snapshots = "$privateUrl/snapshots", + credentialsFile = credentialsFile + ) +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/GitHubPackages.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/GitHubPackages.kt new file mode 100644 index 0000000..fef0682 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/GitHubPackages.kt @@ -0,0 +1,104 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.publish + +import io.spine.internal.gradle.Credentials +import io.spine.internal.gradle.Repository +import net.lingala.zip4j.ZipFile +import org.gradle.api.Project + +/** + * Maven repositories of Spine Event Engine projects hosted at GitHub Packages. + */ +internal object GitHubPackages { + + /** + * Obtains an instance of the GitHub Packages repository with the given name. + */ + fun repository(repoName: String): Repository { + val githubActor: String = actor() + 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) } + ) + } + + private fun actor(): String { + var githubActor: String? = System.getenv("GITHUB_ACTOR") + githubActor = if (githubActor.isNullOrEmpty()) { + "developers@spine.io" + } else { + githubActor + } + return githubActor + } +} + +/** + * This is a trick. Gradle only supports password or AWS credentials. + * Thus, we pass the GitHub token as a "password". + * + * See https://docs.github.com/en/actions/guides/publishing-java-packages-with-gradle#publishing-packages-to-github-packages + */ +private fun Project.credentialsWithToken(githubActor: String) = Credentials( + username = githubActor, + password = readGitHubToken() +) + +private fun Project.readGitHubToken(): String { + val githubToken: String? = System.getenv("GITHUB_TOKEN") + return if (githubToken.isNullOrEmpty()) { + readTokenFromArchive() + } else { + githubToken + } +} + +/** + * Read the personal access token for the `developers@spine.io` account which + * has only the permission to read public GitHub packages. + * + * The token is extracted from the archive called `aus.weis` stored under `buildSrc`. + * The archive has such an unusual name to avoid scanning for tokens placed in repositories + * which is performed by GitHub. Since we do not violate any security, it is OK to + * use such a workaround. + */ +private fun Project.readTokenFromArchive(): String { + val targetDir = "${buildDir}/token" + file(targetDir).mkdirs() + val fileToUnzip = "${rootDir}/buildSrc/aus.weis" + + logger.info( + "GitHub Packages: reading token by unzipping `$fileToUnzip` into `$targetDir`." + ) + ZipFile(fileToUnzip, "123".toCharArray()).extractAll(targetDir) + val file = file("$targetDir/token.txt") + val result = file.readText() + return result +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/IncrementGuard.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/IncrementGuard.kt new file mode 100644 index 0000000..b97a5ee --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/IncrementGuard.kt @@ -0,0 +1,108 @@ +/* + * Copyright 2023, 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. + */ + +@file:Suppress("unused") + +package io.spine.internal.gradle.publish + +import org.gradle.api.Plugin +import org.gradle.api.Project + +/** + * Gradle plugin which adds a [CheckVersionIncrement] task. + * + * The task is called `checkVersionIncrement` inserted before the `check` task. + */ +class IncrementGuard : Plugin { + + companion object { + const val taskName = "checkVersionIncrement" + } + + /** + * Adds the [CheckVersionIncrement] task to the project. + * + * Only adds the check if the project is built on Travis CI and the job is a pull request. + * + * The task only runs on non-master branches on GitHub Actions. This is done + * to prevent unexpected CI fails when re-building `master` multiple times, creating git + * tags, and in other cases that go outside of the "usual" development cycle. + */ + override fun apply(target: Project) { + val tasks = target.tasks + tasks.register(taskName, CheckVersionIncrement::class.java) { + repository = CloudRepo.published + tasks.getByName("check").dependsOn(this) + + shouldRunAfter("test") + if (!shouldCheckVersion()) { + logger.info( + "The build does not represent a GitHub Actions feature branch job, " + + "the `checkVersionIncrement` task is disabled." + ) + this.enabled = false + } + } + } + + /** + * Returns `true` if the current build is a GitHub Actions build which represents a push + * to a feature branch. + * + * Returns `false` if the associated reference is not a branch (e.g. a tag) or if it has + * the name which ends with `master` or `main`. + * + * For example, on the following branches the method would return `false`: + * + * 1. `master`. + * 2. `main`. + * 3. `2.x-jdk8-master`. + * 4. `2.x-jdk8-main`. + * + * @see + * List of default environment variables provided for GitHub Actions builds + */ + private fun shouldCheckVersion(): Boolean { + val event = System.getenv("GITHUB_EVENT_NAME") + val reference = System.getenv("GITHUB_REF") + if (event != "push" || reference == null) { + return false + } + val branch = branchName(reference) + return when { + branch == null -> false + branch.endsWith("master") -> false + branch.endsWith("main") -> false + else -> true + } + } + + private fun branchName(gitHubRef: String): String? { + val matches = Regex("refs/heads/(.+)").matchEntire(gitHubRef) + val branch = matches?.let { it.groupValues[1] } + return branch + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/JarDsl.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/JarDsl.kt new file mode 100644 index 0000000..2bc2870 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/JarDsl.kt @@ -0,0 +1,167 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.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. + * + * This artifact contains compilation output of `test` source set. By default, it is not published. + * + * Take a look on [SpinePublishing.testJar] for a usage example. + + * @see [artifacts] + */ +class TestJar { + + /** + * Set of modules, for which a test JAR will be published. + */ + var inclusions: Set = emptySet() + + /** + * Enables test JAR publishing for all published modules. + */ + 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. + */ +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() + val addTestJar = testJar.inclusions.contains(projectName) || testJar.enabled + return JarFlags( + sourcesJar = true, + javadocJar = true, + addProtoJar, addTestJar, + dokkaJar.kotlin, dokkaJar.java + ) + } + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/ProtoExts.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/ProtoExts.kt new file mode 100644 index 0000000..97e3319 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/ProtoExts.kt @@ -0,0 +1,89 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.publish + +import io.spine.internal.gradle.sourceSets +import java.io.File +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.file.FileTreeElement +import org.gradle.api.file.SourceDirectorySet +import org.gradle.api.tasks.bundling.Jar + +/** + * Tells whether there are any Proto sources in "main" source set. + */ +internal fun Project.hasProto(): Boolean { + val protoSources = protoSources() + val result = protoSources.any { it.exists() } + return result +} + +/** + * Locates directories with proto sources under the "main" source sets. + * + * Special treatment for Proto sources is needed, because they are not Java-related, and, + * thus, not included in `sourceSets["main"].allSource`. + */ +internal fun Project.protoSources(): Set { + val mainSourceSets = sourceSets.filter { + ss -> ss.name.endsWith("main", ignoreCase = true) + } + + val protoExtensions = mainSourceSets.mapNotNull { + it.extensions.findByName("proto") as SourceDirectorySet? + } + + val protoDirs = mutableSetOf() + protoExtensions.forEach { + protoDirs.addAll(it.srcDirs) + } + + return protoDirs +} + +/** + * Checks if the given file belongs to the Google `.proto` sources. + */ +internal fun FileTreeElement.isGoogleProtoSource(): Boolean { + val pathSegments = relativePath.segments + return pathSegments.isNotEmpty() && pathSegments[0].equals("google") +} + +/** + * The reference to the `generateProto` task of a `main` source set. + */ +internal fun Project.generateProto(): Task? = tasks.findByName("generateProto") + +/** + * Makes this [Jar] task depend on the [generateProto] task, if it exists in the same project. + */ +internal fun Jar.dependOnGenerateProto() { + project.generateProto()?.let { + this.dependsOn(it) + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/Publications.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/Publications.kt new file mode 100644 index 0000000..12c0563 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/Publications.kt @@ -0,0 +1,224 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.publish + +import io.spine.internal.gradle.Repository +import io.spine.internal.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. + */ + 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) + } +} + +/** + * 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 ".pom" extension. + * 3. Gradle's metadata file that has ".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). + * 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/internal/gradle/publish/PublishingExts.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/PublishingExts.kt new file mode 100644 index 0000000..78e7395 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/PublishingExts.kt @@ -0,0 +1,264 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.publish + +import dokkaKotlinJar +import io.spine.internal.gradle.Repository +import io.spine.internal.gradle.sourceSets +import java.util.* +import org.gradle.api.InvalidUserDataException +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.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider +import org.gradle.api.tasks.bundling.Jar +import org.gradle.kotlin.dsl.get +import org.gradle.kotlin.dsl.getByType +import org.gradle.kotlin.dsl.named +import org.gradle.kotlin.dsl.register +import org.gradle.kotlin.dsl.the +import org.gradle.kotlin.dsl.withType + +/** + * Obtains [PublishingExtension] of this project. + */ +internal val Project.publishingExtension: PublishingExtension + get() = extensions.getByType() + +/** + * Obtains [PublicationContainer] of this project. + */ +internal val Project.publications: PublicationContainer + get() = publishingExtension.publications + +/** + * Obtains [SpinePublishing] extension from the root project. + */ +internal val Project.spinePublishing: SpinePublishing + get() = this.rootProject.the() + +/** + * Tells if this project has custom publishing. + */ +internal val Project.hasCustomPublishing: Boolean + get() = spinePublishing.modulesWithCustomPublishing.contains(name) + +private const val PUBLISH_TASK = "publish" + +/** + * Locates `publish` task in this [TaskContainer]. + * + * This task publishes all defined publications to all defined repositories. To achieve that, + * the task depends on all `publish`*PubName*`PublicationTo`*RepoName*`Repository` tasks. + * + * Please note, task execution would not copy publications to the local Maven cache. + * + * @see + * Tasks | Maven Publish Plugin + */ +internal val TaskContainer.publish: TaskProvider + get() = named(PUBLISH_TASK) + +/** + * Sets dependencies for `publish` task in this [Project]. + * + * This method performs the following: + * + * 1. When this [Project] is not a root, makes `publish` task in a root project + * depend on a local `publish`. + * 2. Makes local `publish` task verify that credentials are present for each + * of destination repositories. + */ +internal fun Project.configurePublishTask(destinations: Set) { + attachCredentialsVerification(destinations) + bindToRootPublish() +} + +private fun Project.attachCredentialsVerification(destinations: Set) { + val checkCredentials = tasks.registerCheckCredentialsTask(destinations) + val localPublish = tasks.publish + localPublish.configure { dependsOn(checkCredentials) } +} + +private fun Project.bindToRootPublish() { + if (project == rootProject) { + return + } + + val localPublish = tasks.publish + val rootPublish = rootProject.tasks.getOrCreatePublishTask() + rootPublish.configure { dependsOn(localPublish) } +} + +/** + * Use this task accessor when it is not guaranteed that the task is present + * in this [TaskContainer]. + */ +private fun TaskContainer.getOrCreatePublishTask(): TaskProvider = + if (names.contains(PUBLISH_TASK)) { + named(PUBLISH_TASK) + } else { + register(PUBLISH_TASK) + } + +private fun TaskContainer.registerCheckCredentialsTask( + destinations: Set +): TaskProvider = + register("checkCredentials") { + doLast { + destinations.forEach { it.ensureCredentials(project) } + } + } + +private fun Repository.ensureCredentials(project: Project) { + val credentials = credentials(project) + if (Objects.isNull(credentials)) { + throw InvalidUserDataException( + "No valid credentials for repository `${this}`. Please make sure " + + "to pass username/password or a valid `.properties` file." + ) + } +} + +/** + * Excludes Google `.proto` sources from all artifacts. + * + * Goes through all registered `Jar` tasks and filters out Google's files. + */ +@Suppress("unused") +fun TaskContainer.excludeGoogleProtoFromArtifacts() { + withType().configureEach { + exclude { it.isGoogleProtoSource() } + } +} + +/** + * 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: + * + * - Kotlin + * - Java + * - Proto + * + * 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") { + dependOnGenerateProto() + archiveClassifier.set("sources") + from(sourceSets["main"].allSource) // Puts Java and Kotlin sources. + from(protoSources()) // Puts Proto sources. + exclude("desc.ref", "*.desc") // Exclude descriptor files and the descriptor reference. +} + +/** + * Locates or creates `protoJar` task in this [Project]. + * + * 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") { + dependOnGenerateProto() + archiveClassifier.set("proto") + from(protoSources()) +} + +/** + * Locates or creates `testJar` task in this [Project]. + * + * The output of this task is a `jar` archive. The archive contains compilation output + * of `test` source set. + */ +internal fun Project.testJar(): TaskProvider = tasks.getOrCreate("testJar") { + archiveClassifier.set("test") + from(sourceSets["test"].output) +} + +/** + * 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. + */ +fun Project.javadocJar(): TaskProvider = tasks.getOrCreate("javadocJar") { + archiveClassifier.set("javadoc") + from(files("$buildDir/docs/javadoc")) + dependsOn("javadoc") +} + +internal fun TaskContainer.getOrCreate(name: String, init: Jar.() -> Unit): TaskProvider = + if (names.contains(name)) { + named(name) + } else { + register(name) { + init() + } + } + +/** + * Obtains as a set of [Jar] tasks, output of which is used as Maven artifacts. + * + * By default, only a jar with Java compilation output is included into publication. This method + * registers tasks which produce additional artifacts according to the values of [jarFlags]. + * + * @return the list of the registered tasks. + */ +internal fun Project.artifacts(jarFlags: JarFlags): Set> { + val tasks = mutableSetOf>() + + if (jarFlags.sourcesJar) { + tasks.add(sourcesJar()) + } + + if (jarFlags.javadocJar) { + tasks.add(javadocJar()) + } + + // We don't want to have an empty "proto.jar" when a project doesn't have any Proto files. + if (hasProto() && jarFlags.publishProtoJar) { + tasks.add(protoJar()) + } + + // Here, we don't have the corresponding `hasTests()` check, since this artifact is disabled + // by default. And turning it on means "We have tests and need them to be published." + if (jarFlags.publishTestJar) { + tasks.add(testJar()) + } + + if (jarFlags.publishDokkaKotlinJar) { + tasks.add(dokkaKotlinJar()) + } + + return tasks +} + diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/PublishingRepos.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/PublishingRepos.kt new file mode 100644 index 0000000..36fcac4 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/PublishingRepos.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.publish + +import io.spine.internal.gradle.Repository + +/** + * Repositories to which we may publish. + */ +object PublishingRepos { + + val cloudRepo = CloudRepo.destination + + val cloudArtifactRegistry = CloudArtifactRegistry.repository + + /** + * Obtains a GitHub repository by the given name. + */ + fun gitHub(repoName: String): Repository = GitHubPackages.repository(repoName) +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/SpinePublishing.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/SpinePublishing.kt new file mode 100644 index 0000000..f55b85f --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/publish/SpinePublishing.kt @@ -0,0 +1,428 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.publish + +import dokkaJavaJar +import dokkaKotlinJar +import io.spine.internal.gradle.Repository +import org.gradle.api.Project +import org.gradle.api.publish.maven.plugins.MavenPublishPlugin +import org.gradle.kotlin.dsl.apply +import org.gradle.kotlin.dsl.create +import org.gradle.kotlin.dsl.findByType + +/** + * Configures [SpinePublishing] extension. + * + * This extension sets up publishing of artifacts to Maven repositories. + * + * The extension can be configured for single- and multi-module projects. + * + * 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: + * + * ``` + * spinePublishing { + * modules = setOf( + * "subprojectA", + * "subprojectB", + * ) + * destinations = setOf( + * PublishingRepos.cloudRepo, + * PublishingRepos.cloudArtifactRegistry, + * ) + * } + * ``` + * + * When used with a single-module project, the extension should be opened in a project's build file. + * Only destinations should be specified: + * + * ``` + * spinePublishing { + * destinations = setOf( + * PublishingRepos.cloudRepo, + * PublishingRepos.cloudArtifactRegistry, + * ) + * } + * ``` + * + * It is worth to mention, 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 + * of published modules, the extension will create a [publication][StandardJavaPublicationHandler] + * 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 + * 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 + * 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. + * + * @see [artifacts] + */ +fun Project.spinePublishing(block: SpinePublishing.() -> Unit) { + apply() + val name = SpinePublishing::class.java.simpleName + val extension = with(extensions) { + findByType() ?: create(name, project) + } + extension.run { + block() + configured() + } +} + +/** + * A Gradle extension for setting up publishing of spine modules using `maven-publish` plugin. + * + * @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. + * + * @see spinePublishing + */ +open class SpinePublishing(private val project: Project) { + + private val protoJar = ProtoJar() + private val testJar = TestJar() + private val dokkaJar = DokkaJar() + + /** + * Set of modules to be published. + * + * Both the module's name or path can be used. + * + * Use this property if the extension is configured from a root project's build file. + * + * If left empty, the [project], in which the extension is opened, will be published. + * + * Empty by default. + */ + var modules: Set = emptySet() + + /** + * Set of modules that have custom publications and do not need standard ones. + * + * Empty by default. + */ + var modulesWithCustomPublishing: Set = emptySet() + + /** + * Set of repositories, to which the resulting artifacts will be sent. + * + * Usually, Spine-related projects are published to one or more repositories, + * declared in [PublishingRepos]: + * + * ``` + * destinations = setOf( + * PublishingRepos.cloudRepo, + * PublishingRepos.cloudArtifactRegistry, + * PublishingRepos.gitHub("base"), + * ) + * ``` + * + * Empty by default. + */ + var destinations: Set = emptySet() + + /** + * A prefix to be added before the name of each artifact. + * + * The default value is "spine-". + */ + var artifactPrefix: String = "spine-" + + /** + * 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. + * + * Here's an example of how to enable it for some of the published modules: + * + * ``` + * spinePublishing { + * modules = setOf( + * "subprojectA", + * "subprojectB", + * ) + * testJar { + * inclusions = setOf( + * "subprojectB", + * ) + * } + * } + * ``` + * + * For all modules, or when the extension is configured within a published module itself: + * + * ``` + * spinePublishing { + * testJar { + * enabled = true + * } + * } + * ``` + * + * The resulting artifact is available under "test" classifier. For example, + * in Gradle 7+, one could depend on it like this: + * + * ``` + * implementation("io.spine:spine-client:$version@test") + * ``` + */ + 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.internal.dependency.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. + * + * On this stage the extension will validate the received configuration and set up + * `maven-publish` plugin for each published module. + */ + internal fun configured() { + ensureProtoJarExclusionsArePublished() + ensureTestJarInclusionsArePublished() + ensuresModulesNotDuplicated() + + val projectsToPublish = projectsToPublish() + projectsToPublish.forEach { project -> + val jarFlags = JarFlags.create(project.name, protoJar, testJar, dokkaJar) + project.setUpPublishing(jarFlags) + } + } + + /** + * Maps the names of published modules to [Project] instances. + * + * The method considers two options: + * + * 1. The [set][modules] of subprojects to publish is not empty. It means that the extension + * is opened from a root project. + * 2. The [set][modules] is empty. Then, the [project] in which the extension is opened + * will be published. + * + * @see modules + */ + private fun projectsToPublish(): Collection { + if (project.subprojects.isEmpty()) { + return setOf(project) + } + return modules.union(modulesWithCustomPublishing) + .map { name -> project.project(name) } + .ifEmpty { setOf(project) } + } + + /** + * Sets up `maven-publish` plugin for the given project. + * + * Firstly, an instance of [PublicationHandler] is created for the project depending + * on the nature of the publication process configured. + * Then, this the handler is scheduled to apply on [Project.afterEvaluate]. + * + * General rule of thumb is to avoid using [Project.afterEvaluate] of this closure, + * as it configures a project when its configuration is considered completed. + * Which is quite counter-intuitive. + * + * 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. + * + * 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. + */ + private fun Project.setUpPublishing(jarFlags: JarFlags) { + val customPublishing = modulesWithCustomPublishing.contains(name) + val handler = if (customPublishing) { + CustomPublicationHandler(project, destinations) + } else { + StandardJavaPublicationHandler(project, jarFlags, destinations) + } + afterEvaluate { + handler.apply() + } + } + + /** + * Obtains an artifact ID for the given project. + * + * It consists of a project's name and [prefix][artifactPrefix]: + * ``. + */ + fun artifactId(project: Project): String = "$artifactPrefix${project.name}" + + /** + * Ensures that all modules, marked as excluded from [protoJar] publishing, + * are actually published. + * + * It makes no sense to tell a module don't publish [protoJar] artifact, if the module is not + * published at all. + */ + 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") + } + } + + /** + * Ensures that all modules, marked as included into [testJar] publishing, + * are actually published. + * + * It makes no sense to tell a module publish [testJar] artifact, if the module is not + * published at all. + */ + private fun ensureTestJarInclusionsArePublished() { + val nonPublishedInclusions = testJar.inclusions.minus(modules) + if (nonPublishedInclusions.isNotEmpty()) { + error( + "One or more modules are marked as `included into test JAR publication`," + + " but they are not even published: $nonPublishedInclusions." + ) + } + } + + /** + * 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. + * Here we verify that publishing of a module is not configured in both places simultaneously. + */ + private fun ensuresModulesNotDuplicated() { + val rootProject = project.rootProject + if (rootProject == project) { + return + } + + val rootExtension = with(rootProject.extensions) { findByType() } + rootExtension?.let { rootPublishing -> + val thisProject = setOf(project.name, project.path) + if (thisProject.minus(rootPublishing.modules).size != 2) { + error( + "Publishing of `$thisProject` module is already configured in a root project!" + ) + } + } + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/CodebaseFilter.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/CodebaseFilter.kt new file mode 100644 index 0000000..4b0a9cc --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/CodebaseFilter.kt @@ -0,0 +1,113 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.coverage + +import com.google.errorprone.annotations.CanIgnoreReturnValue +import io.spine.internal.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 +import org.gradle.api.tasks.SourceSetOutput + +/** + * Serves to distinguish the `.java` and `.class` files built on top of the Protobuf definitions + * from the human-created production code. + * + * Works on top of the passed [source][srcDirs] and [output][outputDirs] directories, by analyzing + * the source file names and finding the corresponding compiler output. + */ +internal class CodebaseFilter( + private val project: Project, + private val srcDirs: Set, + private val outputDirs: Set +) { + + /** + * Returns the file tree containing the compiled `.class` files which were produced + * from the human-written production code. + * + * Such filtering excludes the output obtained from the generated sources. + */ + internal fun humanProducedCompiledFiles(): List { + log("Source dirs for the code coverage calculation:") + this.srcDirs.forEach { + log(" - $it") + } + + val generatedClassNames = generatedClassNames() + val humanProducedTree = outputDirs + .stream() + .flatMap { it.classesDirs.files.stream() } + .map { srcFile -> + log("Filtering out the generated classes for ${srcFile}.") + project.fileTree(srcFile).without(generatedClassNames) + }.toList() + return humanProducedTree + } + + private fun generatedClassNames(): List { + val generatedSourceFiles = generatedOnly(srcDirs) + val generatedNames = mutableListOf() + generatedSourceFiles + .filter { it.exists() && it.isDirectory } + .forEach { folder -> + folder.walk() + .filter { !it.isDirectory } + .forEach { file -> + file.parseName( + File::asJavaClassName, + File::asGrpcClassName, + File::asSpineClassName + )?.let { clsName -> + generatedNames.add(clsName) + } + } + } + return generatedNames + } + + private fun log(message: String) { + project.logger.info(message) + } +} + +/** + * Excludes the elements which [Java compiled file names][File.asJavaCompiledClassName] + * are present among the passed [names]. + * + * Returns the same instance of `ConfigurableFileTree`, for call chaining. + */ +@CanIgnoreReturnValue +private fun ConfigurableFileTree.without(names: List): ConfigurableFileTree { + this.exclude { element -> + val className = element.file.asJavaCompiledClassName() + names.contains(className) + } + return this +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/FileExtension.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/FileExtension.kt new file mode 100644 index 0000000..75763dd --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/FileExtension.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.coverage + +/** + * File extensions. + */ +internal enum class FileExtension(val value: String) { + + /** + * Extension of a Java source file. + */ + JAVA_SOURCE(".java"), + + /** + * Extension of a Java compiled file. + */ + COMPILED_CLASS(".class"); + + /** + * The number of symbols in the extension. + */ + val length: Int + get() = this.value.length +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/FileExtensions.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/FileExtensions.kt new file mode 100644 index 0000000..69193ba --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/FileExtensions.kt @@ -0,0 +1,129 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.coverage + +import io.spine.internal.gradle.report.coverage.FileExtension.COMPILED_CLASS +import io.spine.internal.gradle.report.coverage.FileExtension.JAVA_SOURCE +import io.spine.internal.gradle.report.coverage.PathMarker.ANONYMOUS_CLASS +import io.spine.internal.gradle.report.coverage.PathMarker.GENERATED +import io.spine.internal.gradle.report.coverage.PathMarker.GRPC_SRC_FOLDER +import io.spine.internal.gradle.report.coverage.PathMarker.JAVA_OUTPUT_FOLDER +import io.spine.internal.gradle.report.coverage.PathMarker.JAVA_SRC_FOLDER +import io.spine.internal.gradle.report.coverage.PathMarker.SPINE_JAVA_SRC_FOLDER +import java.io.File + +/** + * This file contains extension methods and properties for `java.io.File`. + */ + +/** + * Parses the name of a class from the absolute path of this file. + * + * Treats the fragment between the [precedingMarker] and [extension] as the value to look for. + * In case the fragment is located and it contains `/` symbols, they are treated + * as Java package delimiters and are replaced by `.` symbols before returning the value. + * + * If the absolute path of this file has either no [precedingMarker] or no [extension], + * returns `null`. + */ +internal fun File.parseClassName( + precedingMarker: PathMarker, + extension: FileExtension +): String? { + val index = this.absolutePath.lastIndexOf(precedingMarker.infix) + return if (index > 0) { + var inFolder = this.absolutePath.substring(index + precedingMarker.length) + if (inFolder.endsWith(extension.value)) { + inFolder = inFolder.substring(0, inFolder.length - extension.length) + inFolder.replace('/', '.') + } else { + null + } + } else { + null + } +} + +/** + * Attempts to parse the file name with either of the specified [parsers], + * in their respective order. + * + * Returns the first non-`null` parsed value. + * + * If none of the parsers returns non-`null` value, returns `null`. + */ +internal fun File.parseName(vararg parsers: (file: File) -> String?): String? { + for (parser in parsers) { + val className = parser.invoke(this) + if (className != null) { + return className + } + } + return null +} + +/** + * Attempts to parse the Java fully-qualified class name from the absolute path of this file, + * treating it as a path to a human-produced `.java` file. + */ +internal fun File.asJavaClassName(): String? = + this.parseClassName(JAVA_SRC_FOLDER, JAVA_SOURCE) + +/** + * Attempts to parse the Java fully-qualified class name from the absolute path of this file, + * treating it as a path to a compiled `.class` file. + * + * If the `.class` file corresponds to the anonymous class, only the name of the parent + * class is returned. + */ +internal fun File.asJavaCompiledClassName(): String? { + var className = this.parseClassName(JAVA_OUTPUT_FOLDER, COMPILED_CLASS) + if (className != null && className.contains(ANONYMOUS_CLASS.infix)) { + className = className.split(ANONYMOUS_CLASS.infix)[0] + } + return className +} + +/** + * Attempts to parse the Java fully-qualified class name from the absolute path of this file, + * treating it as a path to a gRPC-generated `.java` file. + */ +internal fun File.asGrpcClassName(): String? = + this.parseClassName(GRPC_SRC_FOLDER, JAVA_SOURCE) + +/** + * Attempts to parse the Java fully-qualified class name from the absolute path of this file, + * treating it as a path to a Spine-generated `.java` file. + */ +internal fun File.asSpineClassName(): String? = + this.parseClassName(SPINE_JAVA_SRC_FOLDER, JAVA_SOURCE) + +/** + * Tells whether this file is a part of the generated sources, and not produced by a human. + */ +internal val File.isGenerated + get() = this.absolutePath.contains(GENERATED.infix) diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/FileFilter.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/FileFilter.kt new file mode 100644 index 0000000..cc80ea1 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/FileFilter.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.coverage + +import java.io.File + +/** + * Utilities for filtering the groups of `File`s. + */ +internal object FileFilter { + + /** + * Excludes the generated files from this file collection, leaving only those which were + * created by human beings. + */ + fun producedByHuman(files: Iterable): Iterable { + return files.filter { !it.isGenerated } + } + + /** + * Filters this file collection so that only generated files are present. + */ + fun generatedOnly(files: Iterable): Iterable { + return files.filter { it.isGenerated } + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/JacocoConfig.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/JacocoConfig.kt new file mode 100644 index 0000000..4d789a5 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/JacocoConfig.kt @@ -0,0 +1,223 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.coverage + +import io.spine.internal.gradle.applyPlugin +import io.spine.internal.gradle.findTask +import io.spine.internal.gradle.report.coverage.TaskName.check +import io.spine.internal.gradle.report.coverage.TaskName.copyReports +import io.spine.internal.gradle.report.coverage.TaskName.jacocoRootReport +import io.spine.internal.gradle.report.coverage.TaskName.jacocoTestReport +import io.spine.internal.gradle.sourceSets +import java.io.File +import java.util.* +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.plugins.BasePlugin +import org.gradle.api.tasks.Copy +import org.gradle.api.tasks.SourceSetContainer +import org.gradle.api.tasks.SourceSetOutput +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider +import org.gradle.kotlin.dsl.get +import org.gradle.testing.jacoco.plugins.JacocoPlugin +import org.gradle.testing.jacoco.tasks.JacocoReport + +/** + * Configures JaCoCo plugin to produce `jacocoRootReport` task which accumulates + * the test coverage results from all subprojects in a multi-project Gradle build. + * + * Users must apply `jacoco` plugin to all the subprojects, for which the report aggregation + * is required. + * + * In a single-module Gradle project, this utility is NOT needed. Just a plain `jacoco` plugin + * applied to the project is sufficient. + * + * Therefore, tn case this utility is applied to a single-module Gradle project, + * an `IllegalStateException` is thrown. + */ +@Suppress("unused") +class JacocoConfig( + private val rootProject: Project, + private val reportsDir: File, + private val projects: Iterable +) { + + companion object { + + /** + * A folder under the `buildDir` of the [rootProject] to which the reports will + * be copied when aggregating the coverage reports. + * + * If it does not exist, it will be created. + */ + private const val reportsDirSuffix = "subreports/jacoco/" + + /** + * Applies the JaCoCo plugin to the Gradle project. + * + * If the passed project has no subprojects, an `IllegalStateException` is thrown, + * telling that this utility should NOT be used. + * + * Registers `jacocoRootReport` task which aggregates all coverage reports + * from the subprojects. + */ + fun applyTo(project: Project) { + project.applyPlugin(BasePlugin::class.java) + val javaProjects: Iterable = eligibleProjects(project) + val reportsDir = project.rootProject.buildDir.resolve(reportsDirSuffix) + JacocoConfig(project.rootProject, reportsDir, javaProjects).configure() + } + + /** + * For a multi-module Gradle project, returns those subprojects of the passed [project] + * which have JaCoCo plugin applied. + * + * Throws an exception in case this project has no subprojects. + */ + private fun eligibleProjects(project: Project): Iterable { + val projects: Iterable = + if (project.subprojects.isNotEmpty()) { + project.subprojects.filter { + it.pluginManager.hasPlugin(JacocoPlugin.PLUGIN_EXTENSION_NAME) + } + } else { + throw IllegalStateException( + "In a single-module Gradle project, `JacocoConfig` is NOT needed." + + " Please apply `jacoco` plugin instead." + ) + } + return projects + } + } + + private fun configure() { + val tasks = rootProject.tasks + val copyReports = registerCopy(tasks) + val rootReport = registerRootReport(tasks, copyReports) + rootProject + .findTask(check.name) + .dependsOn(rootReport) + } + + private fun registerRootReport( + tasks: TaskContainer, + copyReports: TaskProvider? + ): TaskProvider { + val allSourceSets = Projects(projects).sourceSets() + val mainJavaSrcDirs = allSourceSets.mainJavaSrcDirs() + val humanProducedSourceFolders = FileFilter.producedByHuman(mainJavaSrcDirs) + + val filter = CodebaseFilter(rootProject, mainJavaSrcDirs, allSourceSets.mainOutputs()) + val humanProducedCompiledFiles = filter.humanProducedCompiledFiles() + + val rootReport = tasks.register(jacocoRootReport.name, JacocoReport::class.java) { + dependsOn(copyReports) + + additionalSourceDirs.from(humanProducedSourceFolders) + sourceDirectories.from(humanProducedSourceFolders) + executionData.from(rootProject.fileTree(reportsDir)) + + classDirectories.from(humanProducedCompiledFiles) + additionalClassDirs.from(humanProducedCompiledFiles) + + reports { + html.required.set(true) + xml.required.set(true) + csv.required.set(false) + } + onlyIf { true } + } + return rootReport + } + + private fun registerCopy(tasks: TaskContainer): TaskProvider { + val everyExecData = mutableListOf() + projects.forEach { project -> + val jacocoTestReport = project.findTask(jacocoTestReport.name) + val executionData = jacocoTestReport.executionData + everyExecData.add(executionData) + } + + val originalLocation = rootProject.files(everyExecData) + + val copyReports = tasks.register(copyReports.name, Copy::class.java) { + from(originalLocation) + into(reportsDir) + rename { + "${UUID.randomUUID()}.exec" + } + dependsOn(projects.map { it.findTask(jacocoTestReport.name) }) + } + return copyReports + } +} + +/** + * Extensions for working with groups of Gradle `Project`s. + */ +private class Projects( + private val projects: Iterable +) { + + /** + * Returns all source sets for this group of projects. + */ + fun sourceSets(): SourceSets { + val sets = projects.asSequence().map { it.sourceSets }.toList() + return SourceSets(sets) + } +} + +/** + * Extensions for working with several of Gradle `SourceSetContainer`s. + */ +private class SourceSets( + private val sourceSets: Iterable +) { + + /** + * Returns all Java source folders corresponding to the `main` source set type. + */ + fun mainJavaSrcDirs(): Set { + return sourceSets + .asSequence() + .flatMap { it["main"].allJava.srcDirs } + .toSet() + } + + /** + * Returns all source set outputs corresponding to the `main` source set type. + */ + fun mainOutputs(): Set { + return sourceSets + .asSequence() + .map { it["main"].output } + .toSet() + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/PathMarker.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/PathMarker.kt new file mode 100644 index 0000000..06b0bc5 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/PathMarker.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.coverage + +/** + * Fragments of file path which allow to detect the type of the file. + */ +internal enum class PathMarker(val infix: String) { + + /** + * Generated files. + */ + GENERATED("generated"), + + /** + * Files produced by humans and written in Java. + */ + JAVA_SRC_FOLDER("/java/"), + + /** + * Java source files generated by Spine framework. + */ + SPINE_JAVA_SRC_FOLDER("main/spine/"), + + /** + * Java source files generated by gRPC plugin. + */ + GRPC_SRC_FOLDER("/main/grpc/"), + + /** + * Among compiler output folders, highlights those containing the compilation result + * of human-produced Java files. + */ + JAVA_OUTPUT_FOLDER("/main/"), + + /** + * Anonymous class. + */ + ANONYMOUS_CLASS("$"); + + /** + * The number of symbols in the marker. + */ + val length: Int + get() = this.infix.length +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/TaskName.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/TaskName.kt new file mode 100644 index 0000000..c81c24c --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/coverage/TaskName.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.coverage + +/** + * The names of Gradle tasks involved into the JaCoCo reporting. + */ +@Suppress("EnumEntryName", "EnumNaming") /* Dubbing the actual values in Gradle. */ +internal enum class TaskName { + jacocoRootReport, + copyReports, + + check, + jacocoTestReport +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/Configuration.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/Configuration.kt new file mode 100644 index 0000000..32b24b2 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/Configuration.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.license + +import com.github.jk1.license.ConfigurationData + +/** + * The names of Gradle `Configuration`s. + */ +@Suppress("EnumEntryName", "EnumNaming") +/* Dubbing the actual values in Gradle. */ +internal enum class Configuration { + runtime, + runtimeClasspath +} + +/** + * Tells whether this configuration data is one of the passed `Configuration` types. + */ +internal fun ConfigurationData.isOneOf(vararg configs: Configuration): Boolean { + configs.forEach { + if (it.name == this.name) { + return true + } + } + return false +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/LicenseReporter.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/LicenseReporter.kt new file mode 100644 index 0000000..f7f1a31 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/LicenseReporter.kt @@ -0,0 +1,156 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.license + +import com.github.jk1.license.LicenseReportExtension +import com.github.jk1.license.LicenseReportExtension.ALL +import com.github.jk1.license.LicenseReportPlugin +import io.spine.internal.gradle.applyPlugin +import io.spine.internal.gradle.findTask +import java.io.File +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.kotlin.dsl.the + +/** + * Generates the license report for all Java dependencies used in a single Gradle project + * and in a repository. + * + * Transitive dependencies are included. + * + * The output file is placed to the root folder of the root Gradle project. + * + * Usage: + * + * ``` + * // ... + * subprojects { + * + * LicenseReporter.generateReportIn(project) + * } + * + * // ... + * + * LicenseReporter.mergeAllReports(project) + * + * ``` + */ +object LicenseReporter { + + /** + * The name of the Gradle task which generates the reports for a specific Gradle project. + */ + private const val projectTaskName = "generateLicenseReport" + + /** + * The name of the Gradle task merging the license reports across all Gradle projects + * in the repository into a single report file. + */ + private const val mergeTaskName = "mergeAllLicenseReports" + + /** + * Enables the generation of the license report for a single Gradle project. + * + * Registers `generateLicenseReport` task, which is later picked up + * by the [merge task][mergeAllReports]. + */ + fun generateReportIn(project: Project) { + project.applyPlugin(LicenseReportPlugin::class.java) + val reportOutputDir = project.buildDir.resolve(Paths.relativePath) + + with(project.the()) { + outputDir = reportOutputDir.absolutePath + excludeGroups = arrayOf("io.spine", "io.spine.tools", "io.spine.gcloud") + configurations = ALL + + renderers = arrayOf(MarkdownReportRenderer(Paths.outputFilename)) + } + } + + /** + * Tells to merge all per-project reports which were previously [generated][generateReportIn] + * for each of the subprojects of the root Gradle project. + * + * The merge result is placed according to [Paths]. + * + * Registers a `mergeAllLicenseReports` which is specified to be executed after `build`. + */ + fun mergeAllReports(project: Project) { + val rootProject = project.rootProject + val mergeTask = rootProject.tasks.register(mergeTaskName) { + val consolidationTask = this + val assembleTask = project.findTask("assemble") + val sourceProjects: Iterable = sourceProjects(rootProject) + sourceProjects.forEach { + val perProjectTask = it.findTask(projectTaskName) + consolidationTask.dependsOn(perProjectTask) + perProjectTask.dependsOn(assembleTask) + } + doLast { + mergeReports(sourceProjects, rootProject) + } + dependsOn(assembleTask) + } + project.findTask("build") + .finalizedBy(mergeTask) + } + + /** + * Determines the source projects for which the resulting report will be produced. + */ + private fun Task.sourceProjects(rootProject: Project): Iterable { + val targetProjects: Iterable = if (rootProject.subprojects.isEmpty()) { + rootProject.logger.debug( + "The license report will be produced for a single root project." + ) + listOf(this.project) + } else { + rootProject.logger.debug( + "The license report will be produced for all subprojects of a root project." + ) + rootProject.subprojects + } + return targetProjects + } + + /** + * Merges the license reports from all [sourceProjects] into a single file under + * the [rootProject]'s root directory. + */ + private fun mergeReports( + sourceProjects: Iterable, + rootProject: Project + ) { + val paths = sourceProjects.map { + "${it.buildDir}/${Paths.relativePath}/${Paths.outputFilename}" + } + println("Merging the license reports from the 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/internal/gradle/report/license/MarkdownReportRenderer.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/MarkdownReportRenderer.kt new file mode 100644 index 0000000..e1cd8af --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/MarkdownReportRenderer.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.license + +import com.github.jk1.license.LicenseReportExtension +import com.github.jk1.license.ProjectData +import com.github.jk1.license.render.ReportRenderer +import io.spine.internal.markup.MarkdownDocument +import java.io.File +import org.gradle.api.Project + +/** + * Renders the dependency report for a single [project][ProjectData] in Markdown. + */ +internal class MarkdownReportRenderer( + private val filename: String +) : ReportRenderer { + + override fun render(data: ProjectData) { + val project = data.project + val outputFile = outputFile(project) + val document = MarkdownDocument() + val template = Template(project, document) + + template.writeHeader() + ProjectDependencies.of(data).printTo(document) + template.writeFooter() + + document.writeToFile(outputFile) + } + + private fun outputFile(project: Project): File { + val config = + project.extensions.findByName("licenseReport") as LicenseReportExtension + return File(config.outputDir).resolve(filename) + } +} + diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/ModuleDataExtensions.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/ModuleDataExtensions.kt new file mode 100644 index 0000000..9adbb3f --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/ModuleDataExtensions.kt @@ -0,0 +1,169 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.license + +import com.github.jk1.license.ModuleData +import io.spine.internal.markup.MarkdownDocument +import kotlin.reflect.KCallable + +/** + * This file declares the Kotlin extensions that help printing `ModuleData` in Markdown format. + */ + +/** + * Prints several of the module data dependencies under the section with the passed [title]. + */ +internal fun MarkdownDocument.printSection( + title: String, + modules: Iterable +): MarkdownDocument { + this.h2(title) + modules.forEach { + printModule(it) + } + return this +} + +/** + * Prints the module metadata to this [MarkdownDocument]. + */ +private fun MarkdownDocument.printModule(module: ModuleData) { + ol() + + this.print(ModuleData::getGroup, module, "Group") + .print(ModuleData::getName, module, "Name") + .print(ModuleData::getVersion, module, "Version") + + val projectUrl = module.projectUrl() + val licenses = module.licenses() + + if (projectUrl.isNullOrEmpty() && licenses.isEmpty()) { + bold("No license information found") + return + } + + @SuppressWarnings("MagicNumber") /* As per the original document layout. */ + val listIndent = 5 + printProjectUrl(projectUrl, listIndent) + printLicenses(licenses, listIndent) + + nl() +} + +/** + * Prints the value of the [ModuleData] property by the passed [getter]. + * + * The property is printed with the passed [title]. + */ +private fun MarkdownDocument.print( + getter: KCallable<*>, + module: ModuleData, + title: String +): MarkdownDocument { + val value = getter.call(module) + if (value != null) { + space().bold(title).and().text(": $value.") + } + return this +} + + +/** + * Prints the URL to the project which provides the dependency. + * + * If the passed project URL is `null` or empty, it is not printed. + */ +@Suppress("SameParameterValue" /* Indentation is consistent across the list. */) +private fun MarkdownDocument.printProjectUrl(projectUrl: String?, indent: Int) { + if (!projectUrl.isNullOrEmpty()) { + ul(indent).bold("Project URL:").and().link(projectUrl) + } +} + +/** + * Prints the links to the source code licenses. + */ +@Suppress("SameParameterValue" /* Indentation is consistent across the list. */) +private fun MarkdownDocument.printLicenses(licenses: Set, indent: Int) { + for (license in licenses) { + ul(indent).bold("License:").and() + if (license.url.isNullOrEmpty()) { + text(license.text) + } else { + link(license.text, license.url) + } + } +} + +/** + * Searches for the URL of the project in the module's metadata. + * + * Returns `null` if none is found. + */ +private fun ModuleData.projectUrl(): String? { + val pomUrl = this.poms.firstOrNull()?.projectUrl + if (!pomUrl.isNullOrBlank()) { + return pomUrl + } + return this.manifests.firstOrNull()?.url +} + +/** + * Collects the links to the source code licenses, under which the module dependency is distributed. + */ +private fun ModuleData.licenses(): Set { + val result = mutableSetOf() + + val manifestLicense: License? = manifests.firstOrNull()?.let { manifest -> + val value = manifest.license + if (!value.isNullOrBlank()) { + if (value.startsWith("http")) { + License(value, value) + } else { + License(value, manifest.url) + } + } + null + } + manifestLicense?.let { result.add(it) } + + val pomLicenses = poms.firstOrNull()?.licenses?.map { license -> + License(license.name, license.url) + } + pomLicenses?.let { + result.addAll(it) + } + return result.toSet() +} + +/** + * The source code license with the URL leading to the license text, as defined + * by the project's dependency. + * + * The URL to the license text may be not defined. + */ +private data class License(val text: String, val url: String?) diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/Paths.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/Paths.kt new file mode 100644 index 0000000..c352ffd --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/Paths.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.license + +/** + * Filesystem paths used by [LicenseReporter]. + */ +internal object Paths { + + /** + * The output filename of the license report. + * + * The file with this name is placed to the root folder of the root Gradle project — + * as the result of the [LicenseReporter] work. + * + * Its contents describe the licensing information for each of the Java dependencies + * which are referenced by Gradle projects in the repository. + */ + internal const val outputFilename = "dependencies.md" + + /** + * The path to a directory, to which a per-project report is generated. + */ + internal const val relativePath = "reports/dependency-license/dependency" +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/ProjectDependencies.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/ProjectDependencies.kt new file mode 100644 index 0000000..fc90bb3 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/ProjectDependencies.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.license + +import com.github.jk1.license.ModuleData +import com.github.jk1.license.ProjectData +import io.spine.internal.markup.MarkdownDocument + +/** + * Dependencies of some [Gradle project][ProjectData] classified by the Gradle configuration + * (such as "runtime") to which they are bound. + */ +internal class ProjectDependencies +private constructor( + private val runtime: Iterable, + private val compileTooling: Iterable +) { + + internal companion object { + + /** + * Creates an instance of [ProjectDependencies] by sorting the module dependencies. + */ + fun of(data: ProjectData): ProjectDependencies { + val runtimeDeps = mutableListOf() + val compileToolingDeps = mutableListOf() + data.configurations.forEach { config -> + if (config.isOneOf(Configuration.runtime, Configuration.runtimeClasspath)) { + runtimeDeps.addAll(config.dependencies) + } else { + compileToolingDeps.addAll(config.dependencies) + } + } + return ProjectDependencies(runtimeDeps.toSortedSet(), compileToolingDeps.toSortedSet()) + } + } + + /** + * Prints the project dependencies along with the licensing information, + * splitting them into "Runtime" and "Compile, tests, and tooling" sections. + */ + internal fun printTo(out: MarkdownDocument) { + out.printSection("Runtime", runtime) + .printSection("Compile, tests, and tooling", compileTooling) + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/Tasks.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/Tasks.kt new file mode 100644 index 0000000..6a57be5 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/Tasks.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.license + +import org.gradle.api.Task +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider + +/** + * Locates `generateLicenseReport` in this [TaskContainer]. + * + * The task generates a license report for a specific Gradle project. License report includes + * information of all dependencies and their licenses. + */ +val TaskContainer.generateLicenseReport: TaskProvider + get() = named("generateLicenseReport") diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/Template.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/Template.kt new file mode 100644 index 0000000..3eccbca --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/license/Template.kt @@ -0,0 +1,75 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.license + +import io.spine.internal.gradle.artifactId +import io.spine.internal.markup.MarkdownDocument +import java.util.* +import org.gradle.api.Project + +/** + * The template text pieces of the license report. + */ +internal class Template( + private val project: Project, + private val out: MarkdownDocument +) { + + private companion object { + private const val longBreak = "\n\n" + } + + internal fun writeHeader() { + out.nl() + .h1( + "Dependencies of " + + "`${project.group}:${project.artifactId}:${project.version}`" + ) + .nl() + } + + internal fun writeFooter() { + 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()}") + .text(" using ") + .link( + "Gradle-License-Report plugin", + "https://github.com/jk1/Gradle-License-Report" + ) + .text(" by Evgeny Naumenko, ") + .text("licensed under ") + .link( + "Apache 2.0 License", + "https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE" + ) + .text(".") + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/DependencyScope.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/DependencyScope.kt new file mode 100644 index 0000000..9c1f1ed --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/DependencyScope.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.pom + +/** + * A Maven dependency scope. + */ +@Suppress("EnumEntryName", "EnumNaming") /* Dubbing the actual values in Gradle. */ +enum class DependencyScope { + undefined, + compile, + provided, + runtime, + test, + system + + /** + `import` is also a scope, however, it can't be used outside the `` + section, which is outside of the scope of this script + **/ +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/DependencyWriter.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/DependencyWriter.kt new file mode 100644 index 0000000..fb1920b --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/DependencyWriter.kt @@ -0,0 +1,185 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.pom + +import groovy.xml.MarkupBuilder +import java.io.Writer +import java.util.* +import kotlin.reflect.full.isSubclassOf +import org.gradle.api.Project +import org.gradle.api.artifacts.Dependency +import org.gradle.api.internal.artifacts.dependencies.AbstractExternalModuleDependency +import org.gradle.kotlin.dsl.withGroovyBuilder + +/** + * Writes the dependencies of a Gradle project in a `pom.xml` format. + * + * Includes the dependencies of the subprojects. Does not include + * the transitive dependencies. + * + * ``` + * + * + * io.spine + * base + * 2.0.0-pre1 + * + * ... + * + * ``` + * + * When there are several versions of the same dependency, only the one with + * the newest version is retained. + * + * @see PomGenerator + */ +internal class DependencyWriter +private constructor( + private val dependencies: SortedSet +) { + internal companion object { + + /** + * Creates the `ProjectDependenciesAsXml` for the passed [project]. + */ + fun of(project: Project): DependencyWriter { + return DependencyWriter(project.dependencies()) + } + } + + /** + * Writes the dependencies in their `pom.xml` format to the passed [out] writer. + * + *

Used writer will not be closed. + */ + fun writeXmlTo(out: Writer) { + val xml = MarkupBuilder(out) + xml.withGroovyBuilder { + "dependencies" { + dependencies.forEach { scopedDep -> + val dependency = scopedDep.dependency() + "dependency" { + "groupId" { xml.text(dependency.group) } + "artifactId" { xml.text(dependency.name) } + "version" { xml.text(dependency.version) } + if (scopedDep.hasDefinedScope()) { + "scope" { xml.text(scopedDep.scopeName()) } + } + } + } + } + } + } +} + +/** + * Returns the [scoped dependencies][ScopedDependency] of a Gradle project. + */ +fun Project.dependencies(): SortedSet { + val dependencies = mutableSetOf() + dependencies.addAll(this.depsFromAllConfigurations()) + + this.subprojects.forEach { subproject -> + val subprojectDeps = subproject.depsFromAllConfigurations() + dependencies.addAll(subprojectDeps) + } + + val result = deduplicate(dependencies) + .map { it.scoped } + .toSortedSet() + return result +} + +/** + * Returns the external dependencies of the project from all the project configurations. + */ +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() } + .forEach { dependency -> + val moduleDependency = ModuleDependency(project, configuration, dependency) + result.add(moduleDependency) + } + } + return result +} + +/** + * Tells whether the dependency is an external module dependency. + */ +private fun Dependency.isExternal(): Boolean { + return this.javaClass.kotlin.isSubclassOf(AbstractExternalModuleDependency::class) +} + +/** + * Filters out duplicated dependencies by group and name. + * + * When there are several versions of the same dependency, the method will retain only + * the one with the newest version. + * + * Sometimes, a project uses several versions of the same dependency. This may happen + * when different modules of the project use different versions of the same dependency. + * But for our `pom.xml`, which has clearly representative character, a single version + * of a dependency is quite enough. + * + * The rejected duplicates are logged. + */ +private fun Project.deduplicate(dependencies: Set): List { + val groups = dependencies.distinctBy { it.gav } + .groupBy { it.run { "$group:$name" } } + + logDuplicates(groups) + + val filtered = groups.map { group -> + group.value.maxByOrNull { dep -> dep.version!! }!! + } + return filtered +} + +private fun Project.logDuplicates(dependencies: Map>) { + dependencies.filter { it.value.size > 1 } + .forEach { (dependency, versions) -> logDuplicate(dependency, versions) } +} + +private fun Project.logDuplicate(dependency: String, versions: List) { + logger.lifecycle("") + logger.lifecycle("The project uses several versions of `$dependency` dependency.") + + versions.forEach { + logger.lifecycle( + "module: {}, configuration: {}, version: {}", + it.module.name, + it.configuration.name, + it.version + ) + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/InceptionYear.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/InceptionYear.kt new file mode 100644 index 0000000..ab4bc1c --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/InceptionYear.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.pom + +import groovy.xml.MarkupBuilder +import java.io.StringWriter +import org.gradle.kotlin.dsl.withGroovyBuilder + +/** + * Information about the Spine's inception year. + */ +internal object InceptionYear { + + private const val SPINE_INCEPTION_YEAR = "2015" + + /** + * Returns a string containing the inception year of Spine in a `pom.xml` format. + */ + override fun toString(): String { + val writer = StringWriter() + val xml = MarkupBuilder(writer) + xml.withGroovyBuilder { + "inceptionYear" { xml.text(SPINE_INCEPTION_YEAR) } + } + return writer.toString() + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/MarkupExtensions.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/MarkupExtensions.kt new file mode 100644 index 0000000..214931a --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/MarkupExtensions.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.pom + +import groovy.xml.MarkupBuilder + +/** + * This file contains extension methods and properties for the Groovy's `MarkupBuilder`. + */ + +/** + * Yields a [value] to the document by converting it to string. + */ +fun MarkupBuilder.text(value: Any?) = this.mkp.yield(value.toString()) diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/ModuleDependency.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/ModuleDependency.kt new file mode 100644 index 0000000..0dbb11c --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/ModuleDependency.kt @@ -0,0 +1,92 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.pom + +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.Dependency + +/** + * A module's dependency. + * + * Contains information about a module and configuration, from which + * the dependency comes. + */ +internal class ModuleDependency( + val module: Project, + val configuration: Configuration, + private val dependency: Dependency, + +) : Dependency by dependency, Comparable { + + companion object { + private val COMPARATOR = compareBy { it.module } + .thenBy { it.configuration.name } + .thenBy { it.group } + .thenBy { it.name } + .thenBy { it.version } + } + + /** + * A project dependency with its [scope][DependencyScope]. + * + * Doesn't contain any info about an origin module and configuration. + */ + val scoped = ScopedDependency.of(dependency, configuration) + + /** + * GAV coordinates of this dependency. + * + * Gradle's [Dependency] is a mutable object. Its properties can change their + * values with time. In parcticular, the version can be changed as more + * configurations are getting resolved. This is why this property is calculated. + */ + val gav: String + get() = "$group:$name:$version" + + override fun compareTo(other: ModuleDependency): Int = COMPARATOR.compare(this, other) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ModuleDependency + + if (module != other.module) return false + if (configuration != other.configuration) return false + if (dependency != other.dependency) return false + + return true + } + + override fun hashCode(): Int { + var result = module.hashCode() + result = 31 * result + configuration.hashCode() + result = 31 * result + dependency.hashCode() + return result + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/PomFormatting.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/PomFormatting.kt new file mode 100644 index 0000000..d25ebd6 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/PomFormatting.kt @@ -0,0 +1,111 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.pom + +import java.io.StringWriter +import java.lang.System.lineSeparator +import java.util.* + +/** + * Helps to format the `pom.xml` file according to its expected XML structure. + */ +internal object PomFormatting { + + private val NL = lineSeparator() + private const val XML_METADATA = "" + private const val PROJECT_SCHEMA_LOCATION = "" + private const val MODEL_VERSION = "4.0.0" + private const val CLOSING_PROJECT_TAG = "" + + /** + * Writes the starting segment of `pom.xml`. + */ + internal fun writeStart(dest: StringWriter) { + dest.write( + XML_METADATA, + NL, + PROJECT_SCHEMA_LOCATION, + NL, + MODEL_VERSION, + NL, + describingComment(), + NL + ) + } + + /** + * Obtains a description comment that describes the nature of the generated `pom.xml` file. + */ + private fun describingComment(): String { + val description = NL + + "This file was generated using the Gradle `generatePom` task. " + + NL + + "This file is not suitable for `maven` build tasks. It only describes the " + + "first-level dependencies of " + + NL + + "all modules and does not describe the project " + + "structure per-subproject." + + NL + return String.format( + Locale.US, + "", + NL, description, NL + ) + } + + /** + * Writes the closing segment of `pom.xml`. + */ + internal fun writeEnd(dest: StringWriter) { + dest.write(CLOSING_PROJECT_TAG) + } + + /** + * 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 + */ + internal fun writeBlocks(destination: StringWriter, vararg lines: String) { + lines.iterator().forEach { + destination.write(it, NL, NL) + } + } + + /** + * Writes each of the passed sequences. + */ + private fun StringWriter.write(vararg content: String) { + content.forEach { + this.write(it) + } + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/PomGenerator.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/PomGenerator.kt new file mode 100644 index 0000000..3535922 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/PomGenerator.kt @@ -0,0 +1,94 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.pom + +import org.gradle.api.Project +import org.gradle.api.plugins.BasePlugin + +/** + * Generates a `pom.xml` file that contains dependencies of the root project as + * well as the dependencies of its subprojects. + * + * Usage: + * ``` + * PomGenerator.applyTo(project) + * ``` + * + * The generated `pom.xml` is not usable for Maven build tasks and is merely a + * description of project dependencies. + * + * Configures the `build` task to generate the `pom.xml` file. + * + * Note that the generated `pom.xml` includes the group ID, artifact ID and the version of the + * project this script was applied to. In case you want to override the default values, do so in + * the `ext` block like so: + * + * ``` + * ext { + * groupId = 'custom-group-id' + * artifactId = 'custom-artifact-id' + * version = 'custom-version' + * } + * ``` + * + * 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. ``. + */ +@Suppress("unused") +object PomGenerator { + + /** + * Configures the generator for the passed [project]. + */ + 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. + */ + project.apply { + plugin(BasePlugin::class.java) + } + + val task = project.tasks.create("generatePom") + task.doLast { + val pomFile = project.projectDir.resolve("pom.xml") + project.delete(pomFile) + + val projectData = project.metadata() + val writer = PomXmlWriter(projectData) + writer.writeTo(pomFile) + } + + val buildTask = project.tasks.findByName("build")!! + buildTask.finalizedBy(task) + + val assembleTask = project.tasks.findByName("assemble")!! + task.dependsOn(assembleTask) + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/PomXmlWriter.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/PomXmlWriter.kt new file mode 100644 index 0000000..c16fe76 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/PomXmlWriter.kt @@ -0,0 +1,86 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.pom + +import io.spine.internal.gradle.report.pom.PomFormatting.writeBlocks +import io.spine.internal.gradle.report.pom.PomFormatting.writeStart +import java.io.File +import java.io.FileWriter +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. + */ +internal class PomXmlWriter +internal constructor( + private val projectMetadata: ProjectMetadata +) { + + /** + * Writes the `pom.xml` file containing dependencies of this project + * and its subprojects to the specified location. + * + *

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 + */ + fun writeTo(file: File) { + val fileWriter = FileWriter(file) + val out = StringWriter() + + writeStart(out) + writeBlocks( + out, + projectMetadata.toString(), + InceptionYear.toString(), + SpineLicense.toString(), + projectDependencies() + ) + PomFormatting.writeEnd(out) + + fileWriter.write(out.toString()) + fileWriter.close() + } + + /** + * Obtains a string that contains project dependencies as XML. + * + *

Obtained string also contains a closing project tag. + */ + private fun projectDependencies(): String { + val destination = StringWriter() + val dependencyWriter = DependencyWriter.of(projectMetadata.project) + dependencyWriter.writeXmlTo(destination) + return destination.toString() + } +} + diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/ProjectMetadata.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/ProjectMetadata.kt new file mode 100644 index 0000000..ad1cbb7 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/ProjectMetadata.kt @@ -0,0 +1,103 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.pom + +import groovy.xml.MarkupBuilder +import java.io.StringWriter +import kotlin.reflect.KProperty +import org.gradle.api.Project +import org.gradle.kotlin.dsl.PropertyDelegate +import org.gradle.kotlin.dsl.extra +import org.gradle.kotlin.dsl.provideDelegate +import org.gradle.kotlin.dsl.withGroovyBuilder + +/** + * Information about the Gradle project. + * + * Includes group ID, artifact name, and the version. + */ +@Suppress("MemberVisibilityCanBePrivate") /* Property values accessed via `KProperty`. */ +internal class ProjectMetadata +internal constructor( + internal val project: Project, + internal val groupId: String, + internal val artifactId: String, + internal val version: String +) { + + /** + * Returns an XML string containing the project metadata. + * + * The XML format is compatible with the one defined for Maven's `pom.xml`. + */ + override fun toString(): String { + val writer = StringWriter() + MarkupBuilder(writer).tagsFor(::groupId, ::artifactId, ::version) + return writer.toString() + } + + private fun MarkupBuilder.tagsFor(vararg property: KProperty<*>) { + property.forEach { + this.withGroovyBuilder { + it.name { this@tagsFor.text(it.call()) } + } + } + } +} + +/** + * Creates a new instance of [ProjectMetadata]. + * + * The required information is first retrieved from the project. + * And if a property is missing from the `project`, it is taken from the `extra` extension + * of project's root project. + */ +internal fun Project.metadata(): ProjectMetadata { + val groupId: String by nonEmptyValue(group) + val artifactId: String by nonEmptyValue(name) + val version: String by project.nonEmptyValue(this.version) + return ProjectMetadata(project, groupId, artifactId, version) +} + +private fun Project.nonEmptyValue(prop: Any): NonEmptyValue { + return NonEmptyValue(prop.toString(), this) +} + +private class NonEmptyValue( + private val defaultValue: String, + private val project: Project +) : PropertyDelegate { + + @Suppress("UNCHECKED_CAST") + override fun getValue(receiver: Any?, property: KProperty<*>): T { + if (defaultValue.isNotEmpty()) { + return defaultValue as T + } + val result = project.rootProject.extra[property.name] + return result as T + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/ScopedDependency.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/ScopedDependency.kt new file mode 100644 index 0000000..5e6bf13 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/ScopedDependency.kt @@ -0,0 +1,179 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.pom + +import io.spine.internal.gradle.report.pom.DependencyScope.compile +import io.spine.internal.gradle.report.pom.DependencyScope.provided +import io.spine.internal.gradle.report.pom.DependencyScope.runtime +import io.spine.internal.gradle.report.pom.DependencyScope.test +import io.spine.internal.gradle.report.pom.DependencyScope.undefined +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.Dependency + +/** + * A project dependency with its [scope][DependencyScope]. + * + * See [More on dependency scopes](https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Scope). + */ +class ScopedDependency +private constructor( + private val dependency: Dependency, + private val scope: DependencyScope +) : Comparable { + + internal companion object { + + /** + * A map that contains the relations of known Gradle configuration names + * to their Maven dependency scope equivalents. + */ + private val CONFIG_TO_SCOPE = mapOf( + + /** + * Configurations from the Gradle Java plugin that are known to be mapped + * to the `compile` scope. + * + * Dependencies with the `compile` Maven scope are propagated to dependent projects. + * + * More on that in the [Gradle docs](https://docs.gradle.org/current/userguide/java_plugin.html#tab:configurations). + */ + "compile" to compile, + "implementation" to compile, + "api" to compile, + + /** + * Configurations from the Gradle Java plugin that are known to be mapped + * to the `runtime` scope. + * + * Dependencies with the `runtime` Maven scopes are required for execution only. + */ + "runtime" to runtime, + "runtimeOnly" to runtime, + "runtimeClasspath" to runtime, + "default" to runtime, + + /** + * Configurations from the Gradle Java plugin that are known to be mapped + * to the `provided` scope. + * + * Dependencies with the `provided` Maven scope are not propagated to dependent projects + * but are required during the compilation. + */ + "compileOnly" to provided, + "compileOnlyApi" to provided, + "annotationProcessor" to provided + ) + + /** + * Creates a `ScopedDependency` for the given [dependency] + * judging on the passed [configuration]. + */ + fun of(dependency: Dependency, configuration: Configuration): ScopedDependency { + val configurationName = configuration.name + val knownScope = CONFIG_TO_SCOPE[configurationName] + return when { + knownScope != null -> ScopedDependency(dependency, knownScope) + isTestsRelated(configurationName) -> ScopedDependency(dependency, test) + else -> ScopedDependency(dependency, undefined) + } + } + + private fun isTestsRelated(configurationName: String): Boolean = + configurationName.startsWith("test", ignoreCase = true) + + /** + * Performs comparison of {@code DependencyWithScope} instances according to these rules: + * + * * Compares the scope of the dependency first. Dependency with lower scope priority + * number goes first. + * + * * For dependencies with same scope, does the lexicographical group name comparison. + * + * * For dependencies within the same group, does the lexicographical artifact + * name comparison. + * + * * For dependencies with the same artifact name, does the lexicographical artifact + * version comparison. + */ + private val COMPARATOR: Comparator = + compareBy { it.dependencyPriority() } + .thenBy { it.dependency.group } + .thenBy { it.dependency.name } + .thenBy { it.dependency.version } + } + + /** + * Returns `true` if this dependency has a defined scope, returns `false` otherwise. + */ + fun hasDefinedScope(): Boolean { + return scope != undefined + } + + /** Obtains the Gradle dependency. */ + fun dependency(): Dependency { + return dependency + } + + /** Obtains the scope name of this dependency .*/ + fun scopeName(): String { + return scope.name + } + + /** + * Obtains the layout priority of a scope. + * + * Layout priority determines what scopes come first in the generated `pom.xml` file. + * Dependencies with a lower priority number go on top. + */ + internal fun dependencyPriority(): Int { + return when (scope) { + compile -> 0 + runtime -> 1 + test -> 2 + else -> 3 + } + } + + override fun compareTo(other: ScopedDependency): Int { + return COMPARATOR.compare(this, other) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is ScopedDependency) return false + + if (dependency.group != other.dependency.group) return false + if (dependency.name != other.dependency.name) return false + if (dependency.version != other.dependency.version) return false + + return true + } + + override fun hashCode(): Int { + return dependency.hashCode() + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/SpineLicense.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/SpineLicense.kt new file mode 100644 index 0000000..080de4c --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/report/pom/SpineLicense.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.report.pom + +import groovy.xml.MarkupBuilder +import java.io.StringWriter +import org.gradle.kotlin.dsl.withGroovyBuilder + +/** + * The licensing information of Spine. + */ +internal object SpineLicense { + + private const val NAME = "Apache License, Version 2.0" + private const val URL = "https://www.apache.org/licenses/LICENSE-2.0.txt" + private const val DISTRIBUTION = "repo" + + /** + * Returns the licensing information as an XML fragment compatible with `pom.xml` format. + */ + override fun toString(): String { + val result = StringWriter() + val xml = MarkupBuilder(result) + xml.withGroovyBuilder { + "licenses" { + "license" { + "name" { xml.text(NAME) } + "url" { xml.text(URL) } + "distribution" { xml.text(DISTRIBUTION) } + } + } + } + return result.toString() + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/testing/Logging.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/testing/Logging.kt new file mode 100644 index 0000000..de74b08 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/testing/Logging.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.testing + +import org.gradle.api.tasks.testing.Test +import org.gradle.api.tasks.testing.TestDescriptor +import org.gradle.api.tasks.testing.TestResult +import org.gradle.api.tasks.testing.logging.TestExceptionFormat +import org.gradle.kotlin.dsl.KotlinClosure2 + +/** + * Configures logging of this [Test] task. + * + * Enables logging of: + * 1. Standard `out` and `err` streams; + * 2. Thrown exceptions. + * + * Additionally, after all the tests are executed, a short summary would be logged. The summary + * consists of the number of tests and their results. + * + * Usage example: + * + *``` + * tasks { + * withType { + * configureLogging() + * } + * } + *``` + */ +fun Test.configureLogging() { + testLogging { + showStandardStreams = true + showExceptions = true + exceptionFormat = TestExceptionFormat.FULL + showStackTraces = true + showCauses = true + } + + fun TestResult.summary(): String = + """ + Test summary: + >> $testCount tests + >> $successfulTestCount succeeded + >> $failedTestCount failed + >> $skippedTestCount skipped + """ + + afterSuite( + + // `GroovyInteroperability` is employed as `afterSuite()` has no equivalent in Kotlin DSL. + // See issue: https://github.com/gradle/gradle/issues/5431 + + KotlinClosure2({ descriptor, result -> + + // If the descriptor has no parent, then it is the root test suite, + // i.e. it includes the info about all the run tests. + + if (descriptor.parent == null) { + logger.lifecycle(result.summary()) + } + }) + ) +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/testing/Multiproject.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/testing/Multiproject.kt new file mode 100644 index 0000000..b9d9c2a --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/testing/Multiproject.kt @@ -0,0 +1,75 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.testing + +import org.gradle.api.Project +import io.spine.internal.gradle.publish.testJar + +/** + * Exposes the test classes of this project as a new "testArtifacts" configuration. + * + * This allows other projects to depend on the test classes from this project within a Gradle + * multi-project build. It is helpful in case the dependant projects re-use abstract test suites + * of some "parent" project. + * + * Please note that this utility requires Gradle `java` plugin to be applied. Hence, it is + * recommended to call this extension method from `java` scope. + * + * Here's an example of how to expose the test classes of "projectA": + * + * ``` + * java { + * exposeTestConfiguration() + * } + * ``` + * + * Here's an example of how to consume the exposed classes in "projectB": + * + * ``` + * dependencies { + * testImplementation(project(path = ":projectA", configuration = "testArtifacts")) + * } + * ``` + * + * Don't forget that this exposure mechanism works only for projects that reside within the same + * multi-project build. In order to share the test classes with external projects, publish a + * dedicated [testJar][io.spine.internal.gradle.publish.SpinePublishing.testJar] artifact. + */ +@Suppress("unused") +fun Project.exposeTestConfiguration() { + + check(pluginManager.hasPlugin("java")) { + "Can't expose the test configuration because `java` plugin has not been applied." + } + + configurations.create("testArtifacts") { + extendsFrom(configurations.getByName("testRuntimeClasspath")) + outgoing { + artifact(testJar()) + } + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/gradle/testing/Tasks.kt b/buildSrc/src/main/kotlin/io/spine/internal/gradle/testing/Tasks.kt new file mode 100644 index 0000000..1d7a656 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/gradle/testing/Tasks.kt @@ -0,0 +1,102 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.gradle.testing + +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.testing.Test +import org.gradle.kotlin.dsl.register + +/** + * Registers [slowTest][SlowTest] and [fastTest][FastTest] tasks in this [TaskContainer]. + * + * Slow tests are registered to run after all fast tests. + * + * Usage example: + * + * ``` + * tasks { + * registerTestTasks() + * } + * ``` + */ +@Suppress("unused") +fun TaskContainer.registerTestTasks() { + withType(Test::class.java).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. + isFailOnNoMatchingTests = false + includeTestsMatching("*Test") + includeTestsMatching("*Spec") + } + } + register("fastTest").let { + register("slowTest") { + shouldRunAfter(it) + } + } +} + +/** + * Name of a tag for annotating a test class or method that is known to be slow and + * should not normally be run together with the main test suite. + * + * @see + * SlowTest + * @see + * Tag + */ +private const val SLOW_TAG = "slow" + +/** + * Executes JUnit tests filtering out the ones tagged as `slow`. + */ +private open class FastTest : Test() { + init { + description = "Executes all JUnit tests but the ones tagged as `slow`." + group = "Verification" + + this.useJUnitPlatform { + excludeTags(SLOW_TAG) + } + } +} + +/** + * Executes JUnit tests tagged as `slow`. + */ +private open class SlowTest : Test() { + init { + description = "Executes JUnit tests tagged as `slow`." + group = "Verification" + // No slow tests -- no problem. + filter.isFailOnNoMatchingTests = false + this.useJUnitPlatform { + includeTags(SLOW_TAG) + } + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/internal/markup/MarkdownDocument.kt b/buildSrc/src/main/kotlin/io/spine/internal/markup/MarkdownDocument.kt new file mode 100644 index 0000000..af9154f --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/internal/markup/MarkdownDocument.kt @@ -0,0 +1,119 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.internal.markup + +import java.io.File + +/** + * A virtual document written in Markdown. + * + * After it's finished, end-users would typically write it to a [real file][writeToFile]. + */ +@Suppress("TooManyFunctions") +class MarkdownDocument { + + private val builder: StringBuilder = StringBuilder() + + /** + * Appends the document with some plain text. + */ + fun text(value: String): MarkdownDocument { + builder.append(value) + return this + } + + /** + * Appends the document with a new line symbol. + */ + fun nl(): MarkdownDocument = text(System.lineSeparator()) + + /** + * Appends the document with a single space. + */ + fun space(): MarkdownDocument = text(" ") + + /** + * Appends the document with a number of space symbols. + */ + private fun space(count: Int): MarkdownDocument { + repeat(count) { + space() + } + return this + } + + /** + * Appends the document with a space symbol. + * + * A DSL-style shortcut for [space]. + */ + fun and(): MarkdownDocument = space() + + /** + * Adds some text rendered in bold. + */ + fun bold(text: String): MarkdownDocument = text("**$text**") + + /** + * Adds a heading of level 1. + */ + fun h1(text: String): MarkdownDocument = nl().text("# $text") + + /** + * Adds a heading of level 2. + */ + fun h2(text: String): MarkdownDocument = nl().text("## $text") + + /** + * Adds a link. + */ + fun link(text: String, url: String): MarkdownDocument = text("[$text](${url})") + + /** + * Adds a link and uses the passed [url] value as both text and a navigation destination. + */ + fun link(url: String): MarkdownDocument = link(url, url) + + /** + * Renders the item of an unordered list, also indenting it by the specified number of spaces. + */ + fun ul(indent: Int): MarkdownDocument = nl().space(indent).text("* ") + + /** + * Renders the item of an ordered list. + */ + fun ol(): MarkdownDocument = nl().text("1. ") + + /** + * Writes the content of this document to the passed file. + * + * If the file exists, it becomes overwritten. + */ + fun writeToFile(file: File) { + file.writeText(builder.toString()) + } +} diff --git a/buildSrc/src/main/kotlin/jacoco-kotlin-jvm.gradle.kts b/buildSrc/src/main/kotlin/jacoco-kotlin-jvm.gradle.kts new file mode 100644 index 0000000..b6b4932 --- /dev/null +++ b/buildSrc/src/main/kotlin/jacoco-kotlin-jvm.gradle.kts @@ -0,0 +1,70 @@ +/* + * Copyright 2023, 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. + */ + +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 Multiplatform 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-kotlin-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 Jacoco task with custom input from this Kotlin Multiplatform project. + */ +val jacocoTestReport: JacocoReport by tasks.getting(JacocoReport::class) { + + 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 new file mode 100644 index 0000000..4f7b9d0 --- /dev/null +++ b/buildSrc/src/main/kotlin/jvm-module.gradle.kts @@ -0,0 +1,216 @@ +/* + * Copyright 2023, 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. + */ + +import BuildSettings.javaVersion +import io.spine.internal.dependency.CheckerFramework +import io.spine.internal.dependency.Dokka +import io.spine.internal.dependency.ErrorProne +import io.spine.internal.dependency.Guava +import io.spine.internal.dependency.JUnit +import io.spine.internal.dependency.JavaX +import io.spine.internal.dependency.Kotest +import io.spine.internal.dependency.Protobuf +import io.spine.internal.dependency.Spine +import io.spine.internal.gradle.checkstyle.CheckStyleConfig +import io.spine.internal.gradle.github.pages.updateGitHubPages +import io.spine.internal.gradle.javac.configureErrorProne +import io.spine.internal.gradle.javac.configureJavac +import io.spine.internal.gradle.javadoc.JavadocConfig +import io.spine.internal.gradle.kotlin.applyJvmToolchain +import io.spine.internal.gradle.kotlin.setFreeCompilerArgs +import io.spine.internal.gradle.report.license.LicenseReporter +import io.spine.internal.gradle.testing.configureLogging +import io.spine.internal.gradle.testing.registerTestTasks +import org.gradle.api.Project +import org.gradle.api.tasks.Delete +import org.gradle.api.tasks.compile.JavaCompile +import org.gradle.jvm.toolchain.JavaLanguageVersion +import org.gradle.kotlin.dsl.dependencies +import org.gradle.kotlin.dsl.getValue +import org.gradle.kotlin.dsl.idea +import org.gradle.kotlin.dsl.invoke +import org.gradle.kotlin.dsl.`java-library` +import org.gradle.kotlin.dsl.kotlin +import org.gradle.kotlin.dsl.provideDelegate +import org.gradle.kotlin.dsl.registering +import org.gradle.kotlin.dsl.withType +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + `java-library` + idea + 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") +} + +LicenseReporter.generateReportIn(project) +JavadocConfig.applyTo(project) +CheckStyleConfig.applyTo(project) + +project.run { + configureJava(javaVersion) + configureKotlin(javaVersion) + addDependencies() + forceConfigurations() + + val generatedDir = "$projectDir/generated" + setTaskDependencies(generatedDir) + setupTests() + + configureGitHubPages() +} + +typealias Module = Project + +fun Module.configureJava(javaVersion: JavaLanguageVersion) { + java { + toolchain.languageVersion.set(javaVersion) + } + + tasks { + withType().configureEach { + configureJavac() + configureErrorProne() + } + } +} + +fun Module.configureKotlin(javaVersion: JavaLanguageVersion) { + kotlin { + applyJvmToolchain(javaVersion.asInt()) + explicitApi() + } + + tasks { + withType().configureEach { + kotlinOptions.jvmTarget = javaVersion.toString() + setFreeCompilerArgs() + } + } + + kover { + useJacoco() + } + + koverReport { + defaults { + xml { + onCheck = true + } + } + } +} + +/** + * These dependencies are applied to all subprojects and do not have to + * be included explicitly. + * + * We expose production code dependencies as API because they are used + * by the framework parts that depend on `base`. + */ +fun Module.addDependencies() = dependencies { + errorprone(ErrorProne.core) + + Protobuf.libs.forEach { api(it) } + api(Guava.lib) + + compileOnlyApi(CheckerFramework.annotations) + compileOnlyApi(JavaX.annotations) + ErrorProne.annotations.forEach { compileOnlyApi(it) } + + implementation(Spine.Logging.lib) + + testImplementation(Guava.testLib) + testImplementation(JUnit.runner) + testImplementation(JUnit.pioneer) + JUnit.api.forEach { testImplementation(it) } + + testImplementation(Spine.testlib) + testImplementation(Kotest.frameworkEngine) + testImplementation(Kotest.datatest) + testImplementation(Kotest.runnerJUnit5Jvm) + testImplementation(JUnit.runner) +} + +fun Module.forceConfigurations() { + with(configurations) { + forceVersions() + excludeProtobufLite() + all { + resolutionStrategy { + force( + JUnit.bom, + JUnit.runner, + Dokka.BasePlugin.lib + ) + } + } + } +} + +fun Module.setupTests() { + tasks { + registerTestTasks() + test.configure { + useJUnitPlatform { + includeEngines("junit-jupiter") + } + configureLogging() + } + } +} + +fun Module.setTaskDependencies(generatedDir: String) { + tasks { + val cleanGenerated by registering(Delete::class) { + delete(generatedDir) + } + clean.configure { + dependsOn(cleanGenerated) + } + + project.afterEvaluate { + val publish = tasks.findByName("publish") + publish?.dependsOn("${project.path}:updateGitHubPages") + } + } + configureTaskDependencies() +} + +fun Module.configureGitHubPages() { + val docletVersion = project.version.toString() + updateGitHubPages(docletVersion) { + allowInternalJavadoc.set(true) + rootFolder.set(rootDir) + } +} diff --git a/buildSrc/src/main/kotlin/pmd-settings.gradle.kts b/buildSrc/src/main/kotlin/pmd-settings.gradle.kts new file mode 100644 index 0000000..a718b1f --- /dev/null +++ b/buildSrc/src/main/kotlin/pmd-settings.gradle.kts @@ -0,0 +1,56 @@ +/* + * Copyright 2023, 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. + */ + +import io.spine.internal.dependency.Pmd + +plugins { + pmd +} + +pmd { + toolVersion = Pmd.version + isConsoleOutput = true + incrementalAnalysis.set(true) + + // The build is going to fail in case of violations. + isIgnoreFailures = false + + // Disable the default rule set to use the custom rules (see below). + ruleSets = listOf() + + // Load PMD settings. + val pmdSettings = file("$rootDir/config/quality/pmd.xml") + val textResource: TextResource = resources.text.fromFile(pmdSettings) + ruleSetConfig = textResource + + reportsDir = file("build/reports/pmd") + + // Just analyze the main sources; do not analyze tests. + val javaExtension: JavaPluginExtension = + project.extensions.getByType(JavaPluginExtension::class.java) + val mainSourceSet = javaExtension.sourceSets.getByName("main") + sourceSets = listOf(mainSourceSet) +} diff --git a/buildSrc/src/main/kotlin/write-manifest.gradle.kts b/buildSrc/src/main/kotlin/write-manifest.gradle.kts new file mode 100644 index 0000000..853bb27 --- /dev/null +++ b/buildSrc/src/main/kotlin/write-manifest.gradle.kts @@ -0,0 +1,164 @@ +/* + * Copyright 2023, 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. + */ + +import io.spine.internal.gradle.publish.SpinePublishing +import java.nio.file.Files.createDirectories +import java.nio.file.Files.createFile +import java.text.SimpleDateFormat +import java.util.* +import java.util.jar.Attributes.Name.IMPLEMENTATION_TITLE +import java.util.jar.Attributes.Name.IMPLEMENTATION_VENDOR +import java.util.jar.Attributes.Name.IMPLEMENTATION_VERSION +import java.util.jar.Attributes.Name.MANIFEST_VERSION +import java.util.jar.Manifest + +plugins { + java +} + +/** + * Obtains a string value of a [System] property with the given key. + */ +fun prop(key: String): String = System.getProperties()[key].toString() + +/** + * Obtains the current time in UTC using ISO 8601 format. + */ +fun currentTime(): String = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").format(Date()) + +/** + * Obtains the information on the JDK used for the build. + */ +fun buildJdk(): String = + "${prop("java.version")} (${prop("java.vendor")} ${prop("java.vm.version")})" + +/** + * Obtains the information on the operating system used for the build. + */ +fun buildOs(): String = + "${prop("os.name")} ${prop("os.arch")} ${prop("os.version")}" + +/** The publishing settings from the root project. */ +val spinePublishing = rootProject.the() +val artifactPrefix = spinePublishing.artifactPrefix + +/** + * Obtains the implementation title for the project using project group, + * artifact prefix from [SpinePublishing], and the name of the project to which + * this script plugin is applied. + */ +fun implementationTitle() = "${project.group}:$artifactPrefix${project.name}" + +/** + * The name of the manifest attribute holding the timestamp of the build. + */ +val buildTimestampAttr = "Build-Timestamp" + +/** + * The attributes we put into the JAR manifest. + * + * This map is shared between the [exposeManifestForTests] task and the action which + * customizes the [Jar] task below. + */ +val manifestAttributes = mapOf( + "Built-By" to prop("user.name"), + buildTimestampAttr to currentTime(), + "Created-By" to "Gradle ${gradle.gradleVersion}", + "Build-Jdk" to buildJdk(), + "Build-OS" to buildOs(), + IMPLEMENTATION_TITLE.toString() to implementationTitle(), + IMPLEMENTATION_VERSION.toString() to project.version, + IMPLEMENTATION_VENDOR.toString() to "TeamDev" +) + +/** + * Creates a manifest file in `resources` so that it is available for the tests. + * + * This task does the same what does the block which configures the `tasks.jar` below. + * We cannot use the manifest file created by the `Jar` task because it's not visible + * when running tests. We cannot depend on the `Jar` from `resources` because it would + * form a circular dependency. + */ +val exposeManifestForTests by tasks.creating { + + val outputFile = layout.buildDirectory.file("resources/main/META-INF/MANIFEST.MF") + outputs.file(outputFile).withPropertyName("manifestFile") + + fun createManifest(): Manifest { + val manifest = Manifest() + + // The manifest version attribute is crucial for writing. + // If it's absent, nothing would be written. + manifest.mainAttributes[MANIFEST_VERSION] = "1.0" + + manifestAttributes.forEach { entry -> + manifest.mainAttributes.putValue(entry.key, entry.value.toString()) + } + return manifest + } + + fun writeManifest(manifest: Manifest) { + val file = outputFile.get().getAsFile() + val path = file.toPath() + createDirectories(path.parent) + if (!file.exists()) { + createFile(path) + } + val stream = file.outputStream() + stream.use { + manifest.write(stream) + } + } + + doLast { + val manifest = createManifest() + writeManifest(manifest) + } +} + +tasks.processResources { + dependsOn(exposeManifestForTests) +} + +tasks.jar { + manifest { + attributes(manifestAttributes) + } +} + +/** + * Makes Gradle ignore the [buildTimestampAttr] attribute during normalization. + * + * See [Java META-INF normalization](https://docs.gradle.org/current/userguide/more_about_tasks.html#sec:meta_inf_normalization) + * section of the Gradle documentation for details. + */ +normalization { + runtimeClasspath { + metaInf { + ignoreAttribute(buildTimestampAttr) + } + } +} diff --git a/buildSrc/src/main/resources/dokka/assets/logo-icon.svg b/buildSrc/src/main/resources/dokka/assets/logo-icon.svg new file mode 100644 index 0000000..ced7f0d --- /dev/null +++ b/buildSrc/src/main/resources/dokka/assets/logo-icon.svg @@ -0,0 +1,17 @@ + + + + diff --git a/buildSrc/src/main/resources/dokka/styles/custom-styles.css b/buildSrc/src/main/resources/dokka/styles/custom-styles.css new file mode 100644 index 0000000..8c442fe --- /dev/null +++ b/buildSrc/src/main/resources/dokka/styles/custom-styles.css @@ -0,0 +1,52 @@ +/* + * Copyright 2023, 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. + */ + +:root, +:root.theme-dark { + --color-dark: #007bff; +} + +:root.theme-dark dt { + color: #fff; +} + +.library-name a::before { + background-image: url('https://spine.io/img/spine-sign-white.svg') +} + +.cover a, +.keyValue > div:first-child > .inline-flex > div:first-child > a { + font-family: var(--default-monospace-font-family); +} + +.keyValue > div:first-child > .inline-flex > div:first-child { + overflow: hidden; + text-overflow: ellipsis; +} + +.symbol.monospace.block { + margin: 10px; +} diff --git a/config b/config new file mode 160000 index 0000000..bb06dc1 --- /dev/null +++ b/config @@ -0,0 +1 @@ +Subproject commit bb06dc1f6d6e3f687a423d08c15c74ce58950849 diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..73833cf --- /dev/null +++ b/gradle.properties @@ -0,0 +1,5 @@ +kotlin.code.style=official +org.gradle.jvmargs=-Xmx8g -Xms256m -XX:MaxMetaspaceSize=1512m -XX:+UseParallelGC +org.gradle.daemon=false +org.gradle.parallel=false +org.gradle.workers.max=1 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..afba109285af78dbd2a1d187e33ac4f87c76e392 GIT binary patch literal 61624 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�Zy z^6AJh1X3pXq!CBB#`$5K8SM`A8- zu91@KW`jScvm}!^xaOr;l$}&)!qA=c4=tjb*AM^d9ZpDQjv*NDBXOUm9fM235A&Im zWb|jcBV^{}f>q*lY$s)A{g3K~i*dC}iz|ddMG+h2%gJJkYA%43!xj8A# zx}S=RPcxSSrC^je-O9-uG*4zN`%yO%D|8Y(M!;etj}#5<%)tweodG864mERu+wUwi zqO?7XNoGj5REy(>@FR?cmjdtzHh0Uyxc{bl7pq)x$iETy-gSOl4<=ay@B=!9(wjJhfW}ymgfT)tNU6b0S)wq zMeKw$AI+3w&@(KkXo2zZi+rD-;<`>S;(xh}N&A!yleW!DXaff`xq(&MU0v$=thsf{ zg(^n}x}gz%(ZMmnHv?lM149>hnCRcQl$2k+_R4YyxfW?lIfN`D`XCfH^dukp(N-@j zMOjDZSdpW2Zto4Xiwh$>MX#mx)#OxcM|qz7llutxlZ_J1E-I`Y&pzh)RfL03EK;d5 zsT1+B_S@MLCz)zQys)rDnV4a5!lT8<#kf<49)lNk;@0XW#dWoeCWlSU+e{zMyS1wNXB%6Un^?S8n~Jr%mk_^NT02xU zcTMjr6I|wbWAcf|&V@-_UA*XcHhl7mB~=D;T8nHdVRQX{LQT~{H7`n|hq82!6^^Qw zk3=bdrx(+2sKb?>S1*r#`#OK-jkDlW+^JkfcM1$YFJ9fi*s(8+3Ci?UHN7bY? zh4N;Ruf^YWl3Qug_Tt8ssOAr0u~l&@T3xKa)~WpBgpn}4a($+RfpKJts{-~X3lBbV zc}00$dp*~Rd#{MEJ)=}o%Ba+MxXj)G#S95An)W3pi<`?g$LYqs4y$@&P;h2dic|#Y zLG)4ki^^AYUpsZAtoN-`*PqRPm+BW{Sv93rQm8yHt2BO(SDmGJrDwCJ{h{LXJS+K? zT1`EUhgnKGwTy3CHN7c~OstGDJK;&0nUisI+TC|(NNeXbcpIy&DJ~-gy%PgMJwLdo zM-N=_#u(Fd`$DV<|BjAmhg*xPy8UhsziP>UzRJia${pQz)OyY|sn2Gsb@F5HMbeG4MJ)A6 zip8_D9EG_-mY)rt>E9tGKb6fE<=v;PY4-MR6_G!&r%+)@O^Sbo&N-QmW{8WLEyL}XI25|Lqcq;31FtfOg)YjO+kPkZx<1Xmr5EtjPCpi(FSH)6*cL~Wd3u@NkeeRsqV;PX~8DoAyr~*@QZEkWN8=j68 zK#oirFgtzpre!U$S(>lCULpEEsv^+Ew$A>6ZcsaAzLnn&J!{=Ke|!u)B`dFIl( z?vlF5euE?z5|cU)OPbl|@}Y3*ZkOOxEGXmrJOU-KoLFT{TuqWvZCG2==*;<06n)skW(dvAJ*9=S9v^7qHS$`Dl`eJ81@Mlj~ z%Bo)zV6lv$?7RyQZk6arskVWO0fvBrre8Jb*1R-cnz|i~~_ZLzp^Z zdUn~P6=9O$!Q)VJRz{VIA?$9b0acoc>g7?zFWpmZ`LCh`ie2bgsRy+C*Kf9A&<|h` zsZ76F{`l!LU2>tQjr$3#kYM{%d`Isn`WyaKUjrDwRSP0!kYpX9^R#RX!bjqmXkl!N zs))gf1ol~L3Xef4B?`<1GD_lBnuW{~+??9GRAgt)(@DZTFH|4Pb1o4CG6_f6rtEL@s<5ctjNIRvCMi=l?B-P+D8i*$H^-jz8Z{US(1{-DrHKNdc1xhp*${Nt%oj8oK2`gW#Eln z_W0bDj>|ck)XEBq1P`QeJDFebd}11SLV)K$4t+l=Q{P6MQl7?TD{C;U&*dbLVA^+O|OPt6jn6n7E<+DFOlud1?|k`TpU64 z;$jlu4;R1(yvFk@WgytV_g~pmB`+$<$!chFsmh@uY-a&yhCdS66WdAK#PQ(!wie!> za^US|K-U#D3pwGEmZaAO5FGbBetWB&z!hL(Y#21lO< z==S{#=CQN3-q!B>xq*jTqmfoF$8F`mZFNt^eYl~ZfNo4ZesiHf6ckDWcr$E=Jljnf2>9=rB~7>G4$a`w_O`ZQ>r=(b4ho+AfwCzm=D{`` zxKUQ313J(GXdjVXY;es$Y=PrSl(Ox@gV<_27CbzWPkyI|JZNrZP?!DnC<2`dh3H?f zl1?xeTOery;+#Pp_VzDOo33PR@(U$^hXMHgO(zGQ-u@f@FXqv(zXpH6P(7H2 z_BZ4J^&wCtEkGBMvvP8VYq*&1nE&7&Q|V%yoCd7S0*oDU|z z;;3i(25RC0#+>LbI=E&a?3fNgAO*FscLLGy4pEgQ+a;py{$7t;FDno1Gd|q8GdaBptjT1bT9H=(4$xg(a^;9al$zc!KrKq zG}eBa?`J81tSKCNupu9b9huAk)ms5{`wf}KcL*v~D`#g=p`T=682*7N*bv<$7ceyg zru~&l5j+Ib4uzYE6ZEf@!Y__6tN~QHfa>f%`(*+Ln!mQ$PpZE)QXFUfR5qAR(m^-e zcFWmK8Hh44whl@1*Qy9}vM%I+s+5DNeg8-*21Yz2%g21|mWF5LAD))kxG9Vie$C1GCQds%bZ6Ads?$z`tU5 z?SB|JXQy=zH6(LHy8kTU;v!ohrDI+JF=6#HPj6L z|5+8_zB(ti&9ez=A-s>L*YYw(a_ang3D#00_4+d%7%~TH_MtMMYJ%-CwE6y#;b4P%poCH0gPXelM>tU415{2?ON$z{cn`ie z;z0Pn#V|%CK#d2vM=<>0K!X2{4v7kl8m4a#Iw|o$Xq2FRsCcNs@b>U-CLN5oKQtaH z9%}rWJv`>@KjQr!%?1_vJW5cJJ?QzIKS3Yd$56fS_t3Dxe#5^OH@lP3zkTvii-zhZ zy$4p>cp%t5huZ&gnnqa?_nIo@#~ChARYp9>ReiBVku_RyDJ v9f-cOr*eQp04g-<;pZOo<=#I*?>`DvQ^o}A^zD`USu`GEG&HBt?O*=~soeXc literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..c7d437b --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.4-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..65dcd68 --- /dev/null +++ b/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 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 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# 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" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +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 %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..0e19579 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,51 @@ +/* +* 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 +* +* 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. +*/ + +@file:Suppress("UnstableApiUsage") // `dependencyResolutionManagement` is `@Incubating`. + +rootProject.name = "ProtoTap" + +include( + "api", + "protoc-plugin", + "gradle-plugin" +) + +dependencyResolutionManagement { + repositories { + mavenLocal() + mavenCentral() + gradlePluginPortal() + } +} + +pluginManagement { + repositories { + mavenLocal() + mavenCentral() + gradlePluginPortal() + } +} From b9ba67a8c2b520db82a69f52720ebd056e1b9f2d Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 25 Apr 2024 21:42:54 +0100 Subject: [PATCH 02/39] Add build scripts --- api/build.gradle.kts | 0 build.gradle.kts | 78 ++++++++++ buildSrc/src/main/kotlin/module.gradle.kts | 170 +++++++++++++++++++++ gradle-plugin/build.gradle.kts | 0 protoc-plugin/build.gradle.kts | 0 version.gradle.kts | 27 ++++ 6 files changed, 275 insertions(+) create mode 100644 api/build.gradle.kts create mode 100644 build.gradle.kts create mode 100644 buildSrc/src/main/kotlin/module.gradle.kts create mode 100644 gradle-plugin/build.gradle.kts create mode 100644 protoc-plugin/build.gradle.kts create mode 100644 version.gradle.kts diff --git a/api/build.gradle.kts b/api/build.gradle.kts new file mode 100644 index 0000000..e69de29 diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..818ba86 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,78 @@ +/* + * Copyright 2023, 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. + */ + +@file:Suppress("RemoveRedundantQualifierName") // To prevent IDEA replacing FQN imports. + +import io.spine.internal.gradle.publish.PublishingRepos +import io.spine.internal.gradle.publish.spinePublishing +import io.spine.internal.gradle.report.coverage.JacocoConfig +import io.spine.internal.gradle.report.license.LicenseReporter +import io.spine.internal.gradle.report.pom.PomGenerator +import io.spine.internal.gradle.standardToSpineSdk + +buildscript { + standardSpineSdkRepositories() + doForceVersions(configurations) +} + +plugins { + idea + jacoco + `project-report` + `gradle-doctor` +} +JacocoConfig.applyTo(project) +PomGenerator.applyTo(project) +LicenseReporter.mergeAllReports(project) + +spinePublishing { + modules = setOf( + "api", + "protoc-plugin", + ) + modulesWithCustomPublishing = setOf( + "gradle-plugin" + ) + destinations = with(PublishingRepos) { + setOf( + cloudArtifactRegistry, + gitHub("ProtoTap") + ) + } + artifactPrefix = "prototap-" +} + +allprojects { + apply(from = "$rootDir/version.gradle.kts") + group = "io.spine.tools" + version = extra["versionToPublish"]!! + + repositories.standardToSpineSdk() +} + +subprojects { + apply(plugin = "module") +} diff --git a/buildSrc/src/main/kotlin/module.gradle.kts b/buildSrc/src/main/kotlin/module.gradle.kts new file mode 100644 index 0000000..cc2d1a0 --- /dev/null +++ b/buildSrc/src/main/kotlin/module.gradle.kts @@ -0,0 +1,170 @@ +/* + * Copyright 2023, 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. + */ + +import io.spine.internal.dependency.CheckerFramework +import io.spine.internal.dependency.Coroutines +import io.spine.internal.dependency.ErrorProne +import io.spine.internal.dependency.FindBugs +import io.spine.internal.dependency.Grpc +import io.spine.internal.dependency.Guava +import io.spine.internal.dependency.JUnit +import io.spine.internal.dependency.Jackson +import io.spine.internal.dependency.Kotlin +import io.spine.internal.dependency.Spine +import io.spine.internal.dependency.Truth +import io.spine.internal.dependency.Validation +import io.spine.internal.gradle.VersionWriter +import io.spine.internal.gradle.checkstyle.CheckStyleConfig +import io.spine.internal.gradle.github.pages.updateGitHubPages +import io.spine.internal.gradle.javac.configureErrorProne +import io.spine.internal.gradle.javac.configureJavac +import io.spine.internal.gradle.javadoc.JavadocConfig +import io.spine.internal.gradle.kotlin.applyJvmToolchain +import io.spine.internal.gradle.kotlin.setFreeCompilerArgs +import io.spine.internal.gradle.publish.IncrementGuard +import io.spine.internal.gradle.report.license.LicenseReporter +import io.spine.internal.gradle.testing.configureLogging +import io.spine.internal.gradle.testing.registerTestTasks +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + `java-library` + kotlin("jvm") + id("net.ltgt.errorprone") + id("detekt-code-analysis") + id("pmd-settings") + id("write-manifest") + jacoco + `project-report` + idea +} +apply() +apply() + +CheckStyleConfig.applyTo(project) +JavadocConfig.applyTo(project) +LicenseReporter.generateReportIn(project) + +project.run { + addDependencies() + forceConfigurations() + + val javaVersion = JavaLanguageVersion.of(11) + configureJava(javaVersion) + configureKotlin(javaVersion) + + configureTests() + + configureGitHubPages() + configureTaskDependencies() +} + +typealias Module = Project + +fun Module.addDependencies() { + dependencies { + errorprone(ErrorProne.core) + + compileOnlyApi(FindBugs.annotations) + compileOnlyApi(CheckerFramework.annotations) + ErrorProne.annotations.forEach { compileOnlyApi(it) } + +// implementation(Guava.lib) + + testImplementation(Guava.testLib) + JUnit.api.forEach { testImplementation(it) } + Truth.libs.forEach { testImplementation(it) } + testRuntimeOnly(JUnit.runner) + } +} + +fun Module.forceConfigurations() { + with(configurations) { + forceVersions() + excludeProtobufLite() + all { + resolutionStrategy { + @Suppress("DEPRECATION") // To force `Kotlin.stdLibJdk7` version. + force( + Kotlin.stdLibJdk7, + JUnit.runner, + Coroutines.jdk8, + Coroutines.core, + Coroutines.bom, + Coroutines.coreJvm, + Jackson.Junior.objects + ) + } + } + } +} + +fun Module.configureJava(javaVersion: JavaLanguageVersion) { + java { + toolchain.languageVersion.set(javaVersion) + } + tasks { + withType().configureEach { + configureJavac() + configureErrorProne() + } + } +} + +fun Module.configureKotlin(javaVersion: JavaLanguageVersion) { + kotlin { + applyJvmToolchain(javaVersion.asInt()) + explicitApi() + } + + tasks { + withType().configureEach { + kotlinOptions.jvmTarget = javaVersion.toString() + setFreeCompilerArgs() + } + } +} + +fun Module.configureTests() { + tasks { + registerTestTasks() + test.configure { + useJUnitPlatform { + includeEngines("junit-jupiter") + } + configureLogging() + } + } +} + +fun Module.configureGitHubPages() { + updateGitHubPages(Spine.ArtifactVersion.javadocTools) { + allowInternalJavadoc.set(true) + rootFolder.set(rootDir) + } +} + + diff --git a/gradle-plugin/build.gradle.kts b/gradle-plugin/build.gradle.kts new file mode 100644 index 0000000..e69de29 diff --git a/protoc-plugin/build.gradle.kts b/protoc-plugin/build.gradle.kts new file mode 100644 index 0000000..e69de29 diff --git a/version.gradle.kts b/version.gradle.kts new file mode 100644 index 0000000..832d4df --- /dev/null +++ b/version.gradle.kts @@ -0,0 +1,27 @@ +/* + * 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 + * + * 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. + */ + +val versionToPublish: String by extra("0.0.1-SNAPSHOT.1") From 02971437a14ae9188ed6a0024d9312abb5bbd74d Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 25 Apr 2024 21:43:50 +0100 Subject: [PATCH 03/39] Auto-updated by IDEA --- .idea/kotlinc.xml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 9bfa224..374970d 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -3,11 +3,7 @@ - - - - + \ No newline at end of file From 9b4722170ca4008e66d3444977b548bdf65b83b1 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 25 Apr 2024 21:47:01 +0100 Subject: [PATCH 04/39] Optimise imports --- buildSrc/src/main/kotlin/module.gradle.kts | 2 -- 1 file changed, 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/module.gradle.kts b/buildSrc/src/main/kotlin/module.gradle.kts index cc2d1a0..0b6e3a7 100644 --- a/buildSrc/src/main/kotlin/module.gradle.kts +++ b/buildSrc/src/main/kotlin/module.gradle.kts @@ -28,14 +28,12 @@ import io.spine.internal.dependency.CheckerFramework import io.spine.internal.dependency.Coroutines import io.spine.internal.dependency.ErrorProne import io.spine.internal.dependency.FindBugs -import io.spine.internal.dependency.Grpc import io.spine.internal.dependency.Guava import io.spine.internal.dependency.JUnit import io.spine.internal.dependency.Jackson import io.spine.internal.dependency.Kotlin import io.spine.internal.dependency.Spine import io.spine.internal.dependency.Truth -import io.spine.internal.dependency.Validation import io.spine.internal.gradle.VersionWriter import io.spine.internal.gradle.checkstyle.CheckStyleConfig import io.spine.internal.gradle.github.pages.updateGitHubPages From 35e5c69042f90decc98f9f1121d1bb59739ab28b Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 25 Apr 2024 21:48:24 +0100 Subject: [PATCH 05/39] Add dependency reports --- dependencies.md | 1453 +++++++++++++++++++++++++++++++++++++++++++++++ pom.xml | 156 +++++ 2 files changed, 1609 insertions(+) create mode 100644 dependencies.md create mode 100644 pom.xml diff --git a/dependencies.md b/dependencies.md new file mode 100644 index 0000000..5166480 --- /dev/null +++ b/dependencies.md @@ -0,0 +1,1453 @@ + + +# Dependencies of `io.spine.tools:prototap-api:0.0.1-SNAPSHOT.1` + +## Runtime +1. **Group** : org.jetbrains. **Name** : annotations. **Version** : 24.0.1. + * **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-stdlib. **Version** : 1.9.23. + * **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-stdlib-jdk7. **Version** : 1.9.23. + * **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-stdlib-jdk8. **Version** : 1.9.23. + * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) + * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +## Compile, tests, and tooling +1. **Group** : aopalliance. **Name** : aopalliance. **Version** : 1.0. + * **Project URL:** [http://aopalliance.sourceforge.net](http://aopalliance.sourceforge.net) + * **License:** Public Domain + +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** : com.beust. **Name** : jcommander. **Version** : 1.82. + * **Project URL:** [https://jcommander.org](https://jcommander.org) + * **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) + +1. **Group** : com.github.kevinstern. **Name** : software-and-algorithms. **Version** : 1.0. + * **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.google.auto. **Name** : auto-common. **Version** : 1.2.2. + * **Project URL:** [https://github.com/google/auto/tree/main/common](https://github.com/google/auto/tree/main/common) + * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.auto.service. **Name** : auto-service-annotations. **Version** : 1.1.1. + * **Project URL:** [https://github.com/google/auto/tree/main/service](https://github.com/google/auto/tree/main/service) + * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.auto.value. **Name** : auto-value-annotations. **Version** : 1.10.2. + * **Project URL:** [https://github.com/google/auto/tree/main/value](https://github.com/google/auto/tree/main/value) + * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +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) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.errorprone. **Name** : error_prone_annotation. **Version** : 2.23.0. + * **Project URL:** [https://errorprone.info/error_prone_annotation](https://errorprone.info/error_prone_annotation) + * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.errorprone. **Name** : error_prone_annotations. **Version** : 2.23.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.errorprone. **Name** : error_prone_check_api. **Version** : 2.23.0. + * **Project URL:** [https://errorprone.info/error_prone_check_api](https://errorprone.info/error_prone_check_api) + * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.errorprone. **Name** : error_prone_core. **Version** : 2.23.0. + * **Project URL:** [https://errorprone.info/error_prone_core](https://errorprone.info/error_prone_core) + * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.errorprone. **Name** : error_prone_type_annotations. **Version** : 2.23.0. + * **Project URL:** [https://errorprone.info/error_prone_type_annotations](https://errorprone.info/error_prone_type_annotations) + * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.errorprone. **Name** : javac. **Version** : 9+181-r4173-1. + * **Project URL:** [https://github.com/google/error-prone-javac](https://github.com/google/error-prone-javac) + * **License:** [GNU General Public License, version 2, with the Classpath Exception](http://openjdk.java.net/legal/gplv2+ce.html) + +1. **Group** : com.google.guava. **Name** : failureaccess. **Version** : 1.0.1. + * **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) + +1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.3-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. + * **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. + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.inject. **Name** : guice. **Version** : 5.1.0. + * **Project URL:** [https://github.com/google/guice](https://github.com/google/guice) + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.j2objc. **Name** : j2objc-annotations. **Version** : 2.8. + * **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. + * **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.truth. **Name** : truth. **Version** : 1.1.5. + * **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. + * **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. + * **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. + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +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** : commons-beanutils. **Name** : commons-beanutils. **Version** : 1.9.4. + * **Project URL:** [https://commons.apache.org/proper/commons-beanutils/](https://commons.apache.org/proper/commons-beanutils/) + * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : commons-codec. **Name** : commons-codec. **Version** : 1.16.0. + * **Project URL:** [https://commons.apache.org/proper/commons-codec/](https://commons.apache.org/proper/commons-codec/) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : commons-collections. **Name** : commons-collections. **Version** : 3.2.2. + * **Project URL:** [http://commons.apache.org/collections/](http://commons.apache.org/collections/) + * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : info.picocli. **Name** : picocli. **Version** : 4.7.4. + * **Project URL:** [https://picocli.info](https://picocli.info) + * **License:** [The Apache Software License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : io.github.davidburstrom.contester. **Name** : contester-breakpoint. **Version** : 0.2.0. + * **Project URL:** [https://github.com/davidburstrom/contester](https://github.com/davidburstrom/contester) + * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : io.github.detekt.sarif4k. **Name** : sarif4k. **Version** : 0.4.0.**No license information found** +1. **Group** : io.github.detekt.sarif4k. **Name** : sarif4k-jvm. **Version** : 0.4.0. + * **Project URL:** [https://detekt.github.io/detekt](https://detekt.github.io/detekt) + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : io.github.eisop. **Name** : dataflow-errorprone. **Version** : 3.34.0-eisop1. + * **Project URL:** [https://eisop.github.io/](https://eisop.github.io/) + * **License:** [GNU General Public License, version 2 (GPL2), with the classpath exception](http://www.gnu.org/software/classpath/license.html) + +1. **Group** : io.github.java-diff-utils. **Name** : java-diff-utils. **Version** : 4.12. + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-api. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-cli. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-core. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-metrics. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-parser. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-psi-utils. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-report-html. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-report-md. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-report-sarif. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-report-txt. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-report-xml. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-complexity. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-coroutines. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-documentation. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-empty. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-errorprone. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-exceptions. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-naming. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-performance. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-style. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-tooling. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-utils. **Version** : 1.23.0. + * **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** : javax.inject. **Name** : javax.inject. **Version** : 1. + * **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. + * **Project URL:** [http://junit.org](http://junit.org) + * **License:** [Eclipse Public License 1.0](http://www.eclipse.org/legal/epl-v10.html) + +1. **Group** : net.java.dev.jna. **Name** : jna. **Version** : 5.6.0. + * **Project URL:** [https://github.com/java-native-access/jna](https://github.com/java-native-access/jna) + * **License:** [Apache License v2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [LGPL, version 2.1](http://www.gnu.org/licenses/licenses.html) + +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. + * **License:** [BSD-style](http://pmd.sourceforge.net/license.html) + +1. **Group** : net.sourceforge.pmd. **Name** : pmd-java. **Version** : 6.55.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** : 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. + * **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.httpcomponents.client5. **Name** : httpclient5. **Version** : 5.1.3. + * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.apache.httpcomponents.core5. **Name** : httpcore5. **Version** : 5.1.3. + * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.apache.httpcomponents.core5. **Name** : httpcore5-h2. **Version** : 5.1.3. + * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.apiguardian. **Name** : apiguardian-api. **Version** : 1.1.2. + * **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.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.hamcrest. **Name** : hamcrest. **Version** : 2.2. + * **Project URL:** [http://hamcrest.org/JavaHamcrest/](http://hamcrest.org/JavaHamcrest/) + * **License:** [BSD License 3](http://opensource.org/licenses/BSD-3-Clause) + +1. **Group** : org.hamcrest. **Name** : hamcrest-core. **Version** : 2.2. + * **Project URL:** [http://hamcrest.org/JavaHamcrest/](http://hamcrest.org/JavaHamcrest/) + * **License:** [BSD License 3](http://opensource.org/licenses/BSD-3-Clause) + +1. **Group** : org.jacoco. **Name** : org.jacoco.agent. **Version** : 0.8.8. + * **License:** [Eclipse Public License 2.0](https://www.eclipse.org/legal/epl-2.0/) + +1. **Group** : org.jacoco. **Name** : org.jacoco.ant. **Version** : 0.8.8. + * **License:** [Eclipse Public License 2.0](https://www.eclipse.org/legal/epl-2.0/) + +1. **Group** : org.jacoco. **Name** : org.jacoco.core. **Version** : 0.8.8. + * **License:** [Eclipse Public License 2.0](https://www.eclipse.org/legal/epl-2.0/) + +1. **Group** : org.jacoco. **Name** : org.jacoco.report. **Version** : 0.8.8. + * **License:** [Eclipse Public License 2.0](https://www.eclipse.org/legal/epl-2.0/) + +1. **Group** : org.javassist. **Name** : javassist. **Version** : 3.28.0-GA. + * **Project URL:** [http://www.javassist.org/](http://www.javassist.org/) + * **License:** [Apache License 2.0](http://www.apache.org/licenses/) + * **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.jetbrains. **Name** : annotations. **Version** : 24.0.1. + * **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.intellij.deps. **Name** : trove4j. **Version** : 1.0.20200330. + * **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-compiler-embeddable. **Version** : 1.8.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** : 1.8.22. + * **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** : 1.8.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** : 1.8.22. + * **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-klib-commonizer-embeddable. **Version** : 1.8.22. + * **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-reflect. **Version** : 1.9.23. + * **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** : 1.8.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** : 1.8.22. + * **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-scripting-common. **Version** : 1.8.22. + * **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-scripting-compiler-embeddable. **Version** : 1.8.22. + * **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-scripting-compiler-impl-embeddable. **Version** : 1.8.22. + * **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-scripting-jvm. **Version** : 1.8.22. + * **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-stdlib. **Version** : 1.9.23. + * **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-stdlib-common. **Version** : 1.9.23.**No license information found** +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-jdk7. **Version** : 1.9.23. + * **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-stdlib-jdk8. **Version** : 1.9.23. + * **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** : kotlinx-html-jvm. **Version** : 0.8.1. + * **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-core. **Version** : 1.4.1.**No license information found** +1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-serialization-core-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. **Version** : 1.4.1.**No license information found** +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.junit. **Name** : junit-bom. **Version** : 5.10.0.**No license information found** +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 5.10.0. + * **Project URL:** [https://junit.org/junit5/](https://junit.org/junit5/) + * **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/) + * **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/) + * **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/) + * **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/) + * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) + +1. **Group** : org.opentest4j. **Name** : opentest4j. **Version** : 1.3.0. + * **Project URL:** [https://github.com/ota4j-team/opentest4j](https://github.com/ota4j-team/opentest4j) + * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.ow2.asm. **Name** : asm. **Version** : 9.2. + * **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.ow2.asm. **Name** : asm-analysis. **Version** : 9.2. + * **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.ow2.asm. **Name** : asm-commons. **Version** : 9.2. + * **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.ow2.asm. **Name** : asm-tree. **Version** : 9.2. + * **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.pcollections. **Name** : pcollections. **Version** : 3.1.4. + * **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.snakeyaml. **Name** : snakeyaml-engine. **Version** : 2.6. + * **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) + +1. **Group** : org.xmlresolver. **Name** : xmlresolver. **Version** : 5.1.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) + + +The dependencies distributed under several licenses, are used according their commercial-use-friendly license. + +This report was generated on **Thu Apr 25 21:47:03 WEST 2024** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). + + + + +# Dependencies of `io.spine.tools:prototap-gradle-plugin:0.0.1-SNAPSHOT.1` + +## Runtime +1. **Group** : org.jetbrains. **Name** : annotations. **Version** : 24.0.1. + * **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-stdlib. **Version** : 1.9.23. + * **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-stdlib-jdk7. **Version** : 1.9.23. + * **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-stdlib-jdk8. **Version** : 1.9.23. + * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) + * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +## Compile, tests, and tooling +1. **Group** : aopalliance. **Name** : aopalliance. **Version** : 1.0. + * **Project URL:** [http://aopalliance.sourceforge.net](http://aopalliance.sourceforge.net) + * **License:** Public Domain + +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** : com.beust. **Name** : jcommander. **Version** : 1.82. + * **Project URL:** [https://jcommander.org](https://jcommander.org) + * **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) + +1. **Group** : com.github.kevinstern. **Name** : software-and-algorithms. **Version** : 1.0. + * **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.google.auto. **Name** : auto-common. **Version** : 1.2.2. + * **Project URL:** [https://github.com/google/auto/tree/main/common](https://github.com/google/auto/tree/main/common) + * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.auto.service. **Name** : auto-service-annotations. **Version** : 1.1.1. + * **Project URL:** [https://github.com/google/auto/tree/main/service](https://github.com/google/auto/tree/main/service) + * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.auto.value. **Name** : auto-value-annotations. **Version** : 1.10.2. + * **Project URL:** [https://github.com/google/auto/tree/main/value](https://github.com/google/auto/tree/main/value) + * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +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) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.errorprone. **Name** : error_prone_annotation. **Version** : 2.23.0. + * **Project URL:** [https://errorprone.info/error_prone_annotation](https://errorprone.info/error_prone_annotation) + * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.errorprone. **Name** : error_prone_annotations. **Version** : 2.23.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.errorprone. **Name** : error_prone_check_api. **Version** : 2.23.0. + * **Project URL:** [https://errorprone.info/error_prone_check_api](https://errorprone.info/error_prone_check_api) + * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.errorprone. **Name** : error_prone_core. **Version** : 2.23.0. + * **Project URL:** [https://errorprone.info/error_prone_core](https://errorprone.info/error_prone_core) + * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.errorprone. **Name** : error_prone_type_annotations. **Version** : 2.23.0. + * **Project URL:** [https://errorprone.info/error_prone_type_annotations](https://errorprone.info/error_prone_type_annotations) + * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.errorprone. **Name** : javac. **Version** : 9+181-r4173-1. + * **Project URL:** [https://github.com/google/error-prone-javac](https://github.com/google/error-prone-javac) + * **License:** [GNU General Public License, version 2, with the Classpath Exception](http://openjdk.java.net/legal/gplv2+ce.html) + +1. **Group** : com.google.guava. **Name** : failureaccess. **Version** : 1.0.1. + * **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) + +1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.3-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. + * **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. + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.inject. **Name** : guice. **Version** : 5.1.0. + * **Project URL:** [https://github.com/google/guice](https://github.com/google/guice) + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.j2objc. **Name** : j2objc-annotations. **Version** : 2.8. + * **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. + * **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.truth. **Name** : truth. **Version** : 1.1.5. + * **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. + * **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. + * **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. + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +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** : commons-beanutils. **Name** : commons-beanutils. **Version** : 1.9.4. + * **Project URL:** [https://commons.apache.org/proper/commons-beanutils/](https://commons.apache.org/proper/commons-beanutils/) + * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : commons-codec. **Name** : commons-codec. **Version** : 1.16.0. + * **Project URL:** [https://commons.apache.org/proper/commons-codec/](https://commons.apache.org/proper/commons-codec/) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : commons-collections. **Name** : commons-collections. **Version** : 3.2.2. + * **Project URL:** [http://commons.apache.org/collections/](http://commons.apache.org/collections/) + * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : info.picocli. **Name** : picocli. **Version** : 4.7.4. + * **Project URL:** [https://picocli.info](https://picocli.info) + * **License:** [The Apache Software License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : io.github.davidburstrom.contester. **Name** : contester-breakpoint. **Version** : 0.2.0. + * **Project URL:** [https://github.com/davidburstrom/contester](https://github.com/davidburstrom/contester) + * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : io.github.detekt.sarif4k. **Name** : sarif4k. **Version** : 0.4.0.**No license information found** +1. **Group** : io.github.detekt.sarif4k. **Name** : sarif4k-jvm. **Version** : 0.4.0. + * **Project URL:** [https://detekt.github.io/detekt](https://detekt.github.io/detekt) + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : io.github.eisop. **Name** : dataflow-errorprone. **Version** : 3.34.0-eisop1. + * **Project URL:** [https://eisop.github.io/](https://eisop.github.io/) + * **License:** [GNU General Public License, version 2 (GPL2), with the classpath exception](http://www.gnu.org/software/classpath/license.html) + +1. **Group** : io.github.java-diff-utils. **Name** : java-diff-utils. **Version** : 4.12. + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-api. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-cli. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-core. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-metrics. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-parser. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-psi-utils. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-report-html. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-report-md. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-report-sarif. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-report-txt. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-report-xml. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-complexity. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-coroutines. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-documentation. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-empty. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-errorprone. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-exceptions. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-naming. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-performance. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-style. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-tooling. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-utils. **Version** : 1.23.0. + * **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** : javax.inject. **Name** : javax.inject. **Version** : 1. + * **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. + * **Project URL:** [http://junit.org](http://junit.org) + * **License:** [Eclipse Public License 1.0](http://www.eclipse.org/legal/epl-v10.html) + +1. **Group** : net.java.dev.jna. **Name** : jna. **Version** : 5.6.0. + * **Project URL:** [https://github.com/java-native-access/jna](https://github.com/java-native-access/jna) + * **License:** [Apache License v2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [LGPL, version 2.1](http://www.gnu.org/licenses/licenses.html) + +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. + * **License:** [BSD-style](http://pmd.sourceforge.net/license.html) + +1. **Group** : net.sourceforge.pmd. **Name** : pmd-java. **Version** : 6.55.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** : 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. + * **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.httpcomponents.client5. **Name** : httpclient5. **Version** : 5.1.3. + * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.apache.httpcomponents.core5. **Name** : httpcore5. **Version** : 5.1.3. + * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.apache.httpcomponents.core5. **Name** : httpcore5-h2. **Version** : 5.1.3. + * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.apiguardian. **Name** : apiguardian-api. **Version** : 1.1.2. + * **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.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.hamcrest. **Name** : hamcrest. **Version** : 2.2. + * **Project URL:** [http://hamcrest.org/JavaHamcrest/](http://hamcrest.org/JavaHamcrest/) + * **License:** [BSD License 3](http://opensource.org/licenses/BSD-3-Clause) + +1. **Group** : org.hamcrest. **Name** : hamcrest-core. **Version** : 2.2. + * **Project URL:** [http://hamcrest.org/JavaHamcrest/](http://hamcrest.org/JavaHamcrest/) + * **License:** [BSD License 3](http://opensource.org/licenses/BSD-3-Clause) + +1. **Group** : org.jacoco. **Name** : org.jacoco.agent. **Version** : 0.8.8. + * **License:** [Eclipse Public License 2.0](https://www.eclipse.org/legal/epl-2.0/) + +1. **Group** : org.jacoco. **Name** : org.jacoco.ant. **Version** : 0.8.8. + * **License:** [Eclipse Public License 2.0](https://www.eclipse.org/legal/epl-2.0/) + +1. **Group** : org.jacoco. **Name** : org.jacoco.core. **Version** : 0.8.8. + * **License:** [Eclipse Public License 2.0](https://www.eclipse.org/legal/epl-2.0/) + +1. **Group** : org.jacoco. **Name** : org.jacoco.report. **Version** : 0.8.8. + * **License:** [Eclipse Public License 2.0](https://www.eclipse.org/legal/epl-2.0/) + +1. **Group** : org.javassist. **Name** : javassist. **Version** : 3.28.0-GA. + * **Project URL:** [http://www.javassist.org/](http://www.javassist.org/) + * **License:** [Apache License 2.0](http://www.apache.org/licenses/) + * **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.jetbrains. **Name** : annotations. **Version** : 24.0.1. + * **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.intellij.deps. **Name** : trove4j. **Version** : 1.0.20200330. + * **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-compiler-embeddable. **Version** : 1.8.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** : 1.8.22. + * **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** : 1.8.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** : 1.8.22. + * **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-klib-commonizer-embeddable. **Version** : 1.8.22. + * **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-reflect. **Version** : 1.9.23. + * **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** : 1.8.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** : 1.8.22. + * **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-scripting-common. **Version** : 1.8.22. + * **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-scripting-compiler-embeddable. **Version** : 1.8.22. + * **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-scripting-compiler-impl-embeddable. **Version** : 1.8.22. + * **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-scripting-jvm. **Version** : 1.8.22. + * **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-stdlib. **Version** : 1.9.23. + * **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-stdlib-common. **Version** : 1.9.23.**No license information found** +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-jdk7. **Version** : 1.9.23. + * **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-stdlib-jdk8. **Version** : 1.9.23. + * **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** : kotlinx-html-jvm. **Version** : 0.8.1. + * **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-core. **Version** : 1.4.1.**No license information found** +1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-serialization-core-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. **Version** : 1.4.1.**No license information found** +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.junit. **Name** : junit-bom. **Version** : 5.10.0.**No license information found** +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 5.10.0. + * **Project URL:** [https://junit.org/junit5/](https://junit.org/junit5/) + * **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/) + * **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/) + * **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/) + * **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/) + * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) + +1. **Group** : org.opentest4j. **Name** : opentest4j. **Version** : 1.3.0. + * **Project URL:** [https://github.com/ota4j-team/opentest4j](https://github.com/ota4j-team/opentest4j) + * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.ow2.asm. **Name** : asm. **Version** : 9.2. + * **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.ow2.asm. **Name** : asm-analysis. **Version** : 9.2. + * **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.ow2.asm. **Name** : asm-commons. **Version** : 9.2. + * **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.ow2.asm. **Name** : asm-tree. **Version** : 9.2. + * **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.pcollections. **Name** : pcollections. **Version** : 3.1.4. + * **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.snakeyaml. **Name** : snakeyaml-engine. **Version** : 2.6. + * **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) + +1. **Group** : org.xmlresolver. **Name** : xmlresolver. **Version** : 5.1.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) + + +The dependencies distributed under several licenses, are used according their commercial-use-friendly license. + +This report was generated on **Thu Apr 25 21:47:03 WEST 2024** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). + + + + +# Dependencies of `io.spine.tools:prototap-protoc-plugin:0.0.1-SNAPSHOT.1` + +## Runtime +1. **Group** : org.jetbrains. **Name** : annotations. **Version** : 24.0.1. + * **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-stdlib. **Version** : 1.9.23. + * **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-stdlib-jdk7. **Version** : 1.9.23. + * **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-stdlib-jdk8. **Version** : 1.9.23. + * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) + * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +## Compile, tests, and tooling +1. **Group** : aopalliance. **Name** : aopalliance. **Version** : 1.0. + * **Project URL:** [http://aopalliance.sourceforge.net](http://aopalliance.sourceforge.net) + * **License:** Public Domain + +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** : com.beust. **Name** : jcommander. **Version** : 1.82. + * **Project URL:** [https://jcommander.org](https://jcommander.org) + * **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) + +1. **Group** : com.github.kevinstern. **Name** : software-and-algorithms. **Version** : 1.0. + * **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.google.auto. **Name** : auto-common. **Version** : 1.2.2. + * **Project URL:** [https://github.com/google/auto/tree/main/common](https://github.com/google/auto/tree/main/common) + * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.auto.service. **Name** : auto-service-annotations. **Version** : 1.1.1. + * **Project URL:** [https://github.com/google/auto/tree/main/service](https://github.com/google/auto/tree/main/service) + * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.auto.value. **Name** : auto-value-annotations. **Version** : 1.10.2. + * **Project URL:** [https://github.com/google/auto/tree/main/value](https://github.com/google/auto/tree/main/value) + * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +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) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.errorprone. **Name** : error_prone_annotation. **Version** : 2.23.0. + * **Project URL:** [https://errorprone.info/error_prone_annotation](https://errorprone.info/error_prone_annotation) + * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.errorprone. **Name** : error_prone_annotations. **Version** : 2.23.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.errorprone. **Name** : error_prone_check_api. **Version** : 2.23.0. + * **Project URL:** [https://errorprone.info/error_prone_check_api](https://errorprone.info/error_prone_check_api) + * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.errorprone. **Name** : error_prone_core. **Version** : 2.23.0. + * **Project URL:** [https://errorprone.info/error_prone_core](https://errorprone.info/error_prone_core) + * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.errorprone. **Name** : error_prone_type_annotations. **Version** : 2.23.0. + * **Project URL:** [https://errorprone.info/error_prone_type_annotations](https://errorprone.info/error_prone_type_annotations) + * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.errorprone. **Name** : javac. **Version** : 9+181-r4173-1. + * **Project URL:** [https://github.com/google/error-prone-javac](https://github.com/google/error-prone-javac) + * **License:** [GNU General Public License, version 2, with the Classpath Exception](http://openjdk.java.net/legal/gplv2+ce.html) + +1. **Group** : com.google.guava. **Name** : failureaccess. **Version** : 1.0.1. + * **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) + +1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.3-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. + * **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. + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.inject. **Name** : guice. **Version** : 5.1.0. + * **Project URL:** [https://github.com/google/guice](https://github.com/google/guice) + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.j2objc. **Name** : j2objc-annotations. **Version** : 2.8. + * **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. + * **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.truth. **Name** : truth. **Version** : 1.1.5. + * **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. + * **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. + * **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. + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +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** : commons-beanutils. **Name** : commons-beanutils. **Version** : 1.9.4. + * **Project URL:** [https://commons.apache.org/proper/commons-beanutils/](https://commons.apache.org/proper/commons-beanutils/) + * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : commons-codec. **Name** : commons-codec. **Version** : 1.16.0. + * **Project URL:** [https://commons.apache.org/proper/commons-codec/](https://commons.apache.org/proper/commons-codec/) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : commons-collections. **Name** : commons-collections. **Version** : 3.2.2. + * **Project URL:** [http://commons.apache.org/collections/](http://commons.apache.org/collections/) + * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : info.picocli. **Name** : picocli. **Version** : 4.7.4. + * **Project URL:** [https://picocli.info](https://picocli.info) + * **License:** [The Apache Software License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : io.github.davidburstrom.contester. **Name** : contester-breakpoint. **Version** : 0.2.0. + * **Project URL:** [https://github.com/davidburstrom/contester](https://github.com/davidburstrom/contester) + * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : io.github.detekt.sarif4k. **Name** : sarif4k. **Version** : 0.4.0.**No license information found** +1. **Group** : io.github.detekt.sarif4k. **Name** : sarif4k-jvm. **Version** : 0.4.0. + * **Project URL:** [https://detekt.github.io/detekt](https://detekt.github.io/detekt) + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : io.github.eisop. **Name** : dataflow-errorprone. **Version** : 3.34.0-eisop1. + * **Project URL:** [https://eisop.github.io/](https://eisop.github.io/) + * **License:** [GNU General Public License, version 2 (GPL2), with the classpath exception](http://www.gnu.org/software/classpath/license.html) + +1. **Group** : io.github.java-diff-utils. **Name** : java-diff-utils. **Version** : 4.12. + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : io.gitlab.arturbosch.detekt. **Name** : detekt-api. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-cli. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-core. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-metrics. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-parser. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-psi-utils. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-report-html. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-report-md. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-report-sarif. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-report-txt. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-report-xml. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-complexity. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-coroutines. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-documentation. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-empty. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-errorprone. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-exceptions. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-naming. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-performance. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-rules-style. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-tooling. **Version** : 1.23.0. + * **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.gitlab.arturbosch.detekt. **Name** : detekt-utils. **Version** : 1.23.0. + * **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** : javax.inject. **Name** : javax.inject. **Version** : 1. + * **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. + * **Project URL:** [http://junit.org](http://junit.org) + * **License:** [Eclipse Public License 1.0](http://www.eclipse.org/legal/epl-v10.html) + +1. **Group** : net.java.dev.jna. **Name** : jna. **Version** : 5.6.0. + * **Project URL:** [https://github.com/java-native-access/jna](https://github.com/java-native-access/jna) + * **License:** [Apache License v2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + * **License:** [LGPL, version 2.1](http://www.gnu.org/licenses/licenses.html) + +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. + * **License:** [BSD-style](http://pmd.sourceforge.net/license.html) + +1. **Group** : net.sourceforge.pmd. **Name** : pmd-java. **Version** : 6.55.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** : 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. + * **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.httpcomponents.client5. **Name** : httpclient5. **Version** : 5.1.3. + * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.apache.httpcomponents.core5. **Name** : httpcore5. **Version** : 5.1.3. + * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.apache.httpcomponents.core5. **Name** : httpcore5-h2. **Version** : 5.1.3. + * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.apiguardian. **Name** : apiguardian-api. **Version** : 1.1.2. + * **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.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.hamcrest. **Name** : hamcrest. **Version** : 2.2. + * **Project URL:** [http://hamcrest.org/JavaHamcrest/](http://hamcrest.org/JavaHamcrest/) + * **License:** [BSD License 3](http://opensource.org/licenses/BSD-3-Clause) + +1. **Group** : org.hamcrest. **Name** : hamcrest-core. **Version** : 2.2. + * **Project URL:** [http://hamcrest.org/JavaHamcrest/](http://hamcrest.org/JavaHamcrest/) + * **License:** [BSD License 3](http://opensource.org/licenses/BSD-3-Clause) + +1. **Group** : org.jacoco. **Name** : org.jacoco.agent. **Version** : 0.8.8. + * **License:** [Eclipse Public License 2.0](https://www.eclipse.org/legal/epl-2.0/) + +1. **Group** : org.jacoco. **Name** : org.jacoco.ant. **Version** : 0.8.8. + * **License:** [Eclipse Public License 2.0](https://www.eclipse.org/legal/epl-2.0/) + +1. **Group** : org.jacoco. **Name** : org.jacoco.core. **Version** : 0.8.8. + * **License:** [Eclipse Public License 2.0](https://www.eclipse.org/legal/epl-2.0/) + +1. **Group** : org.jacoco. **Name** : org.jacoco.report. **Version** : 0.8.8. + * **License:** [Eclipse Public License 2.0](https://www.eclipse.org/legal/epl-2.0/) + +1. **Group** : org.javassist. **Name** : javassist. **Version** : 3.28.0-GA. + * **Project URL:** [http://www.javassist.org/](http://www.javassist.org/) + * **License:** [Apache License 2.0](http://www.apache.org/licenses/) + * **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.jetbrains. **Name** : annotations. **Version** : 24.0.1. + * **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.intellij.deps. **Name** : trove4j. **Version** : 1.0.20200330. + * **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-compiler-embeddable. **Version** : 1.8.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** : 1.8.22. + * **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** : 1.8.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** : 1.8.22. + * **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-klib-commonizer-embeddable. **Version** : 1.8.22. + * **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-reflect. **Version** : 1.9.23. + * **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** : 1.8.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** : 1.8.22. + * **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-scripting-common. **Version** : 1.8.22. + * **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-scripting-compiler-embeddable. **Version** : 1.8.22. + * **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-scripting-compiler-impl-embeddable. **Version** : 1.8.22. + * **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-scripting-jvm. **Version** : 1.8.22. + * **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-stdlib. **Version** : 1.9.23. + * **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-stdlib-common. **Version** : 1.9.23.**No license information found** +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-jdk7. **Version** : 1.9.23. + * **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-stdlib-jdk8. **Version** : 1.9.23. + * **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** : kotlinx-html-jvm. **Version** : 0.8.1. + * **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-core. **Version** : 1.4.1.**No license information found** +1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-serialization-core-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. **Version** : 1.4.1.**No license information found** +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.junit. **Name** : junit-bom. **Version** : 5.10.0.**No license information found** +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter-api. **Version** : 5.10.0. + * **Project URL:** [https://junit.org/junit5/](https://junit.org/junit5/) + * **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/) + * **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/) + * **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/) + * **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/) + * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) + +1. **Group** : org.opentest4j. **Name** : opentest4j. **Version** : 1.3.0. + * **Project URL:** [https://github.com/ota4j-team/opentest4j](https://github.com/ota4j-team/opentest4j) + * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : org.ow2.asm. **Name** : asm. **Version** : 9.2. + * **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.ow2.asm. **Name** : asm-analysis. **Version** : 9.2. + * **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.ow2.asm. **Name** : asm-commons. **Version** : 9.2. + * **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.ow2.asm. **Name** : asm-tree. **Version** : 9.2. + * **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.pcollections. **Name** : pcollections. **Version** : 3.1.4. + * **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.snakeyaml. **Name** : snakeyaml-engine. **Version** : 2.6. + * **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) + +1. **Group** : org.xmlresolver. **Name** : xmlresolver. **Version** : 5.1.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) + + +The dependencies distributed under several licenses, are used according their commercial-use-friendly license. + +This report was generated on **Thu Apr 25 21:47:03 WEST 2024** 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/pom.xml b/pom.xml new file mode 100644 index 0000000..90cf241 --- /dev/null +++ b/pom.xml @@ -0,0 +1,156 @@ + + +4.0.0 + +io.spine.tools +ProtoTap +0.0.1-SNAPSHOT.1 + +2015 + + + + Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + 1.8.22 + compile + + + com.google.guava + guava-testlib + 32.1.3-jre + test + + + com.google.truth + truth + 1.1.5 + test + + + com.google.truth.extensions + truth-java8-extension + 1.1.5 + test + + + com.google.truth.extensions + truth-proto-extension + 1.1.5 + test + + + org.apiguardian + apiguardian-api + 1.1.2 + test + + + org.junit.jupiter + junit-jupiter-api + 5.10.0 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.10.0 + test + + + org.junit.jupiter + junit-jupiter-params + 5.10.0 + test + + + com.google.code.findbugs + jsr305 + 3.0.2 + provided + + + com.google.errorprone + error_prone_annotations + 2.23.0 + provided + + + com.google.errorprone + error_prone_core + 2.23.0 + + + com.google.errorprone + error_prone_type_annotations + 2.23.0 + provided + + + com.google.errorprone + javac + 9+181-r4173-1 + + + com.puppycrawl.tools + checkstyle + 10.12.1 + + + io.gitlab.arturbosch.detekt + detekt-cli + 1.23.0 + + + net.sourceforge.pmd + pmd-java + 6.55.0 + + + org.checkerframework + checker-qual + 3.40.0 + provided + + + org.jacoco + org.jacoco.agent + 0.8.8 + + + org.jacoco + org.jacoco.ant + 0.8.8 + + + org.jetbrains.kotlin + kotlin-compiler-embeddable + 1.8.22 + + + org.jetbrains.kotlin + kotlin-klib-commonizer-embeddable + 1.8.22 + + + org.jetbrains.kotlin + kotlin-scripting-compiler-embeddable + 1.8.22 + + + + \ No newline at end of file From b39fafb0f1b980069ccf1173465d08fc7fe9997b Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 25 Apr 2024 22:07:32 +0100 Subject: [PATCH 06/39] Add name constants --- .../kotlin/io/spine/tools/prototap/Names.kt | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 api/src/main/kotlin/io/spine/tools/prototap/Names.kt diff --git a/api/src/main/kotlin/io/spine/tools/prototap/Names.kt b/api/src/main/kotlin/io/spine/tools/prototap/Names.kt new file mode 100644 index 0000000..74dae2f --- /dev/null +++ b/api/src/main/kotlin/io/spine/tools/prototap/Names.kt @@ -0,0 +1,49 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.tools.prototap + +/** + * Names for the resources ProtoTap produces. + */ +public object Names { + + /** + * The name ProtoTap uses when passing itself to `protoc` compiler. + */ + public const val PROTOC_PLUGIN_NAME: String = "prototap" + + /** + * The name of the file containing `CodeGeneratorRequest` message obtained + * by the ProtoTap `protoc` plugins. + */ + public const val CODE_GENERATOR_REQUEST_FILE: String = "CodeGeneratorRequest.binpb" + + /** + * The name of the descriptor set file obtained by the ProtoTap Gradle plugin. + */ + public const val DESCRIPTOR_SET_FILE: String = "FileDescriptorSet.binpb" +} From 7066b3fb82206e3a5eaf8f4dd38a29d066cf6b80 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Fri, 26 Apr 2024 03:51:32 +0100 Subject: [PATCH 07/39] Tune dependencies --- buildSrc/src/main/kotlin/module.gradle.kts | 3 +- dependencies.md | 251 ++++++++++++++++++++- gradle-plugin/build.gradle.kts | 142 ++++++++++++ pom.xml | 33 +++ 4 files changed, 425 insertions(+), 4 deletions(-) diff --git a/buildSrc/src/main/kotlin/module.gradle.kts b/buildSrc/src/main/kotlin/module.gradle.kts index 0b6e3a7..5aa972f 100644 --- a/buildSrc/src/main/kotlin/module.gradle.kts +++ b/buildSrc/src/main/kotlin/module.gradle.kts @@ -113,7 +113,8 @@ fun Module.forceConfigurations() { Coroutines.core, Coroutines.bom, Coroutines.coreJvm, - Jackson.Junior.objects + Jackson.Junior.objects, + Spine.reflect ) } } diff --git a/dependencies.md b/dependencies.md index 5166480..40e3af1 100644 --- a/dependencies.md +++ b/dependencies.md @@ -480,7 +480,7 @@ The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Apr 25 21:47:03 WEST 2024** 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). +This report was generated on **Fri Apr 26 03:50:37 WEST 2024** 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). @@ -488,14 +488,94 @@ This report was generated on **Thu Apr 25 21:47:03 WEST 2024** using [Gradle-Lic # Dependencies of `io.spine.tools:prototap-gradle-plugin:0.0.1-SNAPSHOT.1` ## 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) + * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.errorprone. **Name** : error_prone_annotations. **Version** : 2.23.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. + * **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) + +1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.3-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** : listenablefuture. **Version** : 9999.0-empty-to-avoid-conflict-with-guava. + * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.j2objc. **Name** : j2objc-annotations. **Version** : 2.8. + * **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. + * **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. + * **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. + * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) + +1. **Group** : com.squareup. **Name** : javapoet. **Version** : 1.13.0. + * **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** : 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) + +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.jboss.forge.roaster. **Name** : roaster-api. **Version** : 2.28.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. + * **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.jetbrains. **Name** : annotations. **Version** : 24.0.1. * **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-gradle-plugin-annotations. **Version** : 1.9.23. + * **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-gradle-plugin-api. **Version** : 1.9.23. + * **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-gradle-plugins-bom. **Version** : 1.9.23.**No license information found** +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-native-utils. **Version** : 1.9.23. + * **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-project-model. **Version** : 1.9.23. + * **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-reflect. **Version** : 1.9.23. + * **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-stdlib. **Version** : 1.9.23. * **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-stdlib-common. **Version** : 1.9.23.**No license information found** 1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-stdlib-jdk7. **Version** : 1.9.23. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -504,6 +584,23 @@ This report was generated on **Thu Apr 25 21:47:03 WEST 2024** using [Gradle-Lic * **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-tooling-core. **Version** : 1.9.23. + * **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-util-io. **Version** : 1.9.23. + * **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-util-klib. **Version** : 1.9.23. + * **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.ow2.asm. **Name** : asm. **Version** : 9.2. + * **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) + ## Compile, tests, and tooling 1. **Group** : aopalliance. **Name** : aopalliance. **Version** : 1.0. * **Project URL:** [http://aopalliance.sourceforge.net](http://aopalliance.sourceforge.net) @@ -569,6 +666,10 @@ This report was generated on **Thu Apr 25 21:47:03 WEST 2024** using [Gradle-Lic * **Project URL:** [https://github.com/google/error-prone-javac](https://github.com/google/error-prone-javac) * **License:** [GNU General Public License, version 2, with the Classpath Exception](http://openjdk.java.net/legal/gplv2+ce.html) +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.1. * **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) @@ -591,10 +692,21 @@ This report was generated on **Thu Apr 25 21:47:03 WEST 2024** using [Gradle-Lic * **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-gradle-plugin. **Version** : 0.9.4. + * **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** : 3.25.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. + * **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. + * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) + 1. **Group** : com.google.truth. **Name** : truth. **Version** : 1.1.5. * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -611,6 +723,10 @@ This report was generated on **Thu Apr 25 21:47:03 WEST 2024** using [Gradle-Lic * **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.squareup. **Name** : javapoet. **Version** : 1.13.0. + * **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** : commons-beanutils. **Name** : commons-beanutils. **Version** : 1.9.4. * **Project URL:** [https://commons.apache.org/proper/commons-beanutils/](https://commons.apache.org/proper/commons-beanutils/) * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -735,6 +851,10 @@ This report was generated on **Thu Apr 25 21:47:03 WEST 2024** using [Gradle-Lic * **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** : 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) + 1. **Group** : javax.inject. **Name** : javax.inject. **Version** : 1. * **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) @@ -743,6 +863,10 @@ This report was generated on **Thu Apr 25 21:47:03 WEST 2024** using [Gradle-Lic * **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.java.dev.jna. **Name** : jna. **Version** : 5.6.0. * **Project URL:** [https://github.com/java-native-access/jna](https://github.com/java-native-access/jna) * **License:** [Apache License v2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -817,6 +941,14 @@ This report was generated on **Thu Apr 25 21:47:03 WEST 2024** using [Gradle-Lic * **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. + * **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. + * **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.jetbrains. **Name** : annotations. **Version** : 24.0.1. * **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) @@ -825,6 +957,14 @@ This report was generated on **Thu Apr 25 21:47:03 WEST 2024** using [Gradle-Lic * **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-android-extensions. **Version** : 1.9.23. + * **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-build-tools-api. **Version** : 1.9.23. + * **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** : 1.8.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) @@ -833,6 +973,18 @@ This report was generated on **Thu Apr 25 21:47:03 WEST 2024** using [Gradle-Lic * **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** : 1.9.23. + * **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-runner. **Version** : 1.9.23. + * **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-client. **Version** : 1.9.23. + * **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** : 1.8.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) @@ -841,10 +993,51 @@ This report was generated on **Thu Apr 25 21:47:03 WEST 2024** using [Gradle-Lic * **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** : 1.9.23. + * **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-gradle-plugin. **Version** : 1.9.23. + * **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-gradle-plugin-annotations. **Version** : 1.9.23. + * **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-gradle-plugin-api. **Version** : 1.9.23. + * **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-gradle-plugin-idea. **Version** : 1.9.23. + * **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-gradle-plugin-idea-proto. **Version** : 1.9.23. + * **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-gradle-plugin-model. **Version** : 1.9.23. + * **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-gradle-plugins-bom. **Version** : 1.9.23.**No license information found** +1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-klib-commonizer-api. **Version** : 1.9.23. + * **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-klib-commonizer-embeddable. **Version** : 1.8.22. * **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-native-utils. **Version** : 1.9.23. + * **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-project-model. **Version** : 1.9.23. + * **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-reflect. **Version** : 1.9.23. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -861,18 +1054,34 @@ This report was generated on **Thu Apr 25 21:47:03 WEST 2024** using [Gradle-Lic * **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-scripting-common. **Version** : 1.9.23. + * **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-scripting-compiler-embeddable. **Version** : 1.8.22. * **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-scripting-compiler-embeddable. **Version** : 1.9.23. + * **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-scripting-compiler-impl-embeddable. **Version** : 1.8.22. * **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-scripting-compiler-impl-embeddable. **Version** : 1.9.23. + * **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-scripting-jvm. **Version** : 1.8.22. * **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-scripting-jvm. **Version** : 1.9.23. + * **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-stdlib. **Version** : 1.9.23. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -886,6 +1095,38 @@ This report was generated on **Thu Apr 25 21:47:03 WEST 2024** using [Gradle-Lic * **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-test. **Version** : 1.9.23. + * **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-test-annotations-common. **Version** : 1.9.23. + * **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-test-common. **Version** : 1.9.23. + * **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-test-junit5. **Version** : 1.9.23. + * **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-tooling-core. **Version** : 1.9.23. + * **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-util-io. **Version** : 1.9.23. + * **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-util-klib. **Version** : 1.9.23. + * **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** : kotlinx-coroutines-core-jvm. **Version** : 1.6.4. + * **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) + 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) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -901,6 +1142,10 @@ This report was generated on **Thu Apr 25 21:47:03 WEST 2024** using [Gradle-Lic * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) 1. **Group** : org.junit. **Name** : junit-bom. **Version** : 5.10.0.**No license information found** +1. **Group** : org.junit.jupiter. **Name** : junit-jupiter. **Version** : 5.10.0. + * **Project URL:** [https://junit.org/junit5/](https://junit.org/junit5/) + * **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/) * **License:** [Eclipse Public License v2.0](https://www.eclipse.org/legal/epl-v20.html) @@ -965,7 +1210,7 @@ This report was generated on **Thu Apr 25 21:47:03 WEST 2024** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Apr 25 21:47:03 WEST 2024** 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). +This report was generated on **Fri Apr 26 03:50:37 WEST 2024** 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). @@ -1450,4 +1695,4 @@ This report was generated on **Thu Apr 25 21:47:03 WEST 2024** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Apr 25 21:47:03 WEST 2024** 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 **Fri Apr 26 03:50:38 WEST 2024** 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-plugin/build.gradle.kts b/gradle-plugin/build.gradle.kts index e69de29..ef21998 100644 --- a/gradle-plugin/build.gradle.kts +++ b/gradle-plugin/build.gradle.kts @@ -0,0 +1,142 @@ +/* + * 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 + * + * 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. + */ + +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import io.spine.internal.dependency.JUnit +import io.spine.internal.dependency.Kotlin +import io.spine.internal.dependency.Protobuf +import io.spine.internal.dependency.Spine +import io.spine.internal.gradle.isSnapshot + +plugins { + `java-gradle-plugin` + `maven-publish` + `version-to-resources` + `write-manifest` + id("com.gradle.plugin-publish") version "1.2.1" + id("com.github.johnrengelman.shadow") +} + +dependencies { + compileOnly(gradleApi()) + compileOnly(gradleKotlinDsl()) + compileOnly(Protobuf.GradlePlugin.lib) + + api(project(":api")) + implementation(Spine.pluginBase) + + implementation(Kotlin.gradlePluginApi) +} + +@Suppress( + "UnstableApiUsage" /* testing suites feature */ +) +testing { + suites { + val test by getting(JvmTestSuite::class) { + useJUnitJupiter(JUnit.version) + dependencies { + implementation(Kotlin.gradlePluginLib) + implementation(gradleKotlinDsl()) + implementation(Protobuf.GradlePlugin.lib) +// implementation(Spine.pluginTestlib) + } + } + + val functionalTest by registering(JvmTestSuite::class) { + useJUnitJupiter(JUnit.version) + dependencies { + implementation(Kotlin.gradlePluginLib) + implementation(Kotlin.testJUnit5) +// implementation(Spine.testlib) +// implementation(Spine.pluginTestlib) + implementation(project(":gradle-plugin")) + } + } + } +} + +/** + * Make functional tests depend on publishing all the submodules to Maven Local so that + * the Gradle plugin can get all the dependencies when it's applied to the test projects. + */ +val functionalTest: Task by tasks.getting { + val task = this + rootProject.subprojects.forEach { subproject -> + task.dependsOn(":${subproject.name}:publishToMavenLocal") + } +} + +java { + withSourcesJar() + withJavadocJar() +} + +val shadowJar by tasks.getting(ShadowJar::class) { + archiveClassifier.set("") +} + +gradlePlugin { + website.set("https://spine.io") + vcsUrl.set("https://github.com/SpineEventEngine/ProtoTap") + plugins { + create("protoTapPlugin") { + id = "io.spine.prototap" + implementationClass = "io.spine.tools.prototap.gradle.Plugin" + displayName = "ProtoTap Gradle Plugin" + description = "Obtains generated code and related data from Protobuf compiler" + tags.set(listOf("protobuf", "protoc", "codegen", "gradle", "plugin")) + } + } + val functionalTest by sourceSets.getting + testSourceSets( + functionalTest + ) +} + +val versionToPublish: String by extra + +val publishPlugins: Task by tasks.getting { + enabled = !versionToPublish.isSnapshot() +} + +val publish: Task by tasks.getting { + dependsOn(publishPlugins) +} + +tasks { + check { + dependsOn(testing.suites.named("functionalTest")) + } + + ideaModule { + notCompatibleWithConfigurationCache("https://github.com/gradle/gradle/issues/13480") + } + + publishPlugins { + notCompatibleWithConfigurationCache("https://github.com/gradle/gradle/issues/21283") + } +} diff --git a/pom.xml b/pom.xml index 90cf241..49cb9ce 100644 --- a/pom.xml +++ b/pom.xml @@ -23,6 +23,18 @@ all modules and does not describe the project structure per-subproject. + + io.spine.tools + spine-plugin-base + 2.0.0-SNAPSHOT.208 + compile + + + org.jetbrains.kotlin + kotlin-gradle-plugin-api + 1.9.23 + compile + org.jetbrains.kotlin kotlin-stdlib-jdk8 @@ -105,6 +117,12 @@ all modules and does not describe the project structure per-subproject. javac 9+181-r4173-1 + + com.google.protobuf + protobuf-gradle-plugin + 0.9.4 + provided + com.puppycrawl.tools checkstyle @@ -141,6 +159,11 @@ all modules and does not describe the project structure per-subproject. kotlin-compiler-embeddable 1.8.22 + + org.jetbrains.kotlin + kotlin-gradle-plugin + 1.9.23 + org.jetbrains.kotlin kotlin-klib-commonizer-embeddable @@ -151,6 +174,16 @@ all modules and does not describe the project structure per-subproject. kotlin-scripting-compiler-embeddable 1.8.22 + + org.jetbrains.kotlin + kotlin-test-junit5 + 1.9.23 + + + org.junit.jupiter + junit-jupiter + 5.10.0 + \ No newline at end of file From ef3d7681ca186075c9a0439556638b0fdf7d8dc9 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Fri, 26 Apr 2024 03:51:48 +0100 Subject: [PATCH 08/39] Draft Gradle plugin --- .../kotlin/io/spine/tools/prototap/Names.kt | 32 +++++ .../spine/tools/prototap/gradle/Extension.kt | 84 +++++++++++ .../io/spine/tools/prototap/gradle/Plugin.kt | 133 ++++++++++++++++++ 3 files changed, 249 insertions(+) create mode 100644 gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Extension.kt create mode 100644 gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Plugin.kt diff --git a/api/src/main/kotlin/io/spine/tools/prototap/Names.kt b/api/src/main/kotlin/io/spine/tools/prototap/Names.kt index 74dae2f..03030dd 100644 --- a/api/src/main/kotlin/io/spine/tools/prototap/Names.kt +++ b/api/src/main/kotlin/io/spine/tools/prototap/Names.kt @@ -31,6 +31,11 @@ package io.spine.tools.prototap */ public object Names { + /** + * The name of a Gradle project extension added by ProtoTap Gradle Plugin. + */ + public const val GRADLE_EXTENSION_NAME: String = "protoTap" + /** * The name ProtoTap uses when passing itself to `protoc` compiler. */ @@ -46,4 +51,31 @@ public object Names { * The name of the descriptor set file obtained by the ProtoTap Gradle plugin. */ public const val DESCRIPTOR_SET_FILE: String = "FileDescriptorSet.binpb" + + /** + * The name of the source set which is used by default for tapping Protobuf compiler. + * + * This constant is used if the `sourceSet` property of the `protoTap` project extension + * added by the ProtoTap Gradle Plugin is not specified. + * + * If a project does not apply the `java-test-fixtures` Gradle plugin, then + * the [`test`][FALLBACK_SOURCE_SET_NAME] source set conventionally will be used. + */ + public const val DEFAULT_SOURCE_SET_NAME: String = "testFixtures" + + /** + * The name of the source set to be used for tapping Protobuf compiler, if + * the project does not have a source set with the [DEFAULT_SOURCE_SET_NAME]. + * + * If the project does not have the [`test`][FALLBACK_SOURCE_SET_NAME] source + * set either, a build time error will occur. + */ + public const val FALLBACK_SOURCE_SET_NAME: String = "test" + + /** + * The classifier used for the executable fat JAR of the ProtoTap `protoc` plugin archive. + */ + public const val PROTOC_PLUGIN_CLASSIFIER: String = "exe" + + public const val TEST_FIXTURES_PLUGIN_NAME: String = "java-test-fixtures" } diff --git a/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Extension.kt b/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Extension.kt new file mode 100644 index 0000000..3d330e2 --- /dev/null +++ b/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Extension.kt @@ -0,0 +1,84 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.tools.prototap.gradle + +import io.spine.tools.gradle.project.sourceSets +import io.spine.tools.prototap.Names.DEFAULT_SOURCE_SET_NAME +import io.spine.tools.prototap.Names.FALLBACK_SOURCE_SET_NAME +import io.spine.tools.prototap.Names.GRADLE_EXTENSION_NAME +import io.spine.tools.prototap.Names.TEST_FIXTURES_PLUGIN_NAME +import org.gradle.api.Project +import org.gradle.api.provider.Property +import org.gradle.api.tasks.SourceSet +import org.gradle.kotlin.dsl.property + +public class Extension(project: Project) { + + public val artifact: Property = project.objects.property() + + public val sourceSet: Property = with(project) { + objects.property().convention( + provider { + findSourceSet() + } + ) + } + + public val generateDescriptorSet: Property = + project.objects.property().convention(false) +} + +private fun Project.findSourceSet(): SourceSet { + var sourceSet: SourceSet? = sourceSets.findByName(DEFAULT_SOURCE_SET_NAME) + if (sourceSet == null) { + sourceSet = sourceSets.findByName(FALLBACK_SOURCE_SET_NAME) + } + sourceSet?.let { + return it + } + failWithMissingSourceSetError() +} + +private fun Project.failWithMissingSourceSetError(): Nothing { + // Use vars instead of direct constant references to make the text look closer to final output. + val protoTap = GRADLE_EXTENSION_NAME + val testFixtures = DEFAULT_SOURCE_SET_NAME + val test = FALLBACK_SOURCE_SET_NAME + val javaTestFixtures = TEST_FIXTURES_PLUGIN_NAME + val propertyName = "$protoTap.sourceSet" + error(""" + Unable to figure out a source set to be used by `$protoTap`. + The project `$name` does not seem to have neither `$testFixtures` nor `$test` source sets. + A source set is not specified directly via the `$propertyName` either. + Please consider one of the following: + 1) Adding the `$javaTestFixtures` plugin to your project and using + the `$testFixtures` source source set for Protobuf types for your tests. + 2) Adding the `$test` source set and putting stub proto files there. + 3) Specifying a custom source set directly via the `$propertyName` property. + """.trimIndent() + ) +} diff --git a/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Plugin.kt b/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Plugin.kt new file mode 100644 index 0000000..1a6e600 --- /dev/null +++ b/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Plugin.kt @@ -0,0 +1,133 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.tools.prototap.gradle + +import com.google.protobuf.gradle.GenerateProtoTask +import io.spine.tools.code.manifest.Version +import io.spine.tools.gradle.Artifact +import io.spine.tools.gradle.artifact +import io.spine.tools.gradle.protobuf.ProtobufDependencies +import io.spine.tools.gradle.protobuf.protobufExtension +import io.spine.tools.prototap.Names.GRADLE_EXTENSION_NAME +import io.spine.tools.prototap.Names.PROTOC_PLUGIN_CLASSIFIER +import io.spine.tools.prototap.Names.PROTOC_PLUGIN_NAME +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.file.DuplicatesStrategy.INCLUDE +import com.google.protobuf.gradle.id +import io.spine.tools.prototap.Names.DESCRIPTOR_SET_FILE +import org.gradle.language.jvm.tasks.ProcessResources + +public class Plugin : Plugin { + + override fun apply(project: Project): Unit = with(project) { + createExtension() + pluginManager.withPlugin(ProtobufDependencies.gradlePlugin.id) { + tapProtobuf() + } + } +} + +private fun Project.createExtension(): Extension { + val extension = Extension(this) + extensions.add(Extension::class.java, GRADLE_EXTENSION_NAME, extension) + return extension +} + +private val Project.extension: Extension + get() = extensions.getByType(Extension::class.java) + +private fun Project.tapProtobuf() { + setProtocArtifact() + createProtocPlugin() + tuneProtoTasks() +} + +private fun Project.setProtocArtifact() { + protobufExtension?.protoc { + val artifact = extension.artifact.get() + if (artifact.isNotBlank()) { + it.artifact = artifact + } + } +} + +private fun Project.createProtocPlugin() { + protobufExtension?.plugins { + it.create(PROTOC_PLUGIN_NAME) { locator -> + locator.artifact = protocPlugin.notation() + } + } +} + +private val protoTapVersion: String by lazy { + Version.fromManifestOf(Plugin::class.java).value +} + +private val protocPlugin: Artifact by lazy { + artifact { + useSpineToolsGroup() + name = "prototap-protoc-plugin" + version = protoTapVersion + classifier = PROTOC_PLUGIN_CLASSIFIER + extension = "jar" + } +} + +private fun Project.tuneProtoTasks() { + val sourceSetName = extension.sourceSet.get().name + + /* The below block adds a configuration action for the `GenerateProtoTaskCollection`. + We cannot do it like `generateProtoTasks.all().forEach { ... }` because it + breaks the configuration order of the `GenerateProtoTaskCollection`. + This, in turn, leads to missing generated sources in the `compileJava` task. */ + protobufExtension?.generateProtoTasks { + it.ofSourceSet(sourceSetName).forEach { task -> + task.tap() + } + } +} + +private fun GenerateProtoTask.tap() { + project.processTaskResources.apply { + from(outputs) + duplicatesStrategy = INCLUDE + } + plugins.apply { + id(PROTOC_PLUGIN_NAME) + } + if (project.extension.generateDescriptorSet.get()) { + descriptorSetOptions.apply { + path = "${project.buildDir}/resources/test/$PROTOC_PLUGIN_NAME/$DESCRIPTOR_SET_FILE" + includeSourceInfo = true + includeImports = true + } + } +} + +private val Project.processTaskResources: ProcessResources + get() = tasks.named("processTestResources", ProcessResources::class.java).get() From 5cfdaa611956d45d0ef57fa241bea96f4f302340 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Fri, 26 Apr 2024 03:51:55 +0100 Subject: [PATCH 09/39] Add missing script --- .../kotlin/version-to-resources.gradle.kts | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 buildSrc/src/main/kotlin/version-to-resources.gradle.kts diff --git a/buildSrc/src/main/kotlin/version-to-resources.gradle.kts b/buildSrc/src/main/kotlin/version-to-resources.gradle.kts new file mode 100644 index 0000000..67b3e5c --- /dev/null +++ b/buildSrc/src/main/kotlin/version-to-resources.gradle.kts @@ -0,0 +1,59 @@ +/* + * 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 + * + * 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. + */ + +plugins { + java +} + +val versionDir = "$buildDir/version" + +/** + * This file, containing the version of ProtoData, is generated at build time and included into + * the project's resources. + * + * Please search for the usages of "version.txt" when making changes. + */ +val versionFile = "$versionDir/version.txt" + +sourceSets { + main { + resources.srcDir(versionDir) + } +} + +val createVersionFile by tasks.registering { + + inputs.property("version", project.version) + outputs.file(versionFile) + + doLast { + file(versionFile).writeText(project.version.toString()) + } +} + +tasks.processResources { + dependsOn(createVersionFile) +} From a3f6d9bc889bf2593fc3b7c79f1c1b6ccff6dbd9 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Fri, 26 Apr 2024 21:30:41 +0100 Subject: [PATCH 10/39] Split tuning of a `GenerateProtoTask` to specific functions --- .../io/spine/tools/prototap/gradle/Plugin.kt | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Plugin.kt b/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Plugin.kt index 1a6e600..ed25e95 100644 --- a/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Plugin.kt +++ b/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Plugin.kt @@ -40,6 +40,7 @@ import org.gradle.api.Project import org.gradle.api.file.DuplicatesStrategy.INCLUDE import com.google.protobuf.gradle.id import io.spine.tools.prototap.Names.DESCRIPTOR_SET_FILE +import org.gradle.api.tasks.TaskContainer import org.gradle.language.jvm.tasks.ProcessResources public class Plugin : Plugin { @@ -58,15 +59,22 @@ private fun Project.createExtension(): Extension { return extension } -private val Project.extension: Extension - get() = extensions.getByType(Extension::class.java) - +/** + * Tunes this Gradle project so that we can collect data from `protoc` and + * Protobuf Gradle Plugin. + */ private fun Project.tapProtobuf() { setProtocArtifact() createProtocPlugin() tuneProtoTasks() } +/** + * Obtains the extension our plugin added to this Gradle project. + */ +private val Project.extension: Extension + get() = extensions.getByType(Extension::class.java) + private fun Project.setProtocArtifact() { protobufExtension?.protoc { val artifact = extension.artifact.get() @@ -107,20 +115,31 @@ private fun Project.tuneProtoTasks() { This, in turn, leads to missing generated sources in the `compileJava` task. */ protobufExtension?.generateProtoTasks { it.ofSourceSet(sourceSetName).forEach { task -> - task.tap() + task.apply { + collectGeneratedJavaCode() + addProtocPlugin() + grabDescriptorSetFile() + } } } } -private fun GenerateProtoTask.tap() { - project.processTaskResources.apply { +private fun GenerateProtoTask.collectGeneratedJavaCode() { + project.tasks.processTaskResources.apply { from(outputs) duplicatesStrategy = INCLUDE } +} + +private fun GenerateProtoTask.addProtocPlugin() { plugins.apply { id(PROTOC_PLUGIN_NAME) } +} + +private fun GenerateProtoTask.grabDescriptorSetFile() { if (project.extension.generateDescriptorSet.get()) { + generateDescriptorSet = true descriptorSetOptions.apply { path = "${project.buildDir}/resources/test/$PROTOC_PLUGIN_NAME/$DESCRIPTOR_SET_FILE" includeSourceInfo = true @@ -129,5 +148,5 @@ private fun GenerateProtoTask.tap() { } } -private val Project.processTaskResources: ProcessResources - get() = tasks.named("processTestResources", ProcessResources::class.java).get() +private val TaskContainer.processTaskResources: ProcessResources + get() = named("processTestResources", ProcessResources::class.java).get() From 25592cfe640e66c903d7c5c321fc91d25d45d2ab Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Fri, 26 Apr 2024 22:43:57 +0100 Subject: [PATCH 11/39] Add the common prefix to the `pluginMaven` publication --- gradle-plugin/build.gradle.kts | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/gradle-plugin/build.gradle.kts b/gradle-plugin/build.gradle.kts index ef21998..8857cb9 100644 --- a/gradle-plugin/build.gradle.kts +++ b/gradle-plugin/build.gradle.kts @@ -30,6 +30,7 @@ import io.spine.internal.dependency.Kotlin import io.spine.internal.dependency.Protobuf import io.spine.internal.dependency.Spine import io.spine.internal.gradle.isSnapshot +import io.spine.internal.gradle.publish.SpinePublishing plugins { `java-gradle-plugin` @@ -103,7 +104,7 @@ gradlePlugin { website.set("https://spine.io") vcsUrl.set("https://github.com/SpineEventEngine/ProtoTap") plugins { - create("protoTapPlugin") { + create("prototapPlugin") { id = "io.spine.prototap" implementationClass = "io.spine.tools.prototap.gradle.Plugin" displayName = "ProtoTap Gradle Plugin" @@ -117,8 +118,36 @@ gradlePlugin { ) } +// Add the common prefix to the `pluginMaven` publication. +// +// The publication is automatically created in `project.afterEvaluate` by Plugin Publishing plugin. +// See https://docs.gradle.org/current/userguide/java_gradle_plugin.html#maven_publish_plugin +// +// We add the prefix also in `afterEvaluate` assuming we're later in the sequence of +// the actions and the publications have been already created. +// +project.afterEvaluate { + publishing { + // Get the prefix used for all the modules of this project. + val prefix = project.rootProject.the().artifactPrefix + + // Add the prefix to the `pluginMaven` publication only. + publications.named("pluginMaven") { + if (!artifactId.startsWith(prefix)) { + artifactId = prefix + artifactId + } + } + + // Do not add the prefix for the publication which produces + // the `io.spine.prototap.gradle.plugin` marker. + } +} + +// The version declared in `version.gradle.kts`. val versionToPublish: String by extra +// Do not attempt to publish snapshot versions to comply with publishing rules. +// See: https://plugins.gradle.org/docs/publish-plugin#approval val publishPlugins: Task by tasks.getting { enabled = !versionToPublish.isSnapshot() } From e4550b0571f3ac82db55432fc9ef7a1cd04395d2 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Fri, 26 Apr 2024 22:44:08 +0100 Subject: [PATCH 12/39] Update build time --- dependencies.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dependencies.md b/dependencies.md index 40e3af1..c5ee415 100644 --- a/dependencies.md +++ b/dependencies.md @@ -480,7 +480,7 @@ The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Apr 26 03:50:37 WEST 2024** 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). +This report was generated on **Fri Apr 26 21:58:35 WEST 2024** 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). @@ -1210,7 +1210,7 @@ This report was generated on **Fri Apr 26 03:50:37 WEST 2024** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Apr 26 03:50:37 WEST 2024** 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). +This report was generated on **Fri Apr 26 21:58:35 WEST 2024** 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). @@ -1695,4 +1695,4 @@ This report was generated on **Fri Apr 26 03:50:37 WEST 2024** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Apr 26 03:50:38 WEST 2024** 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 **Fri Apr 26 21:58:35 WEST 2024** 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 From fefa23cc19d2a1133a9a9b99a5896fedc7b2fd93 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Fri, 26 Apr 2024 22:56:59 +0100 Subject: [PATCH 13/39] Add `foojay-resolver-convention` plugin --- settings.gradle.kts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 0e19579..8db2405 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -28,21 +28,26 @@ rootProject.name = "ProtoTap" +pluginManagement { + repositories { + mavenLocal() + mavenCentral() + gradlePluginPortal() + } +} + include( "api", "protoc-plugin", "gradle-plugin" ) -dependencyResolutionManagement { - repositories { - mavenLocal() - mavenCentral() - gradlePluginPortal() - } +plugins { + // https://github.com/gradle/foojay-toolchains + id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" } -pluginManagement { +dependencyResolutionManagement { repositories { mavenLocal() mavenCentral() From 9d9aadb4379e9665e8a6a44ec2d779146416c397 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Sat, 27 Apr 2024 22:00:55 +0100 Subject: [PATCH 14/39] Add `default-values` test project --- .../spine/tools/prototap/gradle/PluginSpec.kt | 99 +++++++++++++++++++ .../resources/default-values/build.gradle.kts | 58 +++++++++++ .../default-values/settings.gradle.kts | 31 ++++++ .../given/domain/gas_transportation.proto | 65 ++++++++++++ .../proto/given/domain/oil_refinery.proto | 56 +++++++++++ 5 files changed, 309 insertions(+) create mode 100644 gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt create mode 100644 gradle-plugin/src/test/resources/default-values/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/default-values/settings.gradle.kts create mode 100644 gradle-plugin/src/test/resources/default-values/src/testFixtures/proto/given/domain/gas_transportation.proto create mode 100644 gradle-plugin/src/test/resources/default-values/src/testFixtures/proto/given/domain/oil_refinery.proto diff --git a/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt b/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt new file mode 100644 index 0000000..ed731c2 --- /dev/null +++ b/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt @@ -0,0 +1,99 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.tools.prototap.gradle + +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import io.spine.tools.gradle.task.BaseTaskName.build +import io.spine.tools.gradle.testing.GradleProject +import io.spine.tools.prototap.Names.CODE_GENERATOR_REQUEST_FILE +import io.spine.tools.prototap.Names.GRADLE_PLUGIN_ID +import io.spine.tools.prototap.Names.outputFile +import java.io.File +import java.nio.file.Path +import kotlin.io.path.Path +import kotlin.io.path.exists +import org.gradle.api.logging.LogLevel +import org.gradle.testkit.runner.internal.DefaultGradleRunner +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir + +@DisplayName("Gradle plugin should") +internal class PluginSpec { + + private lateinit var projectDir: File + private lateinit var project: GradleProject + private lateinit var resultDir: Path + + @BeforeEach + fun prepareDir(@TempDir tempDir: File) { + projectDir = tempDir + resultDir = Path(outputFile("$projectDir/build", "")) + } + + @Test + fun `run with default values`() { + createProject("default-values") + runBuild() + resultDir.resolve(CODE_GENERATOR_REQUEST_FILE).exists() shouldBe true + resultDir.resolve("java").countFiles() shouldNotBe 0 + } + + private fun createProject(resourceDir: String) { + val version = Plugin.readVersion() + val builder = GradleProject.setupAt(projectDir) + .fromResources(resourceDir) + .withSharedTestKitDirectory() + .replace("@PROTOTAP_PLUGIN_ID@", GRADLE_PLUGIN_ID) + .replace("@PROTOTAP_VERSION@", version) + //.withLoggingLevel(LogLevel.INFO) + /* Uncomment the following if you need to debug the build process. + Please note that: + 1) Test will run much slower. + 2) Under Windows it may cause this issue to occur: + https://github.com/gradle/native-platform/issues/274 + After finishing the debug, please comment out this call again. */ + //.enableRunnerDebug() + .copyBuildSrc() + project = builder.create() + (project.runner as DefaultGradleRunner).withJvmArguments( + "-Xmx8g", + "-XX:MaxMetaspaceSize=1512m", + "-XX:+UseParallelGC", + "-XX:+HeapDumpOnOutOfMemoryError" + ) + } + + private fun runBuild() { + project.executeTask(build) + } +} + +private fun Path.countFiles(): Int = + toFile().walk().count { it.isFile } diff --git a/gradle-plugin/src/test/resources/default-values/build.gradle.kts b/gradle-plugin/src/test/resources/default-values/build.gradle.kts new file mode 100644 index 0000000..e453925 --- /dev/null +++ b/gradle-plugin/src/test/resources/default-values/build.gradle.kts @@ -0,0 +1,58 @@ +/* + * 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 + * + * 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. + */ + +import com.google.protobuf.gradle.protobuf +import io.spine.internal.dependency.Protobuf +import io.spine.internal.dependency.GoogleApis +import io.spine.internal.gradle.standardToSpineSdk + +buildscript { + standardSpineSdkRepositories() +} + +plugins { + java + `java-test-fixtures` + id("com.google.protobuf") + id("@PROTOTAP_PLUGIN_ID@") version "@PROTOTAP_VERSION@" +} + +repositories { + mavenLocal() + standardToSpineSdk() +} + +protobuf { + protoc { + artifact = io.spine.internal.dependency.Protobuf.compiler + } +} + +dependencies { + testFixturesImplementation(Protobuf.javaLib) + // For `google/type/` proto types used in stub domains. + testFixturesImplementation(GoogleApis.commonProtos) +} diff --git a/gradle-plugin/src/test/resources/default-values/settings.gradle.kts b/gradle-plugin/src/test/resources/default-values/settings.gradle.kts new file mode 100644 index 0000000..4aadfdd --- /dev/null +++ b/gradle-plugin/src/test/resources/default-values/settings.gradle.kts @@ -0,0 +1,31 @@ +/* + * 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 + * + * 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. + */ + +pluginManagement { + repositories { + mavenLocal() + } +} diff --git a/gradle-plugin/src/test/resources/default-values/src/testFixtures/proto/given/domain/gas_transportation.proto b/gradle-plugin/src/test/resources/default-values/src/testFixtures/proto/given/domain/gas_transportation.proto new file mode 100644 index 0000000..a796651 --- /dev/null +++ b/gradle-plugin/src/test/resources/default-values/src/testFixtures/proto/given/domain/gas_transportation.proto @@ -0,0 +1,65 @@ +/* +* Copyright 2024, TeamDev. All rights reserved. +* +* Redistribution and use in source and/or binary forms, with or without +* modification, must retain the above copyright notice and the following +* disclaimer. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +syntax = "proto3"; + +package given.domain; + +// +// This is a "vanilla" Protobuf file, which does not use Spine-specific features like +// custom options such as `type_url_prefix` or validation constraints. +// We also use "pure" Protobuf Java `protoc` plugin to process these files so that +// we recreate conditions we have before ProtoData gets into play after `protoc` +// finishes its work. +// + +option java_package = "io.spine.given.domain.gas"; +option java_outer_classname = "GasTransportationProto"; +option java_multiple_files = true; + +import "google/type/latlng.proto"; + +message OilAndGasWell { + google.type.LatLng location = 1; +} + +message Pipe { + google.type.LatLng from = 1; + google.type.LatLng to = 2; +} + +message GasProcessingPlant { + string name = 1; + google.type.LatLng location = 2; + repeated Pipe inbound = 3; + repeated Pipe outbound = 4; +} + +message CompressorStation { + google.type.LatLng location = 1; + repeated Pipe inbound = 2; + repeated Pipe outbound = 3; +} + +message LngStorage { + string name = 1; + google.type.LatLng location = 2; + Pipe inbound = 3; + Pipe outbound = 4; +} diff --git a/gradle-plugin/src/test/resources/default-values/src/testFixtures/proto/given/domain/oil_refinery.proto b/gradle-plugin/src/test/resources/default-values/src/testFixtures/proto/given/domain/oil_refinery.proto new file mode 100644 index 0000000..c85fa31 --- /dev/null +++ b/gradle-plugin/src/test/resources/default-values/src/testFixtures/proto/given/domain/oil_refinery.proto @@ -0,0 +1,56 @@ +/* +* Copyright 2024, TeamDev. All rights reserved. +* +* Redistribution and use in source and/or binary forms, with or without +* modification, must retain the above copyright notice and the following +* disclaimer. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +syntax = "proto3"; + +package given.domain; + +// +// This is a "vanilla" Protobuf file, which does not use Spine-specific features like +// custom options such as `type_url_prefix` or validation constraints. +// We also use "pure" Protobuf Java `protoc` plugin to process these files so that +// we recreate conditions we have before ProtoData gets into play after `protoc` +// finishes its work. +// + +option java_package = "io.spine.given.domain.oil"; +option java_outer_classname = "OilRefineryProto"; +option java_multiple_files = true; + +enum ProductType { + PT_UNKNOWN = 0; + PETROL = 1; + KEROSENE = 2; + DIESEL = 3; + FUEL_OIL = 4; + LUBRICATING_OIL = 5; + PARAFFIN_WAX = 6; + ASPHALT_BASE = 7; +} + +message Product { + ProductType type = 1; + float volume = 2; +} + +message Refinery { + string name = 1; + float input = 2; + repeated Product output = 3; +} From 8ae379a4f2967d09b5d55afb017ea7dda1e8dce8 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Sat, 27 Apr 2024 22:02:43 +0100 Subject: [PATCH 15/39] Fix reference from the plugin marker artifact Also: * Remove unnecessary test suite. We're going to use `test` only. * Ensure task dependencies (just in case). --- gradle-plugin/build.gradle.kts | 36 ++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/gradle-plugin/build.gradle.kts b/gradle-plugin/build.gradle.kts index 8857cb9..05e0145 100644 --- a/gradle-plugin/build.gradle.kts +++ b/gradle-plugin/build.gradle.kts @@ -63,18 +63,7 @@ testing { implementation(Kotlin.gradlePluginLib) implementation(gradleKotlinDsl()) implementation(Protobuf.GradlePlugin.lib) -// implementation(Spine.pluginTestlib) - } - } - - val functionalTest by registering(JvmTestSuite::class) { - useJUnitJupiter(JUnit.version) - dependencies { - implementation(Kotlin.gradlePluginLib) - implementation(Kotlin.testJUnit5) -// implementation(Spine.testlib) -// implementation(Spine.pluginTestlib) - implementation(project(":gradle-plugin")) + implementation(Spine.pluginTestlib) } } } @@ -84,7 +73,7 @@ testing { * Make functional tests depend on publishing all the submodules to Maven Local so that * the Gradle plugin can get all the dependencies when it's applied to the test projects. */ -val functionalTest: Task by tasks.getting { +val test: Task by tasks.getting { val task = this rootProject.subprojects.forEach { subproject -> task.dependsOn(":${subproject.name}:publishToMavenLocal") @@ -112,10 +101,6 @@ gradlePlugin { tags.set(listOf("protobuf", "protoc", "codegen", "gradle", "plugin")) } } - val functionalTest by sourceSets.getting - testSourceSets( - functionalTest - ) } // Add the common prefix to the `pluginMaven` publication. @@ -140,6 +125,21 @@ project.afterEvaluate { // Do not add the prefix for the publication which produces // the `io.spine.prototap.gradle.plugin` marker. + + publications.withType().configureEach { + if (name.endsWith("PluginMarkerMaven")) { + pom.withXml { + asNode().apply { + val root = asElement() + // Get the `dependencies/dependency/artifactId` node. + // It's the second node with such a name from the top. + // The first one is the `artifactId` of the marker itself. + val artifactIdNode = root.getElementsByTagName("artifactId").item(1) + artifactIdNode.textContent = "$prefix${project.name}" + } + } + } + } } } @@ -169,3 +169,5 @@ tasks { notCompatibleWithConfigurationCache("https://github.com/gradle/gradle/issues/21283") } } + +configureTaskDependencies() From c7b0052ef41bd592c58e2a62b2f6ef3f6b139b42 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Sat, 27 Apr 2024 22:03:38 +0100 Subject: [PATCH 16/39] Add `protoc` plugin --- protoc-plugin/build.gradle.kts | 48 +++++++++++++++ .../io/spine/tools/prototap/protoc/Plugin.kt | 59 +++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 protoc-plugin/src/main/kotlin/io/spine/tools/prototap/protoc/Plugin.kt diff --git a/protoc-plugin/build.gradle.kts b/protoc-plugin/build.gradle.kts index e69de29..6c1de65 100644 --- a/protoc-plugin/build.gradle.kts +++ b/protoc-plugin/build.gradle.kts @@ -0,0 +1,48 @@ +/* +* 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 +* +* 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. +*/ + +import io.spine.internal.dependency.Protobuf +import org.gradle.api.file.DuplicatesStrategy.INCLUDE + +dependencies { + implementation(Protobuf.javaLib) +} + +tasks.jar { + manifest { + attributes(mapOf("Main-Class" to "io.spine.tools.prototap.protoc.Plugin")) + } + // Assemble "Fat-JAR" artifact containing all the dependencies. + from(configurations.runtimeClasspath.get().map { + when { + it.isDirectory -> it + else -> zipTree(it) + } + }) + + archiveClassifier.set("exe") + duplicatesStrategy = INCLUDE +} diff --git a/protoc-plugin/src/main/kotlin/io/spine/tools/prototap/protoc/Plugin.kt b/protoc-plugin/src/main/kotlin/io/spine/tools/prototap/protoc/Plugin.kt new file mode 100644 index 0000000..dd1fd83 --- /dev/null +++ b/protoc-plugin/src/main/kotlin/io/spine/tools/prototap/protoc/Plugin.kt @@ -0,0 +1,59 @@ +/* + * 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 + * + * 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. + */ + +@file:JvmName("Plugin") + +package io.spine.tools.prototap.protoc + +import com.google.protobuf.compiler.PluginProtos.CodeGeneratorRequest +import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse +import java.nio.file.StandardOpenOption.CREATE +import java.nio.file.StandardOpenOption.TRUNCATE_EXISTING +import java.util.* +import kotlin.io.path.Path +import kotlin.io.path.writeBytes +import kotlin.text.Charsets.UTF_8 + +/** + * Stores the received `CodeGeneratorRequest` to the file passed as the parameter. + */ +public fun main() { + val request = CodeGeneratorRequest.parseFrom(System.`in`) + val requestFile = Path(request.parameter.decodeBase64()) + requestFile.toFile() + .parentFile + .mkdirs() + requestFile.writeBytes(request.toByteArray(), CREATE, TRUNCATE_EXISTING) + + val emptyResponse = CodeGeneratorResponse.getDefaultInstance() + System.out.write(emptyResponse.toByteArray()) +} + +private fun String.decodeBase64(): String { + val bytes = Base64.getDecoder().decode(this) + return String(bytes, UTF_8) +} + From ae8e9e1b77ccde5bf9e05f3dbf5f30ccb88c29d0 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Sat, 27 Apr 2024 22:04:14 +0100 Subject: [PATCH 17/39] Add convention for `artifact` property --- .../main/kotlin/io/spine/tools/prototap/gradle/Extension.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Extension.kt b/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Extension.kt index 3d330e2..1cde502 100644 --- a/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Extension.kt +++ b/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Extension.kt @@ -38,7 +38,8 @@ import org.gradle.kotlin.dsl.property public class Extension(project: Project) { - public val artifact: Property = project.objects.property() + public val artifact: Property = project.objects.property() + .convention("") public val sourceSet: Property = with(project) { objects.property().convention( From 3ed2b48b64f46f10e64d20853b3186a5cb18f301 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Sat, 27 Apr 2024 22:24:59 +0100 Subject: [PATCH 18/39] Extract path-related utils --- .../kotlin/io/spine/tools/prototap/Names.kt | 19 +++--- .../kotlin/io/spine/tools/prototap/Paths.kt | 63 +++++++++++++++++++ gradle-plugin/build.gradle.kts | 4 -- .../io/spine/tools/prototap/gradle/Plugin.kt | 55 ++++++++++++---- .../spine/tools/prototap/gradle/PluginSpec.kt | 14 +++-- 5 files changed, 123 insertions(+), 32 deletions(-) create mode 100644 api/src/main/kotlin/io/spine/tools/prototap/Paths.kt diff --git a/api/src/main/kotlin/io/spine/tools/prototap/Names.kt b/api/src/main/kotlin/io/spine/tools/prototap/Names.kt index 03030dd..0f933c0 100644 --- a/api/src/main/kotlin/io/spine/tools/prototap/Names.kt +++ b/api/src/main/kotlin/io/spine/tools/prototap/Names.kt @@ -31,6 +31,11 @@ package io.spine.tools.prototap */ public object Names { + /** + * The ID of the ProtoTap Gradle Plugin. + */ + public const val GRADLE_PLUGIN_ID: String = "io.spine.prototap" + /** * The name of a Gradle project extension added by ProtoTap Gradle Plugin. */ @@ -41,17 +46,6 @@ public object Names { */ public const val PROTOC_PLUGIN_NAME: String = "prototap" - /** - * The name of the file containing `CodeGeneratorRequest` message obtained - * by the ProtoTap `protoc` plugins. - */ - public const val CODE_GENERATOR_REQUEST_FILE: String = "CodeGeneratorRequest.binpb" - - /** - * The name of the descriptor set file obtained by the ProtoTap Gradle plugin. - */ - public const val DESCRIPTOR_SET_FILE: String = "FileDescriptorSet.binpb" - /** * The name of the source set which is used by default for tapping Protobuf compiler. * @@ -77,5 +71,8 @@ public object Names { */ public const val PROTOC_PLUGIN_CLASSIFIER: String = "exe" + /** + * The name of the `java-test-fixtures` plugin. + */ public const val TEST_FIXTURES_PLUGIN_NAME: String = "java-test-fixtures" } diff --git a/api/src/main/kotlin/io/spine/tools/prototap/Paths.kt b/api/src/main/kotlin/io/spine/tools/prototap/Paths.kt new file mode 100644 index 0000000..eddff55 --- /dev/null +++ b/api/src/main/kotlin/io/spine/tools/prototap/Paths.kt @@ -0,0 +1,63 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.tools.prototap + +import io.spine.tools.prototap.Names.PROTOC_PLUGIN_NAME +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.io.path.pathString + +public object Paths { + + /** + * The name of the file containing `CodeGeneratorRequest` message obtained + * by the ProtoTap `protoc` plugins. + */ + public const val CODE_GENERATOR_REQUEST_FILE: String = "CodeGeneratorRequest.binpb" + + /** + * The name of the descriptor set file obtained by the ProtoTap Gradle plugin. + */ + public const val DESCRIPTOR_SET_FILE: String = "FileDescriptorSet.binpb" + + public fun outputRoot(buildDir: String): Path = + Paths.get("$buildDir/resources/test/") + + public fun pluginOutputDir(buildDir: String): Path = + outputRoot(buildDir).resolve(PROTOC_PLUGIN_NAME) + + /** + * Obtains a full path to the file with given [shortFileName] under + * the given [buildDir] of a Gradle project. + * + * By convention, the file is placed under the directory named after [PROTOC_PLUGIN_NAME], + * which is created under `$buildDir/resources/test/`. + */ + public fun outputFile(buildDir: String, shortFileName: String): String = + pluginOutputDir(buildDir).resolve(shortFileName).pathString +} + diff --git a/gradle-plugin/build.gradle.kts b/gradle-plugin/build.gradle.kts index 05e0145..f0c726b 100644 --- a/gradle-plugin/build.gradle.kts +++ b/gradle-plugin/build.gradle.kts @@ -157,10 +157,6 @@ val publish: Task by tasks.getting { } tasks { - check { - dependsOn(testing.suites.named("functionalTest")) - } - ideaModule { notCompatibleWithConfigurationCache("https://github.com/gradle/gradle/issues/13480") } diff --git a/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Plugin.kt b/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Plugin.kt index ed25e95..d2f0bc1 100644 --- a/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Plugin.kt +++ b/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Plugin.kt @@ -26,7 +26,9 @@ package io.spine.tools.prototap.gradle +import com.google.common.annotations.VisibleForTesting import com.google.protobuf.gradle.GenerateProtoTask +import com.google.protobuf.gradle.id import io.spine.tools.code.manifest.Version import io.spine.tools.gradle.Artifact import io.spine.tools.gradle.artifact @@ -35,11 +37,13 @@ import io.spine.tools.gradle.protobuf.protobufExtension import io.spine.tools.prototap.Names.GRADLE_EXTENSION_NAME import io.spine.tools.prototap.Names.PROTOC_PLUGIN_CLASSIFIER import io.spine.tools.prototap.Names.PROTOC_PLUGIN_NAME +import io.spine.tools.prototap.Paths.CODE_GENERATOR_REQUEST_FILE +import io.spine.tools.prototap.Paths.DESCRIPTOR_SET_FILE +import io.spine.tools.prototap.Paths.outputFile +import java.util.* import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.file.DuplicatesStrategy.INCLUDE -import com.google.protobuf.gradle.id -import io.spine.tools.prototap.Names.DESCRIPTOR_SET_FILE import org.gradle.api.tasks.TaskContainer import org.gradle.language.jvm.tasks.ProcessResources @@ -51,6 +55,23 @@ public class Plugin : Plugin { tapProtobuf() } } + + public companion object { + + /** + * Reads the version of the plugin from the resources. + */ + @JvmStatic + @VisibleForTesting + public fun readVersion(): String = + Version.fromManifestOf( + // Use the fully qualified name of our `Plugin` class so that we load + // the version for the manifest of our JAR. + // Short name would refer to the imported `Plugin` class from Gradle, and + // we'd get its version. + io.spine.tools.prototap.gradle.Plugin::class.java + ).value + } } private fun Project.createExtension(): Extension { @@ -93,7 +114,7 @@ private fun Project.createProtocPlugin() { } private val protoTapVersion: String by lazy { - Version.fromManifestOf(Plugin::class.java).value + io.spine.tools.prototap.gradle.Plugin.readVersion() } private val protocPlugin: Artifact by lazy { @@ -124,16 +145,23 @@ private fun Project.tuneProtoTasks() { } } -private fun GenerateProtoTask.collectGeneratedJavaCode() { - project.tasks.processTaskResources.apply { - from(outputs) - duplicatesStrategy = INCLUDE - } -} +private fun Project.outputFile(name: String): String = + outputFile(buildDir.path, name) private fun GenerateProtoTask.addProtocPlugin() { plugins.apply { - id(PROTOC_PLUGIN_NAME) + id(PROTOC_PLUGIN_NAME) { + val path = project.outputFile(CODE_GENERATOR_REQUEST_FILE) + val encoded = path.base64Encoded() + option(encoded) + } + } +} + +private fun GenerateProtoTask.collectGeneratedJavaCode() { + project.tasks.processTaskResources.apply { + from(this@collectGeneratedJavaCode.outputs) + duplicatesStrategy = INCLUDE } } @@ -141,7 +169,7 @@ private fun GenerateProtoTask.grabDescriptorSetFile() { if (project.extension.generateDescriptorSet.get()) { generateDescriptorSet = true descriptorSetOptions.apply { - path = "${project.buildDir}/resources/test/$PROTOC_PLUGIN_NAME/$DESCRIPTOR_SET_FILE" + path = project.outputFile(DESCRIPTOR_SET_FILE) includeSourceInfo = true includeImports = true } @@ -150,3 +178,8 @@ private fun GenerateProtoTask.grabDescriptorSetFile() { private val TaskContainer.processTaskResources: ProcessResources get() = named("processTestResources", ProcessResources::class.java).get() + +private fun String.base64Encoded(): String { + val bytes = encodeToByteArray() + return Base64.getEncoder().encodeToString(bytes) +} diff --git a/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt b/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt index ed731c2..8174d6f 100644 --- a/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt +++ b/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt @@ -30,14 +30,13 @@ import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import io.spine.tools.gradle.task.BaseTaskName.build import io.spine.tools.gradle.testing.GradleProject -import io.spine.tools.prototap.Names.CODE_GENERATOR_REQUEST_FILE import io.spine.tools.prototap.Names.GRADLE_PLUGIN_ID -import io.spine.tools.prototap.Names.outputFile +import io.spine.tools.prototap.Paths.CODE_GENERATOR_REQUEST_FILE +import io.spine.tools.prototap.Paths.outputRoot +import io.spine.tools.prototap.Paths.pluginOutputDir import java.io.File import java.nio.file.Path -import kotlin.io.path.Path import kotlin.io.path.exists -import org.gradle.api.logging.LogLevel import org.gradle.testkit.runner.internal.DefaultGradleRunner import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName @@ -50,19 +49,22 @@ internal class PluginSpec { private lateinit var projectDir: File private lateinit var project: GradleProject private lateinit var resultDir: Path + private lateinit var pluginDir: Path @BeforeEach fun prepareDir(@TempDir tempDir: File) { projectDir = tempDir - resultDir = Path(outputFile("$projectDir/build", "")) + val buildDir = "$projectDir/build" + resultDir = outputRoot(buildDir) + pluginDir = pluginOutputDir(buildDir) } @Test fun `run with default values`() { createProject("default-values") runBuild() - resultDir.resolve(CODE_GENERATOR_REQUEST_FILE).exists() shouldBe true resultDir.resolve("java").countFiles() shouldNotBe 0 + pluginDir.resolve(CODE_GENERATOR_REQUEST_FILE).exists() shouldBe true } private fun createProject(resourceDir: String) { From af8bb191d50be28328e3da6add6133c189229e3d Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Sat, 27 Apr 2024 22:25:30 +0100 Subject: [PATCH 19/39] Document `Protobuf.javaLib` property --- .../io/spine/internal/dependency/Protobuf.kt | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Protobuf.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Protobuf.kt index be80d2d..de40557 100644 --- a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Protobuf.kt +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Protobuf.kt @@ -34,12 +34,23 @@ package io.spine.internal.dependency object Protobuf { private const val group = "com.google.protobuf" const val version = "3.25.1" + + /** + * The Java library with Protobuf data types. + */ + const val javaLib = "${group}:protobuf-java:${version}" + + /** + * The Java library containing proto definitions of Google Protobuf types. + */ + @Suppress("unused") + const val protoSrcLib = javaLib + /** - * The Java library containing proto definitions of Google Protobuf. + * All Java and Kotlin libraries we depend on. */ - const val protoSrcLib = "${group}:protobuf-java:${version}" val libs = listOf( - protoSrcLib, + javaLib, "${group}:protobuf-java-util:${version}", "${group}:protobuf-kotlin:${version}" ) From ec5ed8a2a317d98182eff23f0f7e50dee26df550 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Sat, 27 Apr 2024 22:25:40 +0100 Subject: [PATCH 20/39] Update dependency reports --- dependencies.md | 75 ++++++++++++++++++++++++++++++++++++------------- pom.xml | 39 +++++++++++++++---------- 2 files changed, 80 insertions(+), 34 deletions(-) diff --git a/dependencies.md b/dependencies.md index c5ee415..316ad9e 100644 --- a/dependencies.md +++ b/dependencies.md @@ -480,7 +480,7 @@ The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Apr 26 21:58:35 WEST 2024** 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). +This report was generated on **Sat Apr 27 22:24:10 WEST 2024** 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). @@ -666,6 +666,14 @@ This report was generated on **Fri Apr 26 21:58:35 WEST 2024** using [Gradle-Lic * **Project URL:** [https://github.com/google/error-prone-javac](https://github.com/google/error-prone-javac) * **License:** [GNU General Public License, version 2, with the Classpath Exception](http://openjdk.java.net/legal/gplv2+ce.html) +1. **Group** : com.google.flogger. **Name** : flogger. **Version** : 0.7.4. + * **Project URL:** [https://github.com/google/flogger](https://github.com/google/flogger) + * **License:** [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + +1. **Group** : com.google.flogger. **Name** : flogger-system-backend. **Version** : 0.7.4. + * **Project URL:** [https://github.com/google/flogger](https://github.com/google/flogger) + * **License:** [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) + 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) @@ -851,6 +859,29 @@ This report was generated on **Fri Apr 26 21:58:35 WEST 2024** using [Gradle-Lic * **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.kotest. **Name** : kotest-assertions-api. **Version** : 5.8.0.**No license information found** +1. **Group** : io.kotest. **Name** : kotest-assertions-api-jvm. **Version** : 5.8.0. + * **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.8.0.**No license information found** +1. **Group** : io.kotest. **Name** : kotest-assertions-core-jvm. **Version** : 5.8.0. + * **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.8.0.**No license information found** +1. **Group** : io.kotest. **Name** : kotest-assertions-shared-jvm. **Version** : 5.8.0. + * **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.8.0. + * **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-jvm. **Version** : 5.8.0. + * **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** : 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) @@ -911,6 +942,11 @@ This report was generated on **Fri Apr 26 21:58:35 WEST 2024** using [Gradle-Lic * **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.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) + * **License:** [The MIT License](http://opensource.org/licenses/MIT) + 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) @@ -1095,22 +1131,6 @@ This report was generated on **Fri Apr 26 21:58:35 WEST 2024** using [Gradle-Lic * **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-test. **Version** : 1.9.23. - * **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-test-annotations-common. **Version** : 1.9.23. - * **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-test-common. **Version** : 1.9.23. - * **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-test-junit5. **Version** : 1.9.23. - * **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-tooling-core. **Version** : 1.9.23. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -1123,10 +1143,23 @@ This report was generated on **Fri Apr 26 21:58:35 WEST 2024** using [Gradle-Lic * **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.17.3. + * **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.6.4.**No license information found** +1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-core. **Version** : 1.6.4. + * **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) + 1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-core-jvm. **Version** : 1.6.4. * **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) +1. **Group** : org.jetbrains.kotlinx. **Name** : kotlinx-coroutines-jdk8. **Version** : 1.6.4. + * **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) + 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) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -1210,7 +1243,7 @@ This report was generated on **Fri Apr 26 21:58:35 WEST 2024** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Apr 26 21:58:35 WEST 2024** 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). +This report was generated on **Sat Apr 27 22:24:11 WEST 2024** 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). @@ -1218,6 +1251,10 @@ This report was generated on **Fri Apr 26 21:58:35 WEST 2024** using [Gradle-Lic # Dependencies of `io.spine.tools:prototap-protoc-plugin:0.0.1-SNAPSHOT.1` ## Runtime +1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.25.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.jetbrains. **Name** : annotations. **Version** : 24.0.1. * **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) @@ -1695,4 +1732,4 @@ This report was generated on **Fri Apr 26 21:58:35 WEST 2024** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Apr 26 21:58:35 WEST 2024** 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 **Sat Apr 27 22:24:11 WEST 2024** 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/pom.xml b/pom.xml index 49cb9ce..2f4d676 100644 --- a/pom.xml +++ b/pom.xml @@ -23,6 +23,12 @@ all modules and does not describe the project structure per-subproject. + + com.google.protobuf + protobuf-java + 3.25.1 + compile + io.spine.tools spine-plugin-base @@ -65,12 +71,30 @@ all modules and does not describe the project structure per-subproject. 1.1.5 test + + io.spine.tools + spine-plugin-testlib + 2.0.0-SNAPSHOT.208 + test + org.apiguardian apiguardian-api 1.1.2 test + + org.jetbrains.kotlin + kotlin-gradle-plugin + 1.9.23 + test + + + org.junit.jupiter + junit-jupiter + 5.10.0 + test + org.junit.jupiter junit-jupiter-api @@ -159,11 +183,6 @@ all modules and does not describe the project structure per-subproject. kotlin-compiler-embeddable 1.8.22 - - org.jetbrains.kotlin - kotlin-gradle-plugin - 1.9.23 - org.jetbrains.kotlin kotlin-klib-commonizer-embeddable @@ -174,16 +193,6 @@ all modules and does not describe the project structure per-subproject. kotlin-scripting-compiler-embeddable 1.8.22 - - org.jetbrains.kotlin - kotlin-test-junit5 - 1.9.23 - - - org.junit.jupiter - junit-jupiter - 5.10.0 - \ No newline at end of file From b99eb53f812954daf2a2aecf3d1aa992a2e49ca6 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Mon, 29 Apr 2024 10:30:15 +0100 Subject: [PATCH 21/39] Document adding gmarker dependency prefix Also: * Add `prototap` tag to the Gradle plugin. --- gradle-plugin/build.gradle.kts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gradle-plugin/build.gradle.kts b/gradle-plugin/build.gradle.kts index f0c726b..4bd311f 100644 --- a/gradle-plugin/build.gradle.kts +++ b/gradle-plugin/build.gradle.kts @@ -98,7 +98,7 @@ gradlePlugin { implementationClass = "io.spine.tools.prototap.gradle.Plugin" displayName = "ProtoTap Gradle Plugin" description = "Obtains generated code and related data from Protobuf compiler" - tags.set(listOf("protobuf", "protoc", "codegen", "gradle", "plugin")) + tags.set(listOf("protobuf", "protoc", "prototap", "codegen", "gradle", "plugin")) } } } @@ -126,6 +126,7 @@ project.afterEvaluate { // Do not add the prefix for the publication which produces // the `io.spine.prototap.gradle.plugin` marker. + // Add the prefix to the `artifactId` of the plugin dependency used in the marker. publications.withType().configureEach { if (name.endsWith("PluginMarkerMaven")) { pom.withXml { From a2fb60a252ef198a4753c2a560af1f7da06a54e1 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Mon, 29 Apr 2024 22:26:26 +0100 Subject: [PATCH 22/39] Put all files under `prototap` dir --- .../kotlin/io/spine/tools/prototap/Paths.kt | 27 +++++-- .../io/spine/tools/prototap/gradle/Plugin.kt | 71 +++++++++++++------ .../spine/tools/prototap/gradle/PluginSpec.kt | 5 +- .../io/spine/tools/prototap/protoc/Plugin.kt | 7 +- 4 files changed, 75 insertions(+), 35 deletions(-) diff --git a/api/src/main/kotlin/io/spine/tools/prototap/Paths.kt b/api/src/main/kotlin/io/spine/tools/prototap/Paths.kt index eddff55..9aef44c 100644 --- a/api/src/main/kotlin/io/spine/tools/prototap/Paths.kt +++ b/api/src/main/kotlin/io/spine/tools/prototap/Paths.kt @@ -33,6 +33,11 @@ import kotlin.io.path.pathString public object Paths { + /** + * The name of the subdirectory for collecting files. + */ + public const val TARGET_DIR: String = PROTOC_PLUGIN_NAME + /** * The name of the file containing `CodeGeneratorRequest` message obtained * by the ProtoTap `protoc` plugins. @@ -44,20 +49,30 @@ public object Paths { */ public const val DESCRIPTOR_SET_FILE: String = "FileDescriptorSet.binpb" - public fun outputRoot(buildDir: String): Path = - Paths.get("$buildDir/resources/test/") + /** + * Obtains the path to the intermediate directory for storing some of the intercepted + * files before they are copied to the [outputRoot] directory. + */ + public fun interimDir(buildDir: String): Path = + Paths.get("$buildDir/$TARGET_DIR") - public fun pluginOutputDir(buildDir: String): Path = - outputRoot(buildDir).resolve(PROTOC_PLUGIN_NAME) + /** + * Obtains the path to the root directory into which ProtoTap puts all the intercepted files. + * + * By convention, it is a subdirectory named [TARGET_DIR] placed under + * `$buildDir/resources/test` of a Gradle project. + */ + public fun outputRoot(buildDir: String): Path = + Paths.get("$buildDir/resources/test/$TARGET_DIR") /** * Obtains a full path to the file with given [shortFileName] under * the given [buildDir] of a Gradle project. * - * By convention, the file is placed under the directory named after [PROTOC_PLUGIN_NAME], + * By convention, the file is placed under the [TARGET_DIR] directory, * which is created under `$buildDir/resources/test/`. */ public fun outputFile(buildDir: String, shortFileName: String): String = - pluginOutputDir(buildDir).resolve(shortFileName).pathString + outputRoot(buildDir).resolve(shortFileName).pathString } diff --git a/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Plugin.kt b/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Plugin.kt index d2f0bc1..945052f 100644 --- a/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Plugin.kt +++ b/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Plugin.kt @@ -24,6 +24,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +@file:Suppress("UnstableApiUsage") // `ProcessResources` task we use is marked `@Incubating`. + package io.spine.tools.prototap.gradle import com.google.common.annotations.VisibleForTesting @@ -39,14 +41,20 @@ import io.spine.tools.prototap.Names.PROTOC_PLUGIN_CLASSIFIER import io.spine.tools.prototap.Names.PROTOC_PLUGIN_NAME import io.spine.tools.prototap.Paths.CODE_GENERATOR_REQUEST_FILE import io.spine.tools.prototap.Paths.DESCRIPTOR_SET_FILE -import io.spine.tools.prototap.Paths.outputFile +import io.spine.tools.prototap.Paths.TARGET_DIR +import io.spine.tools.prototap.Paths.interimDir +import java.nio.file.Path import java.util.* +import kotlin.io.path.pathString import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.file.DuplicatesStrategy.INCLUDE import org.gradle.api.tasks.TaskContainer import org.gradle.language.jvm.tasks.ProcessResources +/** + * A Gradle plugin which adds ProtoTap plugin to `protoc` and configures the project + * tasks for copying generated source code and other related files into test resources. + */ public class Plugin : Plugin { override fun apply(project: Project): Unit = with(project) { @@ -96,8 +104,8 @@ private fun Project.tapProtobuf() { private val Project.extension: Extension get() = extensions.getByType(Extension::class.java) -private fun Project.setProtocArtifact() { - protobufExtension?.protoc { +private fun Project.setProtocArtifact() = protobufExtension?.run { + protoc { val artifact = extension.artifact.get() if (artifact.isNotBlank()) { it.artifact = artifact @@ -105,23 +113,22 @@ private fun Project.setProtocArtifact() { } } -private fun Project.createProtocPlugin() { - protobufExtension?.plugins { +private fun Project.createProtocPlugin() = protobufExtension?.run { + plugins { it.create(PROTOC_PLUGIN_NAME) { locator -> locator.artifact = protocPlugin.notation() } } } -private val protoTapVersion: String by lazy { - io.spine.tools.prototap.gradle.Plugin.readVersion() -} - +/** + * The Maven artifact of the ProtoTap plugin for `protoc`. + */ private val protocPlugin: Artifact by lazy { artifact { useSpineToolsGroup() name = "prototap-protoc-plugin" - version = protoTapVersion + version = io.spine.tools.prototap.gradle.Plugin.readVersion() classifier = PROTOC_PLUGIN_CLASSIFIER extension = "jar" } @@ -135,9 +142,12 @@ private fun Project.tuneProtoTasks() { breaks the configuration order of the `GenerateProtoTaskCollection`. This, in turn, leads to missing generated sources in the `compileJava` task. */ protobufExtension?.generateProtoTasks { - it.ofSourceSet(sourceSetName).forEach { task -> + it.ofSourceSet(sourceSetName).configureEach { task -> + tasks.processTestResources.run { + copySourcesFrom(task.outputBaseDir) + copyProtocPluginOutput() + } task.apply { - collectGeneratedJavaCode() addProtocPlugin() grabDescriptorSetFile() } @@ -145,23 +155,40 @@ private fun Project.tuneProtoTasks() { } } -private fun Project.outputFile(name: String): String = - outputFile(buildDir.path, name) +private val Project.interimDir: Path + get() = interimDir(buildDir.path) + +private fun Project.interimFile(name: String): String = + interimDir.resolve(name).pathString + +private val Project.codeGeneratorRequestFile: String + get() = interimFile(CODE_GENERATOR_REQUEST_FILE) + +private val Project.descriptorSetFile: String + get() = interimFile(DESCRIPTOR_SET_FILE) private fun GenerateProtoTask.addProtocPlugin() { plugins.apply { id(PROTOC_PLUGIN_NAME) { - val path = project.outputFile(CODE_GENERATOR_REQUEST_FILE) + val path = project.codeGeneratorRequestFile val encoded = path.base64Encoded() option(encoded) } } } -private fun GenerateProtoTask.collectGeneratedJavaCode() { - project.tasks.processTaskResources.apply { - from(this@collectGeneratedJavaCode.outputs) - duplicatesStrategy = INCLUDE +private fun ProcessResources.copySourcesFrom(directory: String) { + from(directory) { spec -> + // Exclude the empty directory automatically created by + // Protobuf Gradle Plugin for our `protoc` plugin. + spec.exclude(PROTOC_PLUGIN_NAME) + spec.into(TARGET_DIR) + } +} + +private fun ProcessResources.copyProtocPluginOutput() { + from(project.interimDir) { spec -> + spec.into(TARGET_DIR) } } @@ -169,14 +196,14 @@ private fun GenerateProtoTask.grabDescriptorSetFile() { if (project.extension.generateDescriptorSet.get()) { generateDescriptorSet = true descriptorSetOptions.apply { - path = project.outputFile(DESCRIPTOR_SET_FILE) + path = project.descriptorSetFile includeSourceInfo = true includeImports = true } } } -private val TaskContainer.processTaskResources: ProcessResources +private val TaskContainer.processTestResources: ProcessResources get() = named("processTestResources", ProcessResources::class.java).get() private fun String.base64Encoded(): String { diff --git a/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt b/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt index 8174d6f..f583725 100644 --- a/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt +++ b/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt @@ -33,7 +33,6 @@ import io.spine.tools.gradle.testing.GradleProject import io.spine.tools.prototap.Names.GRADLE_PLUGIN_ID import io.spine.tools.prototap.Paths.CODE_GENERATOR_REQUEST_FILE import io.spine.tools.prototap.Paths.outputRoot -import io.spine.tools.prototap.Paths.pluginOutputDir import java.io.File import java.nio.file.Path import kotlin.io.path.exists @@ -49,14 +48,12 @@ internal class PluginSpec { private lateinit var projectDir: File private lateinit var project: GradleProject private lateinit var resultDir: Path - private lateinit var pluginDir: Path @BeforeEach fun prepareDir(@TempDir tempDir: File) { projectDir = tempDir val buildDir = "$projectDir/build" resultDir = outputRoot(buildDir) - pluginDir = pluginOutputDir(buildDir) } @Test @@ -64,7 +61,7 @@ internal class PluginSpec { createProject("default-values") runBuild() resultDir.resolve("java").countFiles() shouldNotBe 0 - pluginDir.resolve(CODE_GENERATOR_REQUEST_FILE).exists() shouldBe true + resultDir.resolve(CODE_GENERATOR_REQUEST_FILE).exists() shouldBe true } private fun createProject(resourceDir: String) { diff --git a/protoc-plugin/src/main/kotlin/io/spine/tools/prototap/protoc/Plugin.kt b/protoc-plugin/src/main/kotlin/io/spine/tools/prototap/protoc/Plugin.kt index dd1fd83..98ff5bb 100644 --- a/protoc-plugin/src/main/kotlin/io/spine/tools/prototap/protoc/Plugin.kt +++ b/protoc-plugin/src/main/kotlin/io/spine/tools/prototap/protoc/Plugin.kt @@ -43,9 +43,10 @@ import kotlin.text.Charsets.UTF_8 public fun main() { val request = CodeGeneratorRequest.parseFrom(System.`in`) val requestFile = Path(request.parameter.decodeBase64()) - requestFile.toFile() - .parentFile - .mkdirs() + + val targetDir = requestFile.toFile().parentFile + targetDir.mkdirs() + requestFile.writeBytes(request.toByteArray(), CREATE, TRUNCATE_EXISTING) val emptyResponse = CodeGeneratorResponse.getDefaultInstance() From 4cbc2d9531d405371eab151bbd8976d2888afd09 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Mon, 29 Apr 2024 22:46:38 +0100 Subject: [PATCH 23/39] Add test for proto files under `test` Also: * Suppress and document detekt warnings. --- .../io/spine/tools/prototap/gradle/Plugin.kt | 5 +- .../spine/tools/prototap/gradle/PluginSpec.kt | 18 ++++- .../resources/proto-in-test/build.gradle.kts | 57 ++++++++++++++++ .../proto-in-test/settings.gradle.kts | 31 +++++++++ .../given/domain/gas_transportation.proto | 65 +++++++++++++++++++ .../proto/given/domain/oil_refinery.proto | 56 ++++++++++++++++ 6 files changed, 230 insertions(+), 2 deletions(-) create mode 100644 gradle-plugin/src/test/resources/proto-in-test/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/proto-in-test/settings.gradle.kts create mode 100644 gradle-plugin/src/test/resources/proto-in-test/src/test/proto/given/domain/gas_transportation.proto create mode 100644 gradle-plugin/src/test/resources/proto-in-test/src/test/proto/given/domain/oil_refinery.proto diff --git a/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Plugin.kt b/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Plugin.kt index 945052f..8c37ed2 100644 --- a/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Plugin.kt +++ b/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Plugin.kt @@ -24,7 +24,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -@file:Suppress("UnstableApiUsage") // `ProcessResources` task we use is marked `@Incubating`. +@file:Suppress( + "UnstableApiUsage" /* `ProcessResources` task we use is marked `@Incubating`. */, + "TooManyFunctions" /* Smaller functions are used for better readability. */ +) package io.spine.tools.prototap.gradle diff --git a/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt b/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt index f583725..450ddcb 100644 --- a/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt +++ b/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt @@ -60,10 +60,26 @@ internal class PluginSpec { fun `run with default values`() { createProject("default-values") runBuild() - resultDir.resolve("java").countFiles() shouldNotBe 0 + assertJavaCodeGenerated() + assertRequestFileExits() + } + + @Test + fun `run with proto files under 'tests'`() { + createProject("proto-in-test") + runBuild() + assertJavaCodeGenerated() + assertRequestFileExits() + } + + private fun assertRequestFileExits() { resultDir.resolve(CODE_GENERATOR_REQUEST_FILE).exists() shouldBe true } + private fun assertJavaCodeGenerated() { + resultDir.resolve("java").countFiles() shouldNotBe 0 + } + private fun createProject(resourceDir: String) { val version = Plugin.readVersion() val builder = GradleProject.setupAt(projectDir) diff --git a/gradle-plugin/src/test/resources/proto-in-test/build.gradle.kts b/gradle-plugin/src/test/resources/proto-in-test/build.gradle.kts new file mode 100644 index 0000000..57ff831 --- /dev/null +++ b/gradle-plugin/src/test/resources/proto-in-test/build.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 + * + * 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. + */ + +import com.google.protobuf.gradle.protobuf +import io.spine.internal.dependency.Protobuf +import io.spine.internal.dependency.GoogleApis +import io.spine.internal.gradle.standardToSpineSdk + +buildscript { + standardSpineSdkRepositories() +} + +plugins { + java + id("com.google.protobuf") + id("@PROTOTAP_PLUGIN_ID@") version "@PROTOTAP_VERSION@" +} + +repositories { + mavenLocal() + standardToSpineSdk() +} + +protobuf { + protoc { + artifact = io.spine.internal.dependency.Protobuf.compiler + } +} + +dependencies { + testImplementation(Protobuf.javaLib) + // For `google/type/` proto types used in stub domains. + testImplementation(GoogleApis.commonProtos) +} diff --git a/gradle-plugin/src/test/resources/proto-in-test/settings.gradle.kts b/gradle-plugin/src/test/resources/proto-in-test/settings.gradle.kts new file mode 100644 index 0000000..4aadfdd --- /dev/null +++ b/gradle-plugin/src/test/resources/proto-in-test/settings.gradle.kts @@ -0,0 +1,31 @@ +/* + * 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 + * + * 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. + */ + +pluginManagement { + repositories { + mavenLocal() + } +} diff --git a/gradle-plugin/src/test/resources/proto-in-test/src/test/proto/given/domain/gas_transportation.proto b/gradle-plugin/src/test/resources/proto-in-test/src/test/proto/given/domain/gas_transportation.proto new file mode 100644 index 0000000..a796651 --- /dev/null +++ b/gradle-plugin/src/test/resources/proto-in-test/src/test/proto/given/domain/gas_transportation.proto @@ -0,0 +1,65 @@ +/* +* Copyright 2024, TeamDev. All rights reserved. +* +* Redistribution and use in source and/or binary forms, with or without +* modification, must retain the above copyright notice and the following +* disclaimer. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +syntax = "proto3"; + +package given.domain; + +// +// This is a "vanilla" Protobuf file, which does not use Spine-specific features like +// custom options such as `type_url_prefix` or validation constraints. +// We also use "pure" Protobuf Java `protoc` plugin to process these files so that +// we recreate conditions we have before ProtoData gets into play after `protoc` +// finishes its work. +// + +option java_package = "io.spine.given.domain.gas"; +option java_outer_classname = "GasTransportationProto"; +option java_multiple_files = true; + +import "google/type/latlng.proto"; + +message OilAndGasWell { + google.type.LatLng location = 1; +} + +message Pipe { + google.type.LatLng from = 1; + google.type.LatLng to = 2; +} + +message GasProcessingPlant { + string name = 1; + google.type.LatLng location = 2; + repeated Pipe inbound = 3; + repeated Pipe outbound = 4; +} + +message CompressorStation { + google.type.LatLng location = 1; + repeated Pipe inbound = 2; + repeated Pipe outbound = 3; +} + +message LngStorage { + string name = 1; + google.type.LatLng location = 2; + Pipe inbound = 3; + Pipe outbound = 4; +} diff --git a/gradle-plugin/src/test/resources/proto-in-test/src/test/proto/given/domain/oil_refinery.proto b/gradle-plugin/src/test/resources/proto-in-test/src/test/proto/given/domain/oil_refinery.proto new file mode 100644 index 0000000..c85fa31 --- /dev/null +++ b/gradle-plugin/src/test/resources/proto-in-test/src/test/proto/given/domain/oil_refinery.proto @@ -0,0 +1,56 @@ +/* +* Copyright 2024, TeamDev. All rights reserved. +* +* Redistribution and use in source and/or binary forms, with or without +* modification, must retain the above copyright notice and the following +* disclaimer. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +syntax = "proto3"; + +package given.domain; + +// +// This is a "vanilla" Protobuf file, which does not use Spine-specific features like +// custom options such as `type_url_prefix` or validation constraints. +// We also use "pure" Protobuf Java `protoc` plugin to process these files so that +// we recreate conditions we have before ProtoData gets into play after `protoc` +// finishes its work. +// + +option java_package = "io.spine.given.domain.oil"; +option java_outer_classname = "OilRefineryProto"; +option java_multiple_files = true; + +enum ProductType { + PT_UNKNOWN = 0; + PETROL = 1; + KEROSENE = 2; + DIESEL = 3; + FUEL_OIL = 4; + LUBRICATING_OIL = 5; + PARAFFIN_WAX = 6; + ASPHALT_BASE = 7; +} + +message Product { + ProductType type = 1; + float volume = 2; +} + +message Refinery { + string name = 1; + float input = 2; + repeated Product output = 3; +} From 3976d5316d7fe424236bb25fe6bc54704b0675b4 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Mon, 29 Apr 2024 22:46:51 +0100 Subject: [PATCH 24/39] Update build time --- dependencies.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dependencies.md b/dependencies.md index 316ad9e..e13793c 100644 --- a/dependencies.md +++ b/dependencies.md @@ -480,7 +480,7 @@ The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Sat Apr 27 22:24:10 WEST 2024** 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). +This report was generated on **Mon Apr 29 22:45:10 WEST 2024** 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). @@ -1243,7 +1243,7 @@ This report was generated on **Sat Apr 27 22:24:10 WEST 2024** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Sat Apr 27 22:24:11 WEST 2024** 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). +This report was generated on **Mon Apr 29 22:45:11 WEST 2024** 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). @@ -1732,4 +1732,4 @@ This report was generated on **Sat Apr 27 22:24:11 WEST 2024** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Sat Apr 27 22:24:11 WEST 2024** 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 Apr 29 22:45:11 WEST 2024** 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 From 204a797a0c1363cead66a63e64cc08291cd0b4df Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 30 Apr 2024 11:53:13 +0100 Subject: [PATCH 25/39] Add test for running with settings Also: * Document `Extension`. * Improve documentation of name constants. * Avoid star imports. --- .../kotlin/io/spine/tools/prototap/Names.kt | 17 ++-- .../spine/tools/prototap/gradle/Extension.kt | 29 ++++++ .../io/spine/tools/prototap/gradle/Plugin.kt | 2 +- .../spine/tools/prototap/gradle/PluginSpec.kt | 27 +++++- .../resources/proto-in-test/build.gradle.kts | 2 +- .../resources/with-settings/build.gradle.kts | 90 +++++++++++++++++++ .../with-settings/settings.gradle.kts | 31 +++++++ .../given/domain/gas_transportation.proto | 65 ++++++++++++++ .../proto/given/domain/oil_refinery.proto | 56 ++++++++++++ 9 files changed, 307 insertions(+), 12 deletions(-) create mode 100644 gradle-plugin/src/test/resources/with-settings/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/with-settings/settings.gradle.kts create mode 100644 gradle-plugin/src/test/resources/with-settings/src/test/proto/given/domain/gas_transportation.proto create mode 100644 gradle-plugin/src/test/resources/with-settings/src/test/proto/given/domain/oil_refinery.proto diff --git a/api/src/main/kotlin/io/spine/tools/prototap/Names.kt b/api/src/main/kotlin/io/spine/tools/prototap/Names.kt index 0f933c0..9795a08 100644 --- a/api/src/main/kotlin/io/spine/tools/prototap/Names.kt +++ b/api/src/main/kotlin/io/spine/tools/prototap/Names.kt @@ -39,7 +39,7 @@ public object Names { /** * The name of a Gradle project extension added by ProtoTap Gradle Plugin. */ - public const val GRADLE_EXTENSION_NAME: String = "protoTap" + public const val GRADLE_EXTENSION_NAME: String = "prototap" /** * The name ProtoTap uses when passing itself to `protoc` compiler. @@ -47,22 +47,23 @@ public object Names { public const val PROTOC_PLUGIN_NAME: String = "prototap" /** - * The name of the source set which is used by default for tapping Protobuf compiler. + * The name of the `testFixtures` source set used by default for + * tapping Protobuf compiler. * - * This constant is used if the `sourceSet` property of the `protoTap` project extension + * This constant is used if the `sourceSet` property of + * the [prototap][GRADLE_EXTENSION_NAME] project extension * added by the ProtoTap Gradle Plugin is not specified. * * If a project does not apply the `java-test-fixtures` Gradle plugin, then - * the [`test`][FALLBACK_SOURCE_SET_NAME] source set conventionally will be used. + * the [test][FALLBACK_SOURCE_SET_NAME] source set conventionally will be used. */ public const val DEFAULT_SOURCE_SET_NAME: String = "testFixtures" /** - * The name of the source set to be used for tapping Protobuf compiler, if - * the project does not have a source set with the [DEFAULT_SOURCE_SET_NAME]. + * The name of the `test` source set which is used for tapping Protobuf compiler, if + * the project does not have the [testFixtures][DEFAULT_SOURCE_SET_NAME] source set. * - * If the project does not have the [`test`][FALLBACK_SOURCE_SET_NAME] source - * set either, a build time error will occur. + * If the project does not have the `test` source set either, a build time error will occur. */ public const val FALLBACK_SOURCE_SET_NAME: String = "test" diff --git a/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Extension.kt b/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Extension.kt index 1cde502..e4da292 100644 --- a/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Extension.kt +++ b/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Extension.kt @@ -36,11 +36,35 @@ import org.gradle.api.provider.Property import org.gradle.api.tasks.SourceSet import org.gradle.kotlin.dsl.property +/** + * An extension for a Gradle project which provides options for capturing Protobuf output. + */ public class Extension(project: Project) { + /** + * The `protoc` artifact to be used during for processing proto files. + * + * The default value is empty string, which means that ProtoTap assumes that it + * is used in a project with Protobuf Gradle Plugin fully configured. + * + * This property can be used in rare cases when the artifact is not specified directly + * via the [artifact][com.google.protobuf.gradle.ExecutableLocator.setArtifact] property in + * the [protobuf/protoc][com.google.protobuf.gradle.ProtobufExtension.protoc] block. + */ public val artifact: Property = project.objects.property() .convention("") + /** + * The source set with proto files for installing the tap. + * + * If not specified the Gradle plugin would look for + * the [testFixtures][DEFAULT_SOURCE_SET_NAME] source set. + * + * If it's not available, the plugin would look for + * the [test][FALLBACK_SOURCE_SET_NAME] source set. + * + * If such a source set is not available either, a build time error will occur. + */ public val sourceSet: Property = with(project) { objects.property().convention( provider { @@ -49,6 +73,11 @@ public class Extension(project: Project) { ) } + /** + * Tells if descriptor set file should be captured during the code generation process. + * + * The default value is `false`. + */ public val generateDescriptorSet: Property = project.objects.property().convention(false) } diff --git a/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Plugin.kt b/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Plugin.kt index 8c37ed2..67ab3ae 100644 --- a/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Plugin.kt +++ b/gradle-plugin/src/main/kotlin/io/spine/tools/prototap/gradle/Plugin.kt @@ -47,7 +47,7 @@ import io.spine.tools.prototap.Paths.DESCRIPTOR_SET_FILE import io.spine.tools.prototap.Paths.TARGET_DIR import io.spine.tools.prototap.Paths.interimDir import java.nio.file.Path -import java.util.* +import java.util.Base64 import kotlin.io.path.pathString import org.gradle.api.Plugin import org.gradle.api.Project diff --git a/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt b/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt index 450ddcb..6b43228 100644 --- a/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt +++ b/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt @@ -32,6 +32,7 @@ import io.spine.tools.gradle.task.BaseTaskName.build import io.spine.tools.gradle.testing.GradleProject import io.spine.tools.prototap.Names.GRADLE_PLUGIN_ID import io.spine.tools.prototap.Paths.CODE_GENERATOR_REQUEST_FILE +import io.spine.tools.prototap.Paths.DESCRIPTOR_SET_FILE import io.spine.tools.prototap.Paths.outputRoot import java.io.File import java.nio.file.Path @@ -72,12 +73,34 @@ internal class PluginSpec { assertRequestFileExits() } + @Test + fun `run with 'prototap' settings`() { + createProject("with-settings") + runBuild() + assertJavaCodeGenerated() + assertRequestFileExits() + assertDescriptorSetFileExits() + } + + private fun assertJavaCodeGenerated() { + val javaDir = resultDir.resolve("java") + javaDir.countFiles() shouldNotBe 0 + val packageDir = javaDir.resolve("io/spine/given/domain/gas/") + packageDir.run { + exists() shouldBe true + // Check some Java files too. + resolve("Pipe.java").exists() shouldBe true + resolve("CompressorStation.java").exists() shouldBe true + resolve("LngStorage.java").exists() shouldBe true + } + } + private fun assertRequestFileExits() { resultDir.resolve(CODE_GENERATOR_REQUEST_FILE).exists() shouldBe true } - private fun assertJavaCodeGenerated() { - resultDir.resolve("java").countFiles() shouldNotBe 0 + private fun assertDescriptorSetFileExits() { + resultDir.resolve(DESCRIPTOR_SET_FILE).exists() shouldBe true } private fun createProject(resourceDir: String) { diff --git a/gradle-plugin/src/test/resources/proto-in-test/build.gradle.kts b/gradle-plugin/src/test/resources/proto-in-test/build.gradle.kts index 57ff831..74756c0 100644 --- a/gradle-plugin/src/test/resources/proto-in-test/build.gradle.kts +++ b/gradle-plugin/src/test/resources/proto-in-test/build.gradle.kts @@ -25,8 +25,8 @@ */ import com.google.protobuf.gradle.protobuf -import io.spine.internal.dependency.Protobuf import io.spine.internal.dependency.GoogleApis +import io.spine.internal.dependency.Protobuf import io.spine.internal.gradle.standardToSpineSdk buildscript { diff --git a/gradle-plugin/src/test/resources/with-settings/build.gradle.kts b/gradle-plugin/src/test/resources/with-settings/build.gradle.kts new file mode 100644 index 0000000..dcfeb70 --- /dev/null +++ b/gradle-plugin/src/test/resources/with-settings/build.gradle.kts @@ -0,0 +1,90 @@ +/* + * 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 + * + * 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. + */ + +import com.google.protobuf.gradle.protobuf +import io.spine.internal.dependency.GoogleApis +import io.spine.internal.dependency.JUnit +import io.spine.internal.dependency.Protobuf +import io.spine.internal.gradle.standardToSpineSdk + +buildscript { + standardSpineSdkRepositories() +} + +plugins { + java + id("com.google.protobuf") + id("@PROTOTAP_PLUGIN_ID@") version "@PROTOTAP_VERSION@" +} + +repositories { + mavenLocal() + standardToSpineSdk() +} + +protobuf { + protoc { + artifact = io.spine.internal.dependency.Protobuf.compiler + } +} + +dependencies { + testImplementation(Protobuf.javaLib) + // For `google/type/` proto types used in stub domains. + testImplementation(GoogleApis.commonProtos) +} + + +@Suppress( + "UnstableApiUsage" /* testing suites feature */ +) +testing { + suites { + val test by getting(JvmTestSuite::class) { + useJUnitJupiter(JUnit.version) + dependencies { + implementation(Protobuf.javaLib) + implementation(GoogleApis.commonProtos) + } + } + + val functionalTest by registering(JvmTestSuite::class) { + useJUnitJupiter(JUnit.version) + dependencies { + implementation(Protobuf.javaLib) + implementation(GoogleApis.commonProtos) + } + } + } +} + +val functionalTest: SourceSet by project.sourceSets.getting + +prototap { + artifact.set(io.spine.internal.dependency.Protobuf.compiler) + sourceSet.set(functionalTest) + generateDescriptorSet.set(true) +} diff --git a/gradle-plugin/src/test/resources/with-settings/settings.gradle.kts b/gradle-plugin/src/test/resources/with-settings/settings.gradle.kts new file mode 100644 index 0000000..4aadfdd --- /dev/null +++ b/gradle-plugin/src/test/resources/with-settings/settings.gradle.kts @@ -0,0 +1,31 @@ +/* + * 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 + * + * 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. + */ + +pluginManagement { + repositories { + mavenLocal() + } +} diff --git a/gradle-plugin/src/test/resources/with-settings/src/test/proto/given/domain/gas_transportation.proto b/gradle-plugin/src/test/resources/with-settings/src/test/proto/given/domain/gas_transportation.proto new file mode 100644 index 0000000..a796651 --- /dev/null +++ b/gradle-plugin/src/test/resources/with-settings/src/test/proto/given/domain/gas_transportation.proto @@ -0,0 +1,65 @@ +/* +* Copyright 2024, TeamDev. All rights reserved. +* +* Redistribution and use in source and/or binary forms, with or without +* modification, must retain the above copyright notice and the following +* disclaimer. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +syntax = "proto3"; + +package given.domain; + +// +// This is a "vanilla" Protobuf file, which does not use Spine-specific features like +// custom options such as `type_url_prefix` or validation constraints. +// We also use "pure" Protobuf Java `protoc` plugin to process these files so that +// we recreate conditions we have before ProtoData gets into play after `protoc` +// finishes its work. +// + +option java_package = "io.spine.given.domain.gas"; +option java_outer_classname = "GasTransportationProto"; +option java_multiple_files = true; + +import "google/type/latlng.proto"; + +message OilAndGasWell { + google.type.LatLng location = 1; +} + +message Pipe { + google.type.LatLng from = 1; + google.type.LatLng to = 2; +} + +message GasProcessingPlant { + string name = 1; + google.type.LatLng location = 2; + repeated Pipe inbound = 3; + repeated Pipe outbound = 4; +} + +message CompressorStation { + google.type.LatLng location = 1; + repeated Pipe inbound = 2; + repeated Pipe outbound = 3; +} + +message LngStorage { + string name = 1; + google.type.LatLng location = 2; + Pipe inbound = 3; + Pipe outbound = 4; +} diff --git a/gradle-plugin/src/test/resources/with-settings/src/test/proto/given/domain/oil_refinery.proto b/gradle-plugin/src/test/resources/with-settings/src/test/proto/given/domain/oil_refinery.proto new file mode 100644 index 0000000..c85fa31 --- /dev/null +++ b/gradle-plugin/src/test/resources/with-settings/src/test/proto/given/domain/oil_refinery.proto @@ -0,0 +1,56 @@ +/* +* Copyright 2024, TeamDev. All rights reserved. +* +* Redistribution and use in source and/or binary forms, with or without +* modification, must retain the above copyright notice and the following +* disclaimer. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +syntax = "proto3"; + +package given.domain; + +// +// This is a "vanilla" Protobuf file, which does not use Spine-specific features like +// custom options such as `type_url_prefix` or validation constraints. +// We also use "pure" Protobuf Java `protoc` plugin to process these files so that +// we recreate conditions we have before ProtoData gets into play after `protoc` +// finishes its work. +// + +option java_package = "io.spine.given.domain.oil"; +option java_outer_classname = "OilRefineryProto"; +option java_multiple_files = true; + +enum ProductType { + PT_UNKNOWN = 0; + PETROL = 1; + KEROSENE = 2; + DIESEL = 3; + FUEL_OIL = 4; + LUBRICATING_OIL = 5; + PARAFFIN_WAX = 6; + ASPHALT_BASE = 7; +} + +message Product { + ProductType type = 1; + float volume = 2; +} + +message Refinery { + string name = 1; + float input = 2; + repeated Product output = 3; +} From 6b948dcc0ae2ae1a3d95060b89812f682b333ec1 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 30 Apr 2024 12:38:45 +0100 Subject: [PATCH 26/39] Bump version -> `0.8.0` --- version.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.gradle.kts b/version.gradle.kts index 832d4df..d5aa617 100644 --- a/version.gradle.kts +++ b/version.gradle.kts @@ -24,4 +24,4 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -val versionToPublish: String by extra("0.0.1-SNAPSHOT.1") +val versionToPublish: String by extra("0.8.0") From 91a3550faf07342baaa8a02fb0686e8bff3d3fd5 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 30 Apr 2024 12:45:53 +0100 Subject: [PATCH 27/39] Update dependency reports --- dependencies.md | 12 ++++++------ pom.xml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dependencies.md b/dependencies.md index e13793c..fa1c9f1 100644 --- a/dependencies.md +++ b/dependencies.md @@ -1,6 +1,6 @@ -# Dependencies of `io.spine.tools:prototap-api:0.0.1-SNAPSHOT.1` +# Dependencies of `io.spine.tools:prototap-api:0.8.0` ## Runtime 1. **Group** : org.jetbrains. **Name** : annotations. **Version** : 24.0.1. @@ -480,12 +480,12 @@ The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Mon Apr 29 22:45:10 WEST 2024** 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). +This report was generated on **Tue Apr 30 12:42:47 WEST 2024** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:prototap-gradle-plugin:0.0.1-SNAPSHOT.1` +# Dependencies of `io.spine.tools:prototap-gradle-plugin:0.8.0` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -1243,12 +1243,12 @@ This report was generated on **Mon Apr 29 22:45:10 WEST 2024** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Mon Apr 29 22:45:11 WEST 2024** 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). +This report was generated on **Tue Apr 30 12:42:48 WEST 2024** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:prototap-protoc-plugin:0.0.1-SNAPSHOT.1` +# Dependencies of `io.spine.tools:prototap-protoc-plugin:0.8.0` ## Runtime 1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.25.1. @@ -1732,4 +1732,4 @@ This report was generated on **Mon Apr 29 22:45:11 WEST 2024** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Mon Apr 29 22:45:11 WEST 2024** 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 **Tue Apr 30 12:42:48 WEST 2024** 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/pom.xml b/pom.xml index 2f4d676..03c8aa7 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ all modules and does not describe the project structure per-subproject. --> io.spine.tools ProtoTap -0.0.1-SNAPSHOT.1 +0.8.0 2015 From 3f603c0f14fa5e87c6b58fcaee7bff1e6fa410fa Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 30 Apr 2024 12:46:07 +0100 Subject: [PATCH 28/39] Add test for running via `classpath` dependency --- .../spine/tools/prototap/gradle/PluginSpec.kt | 8 +++ .../resources/via-classpath/build.gradle.kts | 62 ++++++++++++++++++ .../via-classpath/settings.gradle.kts | 31 +++++++++ .../given/domain/gas_transportation.proto | 65 +++++++++++++++++++ .../proto/given/domain/oil_refinery.proto | 56 ++++++++++++++++ 5 files changed, 222 insertions(+) create mode 100644 gradle-plugin/src/test/resources/via-classpath/build.gradle.kts create mode 100644 gradle-plugin/src/test/resources/via-classpath/settings.gradle.kts create mode 100644 gradle-plugin/src/test/resources/via-classpath/src/testFixtures/proto/given/domain/gas_transportation.proto create mode 100644 gradle-plugin/src/test/resources/via-classpath/src/testFixtures/proto/given/domain/oil_refinery.proto diff --git a/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt b/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt index 6b43228..391ada7 100644 --- a/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt +++ b/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt @@ -82,6 +82,14 @@ internal class PluginSpec { assertDescriptorSetFileExits() } + @Test + fun `work as a 'classpath' dependency of 'buildscript'`() { + createProject("via-classpath") + runBuild() + assertJavaCodeGenerated() + assertRequestFileExits() + } + private fun assertJavaCodeGenerated() { val javaDir = resultDir.resolve("java") javaDir.countFiles() shouldNotBe 0 diff --git a/gradle-plugin/src/test/resources/via-classpath/build.gradle.kts b/gradle-plugin/src/test/resources/via-classpath/build.gradle.kts new file mode 100644 index 0000000..e6b9afc --- /dev/null +++ b/gradle-plugin/src/test/resources/via-classpath/build.gradle.kts @@ -0,0 +1,62 @@ +/* + * 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 + * + * 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. + */ + +import com.google.protobuf.gradle.protobuf +import io.spine.internal.dependency.Protobuf +import io.spine.internal.dependency.GoogleApis +import io.spine.internal.gradle.standardToSpineSdk + +buildscript { + standardSpineSdkRepositories() + dependencies { + classpath("io.spine.tools:prototap-gradle-plugin:@PROTOTAP_VERSION@") + } +} + +plugins { + java + `java-test-fixtures` + id("com.google.protobuf") +} + +apply(plugin = "@PROTOTAP_PLUGIN_ID@") + +repositories { + mavenLocal() + standardToSpineSdk() +} + +protobuf { + protoc { + artifact = io.spine.internal.dependency.Protobuf.compiler + } +} + +dependencies { + testFixturesImplementation(Protobuf.javaLib) + // For `google/type/` proto types used in stub domains. + testFixturesImplementation(GoogleApis.commonProtos) +} diff --git a/gradle-plugin/src/test/resources/via-classpath/settings.gradle.kts b/gradle-plugin/src/test/resources/via-classpath/settings.gradle.kts new file mode 100644 index 0000000..4aadfdd --- /dev/null +++ b/gradle-plugin/src/test/resources/via-classpath/settings.gradle.kts @@ -0,0 +1,31 @@ +/* + * 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 + * + * 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. + */ + +pluginManagement { + repositories { + mavenLocal() + } +} diff --git a/gradle-plugin/src/test/resources/via-classpath/src/testFixtures/proto/given/domain/gas_transportation.proto b/gradle-plugin/src/test/resources/via-classpath/src/testFixtures/proto/given/domain/gas_transportation.proto new file mode 100644 index 0000000..a796651 --- /dev/null +++ b/gradle-plugin/src/test/resources/via-classpath/src/testFixtures/proto/given/domain/gas_transportation.proto @@ -0,0 +1,65 @@ +/* +* Copyright 2024, TeamDev. All rights reserved. +* +* Redistribution and use in source and/or binary forms, with or without +* modification, must retain the above copyright notice and the following +* disclaimer. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +syntax = "proto3"; + +package given.domain; + +// +// This is a "vanilla" Protobuf file, which does not use Spine-specific features like +// custom options such as `type_url_prefix` or validation constraints. +// We also use "pure" Protobuf Java `protoc` plugin to process these files so that +// we recreate conditions we have before ProtoData gets into play after `protoc` +// finishes its work. +// + +option java_package = "io.spine.given.domain.gas"; +option java_outer_classname = "GasTransportationProto"; +option java_multiple_files = true; + +import "google/type/latlng.proto"; + +message OilAndGasWell { + google.type.LatLng location = 1; +} + +message Pipe { + google.type.LatLng from = 1; + google.type.LatLng to = 2; +} + +message GasProcessingPlant { + string name = 1; + google.type.LatLng location = 2; + repeated Pipe inbound = 3; + repeated Pipe outbound = 4; +} + +message CompressorStation { + google.type.LatLng location = 1; + repeated Pipe inbound = 2; + repeated Pipe outbound = 3; +} + +message LngStorage { + string name = 1; + google.type.LatLng location = 2; + Pipe inbound = 3; + Pipe outbound = 4; +} diff --git a/gradle-plugin/src/test/resources/via-classpath/src/testFixtures/proto/given/domain/oil_refinery.proto b/gradle-plugin/src/test/resources/via-classpath/src/testFixtures/proto/given/domain/oil_refinery.proto new file mode 100644 index 0000000..c85fa31 --- /dev/null +++ b/gradle-plugin/src/test/resources/via-classpath/src/testFixtures/proto/given/domain/oil_refinery.proto @@ -0,0 +1,56 @@ +/* +* Copyright 2024, TeamDev. All rights reserved. +* +* Redistribution and use in source and/or binary forms, with or without +* modification, must retain the above copyright notice and the following +* disclaimer. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +syntax = "proto3"; + +package given.domain; + +// +// This is a "vanilla" Protobuf file, which does not use Spine-specific features like +// custom options such as `type_url_prefix` or validation constraints. +// We also use "pure" Protobuf Java `protoc` plugin to process these files so that +// we recreate conditions we have before ProtoData gets into play after `protoc` +// finishes its work. +// + +option java_package = "io.spine.given.domain.oil"; +option java_outer_classname = "OilRefineryProto"; +option java_multiple_files = true; + +enum ProductType { + PT_UNKNOWN = 0; + PETROL = 1; + KEROSENE = 2; + DIESEL = 3; + FUEL_OIL = 4; + LUBRICATING_OIL = 5; + PARAFFIN_WAX = 6; + ASPHALT_BASE = 7; +} + +message Product { + ProductType type = 1; + float volume = 2; +} + +message Refinery { + string name = 1; + float input = 2; + repeated Product output = 3; +} From eff2854e986ed28227d522e0ee50304467785b82 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 30 Apr 2024 14:56:34 +0100 Subject: [PATCH 29/39] Exclude unnecessary files from fat JARs --- gradle-plugin/build.gradle.kts | 48 +++++++++++++++++++++++++++++++++- protoc-plugin/build.gradle.kts | 5 +++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/gradle-plugin/build.gradle.kts b/gradle-plugin/build.gradle.kts index 4bd311f..aa47577 100644 --- a/gradle-plugin/build.gradle.kts +++ b/gradle-plugin/build.gradle.kts @@ -48,7 +48,6 @@ dependencies { api(project(":api")) implementation(Spine.pluginBase) - implementation(Kotlin.gradlePluginApi) } @@ -87,6 +86,53 @@ java { val shadowJar by tasks.getting(ShadowJar::class) { archiveClassifier.set("") + exclude( + "org/checkerframework/**", + "org/jboss/**", + + // Exclude license files that cause or may cause issues with LicenseReport. + // We analyze these files when building artifacts we depend on. + "about_files/**", + + "ant_tasks/**", // `resource-ant.jar` is of no use here. + + // Protobuf files. + "google/**", + "spine/**", + "src/**", + + // Java source code files of the package `org.osgi`. + "OSGI-OPT/**", + + // Unnecessary settings from the Eclipse Platform. + "OSGI-INF/**", + + "META-INF/com.android.tools/**", + "META-INF/maven/**", + "META-INF/native-image/**", + "META-INF/proguard/**", + "META-INF/services/org.jboss.forge.roaster.*.*", + "META-INF/eclipse.inf", + + // Checker Framework license. + "META-INF/LICENSE.txt", + + // OSGi notices + "META-INF/NOTICE", + + // Unnecessary stuff from the Eclipse Platform and other dependencies. + ".api_description", + ".options", + "_base_main_unspecified.desc", + "_base_main_unspecified.desc", + "about.html", + "profile.plist", + "*.profile", + "jdtCompilerAdapter.jar", + "plugin.properties", + "plugin.xml", + "systembundle.properties", + ) } gradlePlugin { diff --git a/protoc-plugin/build.gradle.kts b/protoc-plugin/build.gradle.kts index 6c1de65..58d72ad 100644 --- a/protoc-plugin/build.gradle.kts +++ b/protoc-plugin/build.gradle.kts @@ -42,7 +42,10 @@ tasks.jar { else -> zipTree(it) } }) - + exclude( + // Protobuf files. + "google/**", + ) archiveClassifier.set("exe") duplicatesStrategy = INCLUDE } From 054e5a19317abaf2a11abed50a910cdefa31722b Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 30 Apr 2024 20:09:12 +0100 Subject: [PATCH 30/39] Add text to README --- README.md | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 28ff23c..29e254e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,110 @@ -# ProtoTap -Utilities for tapping `protoc` output +# ProtoTap — Gradle plugin for tapping Protobuf + +[![Ubuntu build][ubuntu-build-badge]][gh-actions] +[![license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0) + +[gh-actions]: https://github.com/SpineEventEngine/ProtoTap/actions +[ubuntu-build-badge]: https://github.com/SpineEventEngine/ProtoTap/actions/workflows/build-on-ubuntu.yml/badge.svg + +ProtoTap is a Gradle plugin that allows tapping output of Protobuf compiler used in the project to +which the plugin is applied, and placing them as `test` resources. + +This way authors of code generators can focus on writing tests rather than on "plumbing" with +[Protobuf Plugin for Gradle][protobuf-plugin] or creating a custom diagnostic plugin for +Protobuf Compiler (`protoc`). + +## How it works + +ProtoTap interacts with [Protobuf Plugin for Gradle][protobuf-plugin] for copying generated code and +related files to `test` resources of the project. + +## Basic features + +### Automatic selection of a source set +When tuning code generation, the plugin attempts to find [`testFixtures`][test-fixtures] source set +in the project first. If not found, the plugin attempts to find the `test` source set. + +Using other source sets requires explicit setting, as described in the [section](#settings) below. + +### Copying `protoc` output +The code generated by `protoc` will appear in corresponding subdirectories of the `prototap` +subdirectory under `test/resources`. For example: + +``` +my-project + build + resources + test + prototap + java <-- The output of `java` built-in of `protoc`. + HelloWorld.java +``` +After this, the code of your tests will be able to access the generated code as usual program +resources. + +### Obtaining `CodeGeneratorRequest` file +[`CodeGeneratorRequest`][codegen-request] is passed by `protoc` to its plugins when `.proto` files +are processed. ProtoTap stores the binary version of the request in the file +named `CodeGeneratorRequest.binbp` next to the directories with the generated code: + +``` +my-project + build + resources + test + prototap + java + CodeGeneratorRequest.binbp <—— The request file. +``` + +## Simplest usage + +Adding to your project via Kotlin DSL looks like this: +```kotlin +plugins { + id("io.spine.prototap") version "$version" +} +``` +The above snippet assumes that the plugin with the ID `"com.google.protobuf"` is already added at +some level to your project. + +> [!TIP] +> For the latest ProtoTap version please see [`version.gradle.kts`](version.gradle.kts). +> +> The plugin was developed and tested under Gradle 7.6.4. + +## Using plugin settings +You can tune ProtoTap by using the following DSL: + +```kotlin +prototap { + artifact.set("com.google.protobuf:protoc:3.25.1") + sourceSet.set(functionalTest) + generateDescriptorSet.set(true) +} +``` +The `artifact` property is a convenience shortcut for specifying the `protoc` artifact in case +your project does not have explicit `protobuf` block. + +The `sourceSet` property is for specifying a source set with the proto files of interest, other +than `testFixtures` or `test`. + +The `generateDescriptorSet` property makes the Protobuf Compiler produce a descriptor set file, +which ProtoTap places next to `CodeGeneratorRequest`: + +``` +my-project + build + resources + test + prototap + java + CodeGeneratorRequest.binbp + FileDescriptorSet.binpb <—— The descriptor set file. +``` +By default descriptor set files are not generated. + +[protobuf-plugin]: https://github.com/google/protobuf-gradle-plugin +[test-fixtures]: https://docs.gradle.org/current/userguide/java_testing.html#sec:java_test_fixtures +[codegen-request]: https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/compiler/plugin.proto +[descriptor-set]: https://github.com/google/protobuf-gradle-plugin#generate-descriptor-set-files From b5d85d6618ebca9989fb8e2384b59c44bec222a3 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 30 Apr 2024 20:10:03 +0100 Subject: [PATCH 31/39] Update build time --- dependencies.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dependencies.md b/dependencies.md index fa1c9f1..bb5ab46 100644 --- a/dependencies.md +++ b/dependencies.md @@ -480,7 +480,7 @@ The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Apr 30 12:42:47 WEST 2024** 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). +This report was generated on **Tue Apr 30 20:09:26 WEST 2024** 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). @@ -1243,7 +1243,7 @@ This report was generated on **Tue Apr 30 12:42:47 WEST 2024** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Apr 30 12:42:48 WEST 2024** 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). +This report was generated on **Tue Apr 30 20:09:27 WEST 2024** 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). @@ -1732,4 +1732,4 @@ This report was generated on **Tue Apr 30 12:42:48 WEST 2024** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Apr 30 12:42:48 WEST 2024** 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 **Tue Apr 30 20:09:27 WEST 2024** 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 From 412b0aa5fd5f5af7bebab272cf789d4416b2d7a5 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 30 Apr 2024 23:42:35 +0100 Subject: [PATCH 32/39] Improve readme wording --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 29e254e..2be4398 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,11 @@ Protobuf Compiler (`protoc`). ## How it works ProtoTap interacts with [Protobuf Plugin for Gradle][protobuf-plugin] for copying generated code and -related files to `test` resources of the project. +related files to `test` resources of the project. Files are stored under the directory matching +the following pattern: +``` +$projectDir/build/resources/test/prototap/ +``` ## Basic features @@ -27,8 +31,9 @@ in the project first. If not found, the plugin attempts to find the `test` sourc Using other source sets requires explicit setting, as described in the [section](#settings) below. ### Copying `protoc` output -The code generated by `protoc` will appear in corresponding subdirectories of the `prototap` -subdirectory under `test/resources`. For example: +The code generated by `protoc` will appear in corresponding subdirectories of the `prototap`. +The name of the subdirectory matches the name of the corresponding `protoc` built-in or plugin. +For example, for `java` built-in it would be: ``` my-project From 9e18ca55f6339b037c14332cacf690c8296cabcb Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 1 May 2024 14:59:31 +0100 Subject: [PATCH 33/39] Document `Paths` object --- api/src/main/kotlin/io/spine/tools/prototap/Paths.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/api/src/main/kotlin/io/spine/tools/prototap/Paths.kt b/api/src/main/kotlin/io/spine/tools/prototap/Paths.kt index 9aef44c..4d51f01 100644 --- a/api/src/main/kotlin/io/spine/tools/prototap/Paths.kt +++ b/api/src/main/kotlin/io/spine/tools/prototap/Paths.kt @@ -31,6 +31,12 @@ import java.nio.file.Path import java.nio.file.Paths import kotlin.io.path.pathString +/** + * Constants and utility functions for calculating directory and file names + * used by ProtoTap Gradle and `protoc` plugins. + * + * This object may also be useful for users of ProtoTap to obtain its output. + */ public object Paths { /** From 210f9a10caae3fd898df4df60ac66d9a2719687e Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 1 May 2024 14:59:51 +0100 Subject: [PATCH 34/39] Remove trailing empty line --- api/src/main/kotlin/io/spine/tools/prototap/Paths.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/api/src/main/kotlin/io/spine/tools/prototap/Paths.kt b/api/src/main/kotlin/io/spine/tools/prototap/Paths.kt index 4d51f01..bcb8e60 100644 --- a/api/src/main/kotlin/io/spine/tools/prototap/Paths.kt +++ b/api/src/main/kotlin/io/spine/tools/prototap/Paths.kt @@ -81,4 +81,3 @@ public object Paths { public fun outputFile(buildDir: String, shortFileName: String): String = outputRoot(buildDir).resolve(shortFileName).pathString } - From 1ff7a2bacd522c2d1c18a681d342f3af0f6c3a59 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 1 May 2024 15:00:14 +0100 Subject: [PATCH 35/39] Remove trailing empty line --- .../src/main/kotlin/io/spine/tools/prototap/protoc/Plugin.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/protoc-plugin/src/main/kotlin/io/spine/tools/prototap/protoc/Plugin.kt b/protoc-plugin/src/main/kotlin/io/spine/tools/prototap/protoc/Plugin.kt index 98ff5bb..7bd3e9b 100644 --- a/protoc-plugin/src/main/kotlin/io/spine/tools/prototap/protoc/Plugin.kt +++ b/protoc-plugin/src/main/kotlin/io/spine/tools/prototap/protoc/Plugin.kt @@ -57,4 +57,3 @@ private fun String.decodeBase64(): String { val bytes = Base64.getDecoder().decode(this) return String(bytes, UTF_8) } - From 016fbb3964e330dbf131fe6aa44fc97c3845c754 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 1 May 2024 15:23:33 +0100 Subject: [PATCH 36/39] Improve `main` docs Also: * Use encoded writing for `CodeGeneratorResponse`. This is how older McJava codegen does this. It's also what [protoc docs](https://protobuf.dev/reference/cpp/api-docs/google.protobuf.compiler.plugin.pb/) tell about it. --- .../kotlin/io/spine/tools/prototap/protoc/Plugin.kt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/protoc-plugin/src/main/kotlin/io/spine/tools/prototap/protoc/Plugin.kt b/protoc-plugin/src/main/kotlin/io/spine/tools/prototap/protoc/Plugin.kt index 7bd3e9b..a1351be 100644 --- a/protoc-plugin/src/main/kotlin/io/spine/tools/prototap/protoc/Plugin.kt +++ b/protoc-plugin/src/main/kotlin/io/spine/tools/prototap/protoc/Plugin.kt @@ -32,13 +32,20 @@ import com.google.protobuf.compiler.PluginProtos.CodeGeneratorRequest import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse import java.nio.file.StandardOpenOption.CREATE import java.nio.file.StandardOpenOption.TRUNCATE_EXISTING -import java.util.* +import java.util.Base64 import kotlin.io.path.Path import kotlin.io.path.writeBytes import kotlin.text.Charsets.UTF_8 /** - * Stores the received `CodeGeneratorRequest` to the file passed as the parameter. + * Stores received `CodeGeneratorRequest` message to the file the name of which is passed as + * the value of the [parameter][CodeGeneratorRequest.getParameter] property of the request. + * + * The name of the file is [Base64] encoded. + * + * The function returns empty [CodeGeneratorRequest] written to [System.out] + * according to the `protoc` plugin + * [protocol](https://protobuf.dev/reference/cpp/api-docs/google.protobuf.compiler.plugin.pb/). */ public fun main() { val request = CodeGeneratorRequest.parseFrom(System.`in`) @@ -50,7 +57,7 @@ public fun main() { requestFile.writeBytes(request.toByteArray(), CREATE, TRUNCATE_EXISTING) val emptyResponse = CodeGeneratorResponse.getDefaultInstance() - System.out.write(emptyResponse.toByteArray()) + emptyResponse.writeTo(System.out) } private fun String.decodeBase64(): String { From 23a962241280a276cfdd22840783289a06d45c51 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 1 May 2024 15:23:54 +0100 Subject: [PATCH 37/39] Update build time --- dependencies.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dependencies.md b/dependencies.md index bb5ab46..8042095 100644 --- a/dependencies.md +++ b/dependencies.md @@ -480,7 +480,7 @@ The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Apr 30 20:09:26 WEST 2024** 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). +This report was generated on **Wed May 01 15:21:26 WEST 2024** 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). @@ -1243,7 +1243,7 @@ This report was generated on **Tue Apr 30 20:09:26 WEST 2024** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Apr 30 20:09:27 WEST 2024** 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). +This report was generated on **Wed May 01 15:21:27 WEST 2024** 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). @@ -1732,4 +1732,4 @@ This report was generated on **Tue Apr 30 20:09:27 WEST 2024** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Apr 30 20:09:27 WEST 2024** 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 **Wed May 01 15:21:27 WEST 2024** 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 From 65a0060d3d4a2b1816e6c9fb7571ed2a6138e2a0 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 1 May 2024 15:27:16 +0100 Subject: [PATCH 38/39] Rearrange API calls related to debugging --- .../test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt b/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt index 391ada7..60d900c 100644 --- a/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt +++ b/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt @@ -118,13 +118,13 @@ internal class PluginSpec { .withSharedTestKitDirectory() .replace("@PROTOTAP_PLUGIN_ID@", GRADLE_PLUGIN_ID) .replace("@PROTOTAP_VERSION@", version) - //.withLoggingLevel(LogLevel.INFO) /* Uncomment the following if you need to debug the build process. Please note that: 1) Test will run much slower. 2) Under Windows it may cause this issue to occur: https://github.com/gradle/native-platform/issues/274 After finishing the debug, please comment out this call again. */ + //.withLoggingLevel(LogLevel.INFO) //.enableRunnerDebug() .copyBuildSrc() project = builder.create() From 6fc363b9ffb9f4ef13c5279e1573f2ccc8d80042 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 1 May 2024 15:27:56 +0100 Subject: [PATCH 39/39] Rearrange API calls related to debugging --- .../test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt b/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt index 60d900c..f245323 100644 --- a/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt +++ b/gradle-plugin/src/test/kotlin/io/spine/tools/prototap/gradle/PluginSpec.kt @@ -124,8 +124,8 @@ internal class PluginSpec { 2) Under Windows it may cause this issue to occur: https://github.com/gradle/native-platform/issues/274 After finishing the debug, please comment out this call again. */ - //.withLoggingLevel(LogLevel.INFO) //.enableRunnerDebug() + //.withLoggingLevel(LogLevel.INFO) .copyBuildSrc() project = builder.create() (project.runner as DefaultGradleRunner).withJvmArguments(