diff --git a/.idea/misc.xml b/.idea/misc.xml index 31f80aec..5358548e 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - @@ -41,4 +40,4 @@ - + \ No newline at end of file diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 227e6d26..2cd94daf 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -192,6 +192,14 @@ dependencies { ).forEach { implementation(it) } + + testImplementation(platform("org.junit:junit-bom:5.11.4")) + testImplementation("org.junit.jupiter:junit-jupiter") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") +} + +tasks.test { + useJUnitPlatform() } dependOnBuildSrcJar() diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt index 0b545b1e..344e8a13 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt @@ -46,12 +46,12 @@ object CoreJvmCompiler { /** * The version used to in the build classpath. */ - const val dogfoodingVersion = "2.0.0-SNAPSHOT.055" + const val dogfoodingVersion = "2.0.0-SNAPSHOT.058" /** * The version to be used for integration tests. */ - const val version = "2.0.0-SNAPSHOT.055" + const val version = "2.0.0-SNAPSHOT.058" /** * The ID of the Gradle plugin. diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/docs/UpdatePluginVersion.kt b/buildSrc/src/main/kotlin/io/spine/gradle/docs/UpdatePluginVersion.kt new file mode 100644 index 00000000..e384aa75 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/gradle/docs/UpdatePluginVersion.kt @@ -0,0 +1,124 @@ +/* + * Copyright 2026, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.gradle.docs + +import java.io.File +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.TaskAction + +/** + * Updates the version of a Gradle plugin in `build.gradle.kts` files. + * + * The task searches for plugin declarations in the format + * `id("plugin-id") version "version-number"` and replaces + * the version number with the one found in the version script file. + * + * @property directory + * The directory to scan recursively for `build.gradle.kts` files. + * @property version + * The version number to set for the plugin. + * @property pluginId + * The ID of the plugin whose version should be updated. + * @property kotlinVersion + * Optional. If set, updates the version of the Kotlin plugin declared with + * `kotlin("…") version "…"` syntax in the `plugins` block. + * This option works in combination with the [version] and [pluginId] properties. + */ +abstract class UpdatePluginVersion : DefaultTask() { + + @get:InputDirectory + abstract val directory: DirectoryProperty + + @get:Input + abstract val version: Property + + @get:Input + abstract val pluginId: Property + + @get:Input + @get:Optional + abstract val kotlinVersion: Property + + /** + * Updates plugin versions in build files within the path in the [directory]. + */ + @TaskAction + fun update() { + val rootDir = directory.get().asFile + + val kotlinVersionSet = kotlinVersion.isPresent + val kotlinVer = kotlinVersion.orNull + val id = pluginId.get() + val ver = version.get() + + rootDir.walkTopDown() + .filter { it.name == "build.gradle.kts" } + .forEach { file -> + if (kotlinVersionSet && kotlinVer != null) { + updateKotlinPluginVersion(file, kotlinVer) + } + updatePluginVersion(file, id, ver) + } + } + + private fun updatePluginVersion(file: File, id: String, version: String) { + val content = file.readText() + // Regex to match: id("plugin-id") version "version-number" + val regex = """id\("$id"\)\s+version\s+"([^"]+)"""".toRegex() + + if (regex.containsMatchIn(content)) { + val updatedContent = regex.replace(content) { + "id(\"$id\") version \"$version\"" + } + if (content != updatedContent) { + file.writeText(updatedContent) + logger.info("Updated version of '$id' in `${file.absolutePath}`.") + } + } + } + + private fun updateKotlinPluginVersion(file: File, kotlinVersion: String) { + val content = file.readText() + // Regex to match Kotlin plugin declarations like: kotlin("jvm") version "1.9.0" + val regex = """kotlin\("([^"]+)"\)\s+version\s+"([^"]+)"""".toRegex() + if (regex.containsMatchIn(content)) { + val updatedContent = regex.replace(content) { matchResult -> + val plugin = matchResult.groupValues[1] + "kotlin(\"$plugin\") version \"$kotlinVersion\"" + } + if (content != updatedContent) { + file.writeText(updatedContent) + logger.info("Updated Kotlin plugin version in `${file.absolutePath}`.") + } + } + } +} diff --git a/buildSrc/src/test/kotlin/io/spine/gradle/docs/UpdatePluginVersionTest.kt b/buildSrc/src/test/kotlin/io/spine/gradle/docs/UpdatePluginVersionTest.kt new file mode 100644 index 00000000..43bad6d9 --- /dev/null +++ b/buildSrc/src/test/kotlin/io/spine/gradle/docs/UpdatePluginVersionTest.kt @@ -0,0 +1,117 @@ +/* + * Copyright 2026, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.gradle.docs + +import java.io.File +import org.gradle.testfixtures.ProjectBuilder +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir + +class UpdatePluginVersionTest { + + @TempDir + lateinit var tempDir: File + + private lateinit var buildFile: File + + @BeforeEach + fun setUp() { + val subDir = File(tempDir, "subproject") + subDir.mkdir() + buildFile = File(subDir, "build.gradle.kts") + buildFile.writeText(""" + plugins { + id("io.spine.validation") version "1.0.0" + id("other-plugin") version "0.1.0" + } + """.trimIndent()) + } + + @Test + fun `update plugin version in build file`() { + val project = ProjectBuilder.builder().build() + val task = project.tasks.register("updatePluginVersion", UpdatePluginVersion::class.java) { + directory.set(tempDir) + version.set("2.0.0-TEST") + pluginId.set("io.spine.validation") + } + task.get().update() + + val updatedContent = buildFile.readText() + assertTrue(updatedContent.contains("""id("io.spine.validation") version "2.0.0-TEST"""")) + assertTrue(updatedContent.contains("""id("other-plugin") version "0.1.0"""")) + } + + @Test + fun `update 'kotlin' plugin version when 'kotlinVersion' is set`() { + // Overwrite with a file that uses kotlin("jvm") syntax + buildFile.writeText( + """ + plugins { + kotlin("jvm") version "1.9.10" + id("io.spine.validation") version "1.0.0" + } + """.trimIndent() + ) + + val project = ProjectBuilder.builder().build() + val task = project.tasks.register("updatePluginVersion", UpdatePluginVersion::class.java) { + directory.set(tempDir) + version.set("2.0.0-TEST") + pluginId.set("io.spine.validation") + kotlinVersion.set("2.2.21") + } + task.get().update() + + val updatedContent = buildFile.readText() + assertTrue(updatedContent.contains("""kotlin("jvm") version "2.2.21""")) + assertTrue(updatedContent.contains("""id("io.spine.validation") version "2.0.0-TEST""")) + } + + @Test + fun `handle multiple spaces between id and version`() { + buildFile.writeText(""" + plugins { + id("io.spine.validation") version "1.0.0" + } + """.trimIndent()) + + val project = ProjectBuilder.builder().build() + val task = project.tasks.register("updatePluginVersion", UpdatePluginVersion::class.java) { + directory.set(tempDir) + version.set("2.0.0-TEST") + pluginId.set("io.spine.validation") + } + + task.get().update() + + val updatedContent = buildFile.readText() + assertTrue(updatedContent.contains("""id("io.spine.validation") version "2.0.0-TEST"""")) + } +}