From 4382aae51e8c17605270c80c1c11c5aaea2d3db9 Mon Sep 17 00:00:00 2001 From: Giovanni Ciatto Date: Sun, 2 Apr 2023 11:54:23 +0200 Subject: [PATCH] feat!: add support for selecting the Dokka type to be included in the publication (#567) * feat!: remove class MavenRepositoryDescriptor * feat: support selection of which dokkaTask should be used for generating javadoc artifacts to be published * fix: factorize Dokka-related stuff management * chore(test): test docStyle property * feat(doc): explain property in README.md * docs: improve description of docStyle in the readme --- README.md | 10 +++++ .../gradle/mavencentral/Configuration.kt | 2 +- .../gradle/mavencentral/DocStyle.kt | 8 ++++ .../gradle/mavencentral/DokkaHelper.kt | 36 ++++++++++++++++++ .../mavencentral/MavenRepositoryDescriptor.kt | 38 ------------------- .../gradle/mavencentral/PublishOnCentral.kt | 12 +++--- .../mavencentral/PublishOnCentralExtension.kt | 27 +++++++------ .../gradle/mavencentral/Repository.kt | 16 +++++++- .../test/ktmultiplatform/build.gradle.kts | 2 + .../gradle/test/test0/build.gradle.kts | 2 + 10 files changed, 91 insertions(+), 62 deletions(-) create mode 100644 src/main/kotlin/org/danilopianini/gradle/mavencentral/DocStyle.kt create mode 100644 src/main/kotlin/org/danilopianini/gradle/mavencentral/DokkaHelper.kt delete mode 100644 src/main/kotlin/org/danilopianini/gradle/mavencentral/MavenRepositoryDescriptor.kt diff --git a/README.md b/README.md index a30f07ef..fbf5691b 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,15 @@ publishOnCentral { repoOwner.set("DanySK") // Used to populate the default value for projectUrl and scmConnection projectUrl.set("https://github.com/${repoOwner}/${project.name}") scmConnection.set("scm:git:https://github.com/${repoOwner}/${project.name}") + /* + * If the project is a Kotlin multiplatform project, Dokka can not generate the Javadocs correctly. + * In these cases, the plugin by default may fail. + * This option can be used to select a different documentation format for the Dokka engine, + * in case you want to publish on Central a jar with a reasonable (altough not Javadoc-compatible content) + * we recommend DocStyle.HTML. + */ + docStyle.set(org.danilopianini.gradle.mavencentral.DocStyle.JAVADOC) // alternatives are GFM, HTML, and JEKYLL + /* * The plugin is pre-configured to fetch credentials for Maven Central from the context in the following order: * 1. Environment variables MAVEN_CENTRAL_USERNAME and MAVEN_CENTRAL_PASSWORD @@ -116,6 +125,7 @@ publishOnCentral { nexusUrl = "https://some/valid/nexus/instance" // nexusTimeOut and nexusConnectionTimeOut can be configured, too. } + /* * A simplified handler is available for publishing on the Snapshots repository of Maven Central */ diff --git a/src/main/kotlin/org/danilopianini/gradle/mavencentral/Configuration.kt b/src/main/kotlin/org/danilopianini/gradle/mavencentral/Configuration.kt index f4b683d6..0fe9ac52 100644 --- a/src/main/kotlin/org/danilopianini/gradle/mavencentral/Configuration.kt +++ b/src/main/kotlin/org/danilopianini/gradle/mavencentral/Configuration.kt @@ -75,7 +75,7 @@ fun Project.configureRepository(repoToConfigure: Repository) { publishing.repositories { repository -> repository.maven { mavenArtifactRepository -> mavenArtifactRepository.name = repoToConfigure.name - mavenArtifactRepository.url = URI(repoToConfigure.url) + mavenArtifactRepository.url = repoToConfigure.url.map { URI(it) }.get() mavenArtifactRepository.credentials { credentials -> credentials.username = repoToConfigure.user.orNull credentials.password = repoToConfigure.password.orNull diff --git a/src/main/kotlin/org/danilopianini/gradle/mavencentral/DocStyle.kt b/src/main/kotlin/org/danilopianini/gradle/mavencentral/DocStyle.kt new file mode 100644 index 00000000..59e6c86f --- /dev/null +++ b/src/main/kotlin/org/danilopianini/gradle/mavencentral/DocStyle.kt @@ -0,0 +1,8 @@ +package org.danilopianini.gradle.mavencentral + +/** + * Admissible type of styles used by Dokka to generate documentation from KDoc. + */ +enum class DocStyle { + GFM, HTML, JAVADOC, JEKYLL +} diff --git a/src/main/kotlin/org/danilopianini/gradle/mavencentral/DokkaHelper.kt b/src/main/kotlin/org/danilopianini/gradle/mavencentral/DokkaHelper.kt new file mode 100644 index 00000000..b39052a2 --- /dev/null +++ b/src/main/kotlin/org/danilopianini/gradle/mavencentral/DokkaHelper.kt @@ -0,0 +1,36 @@ +package org.danilopianini.gradle.mavencentral + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.provider.Property +import org.gradle.api.tasks.TaskCollection + +/** + * Looks for the class `DokkaPlugin`, which will only be available if the plugin user is applying the Dokka plugin. + */ +internal val dokkaPluginClass: Result>> = runCatching { + @Suppress("UNCHECKED_CAST") + Class.forName("org.jetbrains.dokka.gradle.DokkaPlugin") as Class> +} + +/** + * Looks for the class `DokkaTask`, which will only be available if the plugin user is applying the Dokka plugin. + */ +internal val dokkaTaskClass: Result> = dokkaPluginClass.mapCatching { + @Suppress("UNCHECKED_CAST") + Class.forName("org.jetbrains.dokka.gradle.DokkaTask") as Class +} + +/** + * Selects the available Dokka tasks supporting the generation of [docStyle]-style documentation. + * There may be no such tasks, if the plugin user did not apply the Dokka plugin. + */ +internal fun Project.dokkaTasksFor(docStyle: Property): TaskCollection = + dokkaTaskClass + .map { dokkaTaskType -> + tasks.withType(dokkaTaskType).matching { + it.name.startsWith("dokka") && it.name.endsWith(docStyle.get().name, ignoreCase = true) + } + } + .getOrElse { tasks.matching { false } } diff --git a/src/main/kotlin/org/danilopianini/gradle/mavencentral/MavenRepositoryDescriptor.kt b/src/main/kotlin/org/danilopianini/gradle/mavencentral/MavenRepositoryDescriptor.kt deleted file mode 100644 index 2fad5c2b..00000000 --- a/src/main/kotlin/org/danilopianini/gradle/mavencentral/MavenRepositoryDescriptor.kt +++ /dev/null @@ -1,38 +0,0 @@ -import org.gradle.api.Project -import org.gradle.api.provider.Property -import org.gradle.kotlin.dsl.property -import java.time.Duration - -/** - * A descriptor of a Maven repository. - * Requires a [name], and optionally authentication in form of [user] and [password]. - */ -class MavenRepositoryDescriptor internal constructor( - project: Project, - var name: String, -) { - /** - * The username. - */ - val user: Property = project.objects.property() - - /** - * The password. - */ - val password: Property = project.objects.property() - - /** - * The Nexus URL, if installed. - */ - var nexusUrl: String? = null - - /** - * The Nexus timeout. - */ - var nexusTimeOut: Duration = Duration.ofMinutes(1) - - /** - * The Nexus connection timeout. - */ - var nexusConnectionTimeout: Duration = nexusTimeOut -} diff --git a/src/main/kotlin/org/danilopianini/gradle/mavencentral/PublishOnCentral.kt b/src/main/kotlin/org/danilopianini/gradle/mavencentral/PublishOnCentral.kt index bf47c7f6..2a0ea8a3 100644 --- a/src/main/kotlin/org/danilopianini/gradle/mavencentral/PublishOnCentral.kt +++ b/src/main/kotlin/org/danilopianini/gradle/mavencentral/PublishOnCentral.kt @@ -82,20 +82,18 @@ class PublishOnCentral : Plugin { } project.tasks.withType(SourceJar::class.java).configureEach { it.sourceSet("main", true) } } - val dokkaPluginClass = runCatching { Class.forName("org.jetbrains.dokka.gradle.DokkaPlugin") } if (dokkaPluginClass.isSuccess) { - @Suppress("UNCHECKED_CAST") - project.plugins.withType(dokkaPluginClass.getOrThrow() as Class>).configureEach { + project.plugins.withType(dokkaPluginClass.getOrThrow()).configureEach { project.tasks.withType(JavadocJar::class.java).configureEach { javadocJar -> - val dokkaJavadoc = checkNotNull(project.tasks.findByName("dokkaJavadoc")) { - "Dokka plugin applied but no dokkaJavadoc task existing!" + val dokkaTask = checkNotNull(project.dokkaTasksFor(extension.docStyle).firstOrNull()) { + "Dokka plugin applied but no task exists for style ${extension.docStyle.get()}!" } - val outputDirectory = dokkaJavadoc.property("outputDirectory") + val outputDirectory = dokkaTask.property("outputDirectory") ?: throw IllegalStateException( "dokkaJavadoc has no property 'outputDirectory' - " + "maybe this version is incompatible with publish-on-central?" ) - javadocJar.dependsOn(dokkaJavadoc) + javadocJar.dependsOn(dokkaTask) javadocJar.from(outputDirectory) } } diff --git a/src/main/kotlin/org/danilopianini/gradle/mavencentral/PublishOnCentralExtension.kt b/src/main/kotlin/org/danilopianini/gradle/mavencentral/PublishOnCentralExtension.kt index 454c0b8b..84913f82 100644 --- a/src/main/kotlin/org/danilopianini/gradle/mavencentral/PublishOnCentralExtension.kt +++ b/src/main/kotlin/org/danilopianini/gradle/mavencentral/PublishOnCentralExtension.kt @@ -1,6 +1,5 @@ package org.danilopianini.gradle.mavencentral -import MavenRepositoryDescriptor import org.gradle.api.Project import org.gradle.api.provider.Property import org.gradle.kotlin.dsl.property @@ -24,7 +23,7 @@ open class PublishOnCentralExtension(val project: Project) { */ val mavenCentral: Repository = Repository( Repository.mavenCentralName, - url = Repository.mavenCentralURL, + url = project.propertyWithDefaultProvider { Repository.mavenCentralURL }, user = project.propertyWithDefaultProvider { System.getenv("MAVEN_CENTRAL_USERNAME") ?: project.properties["mavenCentralUsername"]?.toString() @@ -87,24 +86,24 @@ open class PublishOnCentralExtension(val project: Project) { repoOwner.map { "https://github.com/$it/${project.name}" } ) + /** + * The style of `javadoc` artifacts being published on Maven repositories. + */ + val docStyle: Property = project.objects.property().convention( + DocStyle.JAVADOC + ) + /** * Utility to configure a new Maven repository as target. */ @JvmOverloads fun repository( url: String, name: String = repositoryNameFromURL(url), - configurator: MavenRepositoryDescriptor.() -> Unit = { } + configurator: Repository.() -> Unit = { } ) { - val repoDescriptor = MavenRepositoryDescriptor(project, name).apply(configurator) - val repo = Repository( - repoDescriptor.name, - url, - repoDescriptor.user, - repoDescriptor.password, - repoDescriptor.nexusUrl, - repoDescriptor.nexusTimeOut, - repoDescriptor.nexusConnectionTimeout, - ) + val repo = Repository.fromProject(project, name) + repo.url.set(url) + repo.apply(configurator) project.afterEvaluate { it.configureRepository(repo) } } @@ -113,7 +112,7 @@ open class PublishOnCentralExtension(val project: Project) { */ @JvmOverloads fun mavenCentralSnapshotsRepository( name: String = "MavenCentralSnapshots", - configurator: MavenRepositoryDescriptor.() -> Unit = { }, + configurator: Repository.() -> Unit = { }, ) = repository(url = "https://s01.oss.sonatype.org/content/repositories/snapshots/", name = name) { user.set(mavenCentral.user) password.set(mavenCentral.password) diff --git a/src/main/kotlin/org/danilopianini/gradle/mavencentral/Repository.kt b/src/main/kotlin/org/danilopianini/gradle/mavencentral/Repository.kt index 3394ceec..112560f8 100644 --- a/src/main/kotlin/org/danilopianini/gradle/mavencentral/Repository.kt +++ b/src/main/kotlin/org/danilopianini/gradle/mavencentral/Repository.kt @@ -2,6 +2,7 @@ package org.danilopianini.gradle.mavencentral import org.gradle.api.Project import org.gradle.api.provider.Property +import org.gradle.kotlin.dsl.property import java.time.Duration /** @@ -12,8 +13,8 @@ import java.time.Duration * Time outs can be set with [nexusTimeOut] and [nexusConnectTimeOut]. */ data class Repository( - val name: String, - val url: String, + var name: String, + val url: Property, val user: Property, val password: Property, val nexusUrl: String? = null, @@ -44,5 +45,16 @@ data class Repository( * The Sonatype Nexus instance URL of Maven Central. */ const val mavenCentralNexusUrl = "https://s01.oss.sonatype.org/service/local/" + + /** + * Creates a named [Repository] from a [project] and a [name]. + */ + fun fromProject(project: Project, name: String): Repository = + Repository( + name = name, + url = project.objects.property(), + user = project.objects.property(), + password = project.objects.property() + ) } } diff --git a/src/test/resources/org/danilopianini/gradle/test/ktmultiplatform/build.gradle.kts b/src/test/resources/org/danilopianini/gradle/test/ktmultiplatform/build.gradle.kts index fcc0ff34..f0c09382 100644 --- a/src/test/resources/org/danilopianini/gradle/test/ktmultiplatform/build.gradle.kts +++ b/src/test/resources/org/danilopianini/gradle/test/ktmultiplatform/build.gradle.kts @@ -1,5 +1,6 @@ @file:Suppress("UnstableApiUsage") +import org.danilopianini.gradle.mavencentral.DocStyle import org.danilopianini.gradle.mavencentral.JavadocJar import org.jetbrains.kotlin.gradle.plugin.KotlinTarget import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly @@ -122,6 +123,7 @@ signing { } publishOnCentral { + docStyle.set(DocStyle.HTML) projectLongName.set("Template for Kotlin Multiplatform Project") projectDescription.set("A template repository for Kotlin Multiplatform projects") repository("https://maven.pkg.github.com/danysk/${rootProject.name}".toLowerCase()) { diff --git a/src/test/resources/org/danilopianini/gradle/test/test0/build.gradle.kts b/src/test/resources/org/danilopianini/gradle/test/test0/build.gradle.kts index 1d3a1fa7..7055e9cb 100644 --- a/src/test/resources/org/danilopianini/gradle/test/test0/build.gradle.kts +++ b/src/test/resources/org/danilopianini/gradle/test/test0/build.gradle.kts @@ -1,3 +1,4 @@ +import org.danilopianini.gradle.mavencentral.DocStyle plugins { `java-library` `java-gradle-plugin` @@ -6,6 +7,7 @@ plugins { group = "io.github.danysk" version = "0.1.0" publishOnCentral { + docStyle.set(DocStyle.JAVADOC) repoOwner.set("test") projectDescription.set("test") repository("https://maven.pkg.github.com/OWNER/REPOSITORY") {