Skip to content

Commit 345de8d

Browse files
authored
Revert publishing directly via sonatype API (#3728)
This reverts #3723 and #3724 and returns us back to publishing via the nexus publishing API. The impetus for this is that the last approach got stuck in a publishing state after the upload (see: https://github.com/FoundationDB/fdb-record-layer/actions/runs/19117047380/job/54633288001, which succeeded, but for which we didn't get artifacts actually published despite the server upload succeeding). When we are in this mode, we expect the build to fail with a 400 error letting us know that we didn't upload anything. However, if we didn't `close` the staging repositories (which is the step that is failing), then legacy API would not be expected to upload anything to the central repo. So we need to continue to do that part even though we think it will fail.
1 parent 71ee401 commit 345de8d

File tree

4 files changed

+10
-112
lines changed

4 files changed

+10
-112
lines changed

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ jobs:
185185
- name: Publish Artifacts
186186
uses: ./actions/run-gradle
187187
with:
188-
gradle_command: publish publishMavenCentralBundle -PreleaseBuild=true -PpublishBuild=true -PgithubPublish=true -PcentralPublish=true
188+
gradle_command: publish closeAndReleaseStagingRepositories -PreleaseBuild=true -PpublishBuild=true -PgithubPublish=true -PcentralPublish=true
189189
env:
190190
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_PRIVATE_KEY }}
191191
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_PASSPHRASE }}

build.gradle

Lines changed: 8 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ plugins {
4141
alias(libs.plugins.protobuf)
4242
alias(libs.plugins.versions)
4343
alias(libs.plugins.spotbugs)
44+
alias(libs.plugins.nexus)
4445
alias(libs.plugins.download)
4546
}
4647

@@ -309,109 +310,13 @@ subprojects {
309310
// Configure publishing for maven central. This is done in the top-level build.gradle, and then
310311
// all of the subprojects configure the artifacts they want published by applying
311312
// ${rootDir}/gradle/publishing.gradle in the project gradle. By default, we publish a library
312-
// from each package, but this can be configured by adjusting the publishLibrary variable.
313-
// Each subproject will place its artifacts into the central staging repository,
314-
// which will then be picked up by the bundle task
315-
if (Boolean.parseBoolean(publishBuild) && Boolean.parseBoolean(centralPublish)) {
316-
var stagingRepo = layout.buildDirectory.dir('staging-repo')
317-
318-
// Task to create a bundle zip of all published artifacts
319-
tasks.register('createMavenCentralBundle', Zip) {
320-
description = "Creates a bundle zip of all artifacts for Maven Central upload"
321-
group = "publishing"
322-
323-
dependsOn subprojects.collect { it.tasks.matching { it.name == 'publish' } }
324-
325-
archiveBaseName = "${rootProject.group}-${rootProject.name}"
326-
archiveVersion = project.version
327-
archiveClassifier = 'bundle'
328-
destinationDirectory = layout.buildDirectory.dir('distributions')
329-
330-
from(stagingRepo) {
331-
include "${rootProject.group.replaceAll('.', '/')}/**/${project.version}/*"
332-
exclude '**/maven-metadata*'
333-
}
334-
335-
includeEmptyDirs = false
336-
}
337-
338-
// Task to upload the bundle to Maven Central
339-
tasks.register('publishMavenCentralBundle') {
340-
description = "Uploads the bundle zip to Maven Central Portal"
341-
group = "publishing"
342-
343-
dependsOn tasks.named('createMavenCentralBundle')
344-
345-
doLast {
346-
def sonatypeUsername = findProperty('sonatypeUsername')
347-
def sonatypePassword = findProperty('sonatypePassword')
348-
349-
if (sonatypeUsername == null || sonatypePassword == null) {
350-
throw new GradleException("sonatypeUsername and sonatypePassword properties must be set")
351-
}
352-
353-
// Create base64 encoded credentials
354-
def credentials = "${sonatypeUsername}:${sonatypePassword}"
355-
def encodedCredentials = Base64.getEncoder().encodeToString(credentials.getBytes('UTF-8'))
356-
357-
// Get the bundle file
358-
def bundleTask = tasks.named('createMavenCentralBundle').get()
359-
def bundleFile = bundleTask.archiveFile.get().asFile
360-
361-
if (!bundleFile.exists()) {
362-
throw new GradleException("Bundle file does not exist: ${bundleFile}")
363-
}
364-
365-
logger.lifecycle("Uploading bundle: ${bundleFile.name} (${bundleFile.length() / 1024 / 1024} MB)")
366-
367-
// Upload using HttpURLConnection for multipart/form-data
368-
def url = new URL('https://central.sonatype.com/api/v1/publisher/upload?publishingType=AUTOMATIC')
369-
def connection = url.openConnection() as HttpURLConnection
370-
371-
try {
372-
connection.setRequestMethod('POST')
373-
connection.setDoOutput(true)
374-
connection.setRequestProperty('Authorization', "Bearer ${encodedCredentials}")
375-
376-
def boundary = "----GradleBoundary${UUID.randomUUID().toString()}"
377-
connection.setRequestProperty('Content-Type', "multipart/form-data; boundary=${boundary}")
378-
379-
// Use a chunked streaming mode to break data up into smaller chunks to avoid needing to load everything into memory
380-
connection.setChunkedStreamingMode(8192)
381-
382-
// Use buffered output stream and write directly to avoid loading entire file into memory
383-
connection.outputStream.withStream { output ->
384-
def CRLF = "\r\n"
385-
386-
// Write multipart headers
387-
output.write("--${boundary}${CRLF}".getBytes('UTF-8'))
388-
output.write("Content-Disposition: form-data; name=\"bundle\"; filename=\"${bundleFile.name}\"${CRLF}".getBytes('UTF-8'))
389-
output.write("Content-Type: application/octet-stream${CRLF}".getBytes('UTF-8'))
390-
output.write(CRLF.getBytes('UTF-8'))
391-
392-
// Next write the bundle data to the stream
393-
bundleFile.withInputStream { InputStream input -> output << input }
394-
395-
// Write multipart closing boundary
396-
output.write(CRLF.getBytes('UTF-8'))
397-
output.write("--${boundary}--${CRLF}".getBytes('UTF-8'))
398-
output.flush()
399-
}
400-
401-
def responseCode = connection.responseCode
402-
def responseMessage = connection.responseMessage
403-
404-
if (responseCode >= 200 && responseCode < 300) {
405-
def deploymentId = connection.inputStream.text.trim()
406-
logger.lifecycle("Upload successful!")
407-
logger.lifecycle("Deployment ID: ${deploymentId}")
408-
logger.lifecycle("You can check the status at: https://central.sonatype.com/api/v1/publisher/status?id=${deploymentId}")
409-
} else {
410-
def errorResponse = connection.errorStream?.text ?: responseMessage
411-
throw new GradleException("Upload failed with status ${responseCode}: ${errorResponse}")
412-
}
413-
} finally {
414-
connection.disconnect()
313+
// from each package, but this can be configured by adjusting the publishLibrary variable
314+
if (Boolean.parseBoolean(centralPublish)) {
315+
nexusPublishing {
316+
repositories {
317+
sonatype {
318+
// Update the URL now that the OSSRH service has been sunset: https://central.sonatype.org/news/20250326_ossrh_sunset/
319+
nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/"))
415320
}
416321
}
417322
}

gradle/libs.versions.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ test-compileOnly = [ "autoService", "jsr305" ]
153153
download = { id = "de.undercouch.download", version = "5.6.0" }
154154
gitversion = { id = "com.palantir.git-version", version = "3.1.0" }
155155
jmh = { id = "me.champeau.jmh", version = "0.7.2" }
156+
nexus = { id = "io.github.gradle-nexus.publish-plugin", version = "2.0.0" }
156157
protobuf = { id = "com.google.protobuf", version = "0.9.4" }
157158
serviceloader = { id = "com.github.harbby.gradle.serviceloader", version = "1.1.8" }
158159
shadow = { id = "com.gradleup.shadow", version = "8.3.5" }

gradle/publishing.gradle

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,6 @@ publishing {
113113
}
114114
}
115115
}
116-
117-
if (Boolean.parseBoolean(centralPublish)) {
118-
// Publish this project to the root project's staging repo. This will be
119-
// bundled up by createMavenCentralBundle for publishing
120-
maven {
121-
url = rootProject.layout.buildDirectory.dir("staging-repo")
122-
}
123-
}
124116
}
125117
}
126118
}

0 commit comments

Comments
 (0)