Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: xerial/sbt-sonatype
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 3.9.13
Choose a base ref
...
head repository: xerial/sbt-sonatype
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v3.9.14
Choose a head ref

Commits on May 24, 2022

  1. Copy the full SHA
    6f2573a View commit details
  2. Fix release notes

    xerial committed May 24, 2022
    Copy the full SHA
    652ae9d View commit details

Commits on May 25, 2022

  1. Copy the full SHA
    532c080 View commit details

Commits on Jun 2, 2022

  1. Copy the full SHA
    4b16940 View commit details

Commits on Aug 12, 2022

  1. Copy the full SHA
    688880f View commit details

Commits on Nov 2, 2022

  1. Copy the full SHA
    e39006a View commit details

Commits on Nov 3, 2022

  1. Copy the full SHA
    41b85e5 View commit details
  2. Copy the full SHA
    49f2d5c View commit details

Commits on Nov 4, 2022

  1. Create dependabot.yml

    xerial authored Nov 4, 2022
    Copy the full SHA
    7b06dd1 View commit details
  2. Copy the full SHA
    c879f1c View commit details
  3. Copy the full SHA
    ec7d069 View commit details
  4. Fixes #309: Handle 404 upon repository drop (#327)

    * Fixes #309: Handle 404 upon repository drop
    
    * Fix message
    xerial authored Nov 4, 2022
    Copy the full SHA
    12903a2 View commit details
  5. Copy the full SHA
    b1b61ec View commit details
  6. Bump actions/checkout from 2 to 3 (#328)

    Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
    - [Release notes](https://github.com/actions/checkout/releases)
    - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
    - [Commits](actions/checkout@v2...v3)
    
    ---
    updated-dependencies:
    - dependency-name: actions/checkout
      dependency-type: direct:production
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Nov 4, 2022
    Copy the full SHA
    a908f24 View commit details
  7. Access to snapshto repo

    xerial committed Nov 4, 2022
    Copy the full SHA
    2f350c4 View commit details
  8. Upgrade to sbt 1.7.3

    xerial committed Nov 4, 2022
    Copy the full SHA
    40a4147 View commit details
  9. Add release step note

    xerial committed Nov 4, 2022
    Copy the full SHA
    053e23f View commit details
  10. 3.9.14 release notes

    xerial committed Nov 4, 2022
    Copy the full SHA
    8234b19 View commit details
  11. 3.9.14 release notes

    xerial committed Nov 4, 2022
    Copy the full SHA
    7ea1d4c View commit details
  12. Fix build

    xerial committed Nov 4, 2022
    Copy the full SHA
    0ec6c6f View commit details
  13. Set JDK8 target

    xerial committed Nov 4, 2022
    Copy the full SHA
    cf254b1 View commit details
  14. Add json encoding

    xerial committed Nov 4, 2022
    Copy the full SHA
    3fa6438 View commit details
Showing with 106 additions and 94 deletions.
  1. +11 −0 .github/dependabot.yml
  2. +1 −1 .github/workflows/test.yml
  3. +1 −1 .scalafmt.conf
  4. +13 −0 README.md
  5. +9 −2 ReleaseNotes.md
  6. +6 −22 build.sbt
  7. +1 −1 project/build.properties
  8. +4 −4 project/plugins.sbt
  9. +1 −0 sonatype.sbt
  10. +59 −62 src/main/scala/xerial/sbt/sonatype/SonatypeClient.scala
  11. +0 −1 version.sbt
11 changes: 11 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates

version: 2
updates:
- package-ecosystem: "github-actions" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ jobs:
name: Code Format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: scalafmt
run: ./sbt scalafmtCheckAll
test_jdk11:
2 changes: 1 addition & 1 deletion .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version = 3.5.3
version = 3.6.1
project.layout = StandardConvention
runner.dialect = scala212
maxColumn = 120
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -283,3 +283,16 @@ Then, run `sonatypeReleaseAll` command by specifying your `sonatypeProfileName`.
$ sbt "sonatypeReleaseAll org.xerial"
```



## For sbt-sonatype developers

Releasing sbt-sonatype to Sonatype:

````
## Add a new git tag
$ git tag v3.9.x
$ ./sbt
> publishSigned
> sonatypeBundleRelease
```
11 changes: 9 additions & 2 deletions ReleaseNotes.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
Release Notes
===

# 3.9.14

- Upgraded airframe-http to 22.11.0
- Add more Sonatype API error handling
- Upgrade to sbt 1.7.3
- Use sbt-dynver for versioning

# 3.9.13

- Upgraded airframe-http to 22.5.0 to fix JDK17-specific error (#293)[https://github.com/xerial/sbt-sonatype/issues/293]
- Revereted the fix for #276 due to the regression
- Upgraded airframe-http to 22.5.0 to fix JDK17-specific error [#293](https://github.com/xerial/sbt-sonatype/issues/293)
- Reverted the fix for #276 due to the regression


# 3.9.12
28 changes: 6 additions & 22 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@

Global / onChangedBuildSource := ReloadOnSourceChanges

import ReleaseTransformations._
ThisBuild / dynverSeparator := "-"

lazy val buildSettings: Seq[Setting[_]] = Seq(
organization := "org.xerial.sbt",
@@ -26,32 +26,16 @@ lazy val buildSettings: Seq[Setting[_]] = Seq(
Test / publishArtifact := false,
sbtPlugin := true,
parallelExecution := true,
// Enforcing JDK8 target
javacOptions ++= Seq("-source", "8", "-target", "8"),
scalacOptions ++= Seq("-encoding", "UTF-8", "-deprecation", "-unchecked"),
scriptedBufferLog := false,
scriptedLaunchOpts := {
scriptedLaunchOpts.value ++ Seq("-Xmx1024M", "-Dplugin.version=" + version.value)
},
crossSbtVersions := Vector("1.2.8"),
releaseCrossBuild := false,
releaseTagName := { (ThisBuild / version).value },
releasePublishArtifactsAction := PgpKeys.publishSigned.value,
releaseProcess := Seq[ReleaseStep](
checkSnapshotDependencies,
inquireVersions,
runClean,
releaseStepCommandAndRemaining("^ test"),
setReleaseVersion,
commitReleaseVersion,
tagRelease,
releaseStepCommandAndRemaining("publishSigned"),
releaseStepCommand("sonatypeBundleRelease"),
setNextVersion,
commitNextVersion,
pushChanges
)
}
)

val AIRFRAME_VERSION = "22.5.0"
val AIRFRAME_VERSION = "22.11.0"

// Project modules
lazy val sbtSonatype =
@@ -67,6 +51,6 @@ lazy val sbtSonatype =
"org.wvlet.airframe" %% "airframe-http" % AIRFRAME_VERSION
// A workaround for sbt-pgp, which still depends on scala-parser-combinator 1.x
excludeAll (ExclusionRule("org.scala-lang.modules", "scala-parser-combinators_2.12")),
"org.wvlet.airframe" %% "airspec" % AIRFRAME_VERSION % "test"
"org.wvlet.airframe" %% "airspec" % AIRFRAME_VERSION % Test
)
)
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.6.2
sbt.version=1.7.3
8 changes: 4 additions & 4 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
val SONATYPE_VERSION = sys.env.getOrElse("SONATYPE_VERSION", "3.9.11")
addSbtPlugin("com.github.sbt" % "sbt-release" % "1.1.0")
val SONATYPE_VERSION = sys.env.getOrElse("SONATYPE_VERSION", "3.9.13")
addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % SONATYPE_VERSION)
addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2")
addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.2.0")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6")
addSbtPlugin("com.dwijnand" % "sbt-dynver" % "4.1.1")

libraryDependencies += "org.scala-sbt" %% "scripted-plugin" % sbtVersion.value

resolvers += Resolver.sonatypeRepo("releases")
resolvers += Resolver.sonatypeRepo("snapshots")
1 change: 1 addition & 0 deletions sonatype.sbt
Original file line number Diff line number Diff line change
@@ -12,3 +12,4 @@ licenses := Seq("APL2" -> url("http://www.apache.org/licenses/LICENSE-2.0.txt"))
publishTo := sonatypePublishToBundle.value

//sonatypeLogLevel := "DEBUG"

121 changes: 59 additions & 62 deletions src/main/scala/xerial/sbt/sonatype/SonatypeClient.scala
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
package xerial.sbt.sonatype

import java.io.{File, IOException}
import java.nio.charset.StandardCharsets
import java.util.Base64
import java.util.concurrent.TimeUnit
import org.apache.http.auth.{AuthScope, UsernamePasswordCredentials}
import org.apache.http.impl.client.BasicCredentialsProvider
import org.sonatype.spice.zapper.ParametersBuilder
import org.sonatype.spice.zapper.client.hc4.Hc4ClientBuilder
import sbt.librarymanagement.ivy.{Credentials, DirectCredentials}
import wvlet.airframe.control.{Control, ResultClass, Retry}
import wvlet.airframe.http.HttpHeader.MediaType
import wvlet.airframe.http.HttpMessage.{Request, Response}
import wvlet.airframe.http.client.{URLConnectionClient, URLConnectionClientConfig}
import wvlet.airframe.http.*
import wvlet.airframe.http.HttpHeader.MediaType
import wvlet.airframe.http.HttpMessage.Response
import wvlet.airframe.http.client.URLConnectionClientBackend
import wvlet.log.LogSupport
import xerial.sbt.sonatype.SonatypeException.{
BUNDLE_UPLOAD_FAILURE,
@@ -22,10 +18,13 @@ import xerial.sbt.sonatype.SonatypeException.{
STAGE_IN_PROGRESS
}

import scala.concurrent.{ExecutionContext, Future}
import java.io.{File, IOException}
import java.nio.charset.StandardCharsets
import java.util.Base64
import java.util.concurrent.TimeUnit
import scala.concurrent.duration.Duration

/** REST API Client for Sonatype API (nexus-staigng)
/** REST API Client for Sonatype API (nexus-staging)
* https://repository.sonatype.org/nexus-staging-plugin/default/docs/rest.html
*/
class SonatypeClient(
@@ -62,43 +61,27 @@ class SonatypeClient(
new java.net.URL(repoUri).getPath
}

/** Defined for setting URLConnectionClient specific configurations
*/
object SonatypeClientBackend extends HttpClientBackend {
def newSyncClient(serverAddress: String, clientConfig: HttpClientConfig): HttpSyncClient[Request, Response] = {
new URLConnectionClient(
ServerAddress(serverAddress),
URLConnectionClientConfig(
requestFilter = clientConfig.requestFilter,
retryContext = clientConfig.retryContext,
codecFactory = clientConfig.codecFactory,
readTimeout = Duration(timeoutMillis, TimeUnit.MILLISECONDS)
)
)
}
override def defaultExecutionContext: ExecutionContext = ???
override def defaultRequestRetryer: Retry.RetryContext = HttpClient.defaultHttpClientRetry[Request, Response]
override def newAsyncClient(
serverAddress: String,
clientConfig: HttpClientConfig
): HttpClient[Future, Request, Response] = ???
override def newRPCClientForScalaJS(clientConfig: HttpClientConfig): RPCHttpClient = ???
private val clientConfig = {
Http.client
// Use URLConnectionClient for JDK8 compatibility. Remove this line when using JDK11 or later
.withBackend(URLConnectionClientBackend)
.withJSONEncoding
// Need to set a longer timeout as Sonatype API may not respond quickly
.withReadTimeout(Duration(timeoutMillis, TimeUnit.MILLISECONDS))
// airframe-http will retry the request several times within this timeout duration.
.withRetryContext { context =>
// For individual REST calls, use a normal jittering
context
.withMaxRetry(100)
.withJitter(initialIntervalMillis = 1500, maxIntervalMillis = 30000)
}
.withRequestFilter { request =>
request.withContentTypeJson
.withAccept(MediaType.ApplicationJson)
.withHeader(HttpHeader.Authorization, s"Basic ${base64Credentials}")
}
}

private val clientConfig = HttpClientConfig(SonatypeClientBackend)
// airframe-http will retry the request several times within this timeout duration.
.withRetryContext { context =>
// For individual REST calls, use a normal jittering
context
.withMaxRetry(100)
.withJitter(initialIntervalMillis = 1500, maxIntervalMillis = 30000)
}
.withRequestFilter { request =>
request.withContentTypeJson
.withAccept(MediaType.ApplicationJson)
.withHeader(HttpHeader.Authorization, s"Basic ${base64Credentials}")
}

private val httpClient = clientConfig.newSyncClient(repoUri)

// Create stage is not idempotent, so we just need to wait for a long time without retry
@@ -111,30 +94,32 @@ class SonatypeClient(
Control.closeResources(httpClient, httpClientForCreateStage)
}

import xerial.sbt.sonatype.SonatypeClient._
import xerial.sbt.sonatype.SonatypeClient.*

def stagingRepositoryProfiles: Seq[StagingRepositoryProfile] = {
info("Reading staging repository profiles...")
val result =
httpClient.get[Map[String, Seq[StagingRepositoryProfile]]](s"${pathPrefix}/staging/profile_repositories")
httpClient.readAs[Map[String, Seq[StagingRepositoryProfile]]](
Http.GET(s"${pathPrefix}/staging/profile_repositories")
)
result.getOrElse("data", Seq.empty)
}

def stagingRepository(repositoryId: String) = {
info(s"Searching for repository ${repositoryId} ...")
httpClient.get[String](s"${pathPrefix}/staging/repository/${repositoryId}")
httpClient.readAs[String](Http.GET(s"${pathPrefix}/staging/repository/${repositoryId}"))
}

def stagingProfiles: Seq[StagingProfile] = {
info("Reading staging profiles...")
val result = httpClient.get[StagingProfileResponse](s"${pathPrefix}/staging/profiles")
val result = httpClient.readAs[StagingProfileResponse](Http.GET(s"${pathPrefix}/staging/profiles"))
result.data
}

def createStage(profile: StagingProfile, description: String): StagingRepositoryProfile = {
info(s"Creating a staging repository in profile ${profile.name} with a description key: ${description}")
val ret = httpClientForCreateStage.postOps[Map[String, Map[String, String]], CreateStageResponse](
s"${pathPrefix}/staging/profiles/${profile.id}/start",
val ret = httpClientForCreateStage.call[Map[String, Map[String, String]], CreateStageResponse](
Http.POST(s"${pathPrefix}/staging/profiles/${profile.id}/start"),
Map("data" -> Map("description" -> description))
)
// Extract created staging repository ids
@@ -205,8 +190,8 @@ class SonatypeClient(

def closeStage(currentProfile: StagingProfile, repo: StagingRepositoryProfile): StagingRepositoryProfile = {
info(s"Closing staging repository $repo")
val ret = httpClient.postOps[Map[String, StageTransitionRequest], Response](
s"${pathPrefix}/staging/profiles/${repo.profileId}/finish",
val ret = httpClient.call[Map[String, StageTransitionRequest], Response](
Http.POST(s"${pathPrefix}/staging/profiles/${repo.profileId}/finish"),
newStageTransitionRequest(currentProfile, repo)
)
if (ret.statusCode != HttpStatus.Created_201.code) {
@@ -223,8 +208,8 @@ class SonatypeClient(

def promoteStage(currentProfile: StagingProfile, repo: StagingRepositoryProfile): StagingRepositoryProfile = {
info(s"Promoting staging repository $repo")
val ret = httpClient.postOps[Map[String, StageTransitionRequest], Response](
s"${pathPrefix}/staging/profiles/${repo.profileId}/promote",
val ret = httpClient.call[Map[String, StageTransitionRequest], Response](
Http.POST(s"${pathPrefix}/staging/profiles/${repo.profileId}/promote"),
newStageTransitionRequest(currentProfile, repo)
)
if (ret.statusCode != HttpStatus.Created_201.code) {
@@ -236,14 +221,20 @@ class SonatypeClient(

def dropStage(currentProfile: StagingProfile, repo: StagingRepositoryProfile): Response = {
info(s"Dropping staging repository $repo")
val ret = httpClient.postOps[Map[String, StageTransitionRequest], Response](
s"${pathPrefix}/staging/profiles/${repo.profileId}/drop",
newStageTransitionRequest(currentProfile, repo)
)
if (ret.statusCode != HttpStatus.Created_201.code) {
throw SonatypeException(STAGE_FAILURE, s"Failed to drop the repository. [${ret.status}]: ${ret.contentString}")
try {
val ret = httpClient.call[Map[String, StageTransitionRequest], Response](
Http.POST(s"${pathPrefix}/staging/profiles/${repo.profileId}/drop"),
newStageTransitionRequest(currentProfile, repo)
)
if (ret.statusCode != HttpStatus.Created_201.code) {
throw SonatypeException(STAGE_FAILURE, s"Failed to drop the repository. [${ret.status}]: ${ret.contentString}")
}
ret
} catch {
case e: HttpClientException if e.status == HttpStatus.NotFound_404 =>
warn(s"Staging repository ${repo.profileId} is not found. It might already have been dropped: ${e.getMessage}")
e.response.toHttpResponse
}
ret
}

private def newStageTransitionRequest(
@@ -261,12 +252,18 @@ class SonatypeClient(

def activitiesOf(r: StagingRepositoryProfile): Seq[StagingActivity] = {
debug(s"Checking activity logs of ${r.repositoryId} ...")
httpClient.get[Seq[StagingActivity]](s"${pathPrefix}/staging/repository/${r.repositoryId}/activity")
httpClient.readAs[Seq[StagingActivity]](Http.GET(s"${pathPrefix}/staging/repository/${r.repositoryId}/activity"))
}

def uploadBundle(localBundlePath: File, deployPath: String): Unit = {
retryer
.retryOn {
case e: IOException if e.getMessage.contains("502 Bad Gateway") =>
// #303 502 can be returned during the bundle upload
Retry.retryableFailure(e)
case e: IOException if e.getMessage.contains("Operation timed out") =>
// #223 SSLException
Retry.retryableFailure(e)
case e: IOException if e.getMessage.contains("400 Bad Request") =>
Retry.nonRetryableFailure(
SonatypeException(
1 change: 0 additions & 1 deletion version.sbt
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
ThisBuild / version := "3.9.13"