Merge pull request #8130 from eed3si9n/wip/querystring

[1.x] fix: Fix Sonatype publishing
This commit is contained in:
eugene yokota 2025-05-18 15:39:24 -04:00 committed by GitHub
commit 8a7d84aa08
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 43 additions and 19 deletions

View File

@ -11,6 +11,7 @@ package internal
package sona package sona
import gigahorse.*, support.apachehttp.Gigahorse import gigahorse.*, support.apachehttp.Gigahorse
import java.net.URLEncoder
import java.util.Base64 import java.util.Base64
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.nio.file.Path import java.nio.file.Path
@ -30,7 +31,7 @@ class Sona(client: SonaClient) extends AutoCloseable {
log: Logger, log: Logger,
): Unit = { ): Unit = {
val deploymentId = client.uploadBundle(bundleZipPath, deploymentName, pt, log) val deploymentId = client.uploadBundle(bundleZipPath, deploymentName, pt, log)
client.waitForDeploy(deploymentId, pt, log) client.waitForDeploy(deploymentId, deploymentName, pt, 1, log)
} }
def close(): Unit = client.close() def close(): Unit = client.close()
} }
@ -49,16 +50,17 @@ class SonaClient(reqTransform: Request => Request) extends AutoCloseable {
log: Logger, log: Logger,
): String = { ): String = {
val res = retryF(maxAttempt = 2) { (attempt: Int) => val res = retryF(maxAttempt = 2) { (attempt: Int) =>
log.info(s"uploading bundle to Sonatype Central (attempt: $attempt)") log.info(s"uploading bundle to the Central Portal (attempt: $attempt)")
val req = Gigahorse // addQuery string doesn't work for post
.url(s"${baseUrl}/publisher/upload") val q = queryString(
.addQueryString(
"name" -> deploymentName, "name" -> deploymentName,
"publishingType" -> (publishingType match { "publishingType" -> (publishingType match {
case PublishingType.Automatic => "AUTOMATIC" case PublishingType.Automatic => "AUTOMATIC"
case PublishingType.UserManaged => "USER_MANAGED" case PublishingType.UserManaged => "USER_MANAGED"
}) })
) )
val req = Gigahorse
.url(s"${baseUrl}/publisher/upload?$q")
.post( .post(
MultipartFormBody( MultipartFormBody(
FormPart("bundle", bundleZipPath.toFile()) FormPart("bundle", bundleZipPath.toFile())
@ -70,23 +72,39 @@ class SonaClient(reqTransform: Request => Request) extends AutoCloseable {
awaitWithMessage(res, "uploading...", log) awaitWithMessage(res, "uploading...", log)
} }
def queryString(kv: (String, String)*): String =
kv.map {
case (k, v) =>
val encodedV = URLEncoder.encode(v, "UTF-8")
s"$k=$encodedV"
}
.mkString("&")
def waitForDeploy( def waitForDeploy(
deploymentId: String, deploymentId: String,
deploymentName: String,
publishingType: PublishingType, publishingType: PublishingType,
attempt: Int,
log: Logger, log: Logger,
): Unit = { ): Unit = {
val status = deploymentStatus(deploymentId) val status = deploymentStatus(deploymentId)
log.info(s"deployment $deploymentId ${status.deploymentState}") log.info(s"deployment $deploymentName ${status.deploymentState} ${attempt}/n")
val sleepSec =
if (attempt <= 3) List(5, 5, 10, 15)(attempt)
else 30
status.deploymentState match { status.deploymentState match {
case DeploymentState.FAILED => sys.error(s"deployment $deploymentId failed") case DeploymentState.FAILED => sys.error(s"deployment $deploymentId failed")
case DeploymentState.PENDING | DeploymentState.PUBLISHING | DeploymentState.VALIDATING => case DeploymentState.PENDING | DeploymentState.PUBLISHING | DeploymentState.VALIDATING =>
Thread.sleep(5000) Thread.sleep(sleepSec * 1000L)
waitForDeploy(deploymentId, publishingType, log) waitForDeploy(deploymentId, deploymentName, publishingType, attempt + 1, log)
case DeploymentState.PUBLISHED if publishingType == PublishingType.Automatic => () case DeploymentState.PUBLISHED if publishingType == PublishingType.Automatic => ()
case DeploymentState.VALIDATED if publishingType == PublishingType.UserManaged => () case DeploymentState.VALIDATED if publishingType == PublishingType.UserManaged => ()
case DeploymentState.VALIDATED =>
Thread.sleep(sleepSec * 1000L)
waitForDeploy(deploymentId, deploymentName, publishingType, attempt + 1, log)
case _ => case _ =>
Thread.sleep(5000) Thread.sleep(sleepSec * 1000L)
waitForDeploy(deploymentId, publishingType, log) waitForDeploy(deploymentId, deploymentName, publishingType, attempt + 1, log)
} }
} }

View File

@ -10,7 +10,7 @@ package sbt
import java.io.{ File, PrintWriter } import java.io.{ File, PrintWriter }
import java.nio.file.{ Path => NioPath } import java.nio.file.{ Path => NioPath }
import java.util.Optional import java.util.{ Optional, UUID }
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import lmcoursier.CoursierDependencyResolution import lmcoursier.CoursierDependencyResolution
import lmcoursier.definitions.{ Configuration => CConfiguration } import lmcoursier.definitions.{ Configuration => CConfiguration }
@ -3102,6 +3102,12 @@ object Classpaths {
if (!alreadyContainsCentralCredentials) SysProp.sonatypeCredentalsEnv.toSeq if (!alreadyContainsCentralCredentials) SysProp.sonatypeCredentalsEnv.toSeq
else Nil else Nil
}, },
sonaDeploymentName := {
val o = organization.value
val v = version.value
val uuid = UUID.randomUUID().toString().take(8)
s"$o:$v:$uuid"
},
) )
@nowarn("cat=deprecation") @nowarn("cat=deprecation")

View File

@ -570,6 +570,7 @@ object Keys {
val stagingDirectory = settingKey[File]("Local staging directory for Sonatype publishing").withRank(CSetting) val stagingDirectory = settingKey[File]("Local staging directory for Sonatype publishing").withRank(CSetting)
val sonaBundle = taskKey[File]("Local bundle for Sonatype publishing").withRank(DTask) val sonaBundle = taskKey[File]("Local bundle for Sonatype publishing").withRank(DTask)
val localStaging = settingKey[Option[Resolver]]("Local staging resolver for Sonatype publishing").withRank(CSetting) val localStaging = settingKey[Option[Resolver]]("Local staging resolver for Sonatype publishing").withRank(CSetting)
val sonaDeploymentName = settingKey[String]("The name used for deployment").withRank(DSetting)
val classifiersModule = taskKey[GetClassifiersModule]("classifiers-module").withRank(CTask) val classifiersModule = taskKey[GetClassifiersModule]("classifiers-module").withRank(CTask)
val compatibilityWarningOptions = settingKey[CompatibilityWarningOptions]("Configures warnings around Maven incompatibility.").withRank(CSetting) val compatibilityWarningOptions = settingKey[CompatibilityWarningOptions]("Configures warnings around Maven incompatibility.").withRank(CSetting)

View File

@ -11,7 +11,6 @@ package internal
package librarymanagement package librarymanagement
import java.nio.file.Path import java.nio.file.Path
import java.util.UUID
import sbt.internal.util.MessageOnlyException import sbt.internal.util.MessageOnlyException
import sbt.io.IO import sbt.io.IO
import sbt.io.Path.contentOf import sbt.io.Path.contentOf
@ -40,12 +39,12 @@ object Publishing {
private def sonatypeReleaseAction(pt: PublishingType)(s0: State): State = { private def sonatypeReleaseAction(pt: PublishingType)(s0: State): State = {
val extracted = Project.extract(s0) val extracted = Project.extract(s0)
val log = extracted.get(Keys.sLog) val log = extracted.get(Keys.sLog)
val dn = extracted.get(Keys.sonaDeploymentName)
val (s1, bundle) = extracted.runTask(Keys.sonaBundle, s0) val (s1, bundle) = extracted.runTask(Keys.sonaBundle, s0)
val (s2, creds) = extracted.runTask(Keys.credentials, s1) val (s2, creds) = extracted.runTask(Keys.credentials, s1)
val client = fromCreds(creds) val client = fromCreds(creds)
try { try {
val uuid = UUID.randomUUID().toString().take(8) client.uploadBundle(bundle.toPath(), dn, pt, log)
client.uploadBundle(bundle.toPath(), uuid, pt, log)
s2 s2
} finally { } finally {
client.close() client.close()