mirror of https://github.com/sbt/sbt.git
Merge pull request #8138 from eed3si9n/wip/merge-1.11.x
[2.x] Merge branch '1.11.x'
This commit is contained in:
commit
5fabfdff32
|
|
@ -646,6 +646,7 @@ lazy val remoteCacheProj = (project in file("sbt-remote-cache"))
|
|||
|
||||
// Implementation and support code for defining actions.
|
||||
lazy val actionsProj = (project in file("main-actions"))
|
||||
.enablePlugins(ContrabandPlugin, JsonCodecPlugin)
|
||||
.dependsOn(
|
||||
completeProj,
|
||||
runProj,
|
||||
|
|
@ -661,7 +662,11 @@ lazy val actionsProj = (project in file("main-actions"))
|
|||
testedBaseSettings,
|
||||
name := "Actions",
|
||||
libraryDependencies += sjsonNewScalaJson.value,
|
||||
libraryDependencies += jline3Terminal,
|
||||
libraryDependencies ++= Seq(gigahorseApacheHttp, jline3Terminal),
|
||||
Compile / managedSourceDirectories +=
|
||||
baseDirectory.value / "src" / "main" / "contraband-scala",
|
||||
Compile / generateContrabands / sourceManaged := baseDirectory.value / "src" / "main" / "contraband-scala",
|
||||
Compile / generateContrabands / contrabandFormatsForType := ContrabandConfig.getFormats,
|
||||
mimaSettings,
|
||||
mimaBinaryIssueFilters ++= Seq(
|
||||
// Removed unused private[sbt] nested class
|
||||
|
|
|
|||
17
main-actions/src/main/contraband-scala/sbt/internal/sona/DeploymentState.scala
generated
Normal file
17
main-actions/src/main/contraband-scala/sbt/internal/sona/DeploymentState.scala
generated
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.sona
|
||||
sealed abstract class DeploymentState extends Serializable
|
||||
object DeploymentState {
|
||||
|
||||
|
||||
case object PENDING extends DeploymentState
|
||||
case object VALIDATING extends DeploymentState
|
||||
case object VALIDATED extends DeploymentState
|
||||
case object PUBLISHING extends DeploymentState
|
||||
case object PUBLISHED extends DeploymentState
|
||||
case object FAILED extends DeploymentState
|
||||
}
|
||||
45
main-actions/src/main/contraband-scala/sbt/internal/sona/PublisherStatus.scala
generated
Normal file
45
main-actions/src/main/contraband-scala/sbt/internal/sona/PublisherStatus.scala
generated
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.sona
|
||||
/** https://central.sonatype.org/publish/publish-portal-api/#uploading-a-deployment-bundle */
|
||||
final class PublisherStatus private (
|
||||
val deploymentId: String,
|
||||
val deploymentName: String,
|
||||
val deploymentState: sbt.internal.sona.DeploymentState,
|
||||
val purls: Vector[String]) extends Serializable {
|
||||
|
||||
|
||||
|
||||
override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match {
|
||||
case x: PublisherStatus => (this.deploymentId == x.deploymentId) && (this.deploymentName == x.deploymentName) && (this.deploymentState == x.deploymentState) && (this.purls == x.purls)
|
||||
case _ => false
|
||||
})
|
||||
override def hashCode: Int = {
|
||||
37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.sona.PublisherStatus".##) + deploymentId.##) + deploymentName.##) + deploymentState.##) + purls.##)
|
||||
}
|
||||
override def toString: String = {
|
||||
"PublisherStatus(" + deploymentId + ", " + deploymentName + ", " + deploymentState + ", " + purls + ")"
|
||||
}
|
||||
private def copy(deploymentId: String = deploymentId, deploymentName: String = deploymentName, deploymentState: sbt.internal.sona.DeploymentState = deploymentState, purls: Vector[String] = purls): PublisherStatus = {
|
||||
new PublisherStatus(deploymentId, deploymentName, deploymentState, purls)
|
||||
}
|
||||
def withDeploymentId(deploymentId: String): PublisherStatus = {
|
||||
copy(deploymentId = deploymentId)
|
||||
}
|
||||
def withDeploymentName(deploymentName: String): PublisherStatus = {
|
||||
copy(deploymentName = deploymentName)
|
||||
}
|
||||
def withDeploymentState(deploymentState: sbt.internal.sona.DeploymentState): PublisherStatus = {
|
||||
copy(deploymentState = deploymentState)
|
||||
}
|
||||
def withPurls(purls: Vector[String]): PublisherStatus = {
|
||||
copy(purls = purls)
|
||||
}
|
||||
}
|
||||
object PublisherStatus {
|
||||
|
||||
def apply(deploymentId: String, deploymentName: String, deploymentState: sbt.internal.sona.DeploymentState, purls: Vector[String]): PublisherStatus = new PublisherStatus(deploymentId, deploymentName, deploymentState, purls)
|
||||
}
|
||||
37
main-actions/src/main/contraband-scala/sbt/internal/sona/codec/DeploymentStateFormats.scala
generated
Normal file
37
main-actions/src/main/contraband-scala/sbt/internal/sona/codec/DeploymentStateFormats.scala
generated
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.sona.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait DeploymentStateFormats { self: sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val DeploymentStateFormat: JsonFormat[sbt.internal.sona.DeploymentState] = new JsonFormat[sbt.internal.sona.DeploymentState] {
|
||||
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.sona.DeploymentState = {
|
||||
__jsOpt match {
|
||||
case Some(__js) =>
|
||||
unbuilder.readString(__js) match {
|
||||
case "PENDING" => sbt.internal.sona.DeploymentState.PENDING
|
||||
case "VALIDATING" => sbt.internal.sona.DeploymentState.VALIDATING
|
||||
case "VALIDATED" => sbt.internal.sona.DeploymentState.VALIDATED
|
||||
case "PUBLISHING" => sbt.internal.sona.DeploymentState.PUBLISHING
|
||||
case "PUBLISHED" => sbt.internal.sona.DeploymentState.PUBLISHED
|
||||
case "FAILED" => sbt.internal.sona.DeploymentState.FAILED
|
||||
}
|
||||
case None =>
|
||||
deserializationError("Expected JsString but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.internal.sona.DeploymentState, builder: Builder[J]): Unit = {
|
||||
val str = obj match {
|
||||
case sbt.internal.sona.DeploymentState.PENDING => "PENDING"
|
||||
case sbt.internal.sona.DeploymentState.VALIDATING => "VALIDATING"
|
||||
case sbt.internal.sona.DeploymentState.VALIDATED => "VALIDATED"
|
||||
case sbt.internal.sona.DeploymentState.PUBLISHING => "PUBLISHING"
|
||||
case sbt.internal.sona.DeploymentState.PUBLISHED => "PUBLISHED"
|
||||
case sbt.internal.sona.DeploymentState.FAILED => "FAILED"
|
||||
}
|
||||
builder.writeString(str)
|
||||
}
|
||||
}
|
||||
}
|
||||
10
main-actions/src/main/contraband-scala/sbt/internal/sona/codec/JsonProtocol.scala
generated
Normal file
10
main-actions/src/main/contraband-scala/sbt/internal/sona/codec/JsonProtocol.scala
generated
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.sona.codec
|
||||
trait JsonProtocol extends sjsonnew.BasicJsonProtocol
|
||||
with sbt.internal.sona.codec.DeploymentStateFormats
|
||||
with sbt.internal.sona.codec.PublisherStatusFormats
|
||||
object JsonProtocol extends JsonProtocol
|
||||
33
main-actions/src/main/contraband-scala/sbt/internal/sona/codec/PublisherStatusFormats.scala
generated
Normal file
33
main-actions/src/main/contraband-scala/sbt/internal/sona/codec/PublisherStatusFormats.scala
generated
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* This code is generated using [[https://www.scala-sbt.org/contraband]].
|
||||
*/
|
||||
|
||||
// DO NOT EDIT MANUALLY
|
||||
package sbt.internal.sona.codec
|
||||
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
|
||||
trait PublisherStatusFormats { self: sbt.internal.sona.codec.DeploymentStateFormats & sjsonnew.BasicJsonProtocol =>
|
||||
implicit lazy val PublisherStatusFormat: JsonFormat[sbt.internal.sona.PublisherStatus] = new JsonFormat[sbt.internal.sona.PublisherStatus] {
|
||||
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.sona.PublisherStatus = {
|
||||
__jsOpt match {
|
||||
case Some(__js) =>
|
||||
unbuilder.beginObject(__js)
|
||||
val deploymentId = unbuilder.readField[String]("deploymentId")
|
||||
val deploymentName = unbuilder.readField[String]("deploymentName")
|
||||
val deploymentState = unbuilder.readField[sbt.internal.sona.DeploymentState]("deploymentState")
|
||||
val purls = unbuilder.readField[Vector[String]]("purls")
|
||||
unbuilder.endObject()
|
||||
sbt.internal.sona.PublisherStatus(deploymentId, deploymentName, deploymentState, purls)
|
||||
case None =>
|
||||
deserializationError("Expected JsObject but found None")
|
||||
}
|
||||
}
|
||||
override def write[J](obj: sbt.internal.sona.PublisherStatus, builder: Builder[J]): Unit = {
|
||||
builder.beginObject()
|
||||
builder.addField("deploymentId", obj.deploymentId)
|
||||
builder.addField("deploymentName", obj.deploymentName)
|
||||
builder.addField("deploymentState", obj.deploymentState)
|
||||
builder.addField("purls", obj.purls)
|
||||
builder.endObject()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package sbt.internal.sona
|
||||
@target(Scala)
|
||||
@codecPackage("sbt.internal.sona.codec")
|
||||
@fullCodec("JsonProtocol")
|
||||
|
||||
enum DeploymentState {
|
||||
PENDING
|
||||
VALIDATING
|
||||
VALIDATED
|
||||
PUBLISHING
|
||||
PUBLISHED
|
||||
FAILED
|
||||
}
|
||||
|
||||
## https://central.sonatype.org/publish/publish-portal-api/#uploading-a-deployment-bundle
|
||||
type PublisherStatus {
|
||||
deploymentId: String!
|
||||
deploymentName: String!
|
||||
deploymentState: sbt.internal.sona.DeploymentState!
|
||||
purls: [String]
|
||||
}
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* sbt
|
||||
* Copyright 2023, Scala center
|
||||
* Copyright 2011 - 2022, Lightbend, Inc.
|
||||
* Copyright 2008 - 2010, Mark Harrah
|
||||
* Licensed under Apache License 2.0 (see LICENSE)
|
||||
*/
|
||||
|
||||
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 sbt.util.Logger
|
||||
import sjsonnew.JsonFormat
|
||||
import sjsonnew.support.scalajson.unsafe.{ Converter, Parser }
|
||||
import sjsonnew.shaded.scalajson.ast.unsafe.JValue
|
||||
|
||||
import scala.annotation.nowarn
|
||||
import scala.concurrent.*, duration.*
|
||||
|
||||
class Sona(client: SonaClient) extends AutoCloseable {
|
||||
def uploadBundle(
|
||||
bundleZipPath: Path,
|
||||
deploymentName: String,
|
||||
pt: PublishingType,
|
||||
log: Logger,
|
||||
): Unit = {
|
||||
val deploymentId = client.uploadBundle(bundleZipPath, deploymentName, pt, log)
|
||||
client.waitForDeploy(deploymentId, deploymentName, pt, 1, log)
|
||||
}
|
||||
def close(): Unit = client.close()
|
||||
}
|
||||
|
||||
class SonaClient(reqTransform: Request => Request) extends AutoCloseable {
|
||||
import SonaClient.baseUrl
|
||||
|
||||
val gigahorseConfig = Gigahorse.config
|
||||
.withRequestTimeout(2.minute)
|
||||
.withReadTimeout(2.minute)
|
||||
val http = Gigahorse.http(gigahorseConfig)
|
||||
def uploadBundle(
|
||||
bundleZipPath: Path,
|
||||
deploymentName: String,
|
||||
publishingType: PublishingType,
|
||||
log: Logger,
|
||||
): String = {
|
||||
val res = retryF(maxAttempt = 2) { (attempt: Int) =>
|
||||
log.info(s"uploading bundle to the Central Portal (attempt: $attempt)")
|
||||
// addQuery string doesn't work for post
|
||||
val q = queryString(
|
||||
"name" -> deploymentName,
|
||||
"publishingType" -> (publishingType match {
|
||||
case PublishingType.Automatic => "AUTOMATIC"
|
||||
case PublishingType.UserManaged => "USER_MANAGED"
|
||||
})
|
||||
)
|
||||
val req = Gigahorse
|
||||
.url(s"${baseUrl}/publisher/upload?$q")
|
||||
.post(
|
||||
MultipartFormBody(
|
||||
FormPart("bundle", bundleZipPath.toFile())
|
||||
)
|
||||
)
|
||||
.withRequestTimeout(600.second)
|
||||
http.run(reqTransform(req), Gigahorse.asString)
|
||||
}
|
||||
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(
|
||||
deploymentId: String,
|
||||
deploymentName: String,
|
||||
publishingType: PublishingType,
|
||||
attempt: Int,
|
||||
log: Logger,
|
||||
): Unit = {
|
||||
val status = deploymentStatus(deploymentId)
|
||||
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 {
|
||||
case DeploymentState.FAILED => sys.error(s"deployment $deploymentId failed")
|
||||
case DeploymentState.PENDING | DeploymentState.PUBLISHING | DeploymentState.VALIDATING =>
|
||||
Thread.sleep(sleepSec * 1000L)
|
||||
waitForDeploy(deploymentId, deploymentName, publishingType, attempt + 1, log)
|
||||
case DeploymentState.PUBLISHED if publishingType == PublishingType.Automatic => ()
|
||||
case DeploymentState.VALIDATED if publishingType == PublishingType.UserManaged => ()
|
||||
case DeploymentState.VALIDATED =>
|
||||
Thread.sleep(sleepSec * 1000L)
|
||||
waitForDeploy(deploymentId, deploymentName, publishingType, attempt + 1, log)
|
||||
case _ =>
|
||||
Thread.sleep(sleepSec * 1000L)
|
||||
waitForDeploy(deploymentId, deploymentName, publishingType, attempt + 1, log)
|
||||
}
|
||||
}
|
||||
|
||||
def deploymentStatus(deploymentId: String): PublisherStatus = {
|
||||
val res = retryF(maxAttempt = 5) { (attempt: Int) =>
|
||||
deploymentStatusF(deploymentId)
|
||||
}
|
||||
Await.result(res, 600.seconds)
|
||||
}
|
||||
|
||||
/**
|
||||
* https://central.sonatype.org/publish/publish-portal-api/#verify-status-of-the-deployment
|
||||
*/
|
||||
def deploymentStatusF(deploymentId: String): Future[PublisherStatus] = {
|
||||
val req = Gigahorse
|
||||
.url(s"${baseUrl}/publisher/status")
|
||||
.addQueryString("id" -> deploymentId)
|
||||
.post("", StandardCharsets.UTF_8)
|
||||
http.run(reqTransform(req), SonaClient.asPublisherStatus)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retry future function on any error.
|
||||
*/
|
||||
@nowarn
|
||||
def retryF[A1](maxAttempt: Int)(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)
|
||||
}
|
||||
}
|
||||
impl(0)
|
||||
}
|
||||
|
||||
def awaitWithMessage[A1](f: Future[A1], msg: String, log: Logger): A1 = {
|
||||
import scala.concurrent.ExecutionContext.Implicits.*
|
||||
def loop(attempt: Int): Unit =
|
||||
if (!f.isCompleted) {
|
||||
if (attempt > 0) {
|
||||
log.info(msg)
|
||||
}
|
||||
Future {
|
||||
blocking {
|
||||
Thread.sleep(30.second.toMillis)
|
||||
}
|
||||
}.foreach(_ => loop(attempt + 1))
|
||||
} else ()
|
||||
loop(0)
|
||||
Await.result(f, 600.seconds)
|
||||
}
|
||||
|
||||
def close(): Unit = http.close()
|
||||
}
|
||||
|
||||
object Sona {
|
||||
def host: String = SonaClient.host
|
||||
def oauthClient(userName: String, userToken: String): Sona =
|
||||
new Sona(SonaClient.oauthClient(userName, userToken))
|
||||
}
|
||||
|
||||
object SonaClient {
|
||||
import sbt.internal.sona.codec.JsonProtocol.*
|
||||
val host: String = "central.sonatype.com"
|
||||
val baseUrl: String = s"https://$host/api/v1"
|
||||
val asJson: FullResponse => JValue = (r: FullResponse) =>
|
||||
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))
|
||||
}
|
||||
|
||||
private case class OAuthClient(userName: String, userToken: String)
|
||||
extends Function1[Request, Request] {
|
||||
val base64Credentials =
|
||||
Base64.getEncoder.encodeToString(s"${userName}:${userToken}".getBytes(StandardCharsets.UTF_8))
|
||||
def apply(request: Request): Request =
|
||||
request.addHeaders("Authorization" -> s"Bearer $base64Credentials")
|
||||
override def toString: String = "OAuthClient(****)"
|
||||
}
|
||||
|
||||
sealed trait PublishingType
|
||||
object PublishingType {
|
||||
case object Automatic extends PublishingType
|
||||
case object UserManaged extends PublishingType
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ package sbt
|
|||
|
||||
import java.io.{ File, PrintWriter }
|
||||
import java.nio.file.{ Files, Path as NioPath }
|
||||
import java.util.Optional
|
||||
import java.util.{ Optional, UUID }
|
||||
import java.util.concurrent.TimeUnit
|
||||
import lmcoursier.CoursierDependencyResolution
|
||||
import lmcoursier.definitions.{ Configuration as CConfiguration }
|
||||
|
|
@ -48,6 +48,7 @@ import sbt.internal.server.{
|
|||
ServerHandler,
|
||||
VirtualTerminal
|
||||
}
|
||||
import sbt.internal.sona.Sona
|
||||
import sbt.internal.testing.TestLogger
|
||||
import sbt.internal.util.Attributed.data
|
||||
import sbt.internal.util.Types.*
|
||||
|
|
@ -292,6 +293,16 @@ object Defaults extends BuildCommon {
|
|||
ScalaArtifacts.Artifacts.map(a => InclExclRule(scalaOrganization.value, a)).toSet
|
||||
),
|
||||
csrCacheDirectory := LMCoursier.defaultCacheLocation,
|
||||
stagingDirectory := (ThisBuild / baseDirectory).value / "target" / "sona-staging",
|
||||
localStaging := Some(Resolver.file("local-staging", stagingDirectory.value)),
|
||||
sonaBundle := Publishing
|
||||
.makeBundle(
|
||||
stagingDirectory.value.toPath(),
|
||||
((ThisBuild / baseDirectory).value / "target" / "sona-bundle" / "bundle.zip").toPath()
|
||||
)
|
||||
.toFile(),
|
||||
sonaBundle / aggregate :== false,
|
||||
commands ++= Seq(Publishing.sonaRelease, Publishing.sonaUpload),
|
||||
)
|
||||
|
||||
/** Core non-plugin settings for sbt builds. These *must* be on every build or the sbt engine will fail to run at all. */
|
||||
|
|
@ -2810,7 +2821,21 @@ object Classpaths {
|
|||
makeIvyXml := deliverTask(makeIvyXmlConfiguration).value,
|
||||
publish := publishOrSkip(publishConfiguration, publish / skip).value,
|
||||
publishLocal := publishOrSkip(publishLocalConfiguration, publishLocal / skip).value,
|
||||
publishM2 := publishOrSkip(publishM2Configuration, publishM2 / skip).value
|
||||
publishM2 := publishOrSkip(publishM2Configuration, publishM2 / skip).value,
|
||||
credentials ++= {
|
||||
val alreadyContainsCentralCredentials: Boolean = credentials.value.exists {
|
||||
case d: DirectCredentials => d.host == Sona.host
|
||||
case _ => false
|
||||
}
|
||||
if (!alreadyContainsCentralCredentials) SysProp.sonatypeCredentalsEnv.toSeq
|
||||
else Nil
|
||||
},
|
||||
sonaDeploymentName := {
|
||||
val o = organization.value
|
||||
val v = version.value
|
||||
val uuid = UUID.randomUUID().toString().take(8)
|
||||
s"$o:$v:$uuid"
|
||||
},
|
||||
)
|
||||
|
||||
def baseGlobalDefaults =
|
||||
|
|
|
|||
|
|
@ -600,6 +600,10 @@ object Keys {
|
|||
val forceUpdatePeriod = settingKey[Option[FiniteDuration]]("Duration after which to force a full update to occur").withRank(CSetting)
|
||||
val versionScheme = settingKey[Option[String]]("""Version scheme used for the subproject: Supported values are Some("early-semver"), Some("pvp"), and Some("semver-spec")""").withRank(BSetting)
|
||||
val libraryDependencySchemes = settingKey[Seq[ModuleID]]("""Version scheme to use for specific modules set as "org" %% "name" % "<scheme>": Supported values are "early-semver", "pvp", "semver-spec", "always", and "strict".""").withRank(BSetting)
|
||||
val stagingDirectory = settingKey[File]("Local staging directory for Sonatype publishing").withRank(CSetting)
|
||||
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 classifiersModule = taskKey[GetClassifiersModule]("classifiers-module").withRank(CTask)
|
||||
val compatibilityWarningOptions = settingKey[CompatibilityWarningOptions]("Configures warnings around Maven incompatibility.").withRank(CSetting)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,13 @@ package sbt.internal
|
|||
private[sbt] object Banner {
|
||||
def apply(version: String): Option[String] =
|
||||
version match {
|
||||
case v if v.startsWith("1.11.0") =>
|
||||
Some(s"""
|
||||
|Here are some highlights of sbt 1.11.0:
|
||||
| - The Central Repository publishing
|
||||
|See https://eed3si9n.com/sbt-1.11.0 for full release notes.
|
||||
|Hide the banner for this release by running `skipBanner`.
|
||||
|""".stripMargin.linesIterator.mkString("\n"))
|
||||
case v if v.startsWith("1.10.0") =>
|
||||
Some(s"""
|
||||
|Here are some highlights of sbt 1.10.0:
|
||||
|
|
|
|||
|
|
@ -241,6 +241,17 @@ object SysProp {
|
|||
lazy val sbtCredentialsEnv: Option[Credentials] =
|
||||
sys.env.get("SBT_CREDENTIALS").map(raw => new FileCredentials(new File(raw)))
|
||||
|
||||
def sonatypeCredentalsEnv: Option[Credentials] =
|
||||
for {
|
||||
username <- sys.env.get("SONATYPE_USERNAME")
|
||||
password <- sys.env.get("SONATYPE_PASSWORD")
|
||||
} yield Credentials(
|
||||
"Sonatype Nexus Repository Manager",
|
||||
sona.Sona.host,
|
||||
username,
|
||||
password
|
||||
)
|
||||
|
||||
private[sbt] def setSwovalTempDir(): Unit = {
|
||||
val _ = getOrUpdateSwovalTmpDir(
|
||||
runtimeDirectory.resolve("swoval").toString
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* sbt
|
||||
* Copyright 2023, Scala center
|
||||
* Copyright 2011 - 2022, Lightbend, Inc.
|
||||
* Copyright 2008 - 2010, Mark Harrah
|
||||
* Licensed under Apache License 2.0 (see LICENSE)
|
||||
*/
|
||||
|
||||
package sbt
|
||||
package internal
|
||||
package librarymanagement
|
||||
|
||||
import java.nio.file.Path
|
||||
import sbt.internal.util.MessageOnlyException
|
||||
import sbt.io.IO
|
||||
import sbt.io.Path.contentOf
|
||||
import sbt.librarymanagement.ivy.Credentials
|
||||
import sona.{ Sona, PublishingType }
|
||||
|
||||
object Publishing {
|
||||
val sonaRelease: Command =
|
||||
Command.command("sonaRelease")(sonatypeReleaseAction(PublishingType.Automatic))
|
||||
|
||||
val sonaUpload: Command =
|
||||
Command.command("sonaUpload")(sonatypeReleaseAction(PublishingType.UserManaged))
|
||||
|
||||
def makeBundle(stagingDir: Path, bundlePath: Path): Path = {
|
||||
if (bundlePath.toFile().exists()) {
|
||||
IO.delete(bundlePath.toFile())
|
||||
}
|
||||
IO.zip(
|
||||
sources = contentOf(stagingDir.toFile()),
|
||||
outputZip = bundlePath.toFile(),
|
||||
time = Some(0L),
|
||||
)
|
||||
bundlePath
|
||||
}
|
||||
|
||||
private def sonatypeReleaseAction(pt: 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")) {
|
||||
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 (s1, bundle) = extracted.runTask(Keys.sonaBundle, s0)
|
||||
val (s2, creds) = extracted.runTask(Keys.credentials, s1)
|
||||
val client = fromCreds(creds)
|
||||
try {
|
||||
client.uploadBundle(bundle.toPath(), dn, pt, log)
|
||||
s2
|
||||
} finally {
|
||||
client.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def fromCreds(creds: Seq[Credentials]): 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -115,7 +115,7 @@ object Dependencies {
|
|||
|
||||
// lm dependencies
|
||||
val jsch = "com.github.mwiede" % "jsch" % "0.2.17" intransitive ()
|
||||
val gigahorseApacheHttp = "com.eed3si9n" %% "gigahorse-apache-http" % "0.7.0"
|
||||
val gigahorseApacheHttp = "com.eed3si9n" %% "gigahorse-apache-http" % "0.9.3"
|
||||
|
||||
// lm-coursier dependencies
|
||||
val dataclassScalafixVersion = "0.1.0"
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
sbt.version=1.10.7
|
||||
sbt.version=1.11.0
|
||||
|
|
|
|||
2
sbt
2
sbt
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set +e
|
||||
declare builtin_sbt_version="1.10.11"
|
||||
declare builtin_sbt_version="1.11.0"
|
||||
declare -a residual_args
|
||||
declare -a java_args
|
||||
declare -a scalac_args
|
||||
|
|
|
|||
|
|
@ -18,12 +18,14 @@ lazy val root = (project in file("."))
|
|||
|
||||
// Calling "distinct" as there are different entries for sources and javadoc classifiers with same module
|
||||
val moduleIds = moduleReports.map(_.module).distinct
|
||||
val moduleIdsShort = moduleIds.map(m => s"${m.organization}:${m.name}")
|
||||
val moduleIdsShort = moduleIds
|
||||
.filter(m => m.name != "launcher-interface") // I get different result locally
|
||||
.map(m => s"${m.organization}:${m.name}")
|
||||
|
||||
val expectedModuleIds = Seq(
|
||||
"com.eed3si9n:gigahorse-apache-http_3",
|
||||
"com.eed3si9n:gigahorse-core_3",
|
||||
"com.eed3si9n:shaded-apache-httpasyncclient",
|
||||
"com.eed3si9n:shaded-apache-httpclient5",
|
||||
"com.eed3si9n:shaded-jawn-parser_3",
|
||||
"com.eed3si9n:shaded-scalajson_3",
|
||||
"com.eed3si9n:sjson-new-core_3",
|
||||
|
|
@ -66,7 +68,6 @@ lazy val root = (project in file("."))
|
|||
"org.scala-sbt.jline:jline",
|
||||
"org.scala-sbt:compiler-interface",
|
||||
"org.scala-sbt:io_3",
|
||||
"org.scala-sbt:launcher-interface",
|
||||
"org.scala-sbt:sbinary_3",
|
||||
"org.scala-sbt:template-resolver",
|
||||
"org.scala-sbt:test-interface",
|
||||
|
|
|
|||
Loading…
Reference in New Issue