From 7ae8216755eb126186165c9159e8f63376dae389 Mon Sep 17 00:00:00 2001 From: Mattia Iavarone Date: Thu, 15 Aug 2024 11:58:28 +0200 Subject: [PATCH] Delay sign errors to execution time --- deployer/build.gradle.kts | 2 +- .../tools/deployer/ConfigureSigning.kt | 5 ++-- .../tools/deployer/DeployerPlugin.kt | 13 +++++++---- .../tools/deployer/model/DeploySpec.kt | 17 ++++++++++---- .../deepmedia/tools/deployer/model/Secret.kt | 11 +++++---- .../deepmedia/tools/deployer/model/Signing.kt | 6 +++++ .../tools/deployer/specs/CentralPortal.kt | 23 +++++-------------- .../deepmedia/tools/deployer/specs/Github.kt | 5 ++-- .../tools/deployer/specs/Sonatype.kt | 18 +++++---------- 9 files changed, 51 insertions(+), 49 deletions(-) diff --git a/deployer/build.gradle.kts b/deployer/build.gradle.kts index 16d39f2..26827a3 100644 --- a/deployer/build.gradle.kts +++ b/deployer/build.gradle.kts @@ -33,7 +33,7 @@ gradlePlugin { } group = "io.deepmedia.tools.deployer" -version = "0.14.0-alpha2" // on change, update both docs and README +version = "0.14.0-alpha4" // on change, update both docs and README val javadocs = tasks.register("dokkaJavadocJar") { dependsOn(tasks.dokkaJavadoc) diff --git a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/ConfigureSigning.kt b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/ConfigureSigning.kt index a47182f..b6600a1 100644 --- a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/ConfigureSigning.kt +++ b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/ConfigureSigning.kt @@ -1,6 +1,7 @@ package io.deepmedia.tools.deployer import io.deepmedia.tools.deployer.model.AbstractDeploySpec +import io.deepmedia.tools.deployer.model.Signing import org.gradle.api.Project import org.gradle.api.logging.LogLevel import org.gradle.api.publish.maven.MavenPublication @@ -22,7 +23,7 @@ import org.gradle.security.internal.pgp.BaseInMemoryPgpSignatoryProvider * at execution time, all tasks use the last key-value pair which is not what we want. */ internal fun Project.configureSigning( - info: Pair, + credentials: Signing.Provided, maven: MavenPublication, log: Logger ): Sign { @@ -40,7 +41,7 @@ internal fun Project.configureSigning( } val ext = extensions.getByType(SigningExtension::class) - ext.useInMemoryPgpKeys(info.first, info.second) + ext.useInMemoryPgpKeys(credentials.key, credentials.password) val signatory = ext.signatory return ext.sign(maven).single().apply { setSignatory(signatory) diff --git a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/DeployerPlugin.kt b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/DeployerPlugin.kt index b412c15..b517824 100644 --- a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/DeployerPlugin.kt +++ b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/DeployerPlugin.kt @@ -125,7 +125,7 @@ class DeployerPlugin : Plugin { spec: AbstractDeploySpec<*>, target: Project, repository: MavenArtifactRepository, - signInfo: Pair? + signCredentials: Signing.Credentials ): TaskProvider<*> { val publishing = target.extensions.getByType(PublishingExtension::class.java) val publication = component.maybeCreatePublication(publishing.publications, spec) @@ -144,17 +144,20 @@ class DeployerPlugin : Plugin { target.configureArtifacts(spec, component, publication, log) // Configure signing if present - val sign = when (signInfo) { - null -> null - else -> target.configureSigning(signInfo, publication, log) + val signTask = when (signCredentials) { + is Signing.Provided -> target.configureSigning(signCredentials, publication, log) + else -> null } target.configurePom(spec, component, publication, log) // Add maven validation, mostly for sonatype mavenPublish.configure { - if (sign != null) dependsOn(sign) + if (signTask != null) dependsOn(signTask) onlyIf { component.enabled.get() } doFirst { + if (signCredentials is Signing.NotFound && signCredentials.error != null) { + error(signCredentials.error) + } log { "Starting artifact and POM validation"} spec.validateMavenArtifacts(target, publication.artifacts, log) spec.validateMavenPom(publication.pom) diff --git a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/model/DeploySpec.kt b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/model/DeploySpec.kt index 9e8114a..62e1bd9 100644 --- a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/model/DeploySpec.kt +++ b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/model/DeploySpec.kt @@ -60,11 +60,18 @@ abstract class AbstractDeploySpec constructor( internal open fun registerInitializationTask(target: Project, name: String, repo: MavenArtifactRepository): TaskProvider<*>? = null internal open fun registerFinalizationTask(target: Project, name: String, init: TaskProvider<*>?): TaskProvider<*>? = null - internal open fun readSignCredentials(target: Project): Pair? { - if (!signing.key.isPresent && !signing.password.isPresent) return null - val key = signing.key.orNull?.resolve(target, "spec.signing.key") ?: error("Got signing password, but no key.") - val password = signing.password.orNull?.resolve(target, "spec.signing.password") ?: error("Got signing key, but no password.") - return key to password + internal open fun readSignCredentials(target: Project): Signing.Credentials { + val hasKey = signing.key.isPresent && signing.key.get().hasKey + val hasPassword = signing.password.isPresent && signing.password.get().hasKey + if (!hasKey && !hasPassword) return Signing.NotDeclared + val key = signing.key.orNull?.resolveOrNull(target.providers, target.layout) + val password = signing.password.orNull?.resolveOrNull(target.providers, target.layout) + return when { + key != null && password != null -> Signing.Provided(key, password) + key != null -> Signing.NotFound("Could not resolve signing password at ${this.name}.signing.password (${signing.password.orNull?.key}).") + password != null -> Signing.NotFound("Could not resolve signing key at ${this.name}.signing.key (${signing.key.orNull?.key}).") + else -> Signing.NotFound("Could not resolve signing key (${signing.key.orNull?.key}) and password (${signing.password.orNull?.key}) at ${this.name}.signing.") + } } internal open fun validateMavenArtifacts(target: Project, artifacts: MavenArtifactSet, log: Logger) = Unit diff --git a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/model/Secret.kt b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/model/Secret.kt index 468e571..b68d5fb 100644 --- a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/model/Secret.kt +++ b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/model/Secret.kt @@ -10,12 +10,14 @@ import java.util.* import java.util.concurrent.ConcurrentHashMap class Secret(val key: String) { - internal fun resolve(project: Project, location: String): String { - return resolve(project.providers, project.layout, location) + internal val hasKey get() = key.isNotEmpty() + + internal fun resolveOrThrow(providers: ProviderFactory, layout: ProjectLayout, location: String): String { + return resolveOrNull(providers, layout) ?: error("Secret key $key (from $location) not found in environment variables nor properties.") } - internal fun resolve(providers: ProviderFactory, layout: ProjectLayout, location: String): String { + internal fun resolveOrNull(providers: ProviderFactory, layout: ProjectLayout): String? { + if (!hasKey) return null return findSecret(key, providers, layout) - ?: error("Secret key $key (from $location) not found in environment variables nor properties.") } } @@ -51,4 +53,5 @@ private fun File.localProperties(): Properties? { interface SecretScope { fun secret(key: String) = Secret(key) + fun absent() = Secret("") } \ No newline at end of file diff --git a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/model/Signing.kt b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/model/Signing.kt index 78855e5..816ff65 100644 --- a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/model/Signing.kt +++ b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/model/Signing.kt @@ -18,4 +18,10 @@ open class Signing @Inject constructor(objects: ObjectFactory) : SecretScope { internal fun resolve(target: org.gradle.api.Project, spec: DeploySpec) { // Nothing to do } + + internal sealed interface Credentials + + internal class Provided(val key: String, val password: String) : Credentials + internal class NotFound(val error: String? = null) : Credentials + internal object NotDeclared : Credentials } diff --git a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/specs/CentralPortal.kt b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/specs/CentralPortal.kt index 5bbd952..57f3e42 100644 --- a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/specs/CentralPortal.kt +++ b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/specs/CentralPortal.kt @@ -2,25 +2,15 @@ package io.deepmedia.tools.deployer.specs import io.deepmedia.tools.deployer.Logger import io.deepmedia.tools.deployer.dump -import io.deepmedia.tools.deployer.model.AbstractDeploySpec -import io.deepmedia.tools.deployer.model.Auth -import io.deepmedia.tools.deployer.model.DeploySpec -import io.deepmedia.tools.deployer.model.Secret -import io.deepmedia.tools.deployer.central.ossrh.OssrhInfo -import io.deepmedia.tools.deployer.central.ossrh.OssrhService -import io.deepmedia.tools.deployer.central.ossrh.OssrhServer import io.deepmedia.tools.deployer.central.portal.CentralPortalInfo import io.deepmedia.tools.deployer.central.portal.CentralPortalService +import io.deepmedia.tools.deployer.model.* import io.deepmedia.tools.deployer.tasks.isDocsJar import io.deepmedia.tools.deployer.tasks.isSourcesJar import org.gradle.api.DefaultTask import org.gradle.api.Project import org.gradle.api.artifacts.dsl.RepositoryHandler import org.gradle.api.artifacts.repositories.MavenArtifactRepository -import org.gradle.api.file.ArchiveOperations -import org.gradle.api.file.Directory -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.FileSystemOperations import org.gradle.api.file.ProjectLayout import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property @@ -35,7 +25,6 @@ import org.gradle.kotlin.dsl.property import org.gradle.kotlin.dsl.register import java.io.File import javax.inject.Inject -import kotlin.math.abs import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.minutes @@ -86,9 +75,9 @@ class CentralPortalDeploySpec internal constructor(objects: ObjectFactory, name: } } - override fun readSignCredentials(target: Project): Pair { - val result = super.readSignCredentials(target) ?: error("Signing is mandatory for Central Portal deployments. Please add spec.signing.key and spec.signing.password.") - return result + override fun readSignCredentials(target: Project): Signing.Credentials = when (val result = super.readSignCredentials(target)) { + Signing.NotDeclared -> error("Signing is mandatory for Central Portal deployments. Please add spec.signing.key and spec.signing.password.") + else -> result } override fun validateMavenArtifacts(target: Project, artifacts: MavenArtifactSet, log: Logger) { @@ -165,8 +154,8 @@ internal abstract class CentralPortalInitializationTask @Inject constructor( @TaskAction fun execute() { val auth = auth.get() - val username = auth.user.get().resolve(providers, layout, "spec.auth.user") - val password = auth.password.get().resolve(providers, layout, "spec.auth.password") + val username = auth.user.get().resolveOrThrow(providers, layout, "spec.auth.user") + val password = auth.password.get().resolveOrThrow(providers, layout, "spec.auth.password") val info = CentralPortalInfo(username, password, allowSync.get()) val storageRepository = File(storageRepository.get()) storageRepository.delete() // Delete any stale data diff --git a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/specs/Github.kt b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/specs/Github.kt index a13c891..f78780e 100644 --- a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/specs/Github.kt +++ b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/specs/Github.kt @@ -11,7 +11,6 @@ import org.gradle.api.artifacts.dsl.RepositoryHandler import org.gradle.api.artifacts.repositories.MavenArtifactRepository import org.gradle.api.artifacts.repositories.PasswordCredentials import org.gradle.api.file.ProjectLayout -import org.gradle.api.internal.AbstractTask import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property import org.gradle.api.provider.ProviderFactory @@ -86,7 +85,7 @@ internal open class GithubInitializationTask @Inject constructor( @TaskAction fun execute() { val credentials = credentials.get() val auth = auth.get() - credentials.username = auth.user.get().resolve(providers, layout, "spec.auth.user") - credentials.password = auth.token.get().resolve(providers, layout, "spec.auth.token") + credentials.username = auth.user.get().resolveOrThrow(providers, layout, "spec.auth.user") + credentials.password = auth.token.get().resolveOrThrow(providers, layout, "spec.auth.token") } } \ No newline at end of file diff --git a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/specs/Sonatype.kt b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/specs/Sonatype.kt index ee20b8f..1661a02 100644 --- a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/specs/Sonatype.kt +++ b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/specs/Sonatype.kt @@ -2,13 +2,10 @@ package io.deepmedia.tools.deployer.specs import io.deepmedia.tools.deployer.Logger import io.deepmedia.tools.deployer.dump -import io.deepmedia.tools.deployer.model.AbstractDeploySpec -import io.deepmedia.tools.deployer.model.Auth -import io.deepmedia.tools.deployer.model.DeploySpec -import io.deepmedia.tools.deployer.model.Secret import io.deepmedia.tools.deployer.central.ossrh.OssrhInfo import io.deepmedia.tools.deployer.central.ossrh.OssrhService import io.deepmedia.tools.deployer.central.ossrh.OssrhServer +import io.deepmedia.tools.deployer.model.* import io.deepmedia.tools.deployer.tasks.isDocsJar import io.deepmedia.tools.deployer.tasks.isSourcesJar import org.gradle.api.DefaultTask @@ -109,12 +106,9 @@ class SonatypeDeploySpec internal constructor(objects: ObjectFactory, name: Stri } } - override fun readSignCredentials(target: Project): Pair? { - val result = super.readSignCredentials(target) - if (result == null && maySyncToMavenCentral.get()) { - error("Signing is mandatory for OSSRH/Maven Central deployments. Please add spec.signing.key and spec.signing.password.") - } - return result + override fun readSignCredentials(target: Project): Signing.Credentials = when (val result = super.readSignCredentials(target)) { + Signing.NotDeclared -> error("Signing is mandatory for OSSRH/Maven Central deployments. Please add spec.signing.key and spec.signing.password.") + else -> result } /*override fun provideDefaultDocsForComponent(target: Project, component: Component): Any { @@ -212,8 +206,8 @@ internal abstract class SonatypeInitializationTask @Inject constructor( fun execute() { val repo = repo.get() val auth = auth.get() - val username = auth.user.get().resolve(providers, layout, "spec.auth.user") - val password = auth.password.get().resolve(providers, layout, "spec.auth.password") + val username = auth.user.get().resolveOrThrow(providers, layout, "spec.auth.user") + val password = auth.password.get().resolveOrThrow(providers, layout, "spec.auth.password") repo.credentials.username = username repo.credentials.password = password if (sync.get()) {