mirror of https://github.com/sbt/sbt.git
Merge pull request #8182 from eed3si9n/wip/merge-1.11.x
[2.x] merge 1.11.x
This commit is contained in:
commit
36a4fe4af7
|
|
@ -636,6 +636,7 @@ lazy val dependencyTreeProj = (project in file("dependency-tree"))
|
|||
name := "sbt-dependency-tree",
|
||||
pluginCrossBuild / sbtVersion := version.value,
|
||||
publishMavenStyle := true,
|
||||
sbtPluginPublishLegacyMavenStyle := false,
|
||||
// mimaSettings,
|
||||
mimaPreviousArtifacts := Set.empty,
|
||||
)
|
||||
|
|
@ -1346,8 +1347,10 @@ def customCommands: Seq[Setting[?]] = Seq(
|
|||
|
||||
ThisBuild / pomIncludeRepository := (_ => false) // drop repos other than Maven Central from POM
|
||||
ThisBuild / publishTo := {
|
||||
val nexus = "https://oss.sonatype.org/"
|
||||
Some("releases" at nexus + "service/local/staging/deploy/maven2")
|
||||
val centralSnapshots = "https://central.sonatype.com/repository/maven-snapshots/"
|
||||
val v = (ThisBuild / version).value
|
||||
if (v.endsWith("SNAPSHOT")) Some("central-snapshots" at centralSnapshots)
|
||||
else localStaging.value
|
||||
}
|
||||
ThisBuild / publishMavenStyle := true
|
||||
|
||||
|
|
|
|||
|
|
@ -10,18 +10,20 @@ package sbt
|
|||
package internal
|
||||
package sona
|
||||
|
||||
import gigahorse.*, support.apachehttp.Gigahorse
|
||||
import java.net.URLEncoder
|
||||
import java.util.Base64
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.Path
|
||||
import gigahorse.*
|
||||
import gigahorse.support.apachehttp.Gigahorse
|
||||
import sbt.util.Logger
|
||||
import sjsonnew.JsonFormat
|
||||
import sjsonnew.support.scalajson.unsafe.{ Converter, Parser }
|
||||
import sjsonnew.shaded.scalajson.ast.unsafe.JValue
|
||||
import sjsonnew.support.scalajson.unsafe.{ Converter, Parser }
|
||||
|
||||
import java.net.URLEncoder
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.Path
|
||||
import java.util.Base64
|
||||
import scala.annotation.nowarn
|
||||
import scala.concurrent.*, duration.*
|
||||
import scala.concurrent.*
|
||||
import scala.concurrent.duration.*
|
||||
|
||||
class Sona(client: SonaClient) extends AutoCloseable {
|
||||
def uploadBundle(
|
||||
|
|
@ -36,20 +38,33 @@ class Sona(client: SonaClient) extends AutoCloseable {
|
|||
def close(): Unit = client.close()
|
||||
}
|
||||
|
||||
class SonaClient(reqTransform: Request => Request) extends AutoCloseable {
|
||||
class SonaClient(reqTransform: Request => Request, uploadRequestTimeout: FiniteDuration)
|
||||
extends AutoCloseable {
|
||||
import SonaClient.baseUrl
|
||||
|
||||
val gigahorseConfig = Gigahorse.config
|
||||
.withRequestTimeout(2.minute)
|
||||
.withReadTimeout(2.minute)
|
||||
val http = Gigahorse.http(gigahorseConfig)
|
||||
private val http = {
|
||||
val defaultHttpRequestTimeout = 2.minutes
|
||||
|
||||
val gigahorseConfig = Gigahorse.config
|
||||
.withRequestTimeout(defaultHttpRequestTimeout)
|
||||
.withReadTimeout(defaultHttpRequestTimeout)
|
||||
|
||||
Gigahorse.http(gigahorseConfig)
|
||||
}
|
||||
|
||||
def uploadBundle(
|
||||
bundleZipPath: Path,
|
||||
deploymentName: String,
|
||||
publishingType: PublishingType,
|
||||
log: Logger,
|
||||
): String = {
|
||||
val res = retryF(maxAttempt = 2) { (attempt: Int) =>
|
||||
val maxAttempt = 2
|
||||
val waitDurationBetweenAtttempt = 5.seconds
|
||||
// Adding an extra 5.seconds as security margins
|
||||
val totalAwaitDuration =
|
||||
maxAttempt * uploadRequestTimeout + maxAttempt * waitDurationBetweenAtttempt + 5.seconds
|
||||
|
||||
val res = retryF(maxAttempt, waitDurationBetweenAtttempt) { (attempt: Int) =>
|
||||
log.info(s"uploading bundle to the Central Portal (attempt: $attempt)")
|
||||
// addQuery string doesn't work for post
|
||||
val q = queryString(
|
||||
|
|
@ -66,13 +81,13 @@ class SonaClient(reqTransform: Request => Request) extends AutoCloseable {
|
|||
FormPart("bundle", bundleZipPath.toFile())
|
||||
)
|
||||
)
|
||||
.withRequestTimeout(600.second)
|
||||
.withRequestTimeout(uploadRequestTimeout)
|
||||
http.run(reqTransform(req), Gigahorse.asString)
|
||||
}
|
||||
awaitWithMessage(res, "uploading...", log)
|
||||
awaitWithMessage(res, "uploading...", log, totalAwaitDuration)
|
||||
}
|
||||
|
||||
def queryString(kv: (String, String)*): String =
|
||||
private def queryString(kv: (String, String)*): String =
|
||||
kv.map { case (k, v) =>
|
||||
val encodedV = URLEncoder.encode(v, "UTF-8")
|
||||
s"$k=$encodedV"
|
||||
|
|
@ -106,17 +121,17 @@ class SonaClient(reqTransform: Request => Request) extends AutoCloseable {
|
|||
}
|
||||
}
|
||||
|
||||
def deploymentStatus(deploymentId: String): PublisherStatus = {
|
||||
val res = retryF(maxAttempt = 5) { (attempt: Int) =>
|
||||
private def deploymentStatus(deploymentId: String): PublisherStatus = {
|
||||
val res = retryF(maxAttempt = 5, waitDurationBetweenAttempt = 5.seconds) { (attempt: Int) =>
|
||||
deploymentStatusF(deploymentId)
|
||||
}
|
||||
Await.result(res, 600.seconds)
|
||||
Await.result(res, 10.minutes)
|
||||
}
|
||||
|
||||
/**
|
||||
* https://central.sonatype.org/publish/publish-portal-api/#verify-status-of-the-deployment
|
||||
*/
|
||||
def deploymentStatusF(deploymentId: String): Future[PublisherStatus] = {
|
||||
private def deploymentStatusF(deploymentId: String): Future[PublisherStatus] = {
|
||||
val req = Gigahorse
|
||||
.url(s"${baseUrl}/publisher/status")
|
||||
.addQueryString("id" -> deploymentId)
|
||||
|
|
@ -128,43 +143,52 @@ class SonaClient(reqTransform: Request => Request) extends AutoCloseable {
|
|||
* Retry future function on any error.
|
||||
*/
|
||||
@nowarn
|
||||
def retryF[A1](maxAttempt: Int)(f: Int => Future[A1]): Future[A1] = {
|
||||
private def retryF[A1](maxAttempt: Int, waitDurationBetweenAttempt: FiniteDuration)(
|
||||
f: Int => Future[A1]
|
||||
): Future[A1] = {
|
||||
import scala.concurrent.ExecutionContext.Implicits.*
|
||||
def impl(retry: Int): Future[A1] = {
|
||||
val res = f(retry + 1)
|
||||
res.recoverWith {
|
||||
case _ if retry < maxAttempt =>
|
||||
Thread.sleep(5000)
|
||||
impl(retry + 1)
|
||||
sleep(waitDurationBetweenAttempt).flatMap(_ => impl(retry + 1))
|
||||
}
|
||||
}
|
||||
impl(0)
|
||||
}
|
||||
|
||||
def awaitWithMessage[A1](f: Future[A1], msg: String, log: Logger): A1 = {
|
||||
private def awaitWithMessage[A1](
|
||||
f: Future[A1],
|
||||
msg: String,
|
||||
log: Logger,
|
||||
awaitDuration: FiniteDuration,
|
||||
): A1 = {
|
||||
import scala.concurrent.ExecutionContext.Implicits.*
|
||||
def loop(attempt: Int): Unit =
|
||||
def logLoop(attempt: Int): Unit =
|
||||
if (!f.isCompleted) {
|
||||
if (attempt > 0) {
|
||||
log.info(msg)
|
||||
}
|
||||
Future {
|
||||
blocking {
|
||||
Thread.sleep(30.second.toMillis)
|
||||
}
|
||||
}.foreach(_ => loop(attempt + 1))
|
||||
sleep(30.second).foreach(_ => logLoop(attempt + 1))
|
||||
} else ()
|
||||
loop(0)
|
||||
Await.result(f, 600.seconds)
|
||||
logLoop(0)
|
||||
Await.result(f, awaitDuration)
|
||||
}
|
||||
|
||||
def close(): Unit = http.close()
|
||||
|
||||
private def sleep(duration: FiniteDuration)(implicit executor: ExecutionContext): Future[Unit] =
|
||||
Future {
|
||||
blocking {
|
||||
Thread.sleep(duration.toMillis)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object Sona {
|
||||
def host: String = SonaClient.host
|
||||
def oauthClient(userName: String, userToken: String): Sona =
|
||||
new Sona(SonaClient.oauthClient(userName, userToken))
|
||||
def oauthClient(userName: String, userToken: String, uploadRequestTimeout: FiniteDuration): Sona =
|
||||
new Sona(SonaClient.oauthClient(userName, userToken, uploadRequestTimeout))
|
||||
}
|
||||
|
||||
object SonaClient {
|
||||
|
|
@ -175,8 +199,12 @@ object SonaClient {
|
|||
Parser.parseFromByteBuffer(r.bodyAsByteBuffer).get
|
||||
def as[A1: JsonFormat]: FullResponse => A1 = asJson.andThen(Converter.fromJsonUnsafe[A1])
|
||||
val asPublisherStatus: FullResponse => PublisherStatus = as[PublisherStatus]
|
||||
def oauthClient(userName: String, userToken: String): SonaClient =
|
||||
new SonaClient(OAuthClient(userName, userToken))
|
||||
def oauthClient(
|
||||
userName: String,
|
||||
userToken: String,
|
||||
uploadRequestTimeout: FiniteDuration
|
||||
): SonaClient =
|
||||
new SonaClient(OAuthClient(userName, userToken), uploadRequestTimeout)
|
||||
}
|
||||
|
||||
private case class OAuthClient(userName: String, userToken: String)
|
||||
|
|
|
|||
|
|
@ -2755,7 +2755,7 @@ object Classpaths {
|
|||
private lazy val packagedDefaultArtifacts = packaged(defaultArtifactTasks)
|
||||
private lazy val sbt2Plus: Def.Initialize[Boolean] = Def.setting {
|
||||
val sbtV = (pluginCrossBuild / sbtBinaryVersion).value
|
||||
sbtV != "1.0" && !sbtV.startsWith("0.")
|
||||
!sbtV.startsWith("1.") && !sbtV.startsWith("0.")
|
||||
}
|
||||
val jvmPublishSettings: Seq[Setting[?]] = Seq(
|
||||
artifacts := artifactDefs(defaultArtifactTasks).value,
|
||||
|
|
@ -2872,6 +2872,7 @@ object Classpaths {
|
|||
val uuid = UUID.randomUUID().toString().take(8)
|
||||
s"$o:$v:$uuid"
|
||||
},
|
||||
sonaUploadRequestTimeout := 10.minutes,
|
||||
)
|
||||
|
||||
def baseGlobalDefaults =
|
||||
|
|
|
|||
|
|
@ -629,6 +629,7 @@ object Keys {
|
|||
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 sonaDeploymentName = settingKey[String]("The name used for deployment").withRank(DSetting)
|
||||
val sonaUploadRequestTimeout = settingKey[FiniteDuration]("Request timeout for Sonatype publishing").withRank(DSetting)
|
||||
|
||||
val classifiersModule = taskKey[GetClassifiersModule]("classifiers-module").withRank(CTask)
|
||||
val compatibilityWarningOptions = settingKey[CompatibilityWarningOptions]("Configures warnings around Maven incompatibility.").withRank(CSetting)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
package sbt
|
||||
|
||||
import sbt.librarymanagement.{ MavenRepository, Resolver }
|
||||
import sbt.librarymanagement.Resolver
|
||||
import sbt.librarymanagement.ivy.Credentials
|
||||
|
||||
import java.io.File
|
||||
|
|
@ -42,21 +42,7 @@ object Opts {
|
|||
}
|
||||
object resolver {
|
||||
import sbt.io.syntax.*
|
||||
@deprecated("Use sonatypeOssReleases instead", "1.7.0")
|
||||
val sonatypeReleases = Resolver.sonatypeRepo("releases")
|
||||
// todo: fix
|
||||
// val sonatypeOssReleases = Resolver.sonatypeOssRepos("releases")
|
||||
|
||||
@deprecated("Use sonatypeOssSnapshots instead", "1.7.0")
|
||||
val sonatypeSnapshots = Resolver.sonatypeRepo("snapshots")
|
||||
|
||||
// todo: fix
|
||||
// val sonatypeOssSnapshots = Resolver.sonatypeOssRepos("snapshots")
|
||||
|
||||
val sonatypeStaging = MavenRepository(
|
||||
"sonatype-staging",
|
||||
"https://oss.sonatype.org/service/local/staging/deploy/maven2"
|
||||
)
|
||||
val mavenLocalFile = Resolver.file("Local Repository", userHome / ".m2" / "repository")(using
|
||||
Resolver.defaultPatterns
|
||||
)
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ object LintUnused {
|
|||
shellPrompt,
|
||||
sLog,
|
||||
traceLevel,
|
||||
sonaDeploymentName,
|
||||
),
|
||||
includeLintKeys := Set(
|
||||
scalacOptions,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,9 @@ import sbt.internal.util.MessageOnlyException
|
|||
import sbt.io.IO
|
||||
import sbt.io.Path.contentOf
|
||||
import sbt.librarymanagement.ivy.Credentials
|
||||
import sona.{ Sona, PublishingType }
|
||||
import sona.{ PublishingType, Sona }
|
||||
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
|
||||
object Publishing {
|
||||
val sonaRelease: Command =
|
||||
|
|
@ -36,23 +38,24 @@ object Publishing {
|
|||
bundlePath
|
||||
}
|
||||
|
||||
private def sonatypeReleaseAction(pt: PublishingType)(s0: State): State = {
|
||||
private def sonatypeReleaseAction(publishingType: PublishingType)(s0: State): State = {
|
||||
import sbt.ProjectExtra.extract
|
||||
val extracted = Project.extract(s0)
|
||||
val log = extracted.get(Keys.sLog)
|
||||
val dn = extracted.get(Keys.sonaDeploymentName)
|
||||
val v = extracted.get(Keys.version)
|
||||
if (v.endsWith("-SNAPSHOT")) {
|
||||
val version = extracted.get(Keys.version)
|
||||
if (version.endsWith("-SNAPSHOT")) {
|
||||
log.error("""SNAPSHOTs are not supported on the Central Portal;
|
||||
configure ThisBuild / publishTo to publish directly to the central-snapshots.
|
||||
see https://www.scala-sbt.org/1.x/docs/Using-Sonatype.html for details.""")
|
||||
s0.fail
|
||||
} else {
|
||||
val deploymentName = extracted.get(Keys.sonaDeploymentName)
|
||||
val uploadRequestTimeout = extracted.get(Keys.sonaUploadRequestTimeout)
|
||||
val (s1, bundle) = extracted.runTask(Keys.sonaBundle, s0)
|
||||
val (s2, creds) = extracted.runTask(Keys.credentials, s1)
|
||||
val client = fromCreds(creds)
|
||||
val client = fromCreds(creds, uploadRequestTimeout)
|
||||
try {
|
||||
client.uploadBundle(bundle.toPath(), dn, pt, log)
|
||||
client.uploadBundle(bundle.toPath(), deploymentName, publishingType, log)
|
||||
s2
|
||||
} finally {
|
||||
client.close()
|
||||
|
|
@ -60,10 +63,10 @@ see https://www.scala-sbt.org/1.x/docs/Using-Sonatype.html for details.""")
|
|||
}
|
||||
}
|
||||
|
||||
private def fromCreds(creds: Seq[Credentials]): Sona = {
|
||||
private def fromCreds(creds: Seq[Credentials], uploadRequestTimeout: FiniteDuration): Sona = {
|
||||
val cred = Credentials
|
||||
.forHost(creds, Sona.host)
|
||||
.getOrElse(throw new MessageOnlyException(s"no credentials are found for ${Sona.host}"))
|
||||
Sona.oauthClient(cred.userName, cred.passwd)
|
||||
Sona.oauthClient(cred.userName, cred.passwd, uploadRequestTimeout)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
2
sbt
2
sbt
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set +e
|
||||
declare builtin_sbt_version="1.11.0"
|
||||
declare builtin_sbt_version="1.11.3"
|
||||
declare -a residual_args
|
||||
declare -a java_args
|
||||
declare -a scalac_args
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ lazy val sbtPlugin1 = project.in(file("sbt-plugin-1"))
|
|||
name := "sbt-plugin-1",
|
||||
addSbtPlugin("ch.epfl.scala" % "sbt-plugin-example-diamond" % "0.5.0"),
|
||||
publishTo := Some(resolver),
|
||||
checkPackagedArtifacts := checkPackagedArtifactsDef("sbt-plugin-1", true).value,
|
||||
checkPublish := checkPublishDef("sbt-plugin-1", true).value
|
||||
checkPackagedArtifacts := checkPackagedArtifactsDef("sbt-plugin-1", false).value,
|
||||
checkPublish := checkPublishDef("sbt-plugin-1", false).value
|
||||
)
|
||||
|
||||
lazy val testMaven1 = project.in(file("test-maven-1"))
|
||||
|
|
|
|||
Loading…
Reference in New Issue