Skip to content

Commit

Permalink
HHH-18912 - ORM release process
Browse files Browse the repository at this point in the history
  • Loading branch information
sebersole committed Dec 13, 2024
1 parent 7dfd5e0 commit 2ff5010
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 154 deletions.
45 changes: 22 additions & 23 deletions ci/release/Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ pipeline {
stage('Release check') {
steps {
script {
print "INFO: params.RELEASE_VERSION = ${params.RELEASE_VERSION}"
print "INFO: params.DEVELOPMENT_VERSION = ${params.DEVELOPMENT_VERSION}"
print "INFO: params.RELEASE_DRY_RUN? = ${params.RELEASE_DRY_RUN}"

// Avoid doing a release for commits from a release
def lastCommitter = sh(script: 'git show -s --format=\'%an\'', returnStdout: true).trim()
def secondLastCommitter = sh(script: 'git show -s --format=\'%an\' HEAD~1', returnStdout: true).trim()
Expand Down Expand Up @@ -124,25 +128,18 @@ pipeline {
checkout scmGit(branches: [[name: '*/main']], extensions: [], userRemoteConfigs: [[credentialsId: 'ed25519.Hibernate-CI.github.com', url: 'https://github.com/hibernate/hibernate-release-scripts.git']])
}
configFileProvider([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: 'RELEASE_GPG_PRIVATE_KEY_PATH'),
string(credentialsId: 'release.gpg.passphrase', variable: 'RELEASE_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}",
"DISABLE_REMOTE_GRADLE_CACHE=true"
]) {
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}",
"DISABLE_REMOTE_GRADLE_CACHE=true"
]) {
sh ".release/scripts/prepare-release.sh ${env.PROJECT} ${env.RELEASE_VERSION} ${env.DEVELOPMENT_VERSION}"
}
}
}
}
}
Expand All @@ -155,10 +152,12 @@ pipeline {
}
configFileProvider([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: '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'),
// https://docs.gradle.org/current/userguide/publishing_gradle_plugins.html#account_setup
usernamePassword(credentialsId: 'gradle-plugin-portal-api-key', passwordVariable: 'GRADLE_PUBLISH_SECRET', usernameVariable: 'GRADLE_PUBLISH_KEY'),
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']) {
Expand Down
167 changes: 63 additions & 104 deletions gradle/published-java-module.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ dependencies {
// Publishing

java {
// 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()
}
Expand All @@ -37,6 +39,7 @@ publishing {
publications {
// main publication
publishedArtifacts {
// including sources and javadoc jars, per above
from components.java
}

Expand Down Expand Up @@ -98,137 +101,93 @@ publishing {
}


var signingKey = resolveSigningKey()
var signingPassword = findSigningProperty( "signingPassword" )
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Signing

signing {
useInMemoryPgpKeys( signingKey, signingPassword )
def signPublicationsTask = tasks.register('signPublications') {
description "Grouping task which executes all Sign tasks"

sign publishing.publications.publishedArtifacts
dependsOn tasks.withType( Sign )
}

String resolveSigningKey() {
var key = findSigningProperty( "signingKey" )
if ( key != null ) {
return key
}

var keyFile = findSigningProperty( "signingKeyFile" )
if ( keyFile != null ) {
return new File( keyFile ).text
}

return null
tasks.named( "publishPublishedArtifactsPublicationToSonatypeRepository" ) {
// publishing depends on signing
dependsOn signPublicationsTask
}

String findSigningProperty(String propName) {
if ( System.getProperty( propName ) != null ) {
logger.debug "Found `{}` as a system property", propName
return System.getProperty(propName )
}
else if ( System.getenv().get( propName ) != null ) {
logger.debug "Found `{}` as an env-var property", propName
return System.getenv().get( propName )
}
else if ( project.hasProperty( propName ) ) {
logger.debug "Found `{}` as a project property", propName
return project.hasProperty( propName )
}
else {
logger.debug "Did not find `{}`", propName
return null
}
tasks.register('sign') {
description "Pseudonym for :signPublications"
dependsOn signPublicationsTask
}


var signingTask = project.tasks.getByName( "signPublishedArtifactsPublication" ) as Sign
var signingExtension = project.getExtensions().getByType(SigningExtension) as SigningExtension

task sign {
dependsOn "signPublications"
}
gradle.taskGraph.whenReady { TaskExecutionGraph graph ->
boolean wasSigningRequested = false
boolean wasPublishingRequested = false

graph.allTasks.each {task ->
if ( task instanceof Sign ) {
wasSigningRequested = true
}
else if ( task instanceof PublishToMavenRepository ) {
wasPublishingRequested = true
}
}

task signPublications { t ->
tasks.withType( Sign ).all { s ->
t.dependsOn s
if ( wasPublishingRequested ) {
def ossrhUser = System.getenv().get( "ORG_GRADLE_PROJECT_sonatypeUsername" )
def ossrhPass = System.getenv().get( "ORG_GRADLE_PROJECT_sonatypePassword" )
if ( ossrhUser == null || ossrhPass == null ) {
throw new RuntimeException( "Cannot perform publishing to OSSRH without credentials." )
}
logger.lifecycle "Publishing groupId: '" + project.group + "', version: '" + project.version + "'"
}
}

signingTask.doFirst {
if ( signingKey == null || signingPassword == null ) {
throw new GradleException(
"Cannot perform signing without GPG details. Please set the `signingKey` and `signingKeyFile` properties"
)
if ( wasSigningRequested || wasPublishingRequested ) {
// signing was explicitly requested and/or we are publishing to Sonatype OSSRH
// - we need the signing to happen
signingExtension.required = true

var signingKey = resolveSigningKey()
var signingPassword = resolveSigningPassphrase()
signingExtension.useInMemoryPgpKeys( signingKey, signingPassword )
signingExtension.sign publishing.publications.publishedArtifacts
}
else {
// signing was not explicitly requested and we are not publishing to OSSRH,
// - disable all Sign tasks
tasks.withType( Sign ).each { enabled = false }
}
}


boolean wasSigningExplicitlyRequested() {
// check whether signing task was explicitly requested when running the build
//
// NOTE: due to https://discuss.gradle.org/t/how-to-tell-if-a-task-was-explicitly-asked-for-on-the-command-line/42853/3
// we cannot definitively know whether the task was requested. Gradle really just does not expose this information.
// so we make a convention - we check the "start parameters" object to see which task-names were requested;
// the problem is that these are the raw names directly from the command line. e.g. it is perfectly legal to
// say `gradlew signPubArtPub` in place of `gradlew signPublishedArtifactsPublication` - Gradle will simply
// "expand" the name it finds. However, it does not make that available.
//
// so the convention is that we will check for the following task names
//
// for each of:
// 1. `sign`
// 2. `signPublications`
// 3. `signPublishedArtifactsPublication`
//
// and we check both forms:
// 1. "${taskName}"
// 2. project.path + ":${taskName}"
//
// we need to check both again because of the "start parameters" discussion

def signingTaskNames = ["sign", "signPublications", "signPublishedArtifactsPublication"]

for ( String taskName : signingTaskNames ) {
if ( gradle.startParameter.taskNames.contains( taskName )
|| gradle.startParameter.taskNames.contains( "${project.path}:${taskName}" ) ) {
return true
}
static String resolveSigningKey() {
var key = System.getenv().get( "SIGNING_GPG_PRIVATE_KEY" )
if ( key != null ) {
return key
}

return false
}
var keyFile = System.getenv().get( "SIGNING_GPG_PRIVATE_KEY_PATH" )
if ( keyFile != null ) {
return new File( keyFile ).text
}

if ( wasSigningExplicitlyRequested() ) {
// signing was explicitly requested
signingExtension.required = true
throw new RuntimeException( "Cannot perform signing without GPG details." )
}
else {
gradle.taskGraph.whenReady { graph ->
if ( graph.hasTask( signingTask ) ) {
// signing is scheduled to happen.
//
// we know, from above if-check, that it was not explicitly requested -
// so it is triggered via task dependency. make sure we want it to happen
var publishingTask = project.tasks.getByName( "publishPublishedArtifactsPublicationToSonatypeRepository" ) as PublishToMavenRepository
if ( graph.hasTask( publishingTask ) ) {
// we are publishing to Sonatype OSSRH - we need the signing to happen
signingExtension.required = true
}
else {
// signing was not explicitly requested and we are not publishing to OSSRH,
// so do not sign.
signingTask.enabled = false
}
}

static String resolveSigningPassphrase() {
var passphrase = System.getenv().get( "SIGNING_GPG_PASSPHRASE" )
if ( passphrase == null ) {
throw new RuntimeException( "Cannot perform signing without GPG details." )
}
return passphrase
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Release / publishing tasks

task ciBuild {
tasks.register('ciBuild') {
dependsOn test, tasks.publishToSonatype
}

Expand All @@ -245,7 +204,7 @@ tasks.publishToSonatype.mustRunAfter test
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Ancillary tasks

task showPublications {
tasks.register('showPublications') {
doFirst {
project.publishing.publications.each { publication ->
println "Publication (${publication.name}): ${publication.groupId}:${publication.artifactId}:${publication.version}"
Expand Down
57 changes: 30 additions & 27 deletions tooling/hibernate-gradle-plugin/hibernate-gradle-plugin.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -100,21 +100,6 @@ publishing {
}
}

// local publishing (SNAPSHOT testing), cont.
// - https://github.com/gradle-nexus/publish-plugin/issues/143
// - https://github.com/gradle-nexus/publish-plugin/pull/144
gradle.taskGraph.whenReady {
tasks.withType(PublishToMavenRepository) { PublishToMavenRepository t ->
if ( t.repository == null ) {
logger.info( "Task `{}` had null repository", t.path )
}
else if ( t.repository.name == "sonatype" ) {
logger.debug( "Disabling task `{}` because it publishes to Sonatype", t.path )
t.enabled = false
}
}
}

processResources {
inputs.property( "orm-version", getVersion() )
description = description + " (injected with Hibernate version)"
Expand Down Expand Up @@ -153,20 +138,38 @@ tasks.publish.enabled !project.ormVersion.isSnapshot
tasks.publishPlugins.enabled !project.ormVersion.isSnapshot

gradle.taskGraph.whenReady { tg ->
if ( tg.hasTask( project.tasks.publishPlugins ) && project.tasks.publishPlugins.enabled ) {
// look for sys-prop or env-var overrides of the tokens used for publishing
if ( project.properties.containsKey( 'gradle.publish.key' )
|| project.properties.containsKey( 'gradle.publish.secret' ) ) {
// nothing to do - already explicitly set
}
else {
// use the values from the credentials provider, if any
if ( project.property( 'gradle.publish.key' ) == null ) {
throw new RuntimeException( "`-Pgradle.publish.key=...` not found" )
// verify credentials for publishing the plugin up front to avoid any work (only if we are publishing)
if ( tg.hasTask( ":publishPlugins" ) && project.tasks.publishPlugins.enabled ) {
// we are publishing the plugin - make sure there is a credentials pair
//
// first, check the `GRADLE_PUBLISH_KEY` / `GRADLE_PUBLISH_SECRET` combo (env vars)
// and then the `gradle.publish.key` / `gradle.publish.secret` combo (project prop)
// - see https://docs.gradle.org/current/userguide/publishing_gradle_plugins.html#account_setup
if ( System.getenv().get("GRADLE_PUBLISH_KEY") != null ) {
if ( System.getenv().get("GRADLE_PUBLISH_SECRET") != null ) {
throw new RuntimeException( "`GRADLE_PUBLISH_KEY` specified, but not `GRADLE_PUBLISH_SECRET` for publishing Gradle plugin" )
}
if ( project.property( 'gradle.publish.secret' ) == null ) {
throw new RuntimeException( "`-Pgradle.publish.secret=...` not found" )
}
else if ( project.findProperty( 'gradle.publish.key' ) != null ) {
if ( project.findProperty( 'gradle.publish.secret' ) != null ) {
throw new RuntimeException( "`gradle.publish.key` specified, but not `gradle.publish.secret` for publishing Gradle plugin" )
}
}
else {
throw new RuntimeException( "No credentials specified for publishing Gradle plugin" )
}
}

// local publishing (SNAPSHOT testing), cont.
// - https://github.com/gradle-nexus/publish-plugin/issues/143
// - https://github.com/gradle-nexus/publish-plugin/pull/144
tasks.withType(PublishToMavenRepository) { PublishToMavenRepository t ->
if ( t.repository == null ) {
logger.info( "Task `{}` had null repository", t.path )
}
else if ( t.repository.name == "sonatype" ) {
logger.debug( "Disabling task `{}` because it publishes to Sonatype", t.path )
t.enabled = false
}
}
}

0 comments on commit 2ff5010

Please sign in to comment.