Skip to content

Commit 2d1b405

Browse files
Use JReleaser to publish to new Maven Central API
Publishing to Maven Central using OSSRH is deprecated and is sunset on 2025-06-30. The build needs to be migrated to use the Central Publishing Portal. There is currently no support for this in Gradle's own publishing capability, and no official Sonatype Gradle publishing plugin. JReleaser is currently recommended by Sonatype for Gradle publishing to Maven Central. Signed-off-by: Mark S. Lewis <[email protected]>
1 parent fd50d8a commit 2d1b405

File tree

5 files changed

+117
-100
lines changed

5 files changed

+117
-100
lines changed

.github/workflows/release.yml

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,7 @@ env:
1515
IMAGE_NAME: ${{ github.repository_owner }}/fabric-javaenv
1616

1717
jobs:
18-
# Publishing steps to both the Github Packages and the Sonatype
19-
publishjars:
20-
strategy:
21-
fail-fast: false
22-
matrix:
23-
publish_target:
24-
- publishAllPublicationsToGithubPackagesRepository
25-
- publishAllPublicationsToReleaseRepository
18+
publish-github:
2619
runs-on: ubuntu-latest
2720
permissions:
2821
contents: read
@@ -34,18 +27,38 @@ jobs:
3427
distribution: "temurin"
3528
java-version: 21
3629
- uses: gradle/actions/setup-gradle@v4
37-
- name: Push to registry ${{ matrix.publish_target }}
30+
- name: Publish to GitHub Packages
3831
run: |
39-
set -xev
40-
./gradlew -Psigning.key="${SIGNING_KEY}" -Psigning.password="${SIGNING_PASSWORD}" -PossrhUsername="${OSSRH_USER}" -PossrhPassword="${OSSRH_PASSWORD}" ${TARGET}
32+
./gradlew publishAllPublicationsToGitHubRepository
4133
env:
42-
SIGNING_PASSWORD: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }}
43-
SIGNING_KEY: ${{ secrets.OSSRH_GPG_SECRET_KEY }}
44-
OSSRH_USER: ${{ secrets.OSSRH_USERNAME }}
45-
OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }}
46-
TARGET: ${{ matrix.publish_target }}
34+
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }}
35+
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.OSSRH_GPG_SECRET_KEY }}
4736
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4837

38+
publish-maven:
39+
runs-on: ubuntu-latest
40+
permissions:
41+
contents: read
42+
steps:
43+
- uses: actions/checkout@v4
44+
- uses: actions/setup-java@v4
45+
with:
46+
distribution: "temurin"
47+
java-version: 21
48+
- uses: gradle/actions/setup-gradle@v4
49+
- name: Publish to local staging
50+
run: |
51+
./gradlew publishAllPublicationsToStagingRepository
52+
env:
53+
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }}
54+
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.OSSRH_GPG_SECRET_KEY }}
55+
- name: Publish to Maven Central
56+
run: |
57+
./gradlew jreleaserDeploy --stacktrace
58+
env:
59+
JRELEASER_MAVENCENTRAL_USERNAME: ${{ secrets.MAVENCENTRAL_USERNAME }}
60+
JRELEASER_MAVENCENTRAL_PASSWORD: ${{ secrets.MAVENCENTRAL_PASSWORD }}
61+
4962
docker-build-push:
5063
name: Push Docker image
5164
runs-on: ${{ matrix.arch.runner }}

fabric-chaincode-shim/build.gradle

Lines changed: 82 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66

77
plugins {
88
id 'maven-publish'
9-
id 'jacoco'
109
id 'signing'
10+
id 'org.jreleaser' version '1.18.0'
11+
id 'jacoco'
1112
id 'pmd'
1213
}
1314

@@ -137,7 +138,7 @@ jacocoTestCoverageVerification {
137138
test.finalizedBy(jacocoTestReport)
138139
test.finalizedBy(jacocoTestCoverageVerification)
139140

140-
task licenseCheck {
141+
tasks.register('licenseCheck') {
141142
group = "license"
142143
description = "Checks the License part of each source file"
143144

@@ -146,34 +147,37 @@ task licenseCheck {
146147
def missing = new LinkedList<File>()
147148
sourceSets.forEach {
148149
sourceSet ->
149-
sourceSet.allSource.findAll { !it.path.contains("build") && !(it.path.contains("test") && it.path.contains("resources"))}.each {
150+
sourceSet.allSource.findAll {
151+
!it.path.contains("build") &&
152+
!(it.path.contains("test") && it.path.contains("resources"))
153+
}.each {
150154
file ->
151-
if (!file.name.contains("json")){
152-
BufferedReader r = new BufferedReader(new FileReader(file))
153-
def line, hasSPDX = false, hasTraditional = false
154-
while ((line = r.readLine()) != null) {
155-
if (line.contains("SPDX-License-Identifier")) {
156-
hasSPDX = true
157-
break
155+
if (!file.name.contains("json")) {
156+
BufferedReader r = new BufferedReader(new FileReader(file))
157+
def line, hasSPDX = false, hasTraditional = false
158+
while ((line = r.readLine()) != null) {
159+
if (line.contains("SPDX-License-Identifier")) {
160+
hasSPDX = true
161+
break
162+
}
163+
if (line.contains("http://www.apache.org/licenses/LICENSE-2.0")) {
164+
hasTraditional = true
165+
break
166+
}
158167
}
159-
if (line.contains("http://www.apache.org/licenses/LICENSE-2.0")) {
160-
hasTraditional = true
161-
break
162-
}
163-
}
164-
if (!hasSPDX) {
165-
if (hasTraditional) {
166-
noSPDX.add(file)
167-
} else {
168-
missing.add(file)
168+
if (!hasSPDX) {
169+
if (hasTraditional) {
170+
noSPDX.add(file)
171+
} else {
172+
missing.add(file)
173+
}
169174
}
170175
}
171-
}
172176
}
173177
}
174178

175179
if (noSPDX.isEmpty()) {
176-
println "All remaining files have Apache 2.0 headers"
180+
println "All remaining files have Apache 2.0 headers."
177181
} else {
178182
println "We are standardizing with the SPDX style license headers."
179183
println "The following files contain the traditional license headers which are still valid:"
@@ -212,17 +216,13 @@ javadoc {
212216

213217
classpath = sourceSets.main.runtimeClasspath
214218

215-
javadoc.options.addStringOption('Xdoclint:none', '-quiet')
219+
options.addStringOption('Xdoclint:none', '-quiet')
220+
options.addStringOption('Xwerror', '-quiet')
216221
options.overview = "src/main/java/org/hyperledger/fabric/overview.html"
217-
}
218222

219-
if (JavaVersion.current().isJava8Compatible()) {
220-
project.tasks.withType(Javadoc) {
221-
options.addStringOption('Xdoclint:all', '-quiet')
222-
options.addStringOption('Xwerror', '-quiet')
223-
}
224223
}
225224

225+
def final stagingDeployUrl = layout.buildDirectory.dir('staging-deploy')
226226

227227
publishing {
228228
publications {
@@ -235,10 +235,12 @@ publishing {
235235
name = 'JavaChaincodeShim'
236236
packaging = 'jar'
237237
description = 'Hyperledger Fabric Java Chaincode Shim'
238-
url = 'http://www.hyperledger.org/'
238+
url = 'https://hyperledger.github.io/fabric-chaincode-java/'
239239

240240
scm {
241-
url = 'https://github.com/hyperledger/fabric-chaincode-java.git'
241+
connection = 'scm:git:https://github.com/hyperledger/fabric-chaincode-java.git'
242+
developerConnection = 'scm:git:ssh://github.com:hyperledger/fabric-chaincode-java.git'
243+
url = 'https://github.com/hyperledger/fabric-chaincode-java'
242244
}
243245
licenses {
244246
license {
@@ -248,26 +250,16 @@ publishing {
248250
}
249251

250252
developers {
251-
developer {
252-
id = 'gennadylaventman'
253-
name = 'Gennady Laventman'
254-
255-
}
256-
developer {
257-
id = 'luiss'
258-
name = 'Luis Sanchez'
259-
260-
}
261-
developer {
262-
id = 'C0rWin'
263-
name = 'Artem Barger'
264-
265-
}
266253
developer {
267254
id = 'denyeart'
268255
name = 'David Enyeart'
269256
270257
}
258+
developer {
259+
id = 'bestbeforetoday'
260+
name = 'Mark S. Lewis'
261+
262+
}
271263
}
272264
}
273265
}
@@ -276,17 +268,12 @@ publishing {
276268

277269
repositories {
278270
maven {
279-
name = "release"
280-
url = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
281-
credentials {
282-
username = project.findProperty('ossrhUsername')
283-
password = project.findProperty('ossrhPassword')
284-
}
285-
271+
name = "Staging"
272+
url = stagingDeployUrl
286273
}
287274

288275
maven {
289-
name = "GitHubPackages"
276+
name = "GitHub"
290277
url = "https://maven.pkg.github.com/hyperledger/fabric-chaincode-java"
291278
credentials {
292279
username = System.getenv("GITHUB_ACTOR")
@@ -297,25 +284,42 @@ publishing {
297284
}
298285

299286
signing {
300-
println "Signing"
301-
if (project.findProperty('signing.key')) {
302-
def signingKey = project.findProperty('signing.key')
303-
def signingPassword = project.findProperty('signing.password')
304-
useInMemoryPgpKeys(signingKey, signingPassword)
305-
306-
sign publishing.publications.shim
307-
println "... signed"
308-
} else {
309-
println "... no keys to use "
287+
required = { gradle.taskGraph.hasTask(":${project.name}:publishAllPublicationsToStagingRepository") }
288+
289+
def signingKey = findProperty('signingKey')
290+
def signingPassword = findProperty('signingPassword')
291+
useInMemoryPgpKeys(signingKey, signingPassword)
292+
293+
sign publishing.publications.shim
294+
}
295+
296+
jreleaser {
297+
gitRootSearch = true
298+
deploy {
299+
maven {
300+
mavenCentral {
301+
sonatype {
302+
active = 'ALWAYS'
303+
url = 'https://central.sonatype.com/api/v1/publisher'
304+
sign = false
305+
stagingRepository(file(stagingDeployUrl).toString())
306+
}
307+
}
308+
}
309+
}
310+
release {
311+
github {
312+
enabled = false
313+
}
310314
}
311315
}
312316

313317
// Need to specify the sourcesJar task BEFORE the java{withSourcesJar()} so that it picks up the duplicatesStratergy
314318
// otherwise this fails with a duplicates error.
315319
// (see https://github.com/gradle/gradle/issues/17236)
316320

317-
task sourcesJar(type: Jar) {
318-
duplicatesStrategy = 'include'
321+
tasks.register('sourcesJar', Jar) {
322+
duplicatesStrategy = DuplicatesStrategy.INCLUDE
319323
archiveClassifier = 'sources'
320324
from sourceSets.main.allSource
321325
}
@@ -332,16 +336,16 @@ build.dependsOn licenseCheck
332336
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
333337
import org.gradle.api.tasks.testing.logging.TestLogEvent
334338

335-
tasks.withType(Test) {
339+
tasks.withType(Test).configureEach {
336340

337341
environment "CORE_PEER_LOCALMSPID", "mymsp"
338342

339343
testLogging {
340344
// set options for log level LIFECYCLE
341345
events TestLogEvent.FAILED,
342-
TestLogEvent.PASSED,
343-
TestLogEvent.SKIPPED,
344-
TestLogEvent.STANDARD_OUT
346+
TestLogEvent.PASSED,
347+
TestLogEvent.SKIPPED,
348+
TestLogEvent.STANDARD_OUT
345349
exceptionFormat = TestExceptionFormat.FULL
346350
showExceptions = true
347351
showCauses = true
@@ -350,22 +354,23 @@ tasks.withType(Test) {
350354
// set options for log level DEBUG and INFO
351355
debug {
352356
events TestLogEvent.STARTED,
353-
TestLogEvent.FAILED,
354-
TestLogEvent.PASSED,
355-
TestLogEvent.SKIPPED,
356-
TestLogEvent.STANDARD_ERROR,
357-
TestLogEvent.STANDARD_OUT
357+
TestLogEvent.FAILED,
358+
TestLogEvent.PASSED,
359+
TestLogEvent.SKIPPED,
360+
TestLogEvent.STANDARD_ERROR,
361+
TestLogEvent.STANDARD_OUT
358362
exceptionFormat = TestExceptionFormat.FULL
359363
}
360364
info.events = debug.events
361365
info.exceptionFormat = debug.exceptionFormat
362366

363-
afterSuite { desc, result ->
367+
afterSuite {desc, result ->
364368
if (!desc.parent) { // will match the outermost suite
365369
def output = "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)"
366370
def startItem = '| ', endItem = ' |'
367371
def repeatLength = startItem.length() + output.length() + endItem.length()
368-
println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' + ('-' * repeatLength))
372+
println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' +
373+
('-' * repeatLength))
369374
}
370375
}
371376
}

gradle/wrapper/gradle-wrapper.jar

181 Bytes
Binary file not shown.

gradlew

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,7 @@ done
8686
# shellcheck disable=SC2034
8787
APP_BASE_NAME=${0##*/}
8888
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
89-
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
90-
' "$PWD" ) || exit
89+
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
9190

9291
# Use the maximum available, or set MAX_FD != -1 to use that value.
9392
MAX_FD=maximum
@@ -115,7 +114,7 @@ case "$( uname )" in #(
115114
NONSTOP* ) nonstop=true ;;
116115
esac
117116

118-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
117+
CLASSPATH="\\\"\\\""
119118

120119

121120
# Determine the Java command to use to start the JVM.
@@ -206,15 +205,15 @@ fi
206205
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
207206

208207
# Collect all arguments for the java command:
209-
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
208+
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
210209
# and any embedded shellness will be escaped.
211210
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
212211
# treated as '${Hostname}' itself on the command line.
213212

214213
set -- \
215214
"-Dorg.gradle.appname=$APP_BASE_NAME" \
216215
-classpath "$CLASSPATH" \
217-
org.gradle.wrapper.GradleWrapperMain \
216+
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
218217
"$@"
219218

220219
# Stop when "xargs" is not available.

gradlew.bat

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,11 @@ goto fail
7070
:execute
7171
@rem Setup the command line
7272

73-
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73+
set CLASSPATH=
7474

7575

7676
@rem Execute Gradle
77-
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
77+
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
7878

7979
:end
8080
@rem End local scope for the variables with windows NT shell

0 commit comments

Comments
 (0)