From e78908f881060e166e6377556785bb7acd68b25b Mon Sep 17 00:00:00 2001 From: Osip Fatkullin Date: Tue, 15 Apr 2025 18:56:15 +0200 Subject: [PATCH 01/14] fix: Remove unused gradle-download-task dependency --- build.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index af984b1..470ac73 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -29,7 +29,6 @@ repositories { dependencies { implementation("xyz.jpenilla.run-paper:xyz.jpenilla.run-paper.gradle.plugin:2.3.1") - implementation("de.undercouch:gradle-download-task:5.6.0") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1") implementation("com.charleskorn.kaml:kaml:0.74.0") testImplementation("junit:junit:4.13.1") From f50f716fdffcba11092da7afee756b27fc99a693 Mon Sep 17 00:00:00 2001 From: Osip Fatkullin Date: Wed, 16 Apr 2025 18:11:44 +0200 Subject: [PATCH 02/14] feat: Use jpenilla/resource-factory to generate plugin.yml --- README.md | 1 - build.gradle.kts | 1 + src/main/kotlin/Bukkit.kt | 13 ++-- src/main/kotlin/BukkitExtension.kt | 29 +++++--- src/main/kotlin/BukkitGradlePlugin.kt | 48 ++++--------- src/main/kotlin/extensions/Accessors.kt | 9 +++ .../plugin/PluginConfigurationPlugin.kt | 20 +++--- src/main/kotlin/plugin/PluginYamlFeature.kt | 69 +++++++++++++++++++ src/main/kotlin/server/DevServerPlugin.kt | 6 +- .../BukkitGradlePluginSpec.groovy | 8 +-- .../bukkitgradle/PluginSpecification.groovy | 1 + 11 files changed, 136 insertions(+), 69 deletions(-) create mode 100644 src/main/kotlin/plugin/PluginYamlFeature.kt diff --git a/README.md b/README.md index 3bf736f..3baba95 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,6 @@ Gradle utilities to simplify Bukkit/Spigot plugins writing and debugging. #### Features: -- Automatically applies plugin: java - Sets up compiler encoding to UTF-8 - Sets archivesBaseName to plugin name - Supports APIs: Bukkit, CraftBukkit, Spigot, Paper diff --git a/build.gradle.kts b/build.gradle.kts index 470ac73..c775ad3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -29,6 +29,7 @@ repositories { dependencies { implementation("xyz.jpenilla.run-paper:xyz.jpenilla.run-paper.gradle.plugin:2.3.1") + implementation("xyz.jpenilla.resource-factory:xyz.jpenilla.resource-factory.gradle.plugin:1.2.0") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1") implementation("com.charleskorn.kaml:kaml:0.74.0") testImplementation("junit:junit:4.13.1") diff --git a/src/main/kotlin/Bukkit.kt b/src/main/kotlin/Bukkit.kt index 71ef34e..a071ff8 100644 --- a/src/main/kotlin/Bukkit.kt +++ b/src/main/kotlin/Bukkit.kt @@ -1,16 +1,14 @@ package ru.endlesscode.bukkitgradle import org.gradle.api.provider.Provider -import ru.endlesscode.bukkitgradle.plugin.extension.PluginConfiguration +import ru.endlesscode.bukkitgradle.plugin.plugin import ru.endlesscode.bukkitgradle.server.extension.ServerConfiguration +import xyz.jpenilla.resourcefactory.bukkit.BukkitPluginYaml public interface Bukkit { - /** Plugin plugin. */ - public val plugin: PluginConfiguration - @Deprecated("Use 'plugin' field instead", ReplaceWith("plugin")) - public val meta: PluginConfiguration + public val meta: BukkitPluginYaml get() = plugin /** Dev server configuration. */ @@ -22,5 +20,8 @@ public interface Bukkit { /** Plugin Meta generation enabled. */ @Deprecated("Use 'plugin.generatePluginYaml' instead", ReplaceWith("plugin.generatePluginYaml")) public val generateMeta: Provider - get() = plugin.generatePluginYaml + get() = generatePluginYaml + + /** Whether plugin.yml generation enabled or not. */ + public val generatePluginYaml: Provider } diff --git a/src/main/kotlin/BukkitExtension.kt b/src/main/kotlin/BukkitExtension.kt index fb33d3f..cf328d0 100644 --- a/src/main/kotlin/BukkitExtension.kt +++ b/src/main/kotlin/BukkitExtension.kt @@ -4,19 +4,25 @@ import org.gradle.api.Action import org.gradle.api.Project import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider import org.gradle.kotlin.dsl.getByType import org.gradle.kotlin.dsl.property import ru.endlesscode.bukkitgradle.extensions.finalizedOnRead -import ru.endlesscode.bukkitgradle.plugin.extension.PluginConfigurationImpl +import ru.endlesscode.bukkitgradle.plugin.plugin import ru.endlesscode.bukkitgradle.server.ServerConstants import ru.endlesscode.bukkitgradle.server.extension.ServerConfigurationImpl +import xyz.jpenilla.resourcefactory.bukkit.BukkitPluginYaml public open class BukkitExtension internal constructor( - public final override val plugin: PluginConfigurationImpl, public final override val server: ServerConfigurationImpl, objects: ObjectFactory, ) : Bukkit { + private val _generatePluginYaml: Property = objects.property() + .convention(true) + .finalizedOnRead() + override val generatePluginYaml: Provider = _generatePluginYaml + public final override val apiVersion: Property = objects.property() .convention(ServerConstants.DEFAULT_VERSION) .finalizedOnRead() @@ -25,13 +31,9 @@ public open class BukkitExtension internal constructor( body.execute(server) } - public fun plugin(body: Action) { - body.execute(plugin) - } - @Deprecated("Use 'plugin' instead", ReplaceWith("plugin(body)")) - public fun meta(body: Action) { - plugin(body) + public fun meta(body: Action) { + plugin { body.execute(this) } } /** Disables plugin.yml generation. */ @@ -40,7 +42,16 @@ public open class BukkitExtension internal constructor( ReplaceWith("plugin.disablePluginYamlGeneration()") ) public fun disableMetaGeneration() { - plugin.disablePluginYamlGeneration() + disablePluginYamlGeneration() + } + + /** Disables plugin.yaml parsing and generation. */ + public fun disablePluginYamlGeneration() { + _generatePluginYaml.set(false) + } + + public companion object { + public const val NAME: String = "bukkit" } } diff --git a/src/main/kotlin/BukkitGradlePlugin.kt b/src/main/kotlin/BukkitGradlePlugin.kt index f4b771f..39d9da3 100644 --- a/src/main/kotlin/BukkitGradlePlugin.kt +++ b/src/main/kotlin/BukkitGradlePlugin.kt @@ -2,14 +2,15 @@ package ru.endlesscode.bukkitgradle import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.plugins.JavaBasePlugin import org.gradle.api.tasks.compile.JavaCompile -import org.gradle.kotlin.dsl.* +import org.gradle.kotlin.dsl.apply +import org.gradle.kotlin.dsl.create +import org.gradle.kotlin.dsl.repositories +import org.gradle.kotlin.dsl.withType import ru.endlesscode.bukkitgradle.dependencies.Dependencies -import ru.endlesscode.bukkitgradle.plugin.PluginConfigurationPlugin -import ru.endlesscode.bukkitgradle.plugin.extension.PluginConfigurationImpl -import ru.endlesscode.bukkitgradle.plugin.util.MinecraftVersion -import ru.endlesscode.bukkitgradle.plugin.util.StringUtils +import ru.endlesscode.bukkitgradle.extensions.java +import ru.endlesscode.bukkitgradle.plugin.configurePluginYamlFeature import ru.endlesscode.bukkitgradle.plugin.util.parsedApiVersion import ru.endlesscode.bukkitgradle.plugin.util.resolveMinimalJavaVersion import ru.endlesscode.bukkitgradle.server.DevServerPlugin @@ -30,32 +31,21 @@ public class BukkitGradlePlugin : Plugin { /** Adds needed plugins. */ private fun Project.addPlugins() { - val bukkit = extensions.create("bukkit", createPluginConfiguration(), ServerConfigurationImpl()) + val bukkit = extensions.create( + BukkitExtension.NAME, + ServerConfigurationImpl(), + ) - with(plugins) { - apply("java") - apply() - apply() - } + configurePluginYamlFeature(bukkit) + apply() - extensions.configure { - toolchain { + plugins.withType { + java.toolchain { languageVersion.convention(bukkit.parsedApiVersion.map(::resolveMinimalJavaVersion)) } } } - private fun Project.createPluginConfiguration(): PluginConfigurationImpl { - return PluginConfigurationImpl(objects).apply { - name.convention(project.name) - description.convention(provider { project.description }) - main.convention(name.map { "${project.group}.${StringUtils.toPascalCase(it)}" }) - version.convention(provider { project.version.toString() }) - apiVersion.convention(provider { bukkit.parsedApiVersion.get() }.map(::resolveDefaultApiVersion)) - url.convention(provider { providers.gradleProperty("url").orNull }) - } - } - /** Sets encoding on compile to UTF-8. */ private fun Project.configureEncoding() { tasks.withType().configureEach { @@ -69,12 +59,4 @@ public class BukkitGradlePlugin : Plugin { mavenCentral() } } - - private fun resolveDefaultApiVersion(version: MinecraftVersion): String = when { - // "API version" has been introduced in Spigot 1.13 - version < MinecraftVersion.V1_13_0 -> "" - // From 1.20.5 and onward, a patch version is supported. - version < MinecraftVersion.V1_20_5 -> version.withoutPatch().toString() - else -> version.toString() - } } diff --git a/src/main/kotlin/extensions/Accessors.kt b/src/main/kotlin/extensions/Accessors.kt index e84c692..9bb9950 100644 --- a/src/main/kotlin/extensions/Accessors.kt +++ b/src/main/kotlin/extensions/Accessors.kt @@ -2,7 +2,16 @@ package ru.endlesscode.bukkitgradle.extensions import org.gradle.api.Project import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.SourceSetContainer import org.gradle.kotlin.dsl.the +import xyz.jpenilla.resourcefactory.ResourceFactoryExtension internal val Project.java: JavaPluginExtension get() = the() + +internal val Project.sourceSets: SourceSetContainer + get() = the() + +internal val SourceSet.resourceFactory: ResourceFactoryExtension + get() = the() diff --git a/src/main/kotlin/plugin/PluginConfigurationPlugin.kt b/src/main/kotlin/plugin/PluginConfigurationPlugin.kt index 2f0893d..e2a66b7 100644 --- a/src/main/kotlin/plugin/PluginConfigurationPlugin.kt +++ b/src/main/kotlin/plugin/PluginConfigurationPlugin.kt @@ -10,9 +10,7 @@ import org.gradle.api.file.DuplicatesStrategy import org.gradle.kotlin.dsl.get import org.gradle.kotlin.dsl.named import org.gradle.kotlin.dsl.register -import ru.endlesscode.bukkitgradle.bukkit import ru.endlesscode.bukkitgradle.extensions.java -import ru.endlesscode.bukkitgradle.plugin.extension.PluginConfigurationImpl import ru.endlesscode.bukkitgradle.plugin.task.MergePluginYaml import ru.endlesscode.bukkitgradle.plugin.task.ParsePluginYaml import java.io.File @@ -28,25 +26,23 @@ public class PluginConfigurationPlugin : Plugin { ) val parsePluginYaml = project.tasks.register("parsePluginYaml") { - val bukkit = project.bukkit - this.yaml = yaml - this.plugin = bukkit.plugin as PluginConfigurationImpl + //val bukkit = project.bukkit + //this.plugin = bukkit.plugin as PluginConfigurationImpl this.pluginYamlFile.set(project.findPluginYaml()) - val predicate = bukkit.plugin.generatePluginYaml - onlyIf { predicate.get() } + //val predicate = bukkit.plugin.generatePluginYaml + //onlyIf { predicate.get() } } val mergePluginYaml = project.tasks.register("mergePluginYaml") { - val bukkit = project.bukkit - this.yaml = yaml - this.plugin = bukkit.plugin + //val bukkit = project.bukkit + //this.plugin = bukkit.plugin pluginYaml.set(parsePluginYaml.map { it.pluginYaml.get() }) - val predicate = bukkit.plugin.generatePluginYaml - onlyIf { predicate.get() } + //val predicate = bukkit.plugin.generatePluginYaml + //onlyIf { predicate.get() } } project.tasks.named("processResources") { diff --git a/src/main/kotlin/plugin/PluginYamlFeature.kt b/src/main/kotlin/plugin/PluginYamlFeature.kt new file mode 100644 index 0000000..02aa95e --- /dev/null +++ b/src/main/kotlin/plugin/PluginYamlFeature.kt @@ -0,0 +1,69 @@ +package ru.endlesscode.bukkitgradle.plugin + +import org.gradle.api.Project +import org.gradle.api.plugins.ExtensionAware +import org.gradle.api.plugins.JavaBasePlugin +import org.gradle.kotlin.dsl.apply +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.the +import org.gradle.kotlin.dsl.withType +import ru.endlesscode.bukkitgradle.Bukkit +import ru.endlesscode.bukkitgradle.BukkitExtension +import ru.endlesscode.bukkitgradle.extensions.resourceFactory +import ru.endlesscode.bukkitgradle.extensions.sourceSets +import ru.endlesscode.bukkitgradle.plugin.util.MinecraftVersion +import ru.endlesscode.bukkitgradle.plugin.util.StringUtils +import ru.endlesscode.bukkitgradle.plugin.util.parsedApiVersion +import xyz.jpenilla.resourcefactory.ResourceFactoryPlugin +import xyz.jpenilla.resourcefactory.bukkit.BukkitPluginYaml +import xyz.jpenilla.resourcefactory.bukkit.bukkitPluginYaml + +private const val PLUGIN_EXTENSION_NAME: String = "plugin" + +internal fun Project.configurePluginYamlFeature(bukkit: BukkitExtension) { + project.apply() + + val bukkitPluginYaml = bukkitPluginYaml { + setConventionsFromProjectMeta(project, bukkit) + } + (bukkit as ExtensionAware).extensions.add(PLUGIN_EXTENSION_NAME, bukkitPluginYaml) + + plugins.withType { + sourceSets.named("main") { + resourceFactory.factory(bukkitPluginYaml.resourceFactory()) + } + } +} + +/** + * We use custom implementation of `setConventionsFromProjectMeta` that uses providers to defer project fields reading. + */ +private fun BukkitPluginYaml.setConventionsFromProjectMeta(project: Project, bukkit: BukkitExtension) { + name.convention(project.name) // Project name is immutable, no need to wrap it with provider + description.convention(project.provider { project.description }) + version.convention(project.provider { project.version.toString() }) + apiVersion.convention(bukkit.parsedApiVersion.map(::resolveDefaultApiVersion)) + + // Kept for backward compatibility + // TODO: This is a bit unobvious behavior, so probably we should remove defaults for these properties + main.convention(name.map { "${project.group}.${StringUtils.toPascalCase(it)}" }) + website.convention( + project.providers.gradleProperty("url") + .orElse(project.providers.gradleProperty("website")) + ) +} + +private fun resolveDefaultApiVersion(version: MinecraftVersion): String = when { + // "API version" has been introduced in Spigot 1.13 + version < MinecraftVersion.V1_13_0 -> "" + // From 1.20.5 and onward, a patch version is supported. + version < MinecraftVersion.V1_20_5 -> version.withoutPatch().toString() + else -> version.toString() +} + +public val Bukkit.plugin: BukkitPluginYaml + get() = (this as ExtensionAware).the() + +public fun Bukkit.plugin(configure: BukkitPluginYaml.() -> Unit) { + (this as ExtensionAware).configure(configure) +} diff --git a/src/main/kotlin/server/DevServerPlugin.kt b/src/main/kotlin/server/DevServerPlugin.kt index 5968d15..04d9187 100644 --- a/src/main/kotlin/server/DevServerPlugin.kt +++ b/src/main/kotlin/server/DevServerPlugin.kt @@ -3,7 +3,6 @@ package ru.endlesscode.bukkitgradle.server import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.file.Directory -import org.gradle.api.plugins.JavaPluginExtension import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.TaskProvider @@ -11,6 +10,7 @@ import org.gradle.jvm.toolchain.JavaToolchainService import org.gradle.kotlin.dsl.* import ru.endlesscode.bukkitgradle.Bukkit import ru.endlesscode.bukkitgradle.bukkit +import ru.endlesscode.bukkitgradle.extensions.java import ru.endlesscode.bukkitgradle.plugin.util.resolveMinimalJavaVersion import ru.endlesscode.bukkitgradle.server.extension.ServerConfiguration import ru.endlesscode.bukkitgradle.server.task.CreateIdeaGradleRunConfiguration @@ -54,7 +54,7 @@ public class DevServerPlugin : Plugin { } // RunPaperPlugin uses afterEvaluate under the hood, so we have to use afterEvaluate - // to set our conventions after theirs + // to set our conventions after their ones project.afterEvaluate { configureDefaultJvmForServer() } val prepareServer = registerPrepareServerTask(runServer) @@ -65,7 +65,7 @@ public class DevServerPlugin : Plugin { private fun Project.configureDefaultJvmForServer() { val toolchains = project.extensions.findByType() ?: return - val spec = the().toolchain + val spec = java.toolchain tasks.withType().configureEach { javaLauncher.convention( diff --git a/src/test/groovy/ru/endlesscode/bukkitgradle/BukkitGradlePluginSpec.groovy b/src/test/groovy/ru/endlesscode/bukkitgradle/BukkitGradlePluginSpec.groovy index 96a4336..c20944e 100644 --- a/src/test/groovy/ru/endlesscode/bukkitgradle/BukkitGradlePluginSpec.groovy +++ b/src/test/groovy/ru/endlesscode/bukkitgradle/BukkitGradlePluginSpec.groovy @@ -1,17 +1,15 @@ //file:noinspection ConfigurationAvoidance package ru.endlesscode.bukkitgradle +import org.gradle.api.plugins.JavaPlugin + class BukkitGradlePluginSpec extends PluginSpecification { def setup() { + project.apply(plugin: JavaPlugin) project.apply(plugin: BukkitGradlePlugin) } - def "when initialized - should add required plugins"() { - expect: "java plugin added" - project.pluginManager.hasPlugin("java") - } - def "when initialized - should set default JVM toolchain"(String apiVersion, int jvmVersion) { when: "apiVersion is set" project.bukkit.apiVersion = apiVersion diff --git a/src/test/groovy/ru/endlesscode/bukkitgradle/PluginSpecification.groovy b/src/test/groovy/ru/endlesscode/bukkitgradle/PluginSpecification.groovy index 17a5a15..f179458 100644 --- a/src/test/groovy/ru/endlesscode/bukkitgradle/PluginSpecification.groovy +++ b/src/test/groovy/ru/endlesscode/bukkitgradle/PluginSpecification.groovy @@ -28,6 +28,7 @@ class PluginSpecification extends Specification { //language=kotlin buildFile << ''' plugins { + id("java") id("ru.endlesscode.bukkitgradle") } From a78c741b02a4ff41813f7dd857dda2c1ce3728f9 Mon Sep 17 00:00:00 2001 From: Osip Fatkullin Date: Wed, 16 Apr 2025 22:02:43 +0200 Subject: [PATCH 03/14] feat: Read defaults from existing plugin.yml Also updated tests to reflect the changes --- src/main/kotlin/extensions/Provider.kt | 12 + .../kotlin/plugin/BukkitPluginYamlDefaults.kt | 76 ++++ .../plugin/PluginConfigurationPlugin.kt | 65 --- src/main/kotlin/plugin/PluginYaml.kt | 111 ----- src/main/kotlin/plugin/PluginYamlFeature.kt | 60 ++- .../plugin/extension/PluginConfiguration.kt | 37 -- .../extension/PluginConfigurationImpl.kt | 29 -- .../kotlin/plugin/task/MergePluginYaml.kt | 65 --- .../kotlin/plugin/task/ParsePluginYaml.kt | 101 +++-- .../bukkitgradle/PluginSpecification.groovy | 2 +- .../plugin/task/GeneratePluginYamlSpec.groovy | 405 ++++++++++++++++++ .../plugin/task/MergePluginYamlSpec.groovy | 291 ------------- 12 files changed, 614 insertions(+), 640 deletions(-) create mode 100644 src/main/kotlin/plugin/BukkitPluginYamlDefaults.kt delete mode 100644 src/main/kotlin/plugin/PluginConfigurationPlugin.kt delete mode 100644 src/main/kotlin/plugin/PluginYaml.kt delete mode 100644 src/main/kotlin/plugin/extension/PluginConfiguration.kt delete mode 100644 src/main/kotlin/plugin/extension/PluginConfigurationImpl.kt delete mode 100644 src/main/kotlin/plugin/task/MergePluginYaml.kt create mode 100644 src/test/groovy/ru/endlesscode/bukkitgradle/plugin/task/GeneratePluginYamlSpec.groovy delete mode 100644 src/test/groovy/ru/endlesscode/bukkitgradle/plugin/task/MergePluginYamlSpec.groovy diff --git a/src/main/kotlin/extensions/Provider.kt b/src/main/kotlin/extensions/Provider.kt index 1ff73d5..25f7945 100644 --- a/src/main/kotlin/extensions/Provider.kt +++ b/src/main/kotlin/extensions/Provider.kt @@ -5,7 +5,19 @@ import org.gradle.api.file.Directory import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import java.io.File +import java.util.* internal fun Property.finalizedOnRead(): Property = apply { finalizeValueOnRead() } +// See: https://github.com/gradle/gradle/issues/12388 +@Suppress("UnstableApiUsage") +internal fun Provider.mapNotNull(transform: (T) -> R?): Provider { + return map { value: T -> + val transformedValue = transform(value) + Optional.ofNullable(transformedValue) + } + .filter(Optional::isPresent) + .map(Optional::get) +} + internal fun Project.directoryProvider(file: () -> File?): Provider = layout.dir(provider(file)) diff --git a/src/main/kotlin/plugin/BukkitPluginYamlDefaults.kt b/src/main/kotlin/plugin/BukkitPluginYamlDefaults.kt new file mode 100644 index 0000000..24d9143 --- /dev/null +++ b/src/main/kotlin/plugin/BukkitPluginYamlDefaults.kt @@ -0,0 +1,76 @@ +@file:UseSerializers(PermissionDefaultSerializer::class) + +package ru.endlesscode.bukkitgradle.plugin + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.UseSerializers +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import xyz.jpenilla.resourcefactory.bukkit.BukkitPluginYaml +import xyz.jpenilla.resourcefactory.bukkit.Permission.Default as PermissionDefault + +/** + * Should be synced with [BukkitPluginYaml.Serializable]. + * We can't use the existing class as we want all fields to be nullable. + */ +@Serializable +internal data class BukkitPluginYamlDefaults( + val apiVersion: String? = null, + val name: String? = null, + val version: String? = null, + val main: String? = null, + val description: String? = null, + val load: BukkitPluginYaml.PluginLoadOrder? = null, + val author: String? = null, + val authors: List? = null, + val website: String? = null, + val depend: List? = null, + val softdepend: List? = null, + val loadbefore: List? = null, + val prefix: String? = null, + val defaultPermission: PermissionDefault? = null, + val provides: List? = null, + val libraries: List? = null, + val commands: Map? = null, + val permissions: Map? = null, + val foliaSupported: Boolean? = null, + val paperPluginLoader: String? = null, + val paperSkipLibraries: Boolean? = null, +) { + + @Serializable + internal data class Command( + val description: String? = null, + val aliases: List? = null, + val permission: String? = null, + val permissionMessage: String? = null, + val usage: String? = null, + ) + + @Serializable + internal data class Permission( + val description: String? = null, + val default: PermissionDefault? = null, + val children: Map? = null, + ) +} + +internal object PermissionDefaultSerializer : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor( + "xyz.jpenilla.resourcefactory.bukkit.Permission.Default", + PrimitiveKind.STRING, + ) + + override fun deserialize(decoder: Decoder): PermissionDefault { + val string = decoder.decodeString() + return PermissionDefault.values().first { it.serialized.equals(string, ignoreCase = true) } + } + + override fun serialize(encoder: Encoder, value: PermissionDefault) { + encoder.encodeString(value.serialized) + } +} diff --git a/src/main/kotlin/plugin/PluginConfigurationPlugin.kt b/src/main/kotlin/plugin/PluginConfigurationPlugin.kt deleted file mode 100644 index e2a66b7..0000000 --- a/src/main/kotlin/plugin/PluginConfigurationPlugin.kt +++ /dev/null @@ -1,65 +0,0 @@ -package ru.endlesscode.bukkitgradle.plugin - -import com.charleskorn.kaml.SequenceStyle -import com.charleskorn.kaml.Yaml -import com.charleskorn.kaml.YamlConfiguration -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.file.CopySpec -import org.gradle.api.file.DuplicatesStrategy -import org.gradle.kotlin.dsl.get -import org.gradle.kotlin.dsl.named -import org.gradle.kotlin.dsl.register -import ru.endlesscode.bukkitgradle.extensions.java -import ru.endlesscode.bukkitgradle.plugin.task.MergePluginYaml -import ru.endlesscode.bukkitgradle.plugin.task.ParsePluginYaml -import java.io.File - -public class PluginConfigurationPlugin : Plugin { - - override fun apply(project: Project) { - val yaml = Yaml( - configuration = YamlConfiguration( - encodeDefaults = false, - sequenceStyle = SequenceStyle.Flow - ) - ) - - val parsePluginYaml = project.tasks.register("parsePluginYaml") { - this.yaml = yaml - //val bukkit = project.bukkit - //this.plugin = bukkit.plugin as PluginConfigurationImpl - this.pluginYamlFile.set(project.findPluginYaml()) - - //val predicate = bukkit.plugin.generatePluginYaml - //onlyIf { predicate.get() } - } - - val mergePluginYaml = project.tasks.register("mergePluginYaml") { - this.yaml = yaml - //val bukkit = project.bukkit - //this.plugin = bukkit.plugin - pluginYaml.set(parsePluginYaml.map { it.pluginYaml.get() }) - - //val predicate = bukkit.plugin.generatePluginYaml - //onlyIf { predicate.get() } - } - - project.tasks.named("processResources") { - from(mergePluginYaml) - duplicatesStrategy = DuplicatesStrategy.INCLUDE - } - } - - /** Finds and returns project's plugin.yaml if it exists. */ - private fun Project.findPluginYaml(): File? { - val mainSourceSet = java.sourceSets["main"] - val resourceDir = mainSourceSet.resources.srcDirs.first() - - return File(resourceDir, FILE_NAME).takeIf { it.isFile } - } - - internal companion object { - const val FILE_NAME: String = "plugin.yml" - } -} diff --git a/src/main/kotlin/plugin/PluginYaml.kt b/src/main/kotlin/plugin/PluginYaml.kt deleted file mode 100644 index 78a0666..0000000 --- a/src/main/kotlin/plugin/PluginYaml.kt +++ /dev/null @@ -1,111 +0,0 @@ -package ru.endlesscode.bukkitgradle.plugin - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -internal data class PluginYaml( - - /** The class of the plugin that extends JavaPlugin. */ - val main: String? = null, - - /** Name of the plugin. */ - val name: String? = null, - - /** A human friendly description of the functionality the plugin provides. */ - val description: String? = null, - - /** The name to use when logging to console instead of the plugin's name. */ - val prefix: String? = null, - - /** The version of the plugin. */ - val version: String? = null, - - /** The version of the API that plugin use. */ - @SerialName("api-version") - val apiVersion: String? = null, - - /** Explicitly state when a plugin should be loaded. */ - val load: String? = null, - - /** Uniquely identifies who developed this plugin. */ - val author: String? = null, - - /** - * Allows listing multiple authors if it is a collaborative project. - * @see author - */ - val authors: List? = null, - - /** The contributors to the plugin that aren't the managing author(s). */ - val contributors: List? = null, - - /** The plugin's or author's website. */ - val website: String? = null, - - /** A list of plugins that your plugin requires to load. */ - val depend: List? = null, - - /** A list of plugins that are required for your plugin to have full functionality. */ - val softdepend: List? = null, - - /** A list of plugins that should be loaded after your plugin. */ - val loadbefore: List? = null, - - /** - * This can be used to tell the server that this plugin will provide the functionality - * of some library or other plugin (like an alias system). - * Plugins that (soft)depend on the other plugin will treat your plugin - * as if the other plugin exists when resolving dependencies or using `PluginManager#getPlugin(String)`. - */ - val provides: List? = null, - - /** A list of libraries your plugin needs which can be loaded from Maven Central. */ - val libraries: List? = null, - - /** The name of a command the plugin wishes to register, as well as an optional list of command attributes. */ - val commands: Map? = null, - - /** Permission that the plugin wishes to register. */ - val permissions: Map? = null, - - /** The default value that permissions that don't have a `default` specified will have. */ - @SerialName("default-permission") - val defaultPermission: String? = null, -) : java.io.Serializable - -@Serializable -internal data class PluginCommand( - - /** A short description of what the command does. */ - val description: String? = null, - - /** Alternate command names a user may use instead. */ - val aliases: List? = null, - - /** The most basic permission node required to use the command. */ - val permission: String? = null, - - /** - * A no-permission message which is displayed to a user if they do not have the required permission - * to use this command. - */ - @SerialName("permission-message") - val permissionMessage: String? = null, - - /** A short description of how to use this command. */ - val usage: String? = null -) : java.io.Serializable - -@Serializable -internal data class PluginPermission( - - /** A short description of what this permission allows. */ - val description: String? = null, - - /** The default value of the permission. */ - val default: String? = null, - - /** Children for the permission. */ - val children: Map? = null -) : java.io.Serializable diff --git a/src/main/kotlin/plugin/PluginYamlFeature.kt b/src/main/kotlin/plugin/PluginYamlFeature.kt index 02aa95e..715c061 100644 --- a/src/main/kotlin/plugin/PluginYamlFeature.kt +++ b/src/main/kotlin/plugin/PluginYamlFeature.kt @@ -1,24 +1,42 @@ package ru.endlesscode.bukkitgradle.plugin +import com.charleskorn.kaml.Yaml +import com.charleskorn.kaml.YamlConfiguration +import com.charleskorn.kaml.YamlNamingStrategy import org.gradle.api.Project +import org.gradle.api.file.RegularFile import org.gradle.api.plugins.ExtensionAware import org.gradle.api.plugins.JavaBasePlugin -import org.gradle.kotlin.dsl.apply -import org.gradle.kotlin.dsl.configure -import org.gradle.kotlin.dsl.the -import org.gradle.kotlin.dsl.withType +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.SourceSet +import org.gradle.kotlin.dsl.* import ru.endlesscode.bukkitgradle.Bukkit import ru.endlesscode.bukkitgradle.BukkitExtension +import ru.endlesscode.bukkitgradle.extensions.mapNotNull import ru.endlesscode.bukkitgradle.extensions.resourceFactory import ru.endlesscode.bukkitgradle.extensions.sourceSets +import ru.endlesscode.bukkitgradle.plugin.task.ParsePluginYaml import ru.endlesscode.bukkitgradle.plugin.util.MinecraftVersion import ru.endlesscode.bukkitgradle.plugin.util.StringUtils import ru.endlesscode.bukkitgradle.plugin.util.parsedApiVersion +import xyz.jpenilla.resourcefactory.ExecuteResourceFactories import xyz.jpenilla.resourcefactory.ResourceFactoryPlugin import xyz.jpenilla.resourcefactory.bukkit.BukkitPluginYaml import xyz.jpenilla.resourcefactory.bukkit.bukkitPluginYaml private const val PLUGIN_EXTENSION_NAME: String = "plugin" +private const val SOURCE_SET_NAME: String = "main" // Make it configurable, maybe? +internal const val PLUGIN_YML: String = "plugin.yml" + +private val defaultYaml by lazy { + Yaml( + configuration = YamlConfiguration( + strictMode = false, + decodeEnumCaseInsensitive = true, + yamlNamingStrategy = YamlNamingStrategy.KebabCase, + ) + ) +} internal fun Project.configurePluginYamlFeature(bukkit: BukkitExtension) { project.apply() @@ -29,10 +47,42 @@ internal fun Project.configurePluginYamlFeature(bukkit: BukkitExtension) { (bukkit as ExtensionAware).extensions.add(PLUGIN_EXTENSION_NAME, bukkitPluginYaml) plugins.withType { - sourceSets.named("main") { + val mainSourceSet = sourceSets.named(SOURCE_SET_NAME) { resourceFactory.factory(bukkitPluginYaml.resourceFactory()) } + + val parsePluginYamlProvider = tasks.register("parsePluginYaml") { + yaml.set(defaultYaml) + pluginYaml.set(bukkitPluginYaml) + pluginYamlFile.set(findPluginYaml(mainSourceSet)) + } + + tasks.named("${SOURCE_SET_NAME}ResourceFactory") { + val parsePluginYaml = parsePluginYamlProvider.get() + + // Switch to in-place generation mode if the plugin.yml exists + val pluginYamlFile = parsePluginYaml.pluginYamlFile.orNull?.asFile + if (pluginYamlFile != null) { + dependsOn(parsePluginYaml) + + outputDir.set(pluginYamlFile.parentFile) + // Deduplicate resource dirs after changing outputDir + mainSourceSet.configure { + resources.setSrcDirs(resources.srcDirs.distinct()) + } + } + } + } +} + +private fun Project.findPluginYaml(sourceSetProvider: Provider): Provider { + val fileProvider = sourceSetProvider.mapNotNull { sourceSet -> + sourceSet.resources.sourceDirectories + .map { it.resolve(PLUGIN_YML) } + .find { it.isFile } } + + return layout.file(fileProvider) } /** diff --git a/src/main/kotlin/plugin/extension/PluginConfiguration.kt b/src/main/kotlin/plugin/extension/PluginConfiguration.kt deleted file mode 100644 index 9a796ff..0000000 --- a/src/main/kotlin/plugin/extension/PluginConfiguration.kt +++ /dev/null @@ -1,37 +0,0 @@ -package ru.endlesscode.bukkitgradle.plugin.extension - -import org.gradle.api.provider.Provider -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.Optional - -public interface PluginConfiguration { - - @get:Internal - public val generatePluginYaml: Provider - - @get:Input - public val name: Provider - - @get:Optional - @get:Input - public val description: Provider - - @get:Input - public val main: Provider - - @get:Input - public val version: Provider - - @get:Optional - @get:Input - public val apiVersion: Provider - - @get:Optional - @get:Input - public val url: Provider - - @get:Optional - @get:Input - public val authors: Provider> -} diff --git a/src/main/kotlin/plugin/extension/PluginConfigurationImpl.kt b/src/main/kotlin/plugin/extension/PluginConfigurationImpl.kt deleted file mode 100644 index 3b400d6..0000000 --- a/src/main/kotlin/plugin/extension/PluginConfigurationImpl.kt +++ /dev/null @@ -1,29 +0,0 @@ -package ru.endlesscode.bukkitgradle.plugin.extension - -import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.ListProperty -import org.gradle.api.provider.Property -import org.gradle.kotlin.dsl.listProperty -import org.gradle.kotlin.dsl.property -import ru.endlesscode.bukkitgradle.extensions.finalizedOnRead - -public class PluginConfigurationImpl(objects: ObjectFactory) : PluginConfiguration { - - private val _generatePluginYaml: Property = objects.property() - .convention(true) - .finalizedOnRead() - override val generatePluginYaml: Property = _generatePluginYaml - - override val name: Property = objects.property() - override val description: Property = objects.property() - override val main: Property = objects.property() - override val version: Property = objects.property() - override val apiVersion: Property = objects.property() - override val url: Property = objects.property() - override val authors: ListProperty = objects.listProperty() - - /** Disables plugin.yaml parsing and generation. */ - public fun disablePluginYamlGeneration() { - _generatePluginYaml.set(false) - } -} diff --git a/src/main/kotlin/plugin/task/MergePluginYaml.kt b/src/main/kotlin/plugin/task/MergePluginYaml.kt deleted file mode 100644 index 9a4434f..0000000 --- a/src/main/kotlin/plugin/task/MergePluginYaml.kt +++ /dev/null @@ -1,65 +0,0 @@ -package ru.endlesscode.bukkitgradle.plugin.task - -import com.charleskorn.kaml.Yaml -import kotlinx.serialization.encodeToString -import org.gradle.api.DefaultTask -import org.gradle.api.file.ProjectLayout -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.Property -import org.gradle.api.provider.ProviderFactory -import org.gradle.api.tasks.* -import ru.endlesscode.bukkitgradle.TASKS_GROUP_BUKKIT -import ru.endlesscode.bukkitgradle.plugin.PluginConfigurationPlugin -import ru.endlesscode.bukkitgradle.plugin.PluginYaml -import ru.endlesscode.bukkitgradle.plugin.extension.PluginConfiguration -import java.io.File -import javax.inject.Inject - -/** - * Task that generates plugin.yml for bukkit plugin. - * - * @see ru.endlesscode.bukkitgradle.plugin.PluginConfigurationPlugin - */ -public abstract class MergePluginYaml @Inject internal constructor( - projectLayout: ProjectLayout, - providers: ProviderFactory -): DefaultTask() { - - @get:Internal - internal lateinit var yaml: Yaml - - @get:Nested - internal lateinit var plugin: PluginConfiguration - - @get:Input - internal abstract val pluginYaml: Property - - @get:OutputFile - public abstract val target: RegularFileProperty - - init { - group = TASKS_GROUP_BUKKIT - description = "Generate plugin.yml file" - - val defaultTargetProvider = providers.provider { File(temporaryDir, PluginConfigurationPlugin.FILE_NAME) } - target.convention(projectLayout.file(defaultTargetProvider)) - } - - /** Writes plugin to a target file */ - @TaskAction - public fun mergePluginYaml() { - val merged = pluginYaml.get().copy( - main = plugin.main.get(), - name = plugin.name.get(), - description = plugin.description.orNull, - version = plugin.version.get(), - apiVersion = plugin.apiVersion.orNull?.takeIf { it.isNotEmpty() }, - website = plugin.url.orNull, - authors = plugin.authors.get().takeIf { it.isNotEmpty() } - ) - - target.get() - .asFile - .writeText(yaml.encodeToString(merged)) - } -} diff --git a/src/main/kotlin/plugin/task/ParsePluginYaml.kt b/src/main/kotlin/plugin/task/ParsePluginYaml.kt index 81f761b..cba7c42 100644 --- a/src/main/kotlin/plugin/task/ParsePluginYaml.kt +++ b/src/main/kotlin/plugin/task/ParsePluginYaml.kt @@ -5,65 +5,94 @@ import com.charleskorn.kaml.Yaml import com.charleskorn.kaml.decodeFromStream import org.gradle.api.DefaultTask import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.Provider -import org.gradle.api.provider.ProviderFactory +import org.gradle.api.provider.Property import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.SkipWhenEmpty import org.gradle.api.tasks.TaskAction import ru.endlesscode.bukkitgradle.TASKS_GROUP_BUKKIT -import ru.endlesscode.bukkitgradle.plugin.PluginYaml -import ru.endlesscode.bukkitgradle.plugin.extension.PluginConfigurationImpl -import javax.inject.Inject +import ru.endlesscode.bukkitgradle.plugin.BukkitPluginYamlDefaults +import xyz.jpenilla.resourcefactory.bukkit.BukkitPluginYaml +import xyz.jpenilla.resourcefactory.bukkit.Permission -internal abstract class ParsePluginYaml @Inject constructor( - providers: ProviderFactory -) : DefaultTask() { +internal abstract class ParsePluginYaml : DefaultTask() { @get:Internal - lateinit var yaml: Yaml + abstract val yaml: Property @get:Internal - lateinit var plugin: PluginConfigurationImpl + abstract val pluginYaml: Property - @get:Optional + @get:SkipWhenEmpty @get:InputFile abstract val pluginYamlFile: RegularFileProperty - private var _pluginYaml: PluginYaml? = null - - @get:Internal - val pluginYaml: Provider = providers.provider { checkNotNull(_pluginYaml) } - init { group = TASKS_GROUP_BUKKIT - description = "Parse plugin.yml file if it exists" + description = "Parse plugin.yml file if it exists and add its content as defaults to generation" } @TaskAction - fun parse() { - _pluginYaml = readFromFile() + fun parseAndSetDefaults() { + val defaults = readFromFile() + if (defaults != null) pluginYaml.get().applyConventions(defaults) } - /** Reads plugin.yaml from [pluginYamlFile] and adds conventions for specified fields. */ - private fun readFromFile(): PluginYaml { - if (!pluginYamlFile.isPresent) return PluginYaml() - - val file = pluginYamlFile.get().asFile - val pluginYaml = try { - file.inputStream().use { yaml.decodeFromStream(it) } + private fun readFromFile(): BukkitPluginYamlDefaults? { + return try { + pluginYamlFile.get().asFile + .inputStream() + .use { yaml.get().decodeFromStream(it) } } catch (cause: EmptyYamlDocumentException) { - return PluginYaml() + null + } + } +} + +private fun BukkitPluginYaml.applyConventions(defaults: BukkitPluginYamlDefaults) { + defaults.main?.let(main::convention) + defaults.name?.let(name::convention) + defaults.description?.let(description::convention) + defaults.prefix?.let(prefix::convention) + defaults.version?.let(version::convention) + defaults.apiVersion?.let(apiVersion::convention) + defaults.load?.let(load::convention) + defaults.author?.let(author::convention) + defaults.authors?.let(authors::convention) + defaults.website?.let(website::convention) + defaults.depend?.let(depend::convention) + defaults.softdepend?.let(softDepend::convention) + defaults.loadbefore?.let(loadBefore::convention) + defaults.provides?.let(provides::convention) + defaults.libraries?.let(libraries::convention) + defaults.defaultPermission?.let(defaultPermission::convention) + defaults.foliaSupported?.let(foliaSupported::convention) + defaults.paperPluginLoader?.let(paperPluginLoader::convention) + defaults.paperSkipLibraries?.let(paperSkipLibraries::convention) + + if (commands.isEmpty() && defaults.commands != null) { + for ((name, command) in defaults.commands) { + commands.register(name) { applyConventions(command) } } + } - return pluginYaml.apply { - main?.let(plugin.main::convention) - name?.let(plugin.name::convention) - description?.let(plugin.description::convention) - version?.let(plugin.version::convention) - apiVersion?.let(plugin.apiVersion::convention) - website?.let(plugin.url::convention) - authors?.let(plugin.authors::convention) + if (permissions.isEmpty() && defaults.permissions != null) { + for ((name, permission) in defaults.permissions) { + permissions.register(name) { applyConventions(permission) } } } } + +private fun BukkitPluginYaml.Command.applyConventions(defaults: BukkitPluginYamlDefaults.Command) { + defaults.description?.let(description::convention) + defaults.aliases?.let(aliases::convention) + defaults.permission?.let(permission::convention) + defaults.permissionMessage?.let(permissionMessage::convention) + defaults.usage?.let(usage::convention) +} + +private fun Permission.applyConventions(defaults: BukkitPluginYamlDefaults.Permission) { + defaults.description?.let(description::convention) + defaults.default?.let(default::convention) + defaults.children?.let(children::convention) +} diff --git a/src/test/groovy/ru/endlesscode/bukkitgradle/PluginSpecification.groovy b/src/test/groovy/ru/endlesscode/bukkitgradle/PluginSpecification.groovy index f179458..a3b0c3b 100644 --- a/src/test/groovy/ru/endlesscode/bukkitgradle/PluginSpecification.groovy +++ b/src/test/groovy/ru/endlesscode/bukkitgradle/PluginSpecification.groovy @@ -83,6 +83,6 @@ class PluginSpecification extends Specification { } protected TaskOutcome taskOutcome(String task) { - return result.task(task).outcome + return result.task(task)?.outcome } } diff --git a/src/test/groovy/ru/endlesscode/bukkitgradle/plugin/task/GeneratePluginYamlSpec.groovy b/src/test/groovy/ru/endlesscode/bukkitgradle/plugin/task/GeneratePluginYamlSpec.groovy new file mode 100644 index 0000000..3882161 --- /dev/null +++ b/src/test/groovy/ru/endlesscode/bukkitgradle/plugin/task/GeneratePluginYamlSpec.groovy @@ -0,0 +1,405 @@ +package ru.endlesscode.bukkitgradle.plugin.task + + +import org.gradle.testkit.runner.TaskOutcome +import ru.endlesscode.bukkitgradle.PluginSpecification +import ru.endlesscode.bukkitgradle.plugin.PluginYamlFeatureKt +import ru.endlesscode.bukkitgradle.util.CharsetUtils + +class GeneratePluginYamlSpec extends PluginSpecification { + + private final static RESOURCE_FACTORY = ':mainResourceFactory' + private final static PARSE_PLUGIN_YAML = ':parsePluginYaml' + + private File pluginYamlFile + + def setup() { + pluginYamlFile = file("src/main/resources/$PluginYamlFeatureKt.PLUGIN_YML") + + buildFile << """ + bukkit.apiVersion = "1.16.2" + """.stripIndent() + } + + def 'when run processResources - should also run resource factory'() { + when: + run(':processResources') + + then: + taskOutcome(RESOURCE_FACTORY) == TaskOutcome.SUCCESS + + and: + taskOutcome(PARSE_PLUGIN_YAML) == TaskOutcome.SUCCESS + } + + def 'when run processResources - and plugin.yml generation disabled - should not run resource factory'() { + given: "plugin generation disabled" + buildFile << "bukkit.disablePluginYamlGeneration()" + + when: "run processResources" + run(':processResources') + + then: "task mergePluginYaml completed successfully" + taskOutcome(RESOURCE_FACTORY) == TaskOutcome.SKIPPED + + and: "task mergePluginYaml completed successfully" + taskOutcome(PARSE_PLUGIN_YAML) == TaskOutcome.SKIPPED + } + + def 'when run processResources - and plugin.yml doesnt exist - should skip parsing'() { + given: + pluginYamlFile.delete() + + when: + run(':processResources') + + then: + taskOutcome(RESOURCE_FACTORY) == TaskOutcome.SUCCESS + + and: + taskOutcome(PARSE_PLUGIN_YAML) == null + } + + def 'when run parsing - and plugin.yml doesnt exist - should show NO_SOURCE'() { + given: + pluginYamlFile.delete() + + when: + run(PARSE_PLUGIN_YAML) + + then: + taskOutcome(PARSE_PLUGIN_YAML) == TaskOutcome.NO_SOURCE + } + + def 'when generate plugin.yml - should generate default plugin.yml successfully'() { + when: + run(RESOURCE_FACTORY) + + then: "plugin file content corresponds to default config" + pluginYamlFile.text == """\ + api-version: '1.16' + name: test-plugin + version: '1.0' + main: com.example.testplugin.TestPlugin + """.stripIndent() + } + + def 'when generate plugin.yml - should set api-version'(String apiVersion, String expectedResult) { + when: "api version is $apiVersion" + buildFile << """ + bukkit.apiVersion = "$apiVersion" + """.stripIndent() + + and: + run(RESOURCE_FACTORY) + + then: "plugin file content corresponds to default config" + pluginYamlFile.text == """\ + api-version: $expectedResult + name: test-plugin + version: '1.0' + main: com.example.testplugin.TestPlugin + """.stripIndent() + + where: + apiVersion | expectedResult + "1.16.5" | "'1.16'" + "1.20.4" | "'1.20'" + "1.20.5" | "1.20.5" + "1.21.1" | "1.21.1" + } + + def 'when generate plugin.yml - and generate it again - should skip second task run'() { + when: + run(RESOURCE_FACTORY) + + and: "run generation again" + run(RESOURCE_FACTORY) + + then: + taskOutcome(RESOURCE_FACTORY) == TaskOutcome.UP_TO_DATE + } + + def 'when generate plugin.yml - and changed plugin configuration - should update plugin.yml'() { + when: + run(RESOURCE_FACTORY) + + and: "changed description" + buildFile << 'description = "Plugin description goes here"' + + and: + run(RESOURCE_FACTORY) + + then: + taskOutcome(RESOURCE_FACTORY) == TaskOutcome.SUCCESS + + and: "plugin generated with new description" + pluginYamlFile.text == """\ + api-version: '1.16' + name: test-plugin + version: '1.0' + main: com.example.testplugin.TestPlugin + description: Plugin description goes here + """.stripIndent() + } + + void 'when generate plugin.yml - and properties configured using set'() { + given: + //language=kotlin + buildFile << """ + bukkit { + plugin { + name.set("TestPlugin") + description.set("Test plugin description") + main.set("com.example.plugin.Plugin") + version.set("0.1") + apiVersion.set("1.13") + website.set("https://www.example.com/") + authors.set(listOf("OsipXD", "Contributors")) + } + } + """.stripIndent() + + when: + run(RESOURCE_FACTORY) + + then: + pluginYamlFile.text == """\ + api-version: '1.13' + name: TestPlugin + version: '0.1' + main: com.example.plugin.Plugin + description: Test plugin description + authors: + - OsipXD + - Contributors + website: https://www.example.com/ + """.stripIndent() + } + + void 'when generate plugin.yml - and properties configured using assignment'() { + given: + //language=kotlin + buildFile << """ + bukkit { + plugin { + name = "TestPlugin" + description = "Test plugin description" + main = "com.example.plugin.Plugin" + version = "0.1" + apiVersion = "1.13" + website = "https://www.example.com/" + authors = listOf("OsipXD", "Contributors") + } + } + """.stripIndent() + + when: + run(RESOURCE_FACTORY) + + then: + pluginYamlFile.text == """\ + api-version: '1.13' + name: TestPlugin + version: '0.1' + main: com.example.plugin.Plugin + description: Test plugin description + authors: + - OsipXD + - Contributors + website: https://www.example.com/ + """.stripIndent() + } + + void 'when generate plugin.yml - should parse commands and permissions'() { + given: + pluginYamlFile << """\ + depend: [Vault, ProtocolLib] + default-permission: not op + commands: + example: + description: Just a command + permissions: + example.foo: + description: My foo permission + """.stripIndent() + + when: + run(RESOURCE_FACTORY) + + then: + pluginYamlFile.text == """\ + api-version: '1.16' + name: test-plugin + version: '1.0' + main: com.example.testplugin.TestPlugin + depend: + - Vault + - ProtocolLib + default-permission: not op + commands: + example: + description: Just a command + permissions: + example.foo: + description: My foo permission + """.stripIndent() + } + + // BukkitGradle-26 + void 'when generate plugin.yml - and there are exotic chars in source - should read it correctly'() { + given: "source plugin file with exotic chars" + pluginYamlFile << """\ + commands: + 퀘스트: + description: 퀘스트 명령어 입니다. + """.stripIndent() + + and: "default charset differs from UTF-8" + CharsetUtils.setDefaultCharset('CP866') + + when: "run processResources" + run(RESOURCE_FACTORY) + CharsetUtils.setDefaultCharset('UTF-8') + + then: + pluginYamlFile.text == """\ + api-version: '1.16' + name: test-plugin + version: '1.0' + main: com.example.testplugin.TestPlugin + commands: + 퀘스트: + description: 퀘스트 명령어 입니다. + """.stripIndent() + } + + void 'when generate plugin.yml - and there are fields in source - should prefer values from source'() { + given: "plugin.yml file with some fields" + pluginYamlFile << """\ + name: SourceValue + version: 1.2 + """.stripIndent() + + when: "run processResources" + run(RESOURCE_FACTORY) + + then: "should write plugin and keep values of the source file" + pluginYamlFile.text == """\ + api-version: '1.16' + name: SourceValue + version: '1.2' + main: com.example.testplugin.SourceValue + """.stripIndent() + } + + void 'when generate plugin.yml - and there are conflicting values - should prefer values from build script'() { + given: + pluginYamlFile << """\ + name: SourceValue + version: 1.2 + commands: + source-command: + description: Should be ignored + permissions: + source.permission: + description: Should be ignored + """.stripIndent() + + and: "conflicting fields in build script" + buildFile << """ + bukkit { + plugin { + name = "BuildscriptValue" + version = "1.3" + commands { + register("buildscript-command") { + description = "A command from build script" + } + } + permissions { + register("buildscript.permission") { + description = "A permission from build script" + } + } + } + } + """.stripIndent() + + when: + run(RESOURCE_FACTORY) + + then: "should overwrite source fields" + pluginYamlFile.text == """\ + api-version: '1.16' + name: BuildscriptValue + version: '1.3' + main: com.example.testplugin.BuildscriptValue + commands: + buildscript-command: + description: A command from build script + permissions: + buildscript.permission: + description: A permission from build script + """.stripIndent() + } + + void 'when generate plugin.yml - and it is already full of values - should keep existing file'() { + given: + def content = """\ + api-version: '1.13' + name: TestPlugin + version: '0.1' + main: com.example.plugin.Plugin + description: Test plugin description + load: STARTUP + author: MainAuthor + authors: + - OsipXD + - Contributors + website: https://www.example.com/ + depend: + - Dependency1 + - Dependency2 + softdepend: + - SoftDependency1 + - SoftDependency2 + loadbefore: + - LoadBefore1 + - LoadBefore2 + prefix: TestPrefix + default-permission: op + provides: + - FeatureA + - FeatureB + libraries: + - org.example:library:1.0 + - com.test:util:2.0 + commands: + test: + description: A test command + aliases: + - t + - tst + permission: test.command + permission-message: You need permission + usage: /test + permissions: + test.command: + description: Existing command permission + default: op + children: + test.command.child: true + folia-supported: true + paper-plugin-loader: custom.plugin.Loader + paper-skip-libraries: false + """.stripIndent() + pluginYamlFile << content + + when: + run(RESOURCE_FACTORY) + + then: "the file content should remain the same" + pluginYamlFile.text == content + } +} diff --git a/src/test/groovy/ru/endlesscode/bukkitgradle/plugin/task/MergePluginYamlSpec.groovy b/src/test/groovy/ru/endlesscode/bukkitgradle/plugin/task/MergePluginYamlSpec.groovy deleted file mode 100644 index c674467..0000000 --- a/src/test/groovy/ru/endlesscode/bukkitgradle/plugin/task/MergePluginYamlSpec.groovy +++ /dev/null @@ -1,291 +0,0 @@ -package ru.endlesscode.bukkitgradle.plugin.task - - -import org.gradle.testkit.runner.TaskOutcome -import ru.endlesscode.bukkitgradle.PluginSpecification -import ru.endlesscode.bukkitgradle.plugin.PluginConfigurationPlugin -import ru.endlesscode.bukkitgradle.util.CharsetUtils - -class MergePluginYamlSpec extends PluginSpecification { - - private final static MERGE_PLUGIN_YAML = ':mergePluginYaml' - - private File sourcePluginYamlFile - private File pluginYamlFile - - def setup() { - sourcePluginYamlFile = file("src/main/resources/$PluginConfigurationPlugin.FILE_NAME") - pluginYamlFile = file("build/tmp/mergePluginYaml/$PluginConfigurationPlugin.FILE_NAME") - - buildFile << """ - bukkit.apiVersion = "1.16.2" - """.stripIndent() - } - - def 'when run processResources - should also run mergePluginYaml'() { - when: - run(':processResources') - - then: - taskOutcome(MERGE_PLUGIN_YAML) == TaskOutcome.SUCCESS - - and: - taskOutcome(":parsePluginYaml") == TaskOutcome.SUCCESS - } - - def 'when run processResources - and plugin.yaml generation disabled - should not run mergePluginYaml'() { - given: "plugin generation disabled" - buildFile << "bukkit.plugin.disablePluginYamlGeneration()" - - when: "run processResources" - run(':processResources') - - then: "task mergePluginYaml completed successfully" - taskOutcome(MERGE_PLUGIN_YAML) == TaskOutcome.SKIPPED - - and: "task mergePluginYaml completed successfully" - taskOutcome(":parsePluginYaml") == TaskOutcome.SKIPPED - } - - def 'when merge plugin.yaml - should generate default plugin.yaml successfully'() { - when: - run(MERGE_PLUGIN_YAML) - - then: "plugin file content corresponds to default config" - pluginYamlFile.text == """ - main: "com.example.testplugin.TestPlugin" - name: "test-plugin" - version: "1.0" - api-version: "1.16" - """.stripIndent().trim() - } - - def 'when merge plugin.yaml - should set api-version'(String apiVersion, String expectedResult) { - when: "api version is $apiVersion" - buildFile << """ - bukkit.apiVersion = "$apiVersion" - """.stripIndent() - - and: - run(MERGE_PLUGIN_YAML) - - then: "plugin file content corresponds to default config" - pluginYamlFile.text == """ - main: "com.example.testplugin.TestPlugin" - name: "test-plugin" - version: "1.0" - api-version: "$expectedResult" - """.stripIndent().trim() - - where: - apiVersion | expectedResult - "1.16.5" | "1.16" - "1.20.4" | "1.20" - "1.20.5" | "1.20.5" - "1.21.1" | "1.21.1" - } - - def 'when merge plugin.yaml - and generate it again - should skip second task run'() { - when: - run(MERGE_PLUGIN_YAML) - - and: "run generate again" - run(MERGE_PLUGIN_YAML) - - then: "the task is skipped due to up-to-date" - taskOutcome(MERGE_PLUGIN_YAML) == TaskOutcome.UP_TO_DATE - } - - def 'when merge plugin.yaml - and changed plugin configuration - should update plugin.yaml'() { - when: - run(MERGE_PLUGIN_YAML) - - and: "changed description" - buildFile << 'description = "Plugin can has description"' - - and: "run the task again" - run(MERGE_PLUGIN_YAML) - - then: "the task is successful" - taskOutcome(MERGE_PLUGIN_YAML) == TaskOutcome.SUCCESS - - and: "plugin generated with new description" - pluginYamlFile.text == """ - main: "com.example.testplugin.TestPlugin" - name: "test-plugin" - description: "Plugin can has description" - version: "1.0" - api-version: "1.16" - """.stripIndent().trim() - } - - void 'when merge plugin.yaml - and all properties configured - should write all lines'() { - given: "configured all plugin properties" - //language=kotlin - buildFile << """ - bukkit { - plugin { - name.set("TestPlugin") - description.set("Test plugin description") - main.set("com.example.plugin.Plugin") - version.set("0.1") - apiVersion.set("1.13") - url.set("https://www.example.com/") - authors.set(listOf("OsipXD", "Contributors")) - } - } - """.stripIndent() - - when: "run processResources" - run(MERGE_PLUGIN_YAML) - - then: "should write all lines" - pluginYamlFile.text == """ - main: "com.example.plugin.Plugin" - name: "TestPlugin" - description: "Test plugin description" - version: "0.1" - api-version: "1.13" - authors: ["OsipXD", "Contributors"] - website: "https://www.example.com/" - """.stripIndent().trim() - } - - void 'when merge plugin.yaml - and all properties configured with assignment - should write all lines'() { - given: "configured all plugin properties in old way" - //language=kotlin - buildFile << """ - bukkit { - plugin { - name = "TestPlugin" - description = "Test plugin description" - main = "com.example.plugin.Plugin" - version = "0.1" - url = "https://www.example.com/" - authors = listOf("OsipXD", "Contributors") - } - } - """.stripIndent() - - when: "run processResources" - run(MERGE_PLUGIN_YAML) - - then: "should write all lines" - pluginYamlFile.text == """ - main: "com.example.plugin.Plugin" - name: "TestPlugin" - description: "Test plugin description" - version: "0.1" - api-version: "1.16" - authors: ["OsipXD", "Contributors"] - website: "https://www.example.com/" - """.stripIndent().trim() - } - - void 'when merge plugin.yaml - and there are extra fields in source - should write all lines'() { - given: "source plugin file with extra fields" - sourcePluginYamlFile << """ - depend: [Vault, ProtocolLib] - commands: - example: - description: Just a command - permissions: - example.foo: - description: My foo permission - """.stripIndent() - - when: "run processResources" - run(MERGE_PLUGIN_YAML) - - then: "should write plugin with the extra fields" - pluginYamlFile.text == """ - main: "com.example.testplugin.TestPlugin" - name: "test-plugin" - version: "1.0" - api-version: "1.16" - depend: ["Vault", "ProtocolLib"] - commands: - "example": - description: "Just a command" - permissions: - "example.foo": - description: "My foo permission" - """.stripIndent().trim() - } - - // BukkitGradle-26 - void 'when merge plugin.yaml - and there are exotic chars in source - should read it correctly'() { - given: "source plugin file with exotic chars" - sourcePluginYamlFile << """ - commands: - 퀘스트: - description: 퀘스트 명령어 입니다. - """.stripIndent() - - and: "default charset differs from UTF-8" - CharsetUtils.setDefaultCharset('CP866') - - when: "run processResources" - run(MERGE_PLUGIN_YAML) - CharsetUtils.setDefaultCharset('UTF-8') - - then: - pluginYamlFile.text == """ - main: "com.example.testplugin.TestPlugin" - name: "test-plugin" - version: "1.0" - api-version: "1.16" - commands: - "퀘스트": - description: "퀘스트 명령어 입니다." - """.stripIndent().trim() - } - - void 'when merge plugin.yaml - and there are fields in source - should prefer values from source'() { - given: "source plugin file with extra fields" - sourcePluginYamlFile << """ - name: SourceValue - version: 1.2 - """.stripIndent() - - when: "run processResources" - run(MERGE_PLUGIN_YAML) - - then: "should write plugin and prefer source fields" - pluginYamlFile.text == """ - main: "com.example.testplugin.SourceValue" - name: "SourceValue" - version: "1.2" - api-version: "1.16" - """.stripIndent().trim() - } - - void 'when merge plugin.yaml - and there are conflicting fields in source and in build script - should prefer values from build script'() { - given: "source plugin.yaml file with extra fields" - sourcePluginYamlFile << """ - name: SourceValue - version: 1.2 - """.stripIndent() - - and: "conflicting fields in build script" - buildFile << """ - bukkit { - plugin { - name.set("BuildscriptValue") - version.set("1.3") - } - } - """.stripIndent() - - when: - run(MERGE_PLUGIN_YAML) - - then: "should overwrite source fields" - pluginYamlFile.text == """ - main: "com.example.testplugin.BuildscriptValue" - name: "BuildscriptValue" - version: "1.3" - api-version: "1.16" - """.stripIndent().trim() - } -} From 7a73eacb795f0dcc95feba5bfe68baa09a61d259 Mon Sep 17 00:00:00 2001 From: Osip Fatkullin Date: Wed, 16 Apr 2025 23:15:46 +0200 Subject: [PATCH 04/14] fix: Fix generatePluginYaml flag --- src/main/kotlin/Bukkit.kt | 2 +- src/main/kotlin/BukkitExtension.kt | 15 ++++----------- src/main/kotlin/plugin/PluginYamlFeature.kt | 6 ++++++ .../plugin/task/GeneratePluginYamlSpec.groovy | 12 ++++++------ 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/main/kotlin/Bukkit.kt b/src/main/kotlin/Bukkit.kt index a071ff8..d93a4a7 100644 --- a/src/main/kotlin/Bukkit.kt +++ b/src/main/kotlin/Bukkit.kt @@ -18,7 +18,7 @@ public interface Bukkit { public val apiVersion: Provider /** Plugin Meta generation enabled. */ - @Deprecated("Use 'plugin.generatePluginYaml' instead", ReplaceWith("plugin.generatePluginYaml")) + @Deprecated("Use 'generatePluginYaml' instead", ReplaceWith("generatePluginYaml")) public val generateMeta: Provider get() = generatePluginYaml diff --git a/src/main/kotlin/BukkitExtension.kt b/src/main/kotlin/BukkitExtension.kt index cf328d0..997d399 100644 --- a/src/main/kotlin/BukkitExtension.kt +++ b/src/main/kotlin/BukkitExtension.kt @@ -4,7 +4,6 @@ import org.gradle.api.Action import org.gradle.api.Project import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property -import org.gradle.api.provider.Provider import org.gradle.kotlin.dsl.getByType import org.gradle.kotlin.dsl.property import ru.endlesscode.bukkitgradle.extensions.finalizedOnRead @@ -18,10 +17,9 @@ public open class BukkitExtension internal constructor( objects: ObjectFactory, ) : Bukkit { - private val _generatePluginYaml: Property = objects.property() + override val generatePluginYaml: Property = objects.property() .convention(true) .finalizedOnRead() - override val generatePluginYaml: Provider = _generatePluginYaml public final override val apiVersion: Property = objects.property() .convention(ServerConstants.DEFAULT_VERSION) @@ -38,16 +36,11 @@ public open class BukkitExtension internal constructor( /** Disables plugin.yml generation. */ @Deprecated( - "Use 'plugin.disablePluginYamlGeneration()' instead", - ReplaceWith("plugin.disablePluginYamlGeneration()") + "Use 'generatePluginYaml.set(false)' instead", + ReplaceWith("generatePluginYaml.set(false)") ) public fun disableMetaGeneration() { - disablePluginYamlGeneration() - } - - /** Disables plugin.yaml parsing and generation. */ - public fun disablePluginYamlGeneration() { - _generatePluginYaml.set(false) + generatePluginYaml.set(false) } public companion object { diff --git a/src/main/kotlin/plugin/PluginYamlFeature.kt b/src/main/kotlin/plugin/PluginYamlFeature.kt index 715c061..bc3f939 100644 --- a/src/main/kotlin/plugin/PluginYamlFeature.kt +++ b/src/main/kotlin/plugin/PluginYamlFeature.kt @@ -55,9 +55,15 @@ internal fun Project.configurePluginYamlFeature(bukkit: BukkitExtension) { yaml.set(defaultYaml) pluginYaml.set(bukkitPluginYaml) pluginYamlFile.set(findPluginYaml(mainSourceSet)) + + onlyIf { bukkit.generatePluginYaml.get() } } tasks.named("${SOURCE_SET_NAME}ResourceFactory") { + val generatePluginYaml = bukkit.generatePluginYaml.get() + onlyIf("Flag generatePluginYaml is enabled") { generatePluginYaml } + if (!generatePluginYaml) return@named + val parsePluginYaml = parsePluginYamlProvider.get() // Switch to in-place generation mode if the plugin.yml exists diff --git a/src/test/groovy/ru/endlesscode/bukkitgradle/plugin/task/GeneratePluginYamlSpec.groovy b/src/test/groovy/ru/endlesscode/bukkitgradle/plugin/task/GeneratePluginYamlSpec.groovy index 3882161..7c1422b 100644 --- a/src/test/groovy/ru/endlesscode/bukkitgradle/plugin/task/GeneratePluginYamlSpec.groovy +++ b/src/test/groovy/ru/endlesscode/bukkitgradle/plugin/task/GeneratePluginYamlSpec.groovy @@ -33,17 +33,17 @@ class GeneratePluginYamlSpec extends PluginSpecification { } def 'when run processResources - and plugin.yml generation disabled - should not run resource factory'() { - given: "plugin generation disabled" - buildFile << "bukkit.disablePluginYamlGeneration()" + given: + buildFile << "bukkit.generatePluginYaml = false" - when: "run processResources" + when: run(':processResources') - then: "task mergePluginYaml completed successfully" + then: taskOutcome(RESOURCE_FACTORY) == TaskOutcome.SKIPPED - and: "task mergePluginYaml completed successfully" - taskOutcome(PARSE_PLUGIN_YAML) == TaskOutcome.SKIPPED + and: + taskOutcome(PARSE_PLUGIN_YAML) == null } def 'when run processResources - and plugin.yml doesnt exist - should skip parsing'() { From 186e0e30dde5e262a353320223399a9e76770aaf Mon Sep 17 00:00:00 2001 From: Osip Fatkullin Date: Thu, 17 Apr 2025 16:05:55 +0200 Subject: [PATCH 05/14] fix: Bring applying JavaPlugin back --- src/main/kotlin/BukkitGradlePlugin.kt | 20 ++++---- src/main/kotlin/plugin/PluginYamlFeature.kt | 47 +++++++++---------- .../BukkitGradlePluginSpec.groovy | 1 - .../bukkitgradle/PluginSpecification.groovy | 1 - 4 files changed, 31 insertions(+), 38 deletions(-) diff --git a/src/main/kotlin/BukkitGradlePlugin.kt b/src/main/kotlin/BukkitGradlePlugin.kt index 39d9da3..00edf50 100644 --- a/src/main/kotlin/BukkitGradlePlugin.kt +++ b/src/main/kotlin/BukkitGradlePlugin.kt @@ -3,11 +3,9 @@ package ru.endlesscode.bukkitgradle import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.plugins.JavaBasePlugin +import org.gradle.api.plugins.JavaPlugin import org.gradle.api.tasks.compile.JavaCompile -import org.gradle.kotlin.dsl.apply -import org.gradle.kotlin.dsl.create -import org.gradle.kotlin.dsl.repositories -import org.gradle.kotlin.dsl.withType +import org.gradle.kotlin.dsl.* import ru.endlesscode.bukkitgradle.dependencies.Dependencies import ru.endlesscode.bukkitgradle.extensions.java import ru.endlesscode.bukkitgradle.plugin.configurePluginYamlFeature @@ -29,8 +27,12 @@ public class BukkitGradlePlugin : Plugin { Dependencies.configureProject(project) } - /** Adds needed plugins. */ private fun Project.addPlugins() { + // Apply Java plugin, but only if another JVM-language plugin wasn't applied before + if (!plugins.hasPlugin(JavaBasePlugin::class)) { + plugins.apply(JavaPlugin::class) + } + val bukkit = extensions.create( BukkitExtension.NAME, ServerConfigurationImpl(), @@ -39,21 +41,17 @@ public class BukkitGradlePlugin : Plugin { configurePluginYamlFeature(bukkit) apply() - plugins.withType { - java.toolchain { - languageVersion.convention(bukkit.parsedApiVersion.map(::resolveMinimalJavaVersion)) - } + java.toolchain { + languageVersion.convention(bukkit.parsedApiVersion.map(::resolveMinimalJavaVersion)) } } - /** Sets encoding on compile to UTF-8. */ private fun Project.configureEncoding() { tasks.withType().configureEach { options.encoding = "UTF-8" } } - /** Adds needed repositories. */ private fun Project.addRepositories() { repositories { mavenCentral() diff --git a/src/main/kotlin/plugin/PluginYamlFeature.kt b/src/main/kotlin/plugin/PluginYamlFeature.kt index bc3f939..a67fabb 100644 --- a/src/main/kotlin/plugin/PluginYamlFeature.kt +++ b/src/main/kotlin/plugin/PluginYamlFeature.kt @@ -6,7 +6,6 @@ import com.charleskorn.kaml.YamlNamingStrategy import org.gradle.api.Project import org.gradle.api.file.RegularFile import org.gradle.api.plugins.ExtensionAware -import org.gradle.api.plugins.JavaBasePlugin import org.gradle.api.provider.Provider import org.gradle.api.tasks.SourceSet import org.gradle.kotlin.dsl.* @@ -46,36 +45,34 @@ internal fun Project.configurePluginYamlFeature(bukkit: BukkitExtension) { } (bukkit as ExtensionAware).extensions.add(PLUGIN_EXTENSION_NAME, bukkitPluginYaml) - plugins.withType { - val mainSourceSet = sourceSets.named(SOURCE_SET_NAME) { - resourceFactory.factory(bukkitPluginYaml.resourceFactory()) - } + val mainSourceSet = sourceSets.named(SOURCE_SET_NAME) { + resourceFactory.factory(bukkitPluginYaml.resourceFactory()) + } - val parsePluginYamlProvider = tasks.register("parsePluginYaml") { - yaml.set(defaultYaml) - pluginYaml.set(bukkitPluginYaml) - pluginYamlFile.set(findPluginYaml(mainSourceSet)) + val parsePluginYamlProvider = tasks.register("parsePluginYaml") { + yaml.set(defaultYaml) + pluginYaml.set(bukkitPluginYaml) + pluginYamlFile.set(findPluginYaml(mainSourceSet)) - onlyIf { bukkit.generatePluginYaml.get() } - } + onlyIf { bukkit.generatePluginYaml.get() } + } - tasks.named("${SOURCE_SET_NAME}ResourceFactory") { - val generatePluginYaml = bukkit.generatePluginYaml.get() - onlyIf("Flag generatePluginYaml is enabled") { generatePluginYaml } - if (!generatePluginYaml) return@named + tasks.named("${SOURCE_SET_NAME}ResourceFactory") { + val generatePluginYaml = bukkit.generatePluginYaml.get() + onlyIf("Flag generatePluginYaml is enabled") { generatePluginYaml } + if (!generatePluginYaml) return@named - val parsePluginYaml = parsePluginYamlProvider.get() + val parsePluginYaml = parsePluginYamlProvider.get() - // Switch to in-place generation mode if the plugin.yml exists - val pluginYamlFile = parsePluginYaml.pluginYamlFile.orNull?.asFile - if (pluginYamlFile != null) { - dependsOn(parsePluginYaml) + // Switch to in-place generation mode if the plugin.yml exists + val pluginYamlFile = parsePluginYaml.pluginYamlFile.orNull?.asFile + if (pluginYamlFile != null) { + dependsOn(parsePluginYaml) - outputDir.set(pluginYamlFile.parentFile) - // Deduplicate resource dirs after changing outputDir - mainSourceSet.configure { - resources.setSrcDirs(resources.srcDirs.distinct()) - } + outputDir.set(pluginYamlFile.parentFile) + // Deduplicate resource dirs after changing outputDir + mainSourceSet.configure { + resources.setSrcDirs(resources.srcDirs.distinct()) } } } diff --git a/src/test/groovy/ru/endlesscode/bukkitgradle/BukkitGradlePluginSpec.groovy b/src/test/groovy/ru/endlesscode/bukkitgradle/BukkitGradlePluginSpec.groovy index c20944e..c324f31 100644 --- a/src/test/groovy/ru/endlesscode/bukkitgradle/BukkitGradlePluginSpec.groovy +++ b/src/test/groovy/ru/endlesscode/bukkitgradle/BukkitGradlePluginSpec.groovy @@ -6,7 +6,6 @@ import org.gradle.api.plugins.JavaPlugin class BukkitGradlePluginSpec extends PluginSpecification { def setup() { - project.apply(plugin: JavaPlugin) project.apply(plugin: BukkitGradlePlugin) } diff --git a/src/test/groovy/ru/endlesscode/bukkitgradle/PluginSpecification.groovy b/src/test/groovy/ru/endlesscode/bukkitgradle/PluginSpecification.groovy index a3b0c3b..963e36e 100644 --- a/src/test/groovy/ru/endlesscode/bukkitgradle/PluginSpecification.groovy +++ b/src/test/groovy/ru/endlesscode/bukkitgradle/PluginSpecification.groovy @@ -28,7 +28,6 @@ class PluginSpecification extends Specification { //language=kotlin buildFile << ''' plugins { - id("java") id("ru.endlesscode.bukkitgradle") } From 2b37c4f5c97d0092410b1e1285a1b114380bbd6f Mon Sep 17 00:00:00 2001 From: Osip Fatkullin Date: Thu, 17 Apr 2025 16:55:36 +0200 Subject: [PATCH 06/14] fix: Better errors handling --- src/main/kotlin/plugin/BukkitPluginYamlDefaults.kt | 5 ++++- src/main/kotlin/plugin/PluginYamlFeature.kt | 5 ++++- src/main/kotlin/plugin/task/ParsePluginYaml.kt | 5 +++++ .../bukkitgradle/plugin/task/GeneratePluginYamlSpec.groovy | 7 +++++-- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/plugin/BukkitPluginYamlDefaults.kt b/src/main/kotlin/plugin/BukkitPluginYamlDefaults.kt index 24d9143..4860415 100644 --- a/src/main/kotlin/plugin/BukkitPluginYamlDefaults.kt +++ b/src/main/kotlin/plugin/BukkitPluginYamlDefaults.kt @@ -67,7 +67,10 @@ internal object PermissionDefaultSerializer : KSerializer { override fun deserialize(decoder: Decoder): PermissionDefault { val string = decoder.decodeString() - return PermissionDefault.values().first { it.serialized.equals(string, ignoreCase = true) } + return requireNotNull(PermissionDefault.values().find { it.serialized.equals(string, ignoreCase = true) }) { + val knownValues = PermissionDefault.values().joinToString { it.serialized } + "Unknown permission default '$string'. Allowed: $knownValues" + } } override fun serialize(encoder: Encoder, value: PermissionDefault) { diff --git a/src/main/kotlin/plugin/PluginYamlFeature.kt b/src/main/kotlin/plugin/PluginYamlFeature.kt index a67fabb..973e5a4 100644 --- a/src/main/kotlin/plugin/PluginYamlFeature.kt +++ b/src/main/kotlin/plugin/PluginYamlFeature.kt @@ -99,7 +99,10 @@ private fun BukkitPluginYaml.setConventionsFromProjectMeta(project: Project, buk // Kept for backward compatibility // TODO: This is a bit unobvious behavior, so probably we should remove defaults for these properties - main.convention(name.map { "${project.group}.${StringUtils.toPascalCase(it)}" }) + main.convention(name.mapNotNull { + if (project.group.toString().isEmpty()) return@mapNotNull null + "${project.group}.${StringUtils.toPascalCase(it)}" } + ) website.convention( project.providers.gradleProperty("url") .orElse(project.providers.gradleProperty("website")) diff --git a/src/main/kotlin/plugin/task/ParsePluginYaml.kt b/src/main/kotlin/plugin/task/ParsePluginYaml.kt index cba7c42..8665e52 100644 --- a/src/main/kotlin/plugin/task/ParsePluginYaml.kt +++ b/src/main/kotlin/plugin/task/ParsePluginYaml.kt @@ -4,6 +4,7 @@ import com.charleskorn.kaml.EmptyYamlDocumentException import com.charleskorn.kaml.Yaml import com.charleskorn.kaml.decodeFromStream import org.gradle.api.DefaultTask +import org.gradle.api.GradleException import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.InputFile @@ -44,7 +45,11 @@ internal abstract class ParsePluginYaml : DefaultTask() { .inputStream() .use { yaml.get().decodeFromStream(it) } } catch (cause: EmptyYamlDocumentException) { + logger.debug("plugin.yml is empty – skipping defaults setting", cause) null + } catch (cause: Exception) { + throw GradleException("Failed to parse plugin.yml: ${cause.message}.\n" + + "Ensure the file is valid YAML that matches plugin.yml schema.", cause) } } } diff --git a/src/test/groovy/ru/endlesscode/bukkitgradle/plugin/task/GeneratePluginYamlSpec.groovy b/src/test/groovy/ru/endlesscode/bukkitgradle/plugin/task/GeneratePluginYamlSpec.groovy index 7c1422b..3715a91 100644 --- a/src/test/groovy/ru/endlesscode/bukkitgradle/plugin/task/GeneratePluginYamlSpec.groovy +++ b/src/test/groovy/ru/endlesscode/bukkitgradle/plugin/task/GeneratePluginYamlSpec.groovy @@ -259,8 +259,11 @@ class GeneratePluginYamlSpec extends PluginSpecification { CharsetUtils.setDefaultCharset('CP866') when: "run processResources" - run(RESOURCE_FACTORY) - CharsetUtils.setDefaultCharset('UTF-8') + try { + run(RESOURCE_FACTORY) + } finally { + CharsetUtils.setDefaultCharset('UTF-8') + } then: pluginYamlFile.text == """\ From 13aede97b91928d753cdda1243fc821858f5341e Mon Sep 17 00:00:00 2001 From: Osip Fatkullin Date: Thu, 17 Apr 2025 17:03:02 +0200 Subject: [PATCH 07/14] fix: Address review comments --- src/main/kotlin/BukkitExtension.kt | 3 +- .../kotlin/plugin/BukkitPluginYamlDefaults.kt | 4 +-- src/main/kotlin/plugin/PluginYamlFeature.kt | 18 ++---------- .../kotlin/plugin/task/ParsePluginYaml.kt | 29 ++++++++++--------- .../plugin/task/GeneratePluginYamlSpec.groovy | 15 +++++++--- 5 files changed, 31 insertions(+), 38 deletions(-) diff --git a/src/main/kotlin/BukkitExtension.kt b/src/main/kotlin/BukkitExtension.kt index 997d399..8cf2adc 100644 --- a/src/main/kotlin/BukkitExtension.kt +++ b/src/main/kotlin/BukkitExtension.kt @@ -19,7 +19,6 @@ public open class BukkitExtension internal constructor( override val generatePluginYaml: Property = objects.property() .convention(true) - .finalizedOnRead() public final override val apiVersion: Property = objects.property() .convention(ServerConstants.DEFAULT_VERSION) @@ -31,7 +30,7 @@ public open class BukkitExtension internal constructor( @Deprecated("Use 'plugin' instead", ReplaceWith("plugin(body)")) public fun meta(body: Action) { - plugin { body.execute(this) } + body.execute(plugin) } /** Disables plugin.yml generation. */ diff --git a/src/main/kotlin/plugin/BukkitPluginYamlDefaults.kt b/src/main/kotlin/plugin/BukkitPluginYamlDefaults.kt index 4860415..4b5a6f1 100644 --- a/src/main/kotlin/plugin/BukkitPluginYamlDefaults.kt +++ b/src/main/kotlin/plugin/BukkitPluginYamlDefaults.kt @@ -35,8 +35,8 @@ internal data class BukkitPluginYamlDefaults( val defaultPermission: PermissionDefault? = null, val provides: List? = null, val libraries: List? = null, - val commands: Map? = null, - val permissions: Map? = null, + val commands: Map = emptyMap(), + val permissions: Map = emptyMap(), val foliaSupported: Boolean? = null, val paperPluginLoader: String? = null, val paperSkipLibraries: Boolean? = null, diff --git a/src/main/kotlin/plugin/PluginYamlFeature.kt b/src/main/kotlin/plugin/PluginYamlFeature.kt index 973e5a4..53d6560 100644 --- a/src/main/kotlin/plugin/PluginYamlFeature.kt +++ b/src/main/kotlin/plugin/PluginYamlFeature.kt @@ -1,8 +1,5 @@ package ru.endlesscode.bukkitgradle.plugin -import com.charleskorn.kaml.Yaml -import com.charleskorn.kaml.YamlConfiguration -import com.charleskorn.kaml.YamlNamingStrategy import org.gradle.api.Project import org.gradle.api.file.RegularFile import org.gradle.api.plugins.ExtensionAware @@ -27,16 +24,6 @@ private const val PLUGIN_EXTENSION_NAME: String = "plugin" private const val SOURCE_SET_NAME: String = "main" // Make it configurable, maybe? internal const val PLUGIN_YML: String = "plugin.yml" -private val defaultYaml by lazy { - Yaml( - configuration = YamlConfiguration( - strictMode = false, - decodeEnumCaseInsensitive = true, - yamlNamingStrategy = YamlNamingStrategy.KebabCase, - ) - ) -} - internal fun Project.configurePluginYamlFeature(bukkit: BukkitExtension) { project.apply() @@ -50,7 +37,6 @@ internal fun Project.configurePluginYamlFeature(bukkit: BukkitExtension) { } val parsePluginYamlProvider = tasks.register("parsePluginYaml") { - yaml.set(defaultYaml) pluginYaml.set(bukkitPluginYaml) pluginYamlFile.set(findPluginYaml(mainSourceSet)) @@ -65,10 +51,10 @@ internal fun Project.configurePluginYamlFeature(bukkit: BukkitExtension) { val parsePluginYaml = parsePluginYamlProvider.get() // Switch to in-place generation mode if the plugin.yml exists - val pluginYamlFile = parsePluginYaml.pluginYamlFile.orNull?.asFile - if (pluginYamlFile != null) { + if (parsePluginYaml.pluginYamlFile.isPresent) { dependsOn(parsePluginYaml) + val pluginYamlFile = parsePluginYaml.pluginYamlFile.get().asFile outputDir.set(pluginYamlFile.parentFile) // Deduplicate resource dirs after changing outputDir mainSourceSet.configure { diff --git a/src/main/kotlin/plugin/task/ParsePluginYaml.kt b/src/main/kotlin/plugin/task/ParsePluginYaml.kt index 8665e52..01828a6 100644 --- a/src/main/kotlin/plugin/task/ParsePluginYaml.kt +++ b/src/main/kotlin/plugin/task/ParsePluginYaml.kt @@ -1,8 +1,6 @@ package ru.endlesscode.bukkitgradle.plugin.task -import com.charleskorn.kaml.EmptyYamlDocumentException -import com.charleskorn.kaml.Yaml -import com.charleskorn.kaml.decodeFromStream +import com.charleskorn.kaml.* import org.gradle.api.DefaultTask import org.gradle.api.GradleException import org.gradle.api.file.RegularFileProperty @@ -18,8 +16,15 @@ import xyz.jpenilla.resourcefactory.bukkit.Permission internal abstract class ParsePluginYaml : DefaultTask() { - @get:Internal - abstract val yaml: Property + private val yaml by lazy { + Yaml( + configuration = YamlConfiguration( + strictMode = false, + decodeEnumCaseInsensitive = true, + yamlNamingStrategy = YamlNamingStrategy.KebabCase, + ) + ) + } @get:Internal abstract val pluginYaml: Property @@ -43,7 +48,7 @@ internal abstract class ParsePluginYaml : DefaultTask() { return try { pluginYamlFile.get().asFile .inputStream() - .use { yaml.get().decodeFromStream(it) } + .use { yaml.decodeFromStream(it) } } catch (cause: EmptyYamlDocumentException) { logger.debug("plugin.yml is empty – skipping defaults setting", cause) null @@ -75,16 +80,12 @@ private fun BukkitPluginYaml.applyConventions(defaults: BukkitPluginYamlDefaults defaults.paperPluginLoader?.let(paperPluginLoader::convention) defaults.paperSkipLibraries?.let(paperSkipLibraries::convention) - if (commands.isEmpty() && defaults.commands != null) { - for ((name, command) in defaults.commands) { - commands.register(name) { applyConventions(command) } - } + for ((name, command) in defaults.commands) { + commands.maybeCreate(name).applyConventions(command) } - if (permissions.isEmpty() && defaults.permissions != null) { - for ((name, permission) in defaults.permissions) { - permissions.register(name) { applyConventions(permission) } - } + for ((name, permission) in defaults.permissions) { + permissions.maybeCreate(name).applyConventions(permission) } } diff --git a/src/test/groovy/ru/endlesscode/bukkitgradle/plugin/task/GeneratePluginYamlSpec.groovy b/src/test/groovy/ru/endlesscode/bukkitgradle/plugin/task/GeneratePluginYamlSpec.groovy index 3715a91..3194331 100644 --- a/src/test/groovy/ru/endlesscode/bukkitgradle/plugin/task/GeneratePluginYamlSpec.groovy +++ b/src/test/groovy/ru/endlesscode/bukkitgradle/plugin/task/GeneratePluginYamlSpec.groovy @@ -112,12 +112,15 @@ class GeneratePluginYamlSpec extends PluginSpecification { def 'when generate plugin.yml - and generate it again - should skip second task run'() { when: run(RESOURCE_FACTORY) + def firstRunOutcome = taskOutcome(RESOURCE_FACTORY) and: "run generation again" run(RESOURCE_FACTORY) + def secondRunOutcome = taskOutcome(RESOURCE_FACTORY) then: - taskOutcome(RESOURCE_FACTORY) == TaskOutcome.UP_TO_DATE + firstRunOutcome == TaskOutcome.SUCCESS + secondRunOutcome == TaskOutcome.UP_TO_DATE } def 'when generate plugin.yml - and changed plugin configuration - should update plugin.yml'() { @@ -296,17 +299,17 @@ class GeneratePluginYamlSpec extends PluginSpecification { """.stripIndent() } - void 'when generate plugin.yml - and there are conflicting values - should prefer values from build script'() { + void 'when generate plugin.yml - and there are conflicting values'() { given: pluginYamlFile << """\ name: SourceValue version: 1.2 commands: source-command: - description: Should be ignored + description: A command from source permissions: source.permission: - description: Should be ignored + description: A permission from source """.stripIndent() and: "conflicting fields in build script" @@ -341,9 +344,13 @@ class GeneratePluginYamlSpec extends PluginSpecification { commands: buildscript-command: description: A command from build script + source-command: + description: A command from source permissions: buildscript.permission: description: A permission from build script + source.permission: + description: A permission from source """.stripIndent() } From 10f0bd7207e286ea3d1e6ec447cd7e2bfdd1c0d6 Mon Sep 17 00:00:00 2001 From: Osip Fatkullin Date: Thu, 17 Apr 2025 17:23:08 +0200 Subject: [PATCH 08/14] docs: Add changelog notes --- CHANGELOG.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2df779..7bcd4d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,10 +10,18 @@ Refactor development server implementation to use [jpenilla/run-task](https://github.com/jpenilla/run-task/) plugin and integrate run-paper for server execution, improving maintainability and compatibility with various server versions. - **Breaking change!** - Rename `bukkit.meta { ... }` to `bukkit.plugin { ... }`. - - Task `:parsePluginMetaFile` and `:mergePluginMeta` renamed to `:parsePluginYaml` and `:mergePluginYaml` respectively. - - `bukkit.disableMetaGeneration()` replaced by `bukkit.plugin.disablePluginYamlGeneration()` - - Package `.meta` renamed to `.plugin` to reflect this change + Use [jpenilla/resource-factory](https://github.com/jpenilla/resource-factory) to generate `plugin.yml`. + This change comes with some renames: + - Configuration block `bukkit.meta { ... }` -> `bukkit.plugin { ... }` + - Property `bukkit.plugin.url` -> `bukkit.plugin.website` + - Task `:parsePluginMetaFile` -> `:parsePluginYaml` + - Task `:mergePluginMeta` has been dropped. Use `:mainResourceFactory` instead + - Package `ru.endlesscode.bukkitgradle.meta` -> `ru.endlesscode.bukkitgradle.plugin` +- Change API for disabling `plugin.yml` generation: + ```diff + -bukkit.disableMetaGeneration() + +bukkit.generatePluginYaml.set(false) + ``` - Set the default [JVM toolchain](https://docs.gradle.org/current/userguide/toolchains.html) version instead of setting JVM target and source compatibility to 1.8. By default, the minimal supported JVM version compatible with the specified `bukkit.server.version` is used. From f8a5030f9ed4750b234c6637542b5a5400d6340d Mon Sep 17 00:00:00 2001 From: Osip Fatkullin Date: Thu, 17 Apr 2025 17:23:32 +0200 Subject: [PATCH 09/14] fix: Remove an extra import --- .../ru/endlesscode/bukkitgradle/BukkitGradlePluginSpec.groovy | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/groovy/ru/endlesscode/bukkitgradle/BukkitGradlePluginSpec.groovy b/src/test/groovy/ru/endlesscode/bukkitgradle/BukkitGradlePluginSpec.groovy index c324f31..7d46aa3 100644 --- a/src/test/groovy/ru/endlesscode/bukkitgradle/BukkitGradlePluginSpec.groovy +++ b/src/test/groovy/ru/endlesscode/bukkitgradle/BukkitGradlePluginSpec.groovy @@ -1,8 +1,6 @@ //file:noinspection ConfigurationAvoidance package ru.endlesscode.bukkitgradle -import org.gradle.api.plugins.JavaPlugin - class BukkitGradlePluginSpec extends PluginSpecification { def setup() { From 9553cad96a556c3235d5b3b00ce49db4816b5a44 Mon Sep 17 00:00:00 2001 From: Osip Fatkullin Date: Thu, 17 Apr 2025 17:24:02 +0200 Subject: [PATCH 10/14] fix: Finalize generatePluginYaml value before getting --- src/main/kotlin/extensions/Provider.kt | 10 +++++----- src/main/kotlin/plugin/PluginYamlFeature.kt | 5 +++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/extensions/Provider.kt b/src/main/kotlin/extensions/Provider.kt index 25f7945..5b6051b 100644 --- a/src/main/kotlin/extensions/Provider.kt +++ b/src/main/kotlin/extensions/Provider.kt @@ -1,14 +1,16 @@ package ru.endlesscode.bukkitgradle.extensions -import org.gradle.api.Project -import org.gradle.api.file.Directory import org.gradle.api.provider.Property import org.gradle.api.provider.Provider -import java.io.File import java.util.* internal fun Property.finalizedOnRead(): Property = apply { finalizeValueOnRead() } +internal fun Property.finalizeAndGet(): T { + finalizeValue() + return get() +} + // See: https://github.com/gradle/gradle/issues/12388 @Suppress("UnstableApiUsage") internal fun Provider.mapNotNull(transform: (T) -> R?): Provider { @@ -19,5 +21,3 @@ internal fun Provider.mapNotNull(transform: (T) -> R?): Pr .filter(Optional::isPresent) .map(Optional::get) } - -internal fun Project.directoryProvider(file: () -> File?): Provider = layout.dir(provider(file)) diff --git a/src/main/kotlin/plugin/PluginYamlFeature.kt b/src/main/kotlin/plugin/PluginYamlFeature.kt index 53d6560..6a2dc56 100644 --- a/src/main/kotlin/plugin/PluginYamlFeature.kt +++ b/src/main/kotlin/plugin/PluginYamlFeature.kt @@ -8,6 +8,7 @@ import org.gradle.api.tasks.SourceSet import org.gradle.kotlin.dsl.* import ru.endlesscode.bukkitgradle.Bukkit import ru.endlesscode.bukkitgradle.BukkitExtension +import ru.endlesscode.bukkitgradle.extensions.finalizeAndGet import ru.endlesscode.bukkitgradle.extensions.mapNotNull import ru.endlesscode.bukkitgradle.extensions.resourceFactory import ru.endlesscode.bukkitgradle.extensions.sourceSets @@ -40,11 +41,11 @@ internal fun Project.configurePluginYamlFeature(bukkit: BukkitExtension) { pluginYaml.set(bukkitPluginYaml) pluginYamlFile.set(findPluginYaml(mainSourceSet)) - onlyIf { bukkit.generatePluginYaml.get() } + onlyIf { bukkit.generatePluginYaml.finalizeAndGet() } } tasks.named("${SOURCE_SET_NAME}ResourceFactory") { - val generatePluginYaml = bukkit.generatePluginYaml.get() + val generatePluginYaml = bukkit.generatePluginYaml.finalizeAndGet() onlyIf("Flag generatePluginYaml is enabled") { generatePluginYaml } if (!generatePluginYaml) return@named From ab8b686b1ec0279e1522b837e80c1a8bfca15cf6 Mon Sep 17 00:00:00 2001 From: Osip Fatkullin Date: Thu, 17 Apr 2025 17:32:51 +0200 Subject: [PATCH 11/14] fix: Use PathSensitivity.NONE for pluginYamlFile --- src/main/kotlin/plugin/task/ParsePluginYaml.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/plugin/task/ParsePluginYaml.kt b/src/main/kotlin/plugin/task/ParsePluginYaml.kt index 01828a6..a61aaa9 100644 --- a/src/main/kotlin/plugin/task/ParsePluginYaml.kt +++ b/src/main/kotlin/plugin/task/ParsePluginYaml.kt @@ -5,10 +5,7 @@ import org.gradle.api.DefaultTask import org.gradle.api.GradleException import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.SkipWhenEmpty -import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.* import ru.endlesscode.bukkitgradle.TASKS_GROUP_BUKKIT import ru.endlesscode.bukkitgradle.plugin.BukkitPluginYamlDefaults import xyz.jpenilla.resourcefactory.bukkit.BukkitPluginYaml @@ -31,6 +28,7 @@ internal abstract class ParsePluginYaml : DefaultTask() { @get:SkipWhenEmpty @get:InputFile + @get:PathSensitive(PathSensitivity.NONE) abstract val pluginYamlFile: RegularFileProperty init { From c39457325c54ebbcba573e360ad4fc60ef3c8455 Mon Sep 17 00:00:00 2001 From: Osip Fatkullin Date: Thu, 17 Apr 2025 17:33:20 +0200 Subject: [PATCH 12/14] test: Rewrite content of pluginYamlFile instead of appending --- .../plugin/task/GeneratePluginYamlSpec.groovy | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/groovy/ru/endlesscode/bukkitgradle/plugin/task/GeneratePluginYamlSpec.groovy b/src/test/groovy/ru/endlesscode/bukkitgradle/plugin/task/GeneratePluginYamlSpec.groovy index 3194331..1975050 100644 --- a/src/test/groovy/ru/endlesscode/bukkitgradle/plugin/task/GeneratePluginYamlSpec.groovy +++ b/src/test/groovy/ru/endlesscode/bukkitgradle/plugin/task/GeneratePluginYamlSpec.groovy @@ -216,7 +216,7 @@ class GeneratePluginYamlSpec extends PluginSpecification { void 'when generate plugin.yml - should parse commands and permissions'() { given: - pluginYamlFile << """\ + pluginYamlFile.text = """\ depend: [Vault, ProtocolLib] default-permission: not op commands: @@ -252,7 +252,7 @@ class GeneratePluginYamlSpec extends PluginSpecification { // BukkitGradle-26 void 'when generate plugin.yml - and there are exotic chars in source - should read it correctly'() { given: "source plugin file with exotic chars" - pluginYamlFile << """\ + pluginYamlFile.text = """\ commands: 퀘스트: description: 퀘스트 명령어 입니다. @@ -282,7 +282,7 @@ class GeneratePluginYamlSpec extends PluginSpecification { void 'when generate plugin.yml - and there are fields in source - should prefer values from source'() { given: "plugin.yml file with some fields" - pluginYamlFile << """\ + pluginYamlFile.text = """\ name: SourceValue version: 1.2 """.stripIndent() @@ -301,7 +301,7 @@ class GeneratePluginYamlSpec extends PluginSpecification { void 'when generate plugin.yml - and there are conflicting values'() { given: - pluginYamlFile << """\ + pluginYamlFile.text = """\ name: SourceValue version: 1.2 commands: @@ -404,7 +404,7 @@ class GeneratePluginYamlSpec extends PluginSpecification { paper-plugin-loader: custom.plugin.Loader paper-skip-libraries: false """.stripIndent() - pluginYamlFile << content + pluginYamlFile.text = content when: run(RESOURCE_FACTORY) From 2f57e5d752838bca653735a56585966498ebcaa4 Mon Sep 17 00:00:00 2001 From: Osip Fatkullin Date: Thu, 17 Apr 2025 18:34:42 +0200 Subject: [PATCH 13/14] docs: Revise the quick start guide --- README.md | 118 ++++++++++++++++++++++++------------------------------ 1 file changed, 53 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index 3baba95..039762f 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Gradle utilities to simplify Bukkit/Spigot plugins writing and debugging. - [Installation](#installation) - - [First steps](#first-steps) + - [Quick Start](#quick-start) - [Configuration](#configuration) - [Repositories and Dependencies](#repositories-and-dependencies) - [Running Dev server](#running-dev-server) @@ -34,33 +34,22 @@ Gradle utilities to simplify Bukkit/Spigot plugins writing and debugging. ## Installation -[BukkitGradle on plugins.gradle.org](https://plugins.gradle.org/plugin/ru.endlesscode.bukkitgradle) -> **Note:** Gradle 8.0+ is required +> [!NOTE] +> BukkitGradle requires Gradle 8.0+ to run -#### With new plugins mechanism ```kotlin plugins { id("ru.endlesscode.bukkitgradle") version "0.10.1" } ``` -#### With buildscript and apply -```groovy -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath("gradle.plugin.ru.endlesscode:bukkit-gradle:0.10.1") - } -} +[BukkitGradle on plugins.gradle.org](https://plugins.gradle.org/plugin/ru.endlesscode.bukkitgradle) -apply(plugin: "ru.endlesscode.bukkitgradle") -``` +
-#### Snapshots +Using snapshots -If you want to use snapshots, you can add jitpack repository to `settings.gradle` and use version `develop-SNAPSHOT`: +To use snapshots, add jitpack repository to the `settings.gradle.kts` and specify version `develop-SNAPSHOT`: ```kotlin // settings.gradle @@ -81,88 +70,86 @@ plugins { } ``` -### First steps -Simple `build.gradle` file that use BukkitGradle: +
+ +### Quick Start + +Apply the plugin and configure project's `group`, `description` and `version`. +These values will be used to generate the `plugin.yml` file: + ```kotlin plugins { id("ru.endlesscode.bukkitgradle") version "0.10.1" } - -// Project information + group = "com.example.myplugin" -description = "My first Bukkit plugin with Gradle" +description = "My first Bukkit plugin built by Gradle" version = "0.1" -// Let's add needed API to project +bukkit { + apiVersion = "1.16.5" +} + +// Add the necessary API to the project dependencies { compileOnly(bukkitApi()) - // see section 'Dependencies' for more info + // See the 'Dependencies' section for more info } ``` -> **Note:** `compileOnly` - it's like `provided` scope in Maven. -It means that this dependency will not be included to your final jar. -It's enough! -Will be hooked the latest version of Bukkit and automatically generated `plugin.yml` with next content: +That's it! +During the plugin compilation `plugin.yml` will be generated with the following content: + ```yaml +api-version: '1.16' name: MyPlugin -description: My first Bukkit plugin with Gradle +version: '0.1' main: com.example.myplugin.MyPlugin -version: 0.1 -api-version: 1.16 +description: My first Bukkit plugin built by Gradle ``` -> **Note:** Main class built by following pattern: `.` + +> [!NOTE] +> By default, main class is built by the following pattern: `.` + +Next, you might need to [configure](#configuration) `plugin.yml` content or [run dev server](#running-dev-server). ## Configuration -You can configure attributes that will be placed to `plugin.yml`: + +The `plugin.yml` content can be configured using `bukkit.plugin { ... }` block. + ```kotlin -// Override default configurations bukkit { - // Version of API (if you will not set this property, will be used latest version at moment of BukkitGradle release) + // Version of API. By default, 1.16.5 is used apiVersion = "1.15.2" - // Attributes for plugin.yml + // Configure plugin.yml content plugin { - name.set("MyPlugin") - description.set("My amazing plugin, that doing nothing") - main.set("com.example.plugin.MyPlugin") - version.set("1.0") - url.set("http://www.example.com") // Attribute website - authors.set(["OsipXD", "Contributors"]) + name = "MyPlugin" + description = "My amazing plugin" + main = "com.example.plugin.MyPlugin" + version = "1.0" + authors = listOf("osipxd", "contributors") + depend = listOf("Vault", "Mimic") } } ``` -Will be generated `plugin.yml` file: -```yaml -name: MyPlugin -description: My amazing plugin, that doing nothing -main: com.example.plugin.MyPlugin -version: 1.0 -api-version: 1.15 -website: http://www.example.com -authors: [OsipXD, Contributors] -``` - -If you want to add unsupported by BukkitGradle attributes, like a `depend`, `commands` etc. -Create `plugin.yml` file and put custom attributes there. - ## Repositories and Dependencies -BukkitGradle provides short extension-functions to add common repositories and dependencies. -There are list of its. -Usage example: +BukkitGradle provides shortcuts to add common repositories and dependencies: + ```kotlin repositories { - spigot() // Adds spigot repo + spigot() } dependencies { - compileOnly(paperApi()) // Adds paper-api dependency + compileOnly(paperApi()) } ``` -#### Repositories: +#### Repositories + Name | Url ----------------|------------------------------------------------------------------- spigot | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ @@ -175,7 +162,8 @@ dependencies { aikar | https://repo.aikar.co/content/groups/aikar/ codemc | https://repo.codemc.org/repository/maven-public/ -#### Dependencies: +#### Dependencies + Some dependencies also add a repository needed for them. Name | Signature | Adds repository @@ -187,7 +175,7 @@ Some dependencies also add a repository needed for them. **Note:** `$apiVersion` - is `${version}-R0.1-SNAPSHOT` (where `$version` is `bukkit.version`) -If you need more extension-functions, [create issue][issue]. +If you need more extension-functions, [file an issue][issue]. ## Running Dev server From 22050c072577703a5ec0867a57a30d444b9e4768 Mon Sep 17 00:00:00 2001 From: Osip Fatkullin Date: Thu, 17 Apr 2025 18:35:13 +0200 Subject: [PATCH 14/14] docs: Add a migration guide section for 0.11+ --- README.md | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 039762f..023cb33 100644 --- a/README.md +++ b/README.md @@ -250,21 +250,29 @@ bukkit { ## Migration Guide -### Upgrade from 0.8.x +### Upgrade from 0.10.x -1. Update gradle to 6.6 or newer: - ```shell - $ ./gradlew wrapper --gradle-version 6.7.1 - ``` -1. Use syntax `.set` in `bukkit.meta` instead of `=`: +1. Update Gradle to 8.0 or newer (the latest version is recommended): +```shell +./gradlew wrapper --gradle-version 8.13 +``` + +2. Replace deprecated and removed APIs: ```diff bukkit { - meta { - - desctiption = "My plugin's description" - + description.set("My plugin's description") + - meta { + + plugin { + name = "MyPlugin" + - url = "https://example.com/" + + website = "https://example.com/" } } - ``` + ``` + +3. If you have `plugin.yml`, move it's content to `bukkit.plugin { ... }` block + +### Upgrade from 0.8.x + 1. Use `bukkit.apiVersion` instead of `bukkit.version`: ```diff bukkit { @@ -272,7 +280,7 @@ bukkit { + apiVersion = "1.16.4" } ``` -1. Use `build.server` block instead of `build.run`: +2. Use `build.server` block instead of `build.run`: ```diff bukkit { - run { @@ -281,7 +289,7 @@ bukkit { } } ``` -1. Update arguments assignment syntax: +3. Update arguments assignment syntax: ```diff bukkit { server { @@ -291,7 +299,7 @@ bukkit { } } ``` -1. Replace removed APIs: +4. Replace removed APIs: ```diff repositories { - destroystokyo() @@ -306,8 +314,8 @@ bukkit { + compileOnly(spigot()) } ``` -1. Remove `q` and `qq` functions calls in `meta { ... }` -1. Check generated plugin.yml contents after build. +5. Remove `q` and `qq` functions calls in `meta { ... }` +6. Check generated plugin.yml contents after build. If there are any problems, [create an issue][issue].