From dba0cb40daa8c9285ff918481649045f29d114fe Mon Sep 17 00:00:00 2001 From: nathan Date: Mon, 8 Jun 2020 22:20:22 +0200 Subject: [PATCH] Added retry + delay settings for publication, added GradleUtils plugin support for configuration --- build.gradle.kts | 10 +- .../PublishAndReleaseProjectTask.kt | 4 - src/dorkbox/gradlePublish/PublishPlugin.kt | 130 ++++++++++++++++-- .../gradlePublish/PublishToSonatype.kt | 24 +++- 4 files changed, 147 insertions(+), 21 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 028c1c6..57a9511 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -19,8 +19,6 @@ import java.time.Instant gradle.startParameter.showStacktrace = ShowStacktrace.ALWAYS_FULL // always show the stacktrace! gradle.startParameter.warningMode = WarningMode.All -println("Gradle ${project.gradle.gradleVersion}") - plugins { java `java-gradle-plugin` @@ -29,7 +27,7 @@ plugins { id("com.dorkbox.Licensing") version "1.3" id("com.dorkbox.VersionUpdate") version "1.6.1" - id("com.dorkbox.GradleUtils") version "1.6" + id("com.dorkbox.GradleUtils") version "1.8" kotlin("jvm") version "1.3.72" } @@ -38,7 +36,7 @@ object Extras { // set for the project const val description = "Gradle Plugin to publish projects to the sonatype repository" const val group = "com.dorkbox" - const val version = "1.1" + const val version = "1.2" // set as project.ext const val name = "Gradle Publish" @@ -56,9 +54,7 @@ object Extras { ///// assign 'Extras' /////////////////////////////// GradleUtils.load("$projectDir/../../gradle.properties", Extras) -description = Extras.description -group = Extras.group -version = Extras.version +GradleUtils.fixIntellijPaths() licensing { diff --git a/src/dorkbox/gradlePublish/PublishAndReleaseProjectTask.kt b/src/dorkbox/gradlePublish/PublishAndReleaseProjectTask.kt index 474684c..6f3791e 100644 --- a/src/dorkbox/gradlePublish/PublishAndReleaseProjectTask.kt +++ b/src/dorkbox/gradlePublish/PublishAndReleaseProjectTask.kt @@ -31,8 +31,4 @@ PublishAndReleaseProjectTask : DefaultTask() { @TaskAction fun run() { } - - override fun getRequiredServices(): MutableSet>> { - TODO("Not yet implemented") - } } diff --git a/src/dorkbox/gradlePublish/PublishPlugin.kt b/src/dorkbox/gradlePublish/PublishPlugin.kt index b7cc9ce..7a7dcfe 100644 --- a/src/dorkbox/gradlePublish/PublishPlugin.kt +++ b/src/dorkbox/gradlePublish/PublishPlugin.kt @@ -18,7 +18,9 @@ package dorkbox.gradlePublish import de.marcphilipp.gradle.nexus.NexusPublishExtension import de.marcphilipp.gradle.nexus.NexusRepository import de.marcphilipp.gradle.nexus.NexusRepositoryContainer +import io.codearte.gradle.nexus.CloseRepositoryTask import io.codearte.gradle.nexus.NexusStagingExtension +import io.codearte.gradle.nexus.ReleaseRepositoryTask import org.gradle.api.Action import org.gradle.api.DomainObjectCollection import org.gradle.api.Plugin @@ -27,9 +29,13 @@ import org.gradle.api.publish.PublishingExtension import org.gradle.api.publish.maven.MavenPublication import org.gradle.api.publish.maven.tasks.PublishToMavenLocal import org.gradle.api.publish.maven.tasks.PublishToMavenRepository -import org.gradle.api.tasks.SourceSet import org.gradle.jvm.tasks.Jar +import org.gradle.plugins.signing.SigningExtension +import org.gradle.plugins.signing.signatory.internal.pgp.InMemoryPgpSignatoryProvider +import java.io.File +import java.time.Duration import java.util.* +import java.util.function.BiFunction /** @@ -45,7 +51,6 @@ class PublishPlugin : Plugin { } } - private lateinit var project: Project override fun apply(project: Project) { @@ -65,10 +70,13 @@ class PublishPlugin : Plugin { nexusPublishing { packageGroup.set(project.provider {config.groupId }) + clientTimeout.set(project.provider { config.httpTimeout }) + connectTimeout.set(project.provider { config.httpTimeout }) + repositories(Action { it.sonatype(Action { repo -> - repo.username.set(project.provider { config.sonatype.userName }) - repo.password.set(project.provider { config.sonatype.password }) + assignFromProp("sonatypeUserName", config.sonatype.userName) { repo.username.set(project.provider { it }) } + assignFromProp("sonatypePassword", config.sonatype.password) { repo.password.set(project.provider { it }) } }) }) } @@ -83,6 +91,33 @@ class PublishPlugin : Plugin { pom.organization { } pom.issueManagement { + val sign = project.extensions.getByName("signing") as SigningExtension + + // check what the signatory is. if it's InMemoryPgpSignatoryProvider, then we ALREADY configured it! + if (sign.signatory !is InMemoryPgpSignatoryProvider) { + // we haven't configured it yet AND we don't know which value is set first! + + // setup the sonatype PRIVATE KEY information + assignFromProp("sonatypePrivateKeyFile", "") { + project.extensions.extraProperties["sonatypePrivateKeyFile"] = it + + if (project.extensions.extraProperties.has("sonatypePrivateKeyPassword")) { + sign.apply { + useInMemoryPgpKeys(File(it).readText(), project.extensions.extraProperties["sonatypePrivateKeyPassword"] as String) + } + } + } + + assignFromProp("sonatypePrivateKeyPassword", "") { + project.extensions.extraProperties["sonatypePrivateKeyPassword"] = it + + if (project.extensions.extraProperties.has("sonatypePrivateKeyFile")) { + sign.apply { + useInMemoryPgpKeys(File(project.extensions.extraProperties["sonatypePrivateKeyFile"] as String).readText(), it) + } + } + } + } } pom.scm { } @@ -126,7 +161,7 @@ class PublishPlugin : Plugin { project.tasks.withType { doFirst { - logger.debug("Publishing '${publication.groupId}:${publication.artifactId}:${publication.version}' to ${repository.url}") + println("\tPublishing '${publication.groupId}:${publication.artifactId}:${publication.version}' to ${repository.url}") } onlyIf { @@ -145,13 +180,49 @@ class PublishPlugin : Plugin { val url = "https://oss.sonatype.org/content/repositories/releases/" val projectName = config.groupId.replace('.', '/') - println("Maven URL: $url$projectName/${config.name}/${config.version}/") + println("\tMaven URL: $url$projectName/${config.name}/${config.version}/") } nexusStaging { - username = config.sonatype.userName - password = config.sonatype.password + assignFromProp("sonatypeUserName", config.sonatype.userName) { username = it } + assignFromProp("sonatypePassword", config.sonatype.password) { password = it } } + + project.tasks.findByName("publishToSonatype")?.doFirst { + println("\tPublishing to Sonatype: ${config.groupId}:${config.version}/") + } + + val closeTask = project.tasks.findByName("closeRepository") as CloseRepositoryTask + closeTask.apply { + delayBetweenRetriesInMillis = config.retryDelay.toMillis().toInt() + numberOfRetries = config.retryLimit + } + + val releaseTask = project.tasks.findByName("releaseRepository") as ReleaseRepositoryTask + releaseTask.apply { + delayBetweenRetriesInMillis = config.retryDelay.toMillis().toInt() + numberOfRetries = config.retryLimit + } + + // create the sign task to sign the artifact jars before uploading + val sign = project.extensions.getByName("signing") as SigningExtension + sign.apply { + sign((project.extensions.getByName("publishing") as PublishingExtension).publications.getByName("maven")) + } + + + // output how much the time-outs are + val durationString = config.httpTimeout.toString().substring(2) + .replace("(\\d[HMS])(?!$)", "$1 ").toLowerCase() + + + val fullReleaseTimeout = Duration.ofMillis(config.retryDelay.toMillis() * config.retryLimit) + val fullReleaseString = fullReleaseTimeout.toString().substring(2) + .replace("(\\d[HMS])(?!$)", "$1 ").toLowerCase() + + println("\tMaven publish and release plugin initialized") + println("\t- Sonatype HTTP timeout: $durationString") + println("\t- Sonatype API timeout: $fullReleaseString") } project.childProjects.values.forEach { @@ -187,4 +258,47 @@ class PublishPlugin : Plugin { private fun nexusPublishing(configure: NexusPublishExtension.() -> Unit): Unit = project.extensions.configure("nexusPublishing", configure) + + + @Suppress("UNCHECKED_CAST") + private fun assignFromProp(propertyName: String, defaultValue: String, apply: (value: String)->Unit) { + // THREE possibilities for property registration or assignment + // 1) we have MANUALLY defined this property (via the configuration object) + // 1) gradleUtil properties loaded first + // -> gradleUtil's adds a function that everyone else (plugin/task) can call to get values from properties + // 2) gradleUtil properties loaded last + // -> others add a function that gradleUtil's call to set values from properties + + + // 1 + if (defaultValue.isNotEmpty()) { +// println("ASSIGN DEFAULT: $defaultValue") + apply(defaultValue) + return + } + + // 2 + if (project.extensions.extraProperties.has(propertyName)) { +// println("ASSIGN PROP FROM FILE: $propertyName") + apply(project.extensions.extraProperties[propertyName] as String) + return + } + + // 3 + var loaderFunctions: ArrayList>>? = null + if (project.extensions.extraProperties.has("property_loader_functions")) { + loaderFunctions = project.extensions.extraProperties["property_loader_functions"] as ArrayList>>? + } else { + loaderFunctions = ArrayList>>() + project.extensions.extraProperties["property_loader_functions"] = loaderFunctions + } + +// println("ADD LOADER FUNCTION: $propertyName") + loaderFunctions!!.add(Plugin>() { + if (it.first == propertyName) { +// println("EXECUTE LOADER FUNCTION: $propertyName") + apply(it.second) + } + }) + } } diff --git a/src/dorkbox/gradlePublish/PublishToSonatype.kt b/src/dorkbox/gradlePublish/PublishToSonatype.kt index 04ff974..e604654 100644 --- a/src/dorkbox/gradlePublish/PublishToSonatype.kt +++ b/src/dorkbox/gradlePublish/PublishToSonatype.kt @@ -20,9 +20,11 @@ import org.gradle.api.publish.PublishingExtension import org.gradle.api.publish.maven.MavenPom import org.gradle.api.publish.maven.MavenPublication import org.gradle.api.tasks.Input +import org.gradle.plugins.signing.Sign import org.gradle.plugins.signing.SigningExtension +import org.gradle.plugins.signing.signatory.internal.pgp.InMemoryPgpSignatoryProvider import java.io.File -import java.util.function.Consumer +import java.time.Duration class IssueManagement() { @get:Input @@ -49,6 +51,25 @@ class PrivateKey { } open class PublishToSonatype(val project: Project) { + + /** + * How long the HTTP client will wait before timing out + */ + @get:Input + var httpTimeout = Duration.ofMinutes(5) + + /** + * How many retries to attempt when publishing/releasing to sonatype + */ + @get:Input + var retryLimit = 100 + + /** + * How long between retries to wait when publishing/releasing to sonatype + */ + @get:Input + var retryDelay = Duration.ofSeconds(5) + @get:Input var groupId = "" set(value) { @@ -144,7 +165,6 @@ open class PublishToSonatype(val project: Project) { val sign = project.extensions.getByName("signing") as SigningExtension sign.apply { useInMemoryPgpKeys(File(key.fileName).readText(), key.password) - sign((project.extensions.getByName("publishing") as PublishingExtension).publications.getByName("maven")) } } }