Skip to content

Commit

Permalink
Add more configuration for publish (#2435)
Browse files Browse the repository at this point in the history
Add options for configuring timeouts and retries of publishing operations
  • Loading branch information
MaciejG604 authored Oct 6, 2023
1 parent e10df04 commit 89c2758
Show file tree
Hide file tree
Showing 11 changed files with 166 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class BuildServerProxy(
bspServer().buildTargetOutputPaths(params)

/** As Bloop doesn't support `workspace/reload` requests and we have to reload it on Scala CLI's
* end, this is used instead of [[BspServer]]'s [[BspServerForwardStubs]].workspaceReload().
* end, this is used instead of [[BspServer]]'s [[BuildServerForwardStubs]].workspaceReload().
*/
override def workspaceReload(): CompletableFuture[AnyRef] =
onReload()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import scala.cli.publish.BouncycastleSignerMaker
import scala.cli.util.ArgHelpers.*
import scala.cli.util.ConfigDbUtils
import scala.cli.util.ConfigPasswordOptionHelpers.*
import scala.concurrent.duration.DurationInt
import scala.util.control.NonFatal

object Publish extends ScalaCommand[PublishOptions] with BuildCommandHelpers {
Expand All @@ -91,6 +92,7 @@ object Publish extends ScalaCommand[PublishOptions] with BuildCommandHelpers {
sharedPublish: SharedPublishOptions,
publishRepo: PublishRepositoryOptions,
scalaSigning: PgpScalaSigningOptions,
publishConnection: PublishConnectionOptions,
mainClass: MainClassOptions,
ivy2LocalLike: Option[Boolean]
): Either[BuildException, BuildOptions] = either {
Expand Down Expand Up @@ -122,7 +124,12 @@ object Publish extends ScalaCommand[PublishOptions] with BuildCommandHelpers {
val input = sharedPublish.checksum.flatMap(_.split(",")).map(_.trim).filter(_.nonEmpty)
if (input.isEmpty) None
else Some(input)
}
},
connectionTimeoutRetries = publishConnection.connectionTimeoutRetries,
connectionTimeoutSeconds = publishConnection.connectionTimeoutSeconds,
responseTimeoutSeconds = publishConnection.responseTimeoutSeconds,
stagingRepoRetries = publishConnection.stagingRepoRetries,
stagingRepoWaitTimeMilis = publishConnection.stagingRepoWaitTimeMilis
)
baseOptions.copy(
mainClass = mainClass.mainClass.filter(_.nonEmpty),
Expand Down Expand Up @@ -212,6 +219,7 @@ object Publish extends ScalaCommand[PublishOptions] with BuildCommandHelpers {
options.sharedPublish,
options.publishRepo,
options.signingCli,
options.connectionOptions,
options.mainClass,
options.ivy2LocalLike
).orExit(logger)
Expand Down Expand Up @@ -819,7 +827,11 @@ object Publish extends ScalaCommand[PublishOptions] with BuildCommandHelpers {
ivy2HomeOpt,
publishOptions.contextual(isCi).repositoryIsIvy2LocalLike.getOrElse(false),
es,
logger
logger,
publishOptions.contextual(isCi).connectionTimeoutRetries,
publishOptions.contextual(isCi).connectionTimeoutSeconds,
publishOptions.contextual(isCi).stagingRepoRetries,
publishOptions.contextual(isCi).stagingRepoWaitTimeMilis
)
}
}
Expand Down Expand Up @@ -1043,7 +1055,12 @@ object Publish extends ScalaCommand[PublishOptions] with BuildCommandHelpers {

val baseUpload =
if (retainedRepo.root.startsWith("http://") || retainedRepo.root.startsWith("https://"))
HttpURLConnectionUpload.create()
HttpURLConnectionUpload.create(
publishOptions.contextual(isCi)
.connectionTimeoutSeconds.map(_.seconds.toMillis.toInt),
publishOptions.contextual(isCi)
.responseTimeoutSeconds.map(_.seconds.toMillis.toInt)
)
else
FileUpload(Paths.get(new URI(retainedRepo.root)))

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package scala.cli.commands.publish

import caseapp.*

import scala.cli.commands.shared.{HelpGroup, SharedVersionOptions}
import scala.cli.commands.tags

// format: off
final case class PublishConnectionOptions(
@Group(HelpGroup.Publishing.toString)
@HelpMessage("Connection timeout, in seconds.")
@Tag(tags.restricted)
@Hidden
connectionTimeoutSeconds: Option[Int] = None,

@Group(HelpGroup.Publishing.toString)
@HelpMessage("How many times to retry establishing the connection on timeout.")
@Tag(tags.restricted)
@Hidden
connectionTimeoutRetries: Option[Int] = None,

@Group(HelpGroup.Publishing.toString)
@HelpMessage("Waiting for response timeout, in seconds.")
@Tag(tags.restricted)
@Hidden
responseTimeoutSeconds: Option[Int] = None,

@Group(HelpGroup.Publishing.toString)
@HelpMessage("How many times to retry the staging repository operations on failure.")
@Tag(tags.restricted)
@Hidden
stagingRepoRetries: Option[Int] = None,

@Group(HelpGroup.Publishing.toString)
@HelpMessage("Time to wait between staging repository operation retries, in milliseconds.")
@Tag(tags.restricted)
@Hidden
stagingRepoWaitTimeMilis: Option[Int] = None
)
// format: on

object PublishConnectionOptions {
implicit lazy val parser: Parser[PublishConnectionOptions] = Parser.derive
implicit lazy val help: Help[PublishConnectionOptions] = Help.derive
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ object PublishLocal extends ScalaCommand[PublishLocalOptions] {
options.sharedPublish,
PublishRepositoryOptions(),
options.scalaSigning,
PublishConnectionOptions(),
options.mainClass,
None
).orExit(logger)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ final case class PublishOptions(
sharedPublish: SharedPublishOptions = SharedPublishOptions(),
@Recurse
signingCli: PgpScalaSigningOptions = PgpScalaSigningOptions(),
@Recurse
connectionOptions: PublishConnectionOptions = PublishConnectionOptions(),

@Group(HelpGroup.Publishing.toString)
@Tag(tags.restricted)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package scala.cli.commands.publish
import coursier.core.Authentication
import coursier.maven.MavenRepository
import coursier.publish.sonatype.SonatypeApi
import coursier.publish.util.EmaRetryParams
import coursier.publish.{Hooks, PublishRepository}

import java.util.concurrent.ScheduledExecutorService
Expand Down Expand Up @@ -50,15 +51,35 @@ object RepoParams {
ivy2HomeOpt: Option[os.Path],
isIvy2LocalLike: Boolean,
es: ScheduledExecutorService,
logger: Logger
logger: Logger,
connectionTimeoutRetries: Option[Int] = None,
connectionTimeoutSeconds: Option[Int] = None,
stagingRepoRetries: Option[Int] = None,
stagingRepoWaitTimeMilis: Option[Int] = None
): Either[BuildException, RepoParams] = either {
repo match {
case "ivy2-local" =>
RepoParams.ivy2Local(ivy2HomeOpt)
case "sonatype" | "central" | "maven-central" | "mvn-central" =>
RepoParams.centralRepo("https://oss.sonatype.org", es, logger)
RepoParams.centralRepo(
"https://oss.sonatype.org",
connectionTimeoutRetries,
connectionTimeoutSeconds,
stagingRepoRetries,
stagingRepoWaitTimeMilis,
es,
logger
)
case "sonatype-s01" | "central-s01" | "maven-central-s01" | "mvn-central-s01" =>
RepoParams.centralRepo("https://s01.oss.sonatype.org", es, logger)
RepoParams.centralRepo(
"https://s01.oss.sonatype.org",
connectionTimeoutRetries,
connectionTimeoutSeconds,
stagingRepoRetries,
stagingRepoWaitTimeMilis,
es,
logger
)
case "github" =>
value(RepoParams.gitHubRepo(vcsUrlOpt, workspace, logger))
case repoStr if repoStr.startsWith("github:") && repoStr.count(_ == '/') == 1 =>
Expand Down Expand Up @@ -89,10 +110,29 @@ object RepoParams {
}
}

def centralRepo(base: String, es: ScheduledExecutorService, logger: Logger) = {
def centralRepo(
base: String,
connectionTimeoutRetries: Option[Int],
connectionTimeoutSeconds: Option[Int],
stagingRepoRetries: Option[Int],
stagingRepoWaitTimeMilis: Option[Int],
es: ScheduledExecutorService,
logger: Logger
) = {
val repo0 = PublishRepository.Sonatype(MavenRepository(base))
val backend = ScalaCliSttpBackend.httpURLConnection(logger)
val api = SonatypeApi(backend, base + "/service/local", None, logger.verbosity)
val backend = ScalaCliSttpBackend.httpURLConnection(logger, connectionTimeoutSeconds)
val api = SonatypeApi(
backend,
base + "/service/local",
None,
logger.verbosity,
retryOnTimeout = connectionTimeoutRetries.getOrElse(3),
stagingRepoRetryParams = EmaRetryParams(
stagingRepoRetries.getOrElse(3),
stagingRepoWaitTimeMilis.getOrElse(10 * 1000),
2.0f
)
)
val hooks0 = Hooks.sonatype(
repo0,
api,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package scala.cli.commands.util

import sttp.capabilities.Effect
import sttp.client3._
import sttp.client3.*
import sttp.monad.MonadError

import scala.build.Logger
import scala.concurrent.duration
import scala.concurrent.duration.FiniteDuration
import scala.util.Try

class ScalaCliSttpBackend(
Expand Down Expand Up @@ -38,9 +40,14 @@ class ScalaCliSttpBackend(
}

object ScalaCliSttpBackend {
def httpURLConnection(logger: Logger): ScalaCliSttpBackend =
def httpURLConnection(logger: Logger, timeoutSeconds: Option[Int] = None): ScalaCliSttpBackend =
new ScalaCliSttpBackend(
HttpURLConnectionBackend(),
HttpURLConnectionBackend(
options = timeoutSeconds
.fold(SttpBackendOptions.Default)(t =>
SttpBackendOptions.connectionTimeout(FiniteDuration(t, duration.SECONDS))
)
),
logger
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ final case class PublishContextualOptions(
repoPassword: Option[PasswordOption] = None,
repoRealm: Option[String] = None,
computeVersion: Option[ComputeVersion] = None,
checksums: Option[Seq[String]] = None
checksums: Option[Seq[String]] = None,
connectionTimeoutRetries: Option[Int] = None,
connectionTimeoutSeconds: Option[Int] = None,
responseTimeoutSeconds: Option[Int] = None,
stagingRepoRetries: Option[Int] = None,
stagingRepoWaitTimeMilis: Option[Int] = None
)

object PublishContextualOptions {
Expand Down
2 changes: 1 addition & 1 deletion project/deps.sc
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ object Deps {
def coursierJvm = ivy"io.get-coursier:coursier-jvm_2.13:${Versions.coursier}"
def coursierLauncher = ivy"io.get-coursier:coursier-launcher_2.13:${Versions.coursier}"
def coursierProxySetup = ivy"io.get-coursier:coursier-proxy-setup:${Versions.coursier}"
def coursierPublish = ivy"io.get-coursier.publish:publish_2.13:0.1.5"
def coursierPublish = ivy"io.get-coursier.publish:publish_2.13:0.1.6"
def dependency = ivy"io.get-coursier::dependency:0.2.3"
def dockerClient = ivy"com.spotify:docker-client:8.16.0"
// TODO bump once 0.15.5 is out
Expand Down
33 changes: 33 additions & 0 deletions website/docs/reference/cli-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -2092,6 +2092,39 @@ Available in commands:
### `--key`

[Internal]
### Publish connection options

Available in commands:

[`publish`](./commands.md#publish)

<!-- Automatically generated, DO NOT EDIT MANUALLY -->

### `--connection-timeout-seconds`

[Internal]
Connection timeout, in seconds.

### `--connection-timeout-retries`

[Internal]
How many times to retry establishing the connection on timeout.

### `--response-timeout-seconds`

[Internal]
Waiting for response timeout, in seconds.

### `--staging-repo-retries`

[Internal]
How many times to retry the staging repository operations on failure.

### `--staging-repo-wait-time-milis`

[Internal]
Time to wait between staging repository operation retries, in milliseconds.

### Setup IDE options

Available in commands:
Expand Down
2 changes: 1 addition & 1 deletion website/docs/reference/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ The `publish` sub-command is experimental.
Please bear in mind that non-ideal user experience should be expected.
If you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli

Accepts option groups: [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [pgp scala signing](./cli-options.md#pgp-scala-signing-options), [publish](./cli-options.md#publish-options), [publish params](./cli-options.md#publish-params-options), [publish repository](./cli-options.md#publish-repository-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options)
Accepts option groups: [compilation server](./cli-options.md#compilation-server-options), [coursier](./cli-options.md#coursier-options), [cross](./cli-options.md#cross-options), [debug](./cli-options.md#debug-options), [dependency](./cli-options.md#dependency-options), [global suppress warning](./cli-options.md#global-suppress-warning-options), [help group](./cli-options.md#help-group-options), [input](./cli-options.md#input-options), [jvm](./cli-options.md#jvm-options), [logging](./cli-options.md#logging-options), [main class](./cli-options.md#main-class-options), [markdown](./cli-options.md#markdown-options), [pgp scala signing](./cli-options.md#pgp-scala-signing-options), [publish](./cli-options.md#publish-options), [publish connection](./cli-options.md#publish-connection-options), [publish params](./cli-options.md#publish-params-options), [publish repository](./cli-options.md#publish-repository-options), [python](./cli-options.md#python-options), [Scala.js](./cli-options.md#scalajs-options), [Scala Native](./cli-options.md#scala-native-options), [scalac](./cli-options.md#scalac-options), [scalac extra](./cli-options.md#scalac-extra-options), [shared](./cli-options.md#shared-options), [snippet](./cli-options.md#snippet-options), [source generator](./cli-options.md#source-generator-options), [suppress warning](./cli-options.md#suppress-warning-options), [verbosity](./cli-options.md#verbosity-options), [version](./cli-options.md#version-options), [watch](./cli-options.md#watch-options), [workspace](./cli-options.md#workspace-options)

## publish local

Expand Down

0 comments on commit 89c2758

Please sign in to comment.