diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index b6de4deefe..a79079e0fd 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -185,7 +185,7 @@ jobs:
- name: Publish Artifacts
uses: ./actions/run-gradle
with:
- gradle_command: publish closeAndReleaseStagingRepositories -PreleaseBuild=true -PpublishBuild=true -PgithubPublish=true -PcentralPublish=true
+ gradle_command: publish publishMavenCentralBundle -PreleaseBuild=true -PpublishBuild=true -PgithubPublish=true -PcentralPublish=true
env:
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_PRIVATE_KEY }}
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_PASSPHRASE }}
diff --git a/build.gradle b/build.gradle
index d4dc5c72ea..430bdfd5f9 100644
--- a/build.gradle
+++ b/build.gradle
@@ -41,7 +41,6 @@ plugins {
alias(libs.plugins.protobuf)
alias(libs.plugins.versions)
alias(libs.plugins.spotbugs)
- alias(libs.plugins.nexus)
alias(libs.plugins.download)
}
@@ -310,13 +309,104 @@ subprojects {
// Configure publishing for maven central. This is done in the top-level build.gradle, and then
// all of the subprojects configure the artifacts they want published by applying
// ${rootDir}/gradle/publishing.gradle in the project gradle. By default, we publish a library
-// from each package, but this can be configured by adjusting the publishLibrary variable
-if (Boolean.parseBoolean(centralPublish)) {
- nexusPublishing {
- repositories {
- sonatype {
- // Update the URL now that the OSSRH service has been sunset: https://central.sonatype.org/news/20250326_ossrh_sunset/
- nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/"))
+// from each package, but this can be configured by adjusting the publishLibrary variable.
+// Each subproject will place its artifacts into the central staging repository,
+// which will then be picked up by the bundle task
+if (Boolean.parseBoolean(publishBuild) && Boolean.parseBoolean(centralPublish)) {
+ var stagingRepo = layout.buildDirectory.dir('staging-repo')
+
+ // Task to create a bundle zip of all published artifacts
+ tasks.register('createMavenCentralBundle', Zip) {
+ description = "Creates a bundle zip of all artifacts for Maven Central upload"
+ group = "publishing"
+
+ dependsOn subprojects.collect { it.tasks.matching { it.name == 'publish' } }
+
+ archiveBaseName = "${rootProject.group}-${rootProject.name}"
+ archiveVersion = project.version
+ archiveClassifier = 'bundle'
+ destinationDirectory = layout.buildDirectory.dir('distributions')
+
+ from(stagingRepo) {
+ include "${rootProject.group.replaceAll('.', '/')}/**/${project.version}/*"
+ exclude '**/maven-metadata*'
+ }
+
+ includeEmptyDirs = false
+ }
+
+ // Task to upload the bundle to Maven Central
+ tasks.register('publishMavenCentralBundle') {
+ description = "Uploads the bundle zip to Maven Central Portal"
+ group = "publishing"
+
+ dependsOn tasks.named('createMavenCentralBundle')
+
+ doLast {
+ def sonatypeUsername = findProperty('sonatypeUsername')
+ def sonatypePassword = findProperty('sonatypePassword')
+
+ if (sonatypeUsername == null || sonatypePassword == null) {
+ throw new GradleException("sonatypeUsername and sonatypePassword properties must be set")
+ }
+
+ // Create base64 encoded credentials
+ def credentials = "${sonatypeUsername}:${sonatypePassword}"
+ def encodedCredentials = Base64.getEncoder().encodeToString(credentials.getBytes('UTF-8'))
+
+ // Get the bundle file
+ def bundleTask = tasks.named('createMavenCentralBundle').get()
+ def bundleFile = bundleTask.archiveFile.get().asFile
+
+ if (!bundleFile.exists()) {
+ throw new GradleException("Bundle file does not exist: ${bundleFile}")
+ }
+
+ logger.lifecycle("Uploading bundle: ${bundleFile.name} (${bundleFile.length() / 1024 / 1024} MB)")
+
+ // Upload using HttpURLConnection for multipart/form-data
+ def url = new URL('https://central.sonatype.com/api/v1/publisher/upload?publishingType=AUTOMATIC')
+ def connection = url.openConnection() as HttpURLConnection
+
+ try {
+ connection.setRequestMethod('POST')
+ connection.setDoOutput(true)
+ connection.setRequestProperty('Authorization', "Bearer ${encodedCredentials}")
+
+ def boundary = "----GradleBoundary${System.currentTimeMillis()}"
+ connection.setRequestProperty('Content-Type', "multipart/form-data; boundary=${boundary}")
+
+ connection.outputStream.withWriter('UTF-8') { writer ->
+ writer.write("--${boundary}\r\n")
+ writer.write("Content-Disposition: form-data; name=\"bundle\"; filename=\"${bundleFile.name}\"\r\n")
+ writer.write("Content-Type: application/octet-stream\r\n")
+ writer.write("\r\n")
+ writer.flush()
+
+ // Write the file bytes
+ bundleFile.withInputStream { input ->
+ connection.outputStream << input
+ }
+
+ writer.write("\r\n")
+ writer.write("--${boundary}--\r\n")
+ writer.flush()
+ }
+
+ def responseCode = connection.responseCode
+ def responseMessage = connection.responseMessage
+
+ if (responseCode >= 200 && responseCode < 300) {
+ def deploymentId = connection.inputStream.text.trim()
+ logger.lifecycle("Upload successful!")
+ logger.lifecycle("Deployment ID: ${deploymentId}")
+ logger.lifecycle("You can check the status at: https://central.sonatype.com/api/v1/publisher/status?id=${deploymentId}")
+ } else {
+ def errorResponse = connection.errorStream?.text ?: responseMessage
+ throw new GradleException("Upload failed with status ${responseCode}: ${errorResponse}")
+ }
+ } finally {
+ connection.disconnect()
}
}
}
diff --git a/docs/sphinx/source/ReleaseNotes.md b/docs/sphinx/source/ReleaseNotes.md
index 3d0f5b51b5..521b0a62c8 100644
--- a/docs/sphinx/source/ReleaseNotes.md
+++ b/docs/sphinx/source/ReleaseNotes.md
@@ -7,37 +7,6 @@ As the [versioning guide](Versioning.md) details, it cannot always be determined
## 4.8
-### 4.8.9.0
-
-
-
-
-
-
-* Increase heap size for JReleaser - [PR #3720](https://github.com/FoundationDB/fdb-record-layer/pull/3720)
-* Clean up 4.8.4.0 and 4.8.7.0 release notes - [PR #3719](https://github.com/FoundationDB/fdb-record-layer/pull/3719)
-* Upload the jreleaser trace logs & config - [PR #3718](https://github.com/FoundationDB/fdb-record-layer/pull/3718)
-* Reduce test load to reduce flakiness - [PR #3712](https://github.com/FoundationDB/fdb-record-layer/pull/3712)
-* Update the release plugin to use the central publishing API directly - [PR #3710](https://github.com/FoundationDB/fdb-record-layer/pull/3710)
-
- Build/Test/Documentation/Style Improvements (click to expand)
-
-