From 8a4c959faba42b984bdd74460024ba32a3ef4bad Mon Sep 17 00:00:00 2001
From: Steve Ebersole <steve@hibernate.org>
Date: Thu, 12 Dec 2024 11:38:34 -0600
Subject: [PATCH] [#2027] Use env-vars for passing secrets used during release

---
 build.gradle                    | 18 +--------
 ci/release/Jenkinsfile          | 38 ++++++++-----------
 ci/snapshot-publish.Jenkinsfile |  9 ++---
 publish.gradle                  | 65 ++++++++++++++++++++++++++-------
 4 files changed, 72 insertions(+), 58 deletions(-)

diff --git a/build.gradle b/build.gradle
index 76339b6d8..b0c88fc11 100644
--- a/build.gradle
+++ b/build.gradle
@@ -12,15 +12,6 @@ group = "org.hibernate.reactive"
 // leverage the ProjectVersion which comes from the `local.versions` plugin
 version = project.projectVersion.fullName
 
-ext {
-    if ( !project.hasProperty( 'hibernatePublishUsername' ) ) {
-        hibernatePublishUsername = null
-    }
-    if ( !project.hasProperty( 'hibernatePublishPassword' ) ) {
-        hibernatePublishPassword = null
-    }
-}
-
 // Versions which need to be aligned across modules; this also
 // allows overriding the build using a parameter, which can be
 // useful to monitor compatibility for upcoming versions on CI:
@@ -39,15 +30,10 @@ ext {
     logger.lifecycle "Vert.x SQL Client Version: " + project.vertxSqlClientVersion
 }
 
-// To release, see task ciRelease in release/build.gradle
-// To publish on Sonatype (Maven Central):
-// ./gradlew publishToSonatype closeAndReleaseStagingRepository -PhibernatePublishUsername="<YOUR USERNAME>" -PhibernatePublishPassword="<YOUR PASSWORD>"
+// Publishing to Sonatype (Maven Central):
 nexusPublishing {
     repositories {
-        sonatype {
-            username = project.hibernatePublishUsername
-            password = project.hibernatePublishPassword
-        }
+        sonatype()
     }
 }
 
diff --git a/ci/release/Jenkinsfile b/ci/release/Jenkinsfile
index 5fe408354..87e009d49 100644
--- a/ci/release/Jenkinsfile
+++ b/ci/release/Jenkinsfile
@@ -165,24 +165,18 @@ pipeline {
 							configFile(fileId: 'release.config.ssh', targetLocation: "${env.HOME}/.ssh/config"),
 							configFile(fileId: 'release.config.ssh.knownhosts', targetLocation: "${env.HOME}/.ssh/known_hosts")
 					]) {
-						withCredentials([
-							usernamePassword(credentialsId: 'ossrh.sonatype.org', passwordVariable: 'OSSRH_PASSWORD', usernameVariable: 'OSSRH_USER'),
-							usernamePassword(credentialsId: 'gradle-plugin-portal-api-key', passwordVariable: 'PLUGIN_PORTAL_PASSWORD', usernameVariable: 'PLUGIN_PORTAL_USERNAME'),
-							file(credentialsId: 'release.gpg.private-key', variable: 'SIGNING_GPG_PRIVATE_KEY_PATH'),
-							string(credentialsId: 'release.gpg.passphrase', variable: 'SIGNING_GPG_PASSPHRASE')
-						]) {
-							sshagent(['ed25519.Hibernate-CI.github.com', 'hibernate.filemgmt.jboss.org', 'hibernate-ci.frs.sourceforge.net']) {
-								// set release version
-								// update changelog from JIRA
-								// tags the version
-								// changes the version to the provided development version
-								withEnv([
-										"BRANCH=${env.GIT_BRANCH}",
-										// Increase the amount of memory for this part since asciidoctor doc rendering consumes a lot of metaspace
-										"GRADLE_OPTS=-Dorg.gradle.jvmargs='-Dlog4j2.disableJmx -Xmx4g -XX:MaxMetaspaceSize=768m -XX:+HeapDumpOnOutOfMemoryError -Duser.language=en -Duser.country=US -Duser.timezone=UTC -Dfile.encoding=UTF-8'"
-								]) {
-									sh ".release/scripts/prepare-release.sh ${env.PROJECT} ${env.RELEASE_VERSION} ${env.DEVELOPMENT_VERSION}"
-								}
+
+					sshagent(['ed25519.Hibernate-CI.github.com', 'hibernate.filemgmt.jboss.org', 'hibernate-ci.frs.sourceforge.net']) {
+							// set release version
+							// update changelog from JIRA
+							// tags the version
+							// changes the version to the provided development version
+							withEnv([
+									"BRANCH=${env.GIT_BRANCH}",
+									// Increase the amount of memory for this part since asciidoctor doc rendering consumes a lot of metaspace
+									"GRADLE_OPTS=-Dorg.gradle.jvmargs='-Dlog4j2.disableJmx -Xmx4g -XX:MaxMetaspaceSize=768m -XX:+HeapDumpOnOutOfMemoryError -Duser.language=en -Duser.country=US -Duser.timezone=UTC -Dfile.encoding=UTF-8'"
+							]) {
+								sh ".release/scripts/prepare-release.sh ${env.PROJECT} ${env.RELEASE_VERSION} ${env.DEVELOPMENT_VERSION}"
 							}
 						}
 					}
@@ -199,10 +193,10 @@ pipeline {
 							configFile(fileId: 'release.config.ssh.knownhosts', targetLocation: "${env.HOME}/.ssh/known_hosts")
 					]) {
 						withCredentials([
-							usernamePassword(credentialsId: 'ossrh.sonatype.org', passwordVariable: 'OSSRH_PASSWORD', usernameVariable: 'OSSRH_USER'),
-							usernamePassword(credentialsId: 'gradle-plugin-portal-api-key', passwordVariable: 'PLUGIN_PORTAL_PASSWORD', usernameVariable: 'PLUGIN_PORTAL_USERNAME'),
-							file(credentialsId: 'release.gpg.private-key', variable: 'RELEASE_GPG_PRIVATE_KEY_PATH'),
-							string(credentialsId: 'release.gpg.passphrase', variable: 'RELEASE_GPG_PASSPHRASE')
+							// https://github.com/gradle-nexus/publish-plugin#publishing-to-maven-central-via-sonatype-ossrh
+							usernamePassword(credentialsId: 'ossrh.sonatype.org', passwordVariable: 'ORG_GRADLE_PROJECT_sonatypePassword', usernameVariable: 'ORG_GRADLE_PROJECT_sonatypeUsername'),
+							file(credentialsId: 'release.gpg.private-key', variable: 'SIGNING_GPG_PRIVATE_KEY_PATH'),
+							string(credentialsId: 'release.gpg.passphrase', variable: 'SIGNING_GPG_PASSPHRASE')
 							gitUsernamePassword(credentialsId: 'username-and-token.Hibernate-CI.github.com', gitToolName: 'Default')
 						]) {
 							sshagent(['ed25519.Hibernate-CI.github.com', 'hibernate.filemgmt.jboss.org', 'hibernate-ci.frs.sourceforge.net']) {
diff --git a/ci/snapshot-publish.Jenkinsfile b/ci/snapshot-publish.Jenkinsfile
index 83fd95a4a..c2fba1d6b 100644
--- a/ci/snapshot-publish.Jenkinsfile
+++ b/ci/snapshot-publish.Jenkinsfile
@@ -31,15 +31,12 @@ pipeline {
 		stage('Publish') {
 			steps {
 				withCredentials([
-					usernamePassword(credentialsId: 'ossrh.sonatype.org', usernameVariable: 'hibernatePublishUsername', passwordVariable: 'hibernatePublishPassword'),
+					// https://github.com/gradle-nexus/publish-plugin#publishing-to-maven-central-via-sonatype-ossrh
+					usernamePassword(credentialsId: 'ossrh.sonatype.org', usernameVariable: 'ORG_GRADLE_PROJECT_sonatypeUsername', passwordVariable: 'ORG_GRADLE_PROJECT_sonatypePassword'),
 					file(credentialsId: 'release.gpg.private-key', variable: 'SIGNING_GPG_PRIVATE_KEY_PATH'),
 					string(credentialsId: 'release.gpg.passphrase', variable: 'SIGNING_GPG_PASSPHRASE')
 				]) {
-					sh '''./gradlew clean publish \
-						-PhibernatePublishUsername=$hibernatePublishUsername \
-						-PhibernatePublishPassword=$hibernatePublishPassword \
-						--no-scan \
-					'''
+					sh "./gradlew clean publish --no-scan"
 				}
 			}
         }
diff --git a/publish.gradle b/publish.gradle
index 39e0e16b2..0271197d4 100644
--- a/publish.gradle
+++ b/publish.gradle
@@ -5,8 +5,8 @@ apply plugin: 'signing'
 // Java / publishing
 
 java {
-    // include javadoc and sources jar in the Java component
-    // 		- classes jar included by default
+    // Configure the Java "software component" to include javadoc and sources jars in addition to the classes jar.
+    // Ultimately, this component is what makes up the publication for this project.
     withJavadocJar()
     withSourcesJar()
 }
@@ -36,7 +36,7 @@ javadoc {
 
 publishing {
     publications {
-        publishedArtifacts(MavenPublication) {
+        register( "publishedArtifacts", MavenPublication) {
             from components.java
 
             pom {
@@ -78,35 +78,39 @@ publishing {
 }
 
 
-// signing
+// Signing
 
 var signingExtension = project.getExtensions().getByType(SigningExtension) as SigningExtension
 
-// create a `signPublications` "grouping" task which will execute all Sign tasks
-def signPublicationsTask = tasks.register('signPublications')
+def signPublicationsTask = tasks.register('signPublications') {
+    description "Grouping task which executes all Sign tasks"
+    dependsOn tasks.withType( Sign )
+}
+
 tasks.named( "publishPublishedArtifactsPublicationToSonatypeRepository" ) {
+    // publishing depends on signing
     dependsOn signPublicationsTask
 }
 
 gradle.taskGraph.whenReady { TaskExecutionGraph graph ->
     boolean wasSigningRequested = false
     boolean wasPublishingRequested = false
-    List<Sign> signingTasks = []
 
     graph.allTasks.each {task ->
-        logger.lifecycle( "Checking task : $task" )
         if ( task instanceof Sign ) {
-            logger.lifecycle( "    - Task is Sign" )
-            signingTasks.add( task )
             wasSigningRequested = true
         }
         else if ( task instanceof PublishToMavenRepository ) {
-            logger.lifecycle( "    - Task is PublishToMavenRepository" )
             wasPublishingRequested = true
         }
     }
 
     if ( wasPublishingRequested ) {
+        def publishUser = resolvePublishUser()
+        def publishPass = resolvePublishPass()
+        if ( publishUser == null || publishPass == null ) {
+            throw new RuntimeException( "Cannot perform publishing to OSSRH without credentials." )
+        }
         logger.lifecycle "Publishing groupId: '" + project.group + "', version: '" + project.version + "'"
     }
 
@@ -119,14 +123,47 @@ gradle.taskGraph.whenReady { TaskExecutionGraph graph ->
         var signingPassword = resolveSigningPassphrase()
         signingExtension.useInMemoryPgpKeys( signingKey, signingPassword )
         signingExtension.sign publishing.publications.publishedArtifacts
-
-        signPublicationsTask.get().dependsOn( signingTasks )
     }
     else {
         // signing was not explicitly requested and we are not publishing to OSSRH,
         // 		- disable all Sign tasks
-        signingTasks.each { enabled = false }
+        tasks.withType( Sign ).each { enabled = false }
+    }
+}
+
+String resolvePublishUser() {
+    var envVar = System.getenv().get( "ORG_GRADLE_PROJECT_sonatypeUsername" )
+    if ( envVar != null ) {
+        return envVar
+    }
+
+    def projectProp = projectPropOrNull( "sonatypeUsername" )
+    if ( projectProp != null ) {
+        return projectProp
+    }
+
+    return null
+}
+
+String resolvePublishPass() {
+    var envVar = System.getenv().get( "ORG_GRADLE_PROJECT_sonatypePassword" )
+    if ( envVar != null ) {
+        return envVar
+    }
+
+    def projectProp = projectPropOrNull( "sonatypePassword" )
+    if ( projectProp != null ) {
+        return projectProp
+    }
+
+    return null
+}
+
+String projectPropOrNull(String name) {
+    if ( project.hasProperty( name ) ) {
+        return project.findProperty( name )
     }
+    return null;
 }