diff --git a/modules/cli-options/src/main/scala/scala/cli/commands/publish/PublishLocalOptions.scala b/modules/cli-options/src/main/scala/scala/cli/commands/publish/PublishLocalOptions.scala
new file mode 100644
index 0000000000..c1b621d521
--- /dev/null
+++ b/modules/cli-options/src/main/scala/scala/cli/commands/publish/PublishLocalOptions.scala
@@ -0,0 +1,27 @@
+package scala.cli.commands.publish
+
+import caseapp._
+
+import scala.cli.commands.{CompileCrossOptions, MainClassOptions, SharedOptions, SharedWatchOptions}
+
+// format: off
+final case class PublishLocalOptions(
+ @Recurse
+ shared: SharedOptions = SharedOptions(),
+ @Recurse
+ watch: SharedWatchOptions = SharedWatchOptions(),
+ @Recurse
+ compileCross: CompileCrossOptions = CompileCrossOptions(),
+ @Recurse
+ mainClass: MainClassOptions = MainClassOptions(),
+ @Recurse
+ publishParams: PublishParamsOptions = PublishParamsOptions(),
+ @Recurse
+ sharedPublish: SharedPublishOptions = SharedPublishOptions()
+)
+// format: on
+
+object PublishLocalOptions {
+ implicit lazy val parser: Parser[PublishLocalOptions] = Parser.derive
+ implicit lazy val help: Help[PublishLocalOptions] = Help.derive
+}
diff --git a/modules/cli-options/src/main/scala/scala/cli/commands/publish/PublishOptions.scala b/modules/cli-options/src/main/scala/scala/cli/commands/publish/PublishOptions.scala
index 8477909e2c..cb00587652 100644
--- a/modules/cli-options/src/main/scala/scala/cli/commands/publish/PublishOptions.scala
+++ b/modules/cli-options/src/main/scala/scala/cli/commands/publish/PublishOptions.scala
@@ -3,8 +3,6 @@ package scala.cli.commands.publish
import caseapp._
import scala.cli.commands.{CompileCrossOptions, MainClassOptions, SharedOptions, SharedWatchOptions}
-import scala.cli.signing.shared.PasswordOption
-import scala.cli.signing.util.ArgParsers._
// format: off
final case class PublishOptions(
@@ -16,52 +14,16 @@ final case class PublishOptions(
compileCross: CompileCrossOptions = CompileCrossOptions(),
@Recurse
mainClass: MainClassOptions = MainClassOptions(),
+ @Recurse
+ publishParams: PublishParamsOptions = PublishParamsOptions(),
+ @Recurse
+ publishRepo: PublishRepositoryOptions = PublishRepositoryOptions(),
@Recurse
sharedPublish: SharedPublishOptions = SharedPublishOptions(),
- @Group("Publishing")
- @HelpMessage("Directory where temporary files for publishing should be written")
- @Hidden
- workingDir: Option[String] = None,
-
@Group("Publishing")
@Hidden
- @HelpMessage("Scala version suffix to append to the module name, like \"_2.13\" or \"_3\"")
- @ValueDescription("suffix")
- scalaVersionSuffix: Option[String] = None,
- @Group("Publishing")
- @Hidden
- @HelpMessage("Scala platform suffix to append to the module name, like \"_sjs1\" or \"_native0.4\"")
- @ValueDescription("suffix")
- scalaPlatformSuffix: Option[String] = None,
-
- @Group("Publishing")
- @HelpMessage("Whether to build and publish source JARs")
- sources: Option[Boolean] = None,
-
- @Group("Publishing")
- @HelpMessage("Whether to build and publish doc JARs")
- @ExtraName("scaladoc")
- @ExtraName("javadoc")
- doc: Option[Boolean] = None,
-
- @Group("Publishing")
- @HelpMessage("ID of the GPG key to use to sign artifacts")
- @ValueDescription("key-id")
- @ExtraName("K")
- gpgKey: Option[String] = None,
-
- @Group("Publishing")
- @HelpMessage("Method to use to sign artifacts")
- @ValueDescription("gpg|bc")
- signer: Option[String] = None,
-
- @Group("Publishing")
- @HelpMessage("gpg command-line options")
- @ValueDescription("argument")
- @ExtraName("G")
- @ExtraName("gpgOpt")
- gpgOption: List[String] = Nil
+ ivy2LocalLike: Option[Boolean] = None
)
// format: on
diff --git a/modules/cli-options/src/main/scala/scala/cli/commands/publish/PublishParamsOptions.scala b/modules/cli-options/src/main/scala/scala/cli/commands/publish/PublishParamsOptions.scala
new file mode 100644
index 0000000000..5fa9b66347
--- /dev/null
+++ b/modules/cli-options/src/main/scala/scala/cli/commands/publish/PublishParamsOptions.scala
@@ -0,0 +1,61 @@
+package scala.cli.commands.publish
+
+import caseapp._
+
+import scala.cli.signing.shared.PasswordOption
+import scala.cli.signing.util.ArgParsers._
+
+// format: off
+final case class PublishParamsOptions(
+
+ @Group("Publishing")
+ @HelpMessage("Organization to publish artifacts under")
+ organization: Option[String] = None,
+ @Group("Publishing")
+ @HelpMessage("Name to publish artifacts as")
+ name: Option[String] = None,
+ @Group("Publishing")
+ @HelpMessage("Final name to publish artifacts as, including Scala version and platform suffixes if any")
+ moduleName: Option[String] = None,
+ @Group("Publishing")
+ @HelpMessage("Version to publish artifacts as")
+ version: Option[String] = None,
+ @Group("Publishing")
+ @HelpMessage("How to compute the version to publish artifacts as")
+ computeVersion: Option[String] = None,
+ @Group("Publishing")
+ @HelpMessage("URL to put in publishing metadata")
+ url: Option[String] = None,
+ @Group("Publishing")
+ @HelpMessage("License to put in publishing metadata")
+ @ValueDescription("name:URL")
+ license: Option[String] = None,
+ @Group("Publishing")
+ @HelpMessage("VCS information to put in publishing metadata")
+ vcs: Option[String] = None,
+ @Group("Publishing")
+ @HelpMessage("Description to put in publishing metadata")
+ description: Option[String] = None,
+ @Group("Publishing")
+ @HelpMessage("Developer(s) to add in publishing metadata, like \"alex|Alex|https://alex.info\" or \"alex|Alex|https://alex.info|alex@alex.me\"")
+ @ValueDescription("id|name|URL|email")
+ developer: List[String] = Nil,
+
+ @Group("Publishing")
+ @HelpMessage("Secret key to use to sign artifacts with BouncyCastle")
+ secretKey: Option[PasswordOption] = None,
+
+ @Group("Publishing")
+ @HelpMessage("Password of secret key to use to sign artifacts with BouncyCastle")
+ @ValueDescription("value:…")
+ @ExtraName("secretKeyPass")
+ secretKeyPassword: Option[PasswordOption] = None
+
+)
+// format: on
+
+object PublishParamsOptions {
+ lazy val parser: Parser[PublishParamsOptions] = Parser.derive
+ implicit lazy val parserAux: Parser.Aux[PublishParamsOptions, parser.D] = parser
+ implicit lazy val help: Help[PublishParamsOptions] = Help.derive
+}
diff --git a/modules/cli-options/src/main/scala/scala/cli/commands/publish/PublishRepositoryOptions.scala b/modules/cli-options/src/main/scala/scala/cli/commands/publish/PublishRepositoryOptions.scala
new file mode 100644
index 0000000000..eb2ec13843
--- /dev/null
+++ b/modules/cli-options/src/main/scala/scala/cli/commands/publish/PublishRepositoryOptions.scala
@@ -0,0 +1,35 @@
+package scala.cli.commands.publish
+
+import caseapp._
+
+import scala.cli.signing.shared.PasswordOption
+import scala.cli.signing.util.ArgParsers._
+
+// format: off
+final case class PublishRepositoryOptions(
+
+ @Group("Publishing")
+ @HelpMessage("Repository to publish to")
+ @ValueDescription("URL or path")
+ @ExtraName("R")
+ @ExtraName("publishRepo")
+ publishRepository: Option[String] = None,
+
+ @Group("Publishing")
+ @HelpMessage("User to use with publishing repository")
+ @ValueDescription("user")
+ user: Option[PasswordOption] = None,
+
+ @Group("Publishing")
+ @HelpMessage("Password to use with publishing repository")
+ @ValueDescription("value:…")
+ password: Option[PasswordOption] = None
+
+)
+// format: on
+
+object PublishRepositoryOptions {
+ lazy val parser: Parser[PublishRepositoryOptions] = Parser.derive
+ implicit lazy val parserAux: Parser.Aux[PublishRepositoryOptions, parser.D] = parser
+ implicit lazy val help: Help[PublishRepositoryOptions] = Help.derive
+}
diff --git a/modules/cli-options/src/main/scala/scala/cli/commands/publish/SharedPublishOptions.scala b/modules/cli-options/src/main/scala/scala/cli/commands/publish/SharedPublishOptions.scala
index 47d2905301..9e75f9bb7e 100644
--- a/modules/cli-options/src/main/scala/scala/cli/commands/publish/SharedPublishOptions.scala
+++ b/modules/cli-options/src/main/scala/scala/cli/commands/publish/SharedPublishOptions.scala
@@ -2,71 +2,57 @@ package scala.cli.commands.publish
import caseapp._
-import scala.cli.signing.shared.PasswordOption
-import scala.cli.signing.util.ArgParsers._
-
// format: off
final case class SharedPublishOptions(
@Group("Publishing")
- @HelpMessage("Organization to publish artifacts under")
- organization: Option[String] = None,
- @Group("Publishing")
- @HelpMessage("Name to publish artifacts as")
- name: Option[String] = None,
- @Group("Publishing")
- @HelpMessage("Final name to publish artifacts as, including Scala version and platform suffixes if any")
- moduleName: Option[String] = None,
- @Group("Publishing")
- @HelpMessage("Version to publish artifacts as")
- version: Option[String] = None,
- @Group("Publishing")
- @HelpMessage("How to compute the version to publish artifacts as")
- computeVersion: Option[String] = None,
- @Group("Publishing")
- @HelpMessage("URL to put in publishing metadata")
- url: Option[String] = None,
- @Group("Publishing")
- @HelpMessage("License to put in publishing metadata")
- @ValueDescription("name:URL")
- license: Option[String] = None,
+ @HelpMessage("Directory where temporary files for publishing should be written")
+ @Hidden
+ workingDir: Option[String] = None,
+
@Group("Publishing")
- @HelpMessage("VCS information to put in publishing metadata")
- vcs: Option[String] = None,
+ @Hidden
+ @HelpMessage("Scala version suffix to append to the module name, like \"_2.13\" or \"_3\"")
+ @ValueDescription("suffix")
+ scalaVersionSuffix: Option[String] = None,
@Group("Publishing")
- @HelpMessage("Description to put in publishing metadata")
- description: Option[String] = None,
+ @Hidden
+ @HelpMessage("Scala platform suffix to append to the module name, like \"_sjs1\" or \"_native0.4\"")
+ @ValueDescription("suffix")
+ scalaPlatformSuffix: Option[String] = None,
+
@Group("Publishing")
- @HelpMessage("Developer(s) to add in publishing metadata, like \"alex|Alex|https://alex.info\" or \"alex|Alex|https://alex.info|alex@alex.me\"")
- @ValueDescription("id|name|URL|email")
- developer: List[String] = Nil,
+ @HelpMessage("Whether to build and publish source JARs")
+ sources: Option[Boolean] = None,
@Group("Publishing")
- @HelpMessage("Repository to publish to")
- @ValueDescription("URL or path")
- @ExtraName("R")
- @ExtraName("publishRepo")
- publishRepository: Option[String] = None,
+ @HelpMessage("Whether to build and publish doc JARs")
+ @ExtraName("scaladoc")
+ @ExtraName("javadoc")
+ doc: Option[Boolean] = None,
@Group("Publishing")
- @HelpMessage("User to use with publishing repository")
- @ValueDescription("user")
- user: Option[PasswordOption] = None,
+ @HelpMessage("ID of the GPG key to use to sign artifacts")
+ @ValueDescription("key-id")
+ @ExtraName("K")
+ gpgKey: Option[String] = None,
@Group("Publishing")
- @HelpMessage("Password to use with publishing repository")
- @ValueDescription("value:…")
- password: Option[PasswordOption] = None,
+ @HelpMessage("Method to use to sign artifacts")
+ @ValueDescription("gpg|bc")
+ signer: Option[String] = None,
@Group("Publishing")
- @HelpMessage("Secret key to use to sign artifacts with BouncyCastle")
- secretKey: Option[PasswordOption] = None,
+ @HelpMessage("gpg command-line options")
+ @ValueDescription("argument")
+ @ExtraName("G")
+ @ExtraName("gpgOpt")
+ gpgOption: List[String] = Nil,
@Group("Publishing")
- @HelpMessage("Password of secret key to use to sign artifacts with BouncyCastle")
- @ValueDescription("value:…")
- @ExtraName("secretKeyPass")
- secretKeyPassword: Option[PasswordOption] = None
+ @HelpMessage("Set Ivy 2 home directory")
+ @ValueDescription("path")
+ ivy2Home: Option[String] = None
)
// format: on
diff --git a/modules/cli/src/main/scala/scala/cli/ScalaCliCommands.scala b/modules/cli/src/main/scala/scala/cli/ScalaCliCommands.scala
index 49597e8b72..345677bc37 100644
--- a/modules/cli/src/main/scala/scala/cli/ScalaCliCommands.scala
+++ b/modules/cli/src/main/scala/scala/cli/ScalaCliCommands.scala
@@ -9,7 +9,7 @@ import scala.cli.commands._
import scala.cli.commands.bloop.BloopOutput
import scala.cli.commands.github.{SecretCreate, SecretList}
import scala.cli.commands.pgp.{PgpCommands, PgpCommandsSubst}
-import scala.cli.commands.publish.Publish
+import scala.cli.commands.publish.{Publish, PublishLocal}
class ScalaCliCommands(
val progName: String,
@@ -45,6 +45,7 @@ class ScalaCliCommands(
Repl,
Package,
Publish,
+ PublishLocal,
Run,
SecretCreate,
SecretList,
diff --git a/modules/cli/src/main/scala/scala/cli/commands/publish/Ivy.scala b/modules/cli/src/main/scala/scala/cli/commands/publish/Ivy.scala
new file mode 100644
index 0000000000..590ae1e0a5
--- /dev/null
+++ b/modules/cli/src/main/scala/scala/cli/commands/publish/Ivy.scala
@@ -0,0 +1,130 @@
+package scala.cli.commands.publish
+
+import coursier.core.{Configuration, ModuleName, Organization, Type}
+import coursier.publish.Pom
+import coursier.publish.Pom.{Developer, License, Scm}
+
+import java.time.format.DateTimeFormatterBuilder
+import java.time.temporal.ChronoField
+import java.time.{LocalDateTime, ZoneOffset}
+
+import scala.collection.mutable
+import scala.xml.NodeSeq
+
+object Ivy {
+
+ private lazy val dateFormatter = new DateTimeFormatterBuilder()
+ .appendValue(ChronoField.YEAR, 4)
+ .appendValue(ChronoField.MONTH_OF_YEAR, 2)
+ .appendValue(ChronoField.DAY_OF_MONTH, 2)
+ .appendValue(ChronoField.HOUR_OF_DAY, 2)
+ .appendValue(ChronoField.MINUTE_OF_HOUR, 2)
+ .appendValue(ChronoField.SECOND_OF_MINUTE, 2)
+ .toFormatter
+
+ def create(
+ organization: Organization,
+ moduleName: ModuleName,
+ version: String,
+ packaging: Option[Type] = None,
+ description: Option[String] = None,
+ url: Option[String] = None,
+ name: Option[String] = None,
+ // TODO Accept full-fledged coursier.Dependency
+ dependencies: Seq[(Organization, ModuleName, String, Option[Configuration])] = Nil,
+ license: Option[License] = None,
+ scm: Option[Scm] = None,
+ developers: Seq[Developer] = Nil,
+ time: LocalDateTime = LocalDateTime.now(ZoneOffset.UTC),
+ hasPom: Boolean = true,
+ hasDoc: Boolean = true,
+ hasSources: Boolean = true
+ ): String = {
+
+ val nodes = new mutable.ListBuffer[NodeSeq]
+
+ nodes += {
+ val desc = (description, url) match {
+ case (Some(d), Some(u)) =>
+ Seq({d})
+ case (Some(d), None) =>
+ Seq({d})
+ case (None, Some(u)) =>
+ Seq()
+ case (None, None) =>
+ Nil
+ }
+
+ {desc}
+
+ }
+
+ nodes += {
+
+ val docConf =
+ if (hasDoc) Seq()
+ else Nil
+ val sourcesConf =
+ if (hasSources) Seq()
+ else Nil
+ val pomConf =
+ if (hasPom) Seq()
+ else Nil
+
+
+
+
+
+
+ {docConf}
+ {sourcesConf}
+ {pomConf}
+
+ }
+
+ nodes += {
+
+ val docPub =
+ if (hasDoc) Seq()
+ else Nil
+ val sourcesPub =
+ if (hasSources) Seq()
+ else Nil
+ val pomPub =
+ if (hasPom) Seq()
+ else Nil
+
+
+
+ {docPub}
+ {sourcesPub}
+ {pomPub}
+
+ }
+
+ nodes += {
+ val depNodes = dependencies.map {
+ case (org, name, ver, confOpt) =>
+ val conf = confOpt.map(_.value).getOrElse("compile")
+ val confSpec = s"$conf->default(compile)"
+
+ }
+
+ {depNodes}
+
+ }
+
+ Pom.print(
+
+ {nodes.result()}
+
+ )
+ }
+
+}
diff --git a/modules/cli/src/main/scala/scala/cli/commands/publish/Publish.scala b/modules/cli/src/main/scala/scala/cli/commands/publish/Publish.scala
index 05527d81b5..30e4bb30db 100644
--- a/modules/cli/src/main/scala/scala/cli/commands/publish/Publish.scala
+++ b/modules/cli/src/main/scala/scala/cli/commands/publish/Publish.scala
@@ -18,23 +18,31 @@ import java.io.OutputStreamWriter
import java.net.URI
import java.nio.charset.StandardCharsets
import java.nio.file.{Path => NioPath, Paths}
-import java.time.Instant
+import java.time.{Instant, LocalDateTime, ZoneOffset}
import java.util.concurrent.Executors
import java.util.function.Supplier
import scala.build.EitherCps.{either, value}
import scala.build.Ops._
+import scala.build._
+import scala.build.compiler.ScalaCompilerMaker
import scala.build.errors.{BuildException, CompositeBuildException, NoMainClassFoundError}
import scala.build.internal.Util
import scala.build.internal.Util.ScalaDependencyOps
import scala.build.options.publish.{ComputeVersion, Developer, License, Signer => PSigner, Vcs}
import scala.build.options.{BuildOptions, ConfigMonoid, Scope}
-import scala.build.{Build, BuildThreads, Builds, Logger, Os, Positioned}
import scala.cli.CurrentParams
import scala.cli.commands.pgp.PgpExternalCommand
+import scala.cli.commands.publish.{PublishParamsOptions, PublishRepositoryOptions}
import scala.cli.commands.util.ScalaCliSttpBackend
import scala.cli.commands.util.SharedOptionsUtil._
-import scala.cli.commands.{Package => PackageCmd, ScalaCommand, WatchUtil}
+import scala.cli.commands.{
+ MainClassOptions,
+ Package => PackageCmd,
+ ScalaCommand,
+ SharedOptions,
+ WatchUtil
+}
import scala.cli.errors.{FailedToSignFileError, MissingPublishOptionError, UploadError}
import scala.cli.packaging.Library
import scala.cli.publish.BouncycastleSignerMaker
@@ -46,64 +54,71 @@ object Publish extends ScalaCommand[PublishOptions] {
override def sharedOptions(options: PublishOptions) =
Some(options.shared)
- def mkBuildOptions(ops: PublishOptions): Either[BuildException, BuildOptions] = either {
- import ops._
+ def mkBuildOptions(
+ shared: SharedOptions,
+ publishParams: PublishParamsOptions,
+ sharedPublish: SharedPublishOptions,
+ publishRepo: PublishRepositoryOptions,
+ mainClass: MainClassOptions,
+ ivy2LocalLike: Option[Boolean]
+ ): Either[BuildException, BuildOptions] = either {
val baseOptions = shared.buildOptions(enableJmh = false, jmhVersion = None)
baseOptions.copy(
mainClass = mainClass.mainClass.filter(_.nonEmpty),
notForBloopOptions = baseOptions.notForBloopOptions.copy(
publishOptions = baseOptions.notForBloopOptions.publishOptions.copy(
- organization = sharedPublish.organization.map(_.trim).filter(_.nonEmpty).map(
+ organization = publishParams.organization.map(_.trim).filter(_.nonEmpty).map(
Positioned.commandLine(_)
),
- name = sharedPublish.name.map(_.trim).filter(_.nonEmpty).map(Positioned.commandLine(_)),
+ name = publishParams.name.map(_.trim).filter(_.nonEmpty).map(Positioned.commandLine(_)),
moduleName =
- sharedPublish.moduleName.map(_.trim).filter(_.nonEmpty).map(Positioned.commandLine(_)),
+ publishParams.moduleName.map(_.trim).filter(_.nonEmpty).map(Positioned.commandLine(_)),
version =
- sharedPublish.version.map(_.trim).filter(_.nonEmpty).map(Positioned.commandLine(_)),
- url = sharedPublish.url.map(_.trim).filter(_.nonEmpty).map(Positioned.commandLine(_)),
+ publishParams.version.map(_.trim).filter(_.nonEmpty).map(Positioned.commandLine(_)),
+ url = publishParams.url.map(_.trim).filter(_.nonEmpty).map(Positioned.commandLine(_)),
license = value {
- sharedPublish.license
+ publishParams.license
.map(_.trim).filter(_.nonEmpty)
.map(Positioned.commandLine(_))
.map(License.parse(_))
.sequence
},
versionControl = value {
- sharedPublish.vcs
+ publishParams.vcs
.map(_.trim).filter(_.nonEmpty)
.map(Positioned.commandLine(_))
.map(Vcs.parse(_))
.sequence
},
- description = sharedPublish.description.map(_.trim).filter(_.nonEmpty),
+ description = publishParams.description.map(_.trim).filter(_.nonEmpty),
developers = value {
- sharedPublish.developer
+ publishParams.developer
.filter(_.trim.nonEmpty)
.map(Positioned.commandLine(_))
.map(Developer.parse(_))
.sequence
.left.map(CompositeBuildException(_))
},
- scalaVersionSuffix = scalaVersionSuffix.map(_.trim),
- scalaPlatformSuffix = scalaPlatformSuffix.map(_.trim),
- repository = sharedPublish.publishRepository.filter(_.trim.nonEmpty),
- sourceJar = sources,
- docJar = doc,
- gpgSignatureId = gpgKey.map(_.trim).filter(_.nonEmpty),
- gpgOptions = gpgOption,
- secretKey = sharedPublish.secretKey,
- secretKeyPassword = sharedPublish.secretKeyPassword,
- repoUser = sharedPublish.user,
- repoPassword = sharedPublish.password,
+ scalaVersionSuffix = sharedPublish.scalaVersionSuffix.map(_.trim),
+ scalaPlatformSuffix = sharedPublish.scalaPlatformSuffix.map(_.trim),
+ repository = publishRepo.publishRepository.filter(_.trim.nonEmpty),
+ repositoryIsIvy2LocalLike = ivy2LocalLike,
+ sourceJar = sharedPublish.sources,
+ docJar = sharedPublish.doc,
+ gpgSignatureId = sharedPublish.gpgKey.map(_.trim).filter(_.nonEmpty),
+ gpgOptions = sharedPublish.gpgOption,
+ secretKey = publishParams.secretKey,
+ secretKeyPassword = publishParams.secretKeyPassword,
+ repoUser = publishRepo.user,
+ repoPassword = publishRepo.password,
signer = value {
- signer
+ sharedPublish.signer
.map(Positioned.commandLine(_))
.map(PSigner.parse(_))
.sequence
},
computeVersion = value {
- sharedPublish.computeVersion
+ publishParams.computeVersion
.map(Positioned.commandLine(_))
.map(ComputeVersion.parse(_))
.sequence
@@ -115,22 +130,30 @@ object Publish extends ScalaCommand[PublishOptions] {
def run(options: PublishOptions, args: RemainingArgs): Unit = {
maybePrintGroupHelp(options)
+
CurrentParams.verbosity = options.shared.logging.verbosity
val inputs = options.shared.inputsOrExit(args)
CurrentParams.workspaceOpt = Some(inputs.workspace)
- val logger = options.shared.logger
- val initialBuildOptions = mkBuildOptions(options).orExit(logger)
- val threads = BuildThreads.create()
+ val logger = options.shared.logger
+ val initialBuildOptions = mkBuildOptions(
+ options.shared,
+ options.publishParams,
+ options.sharedPublish,
+ options.publishRepo,
+ options.mainClass,
+ options.ivy2LocalLike
+ ).orExit(logger)
+ val threads = BuildThreads.create()
val compilerMaker = options.shared.compilerMaker(threads)
val docCompilerMaker = options.shared.compilerMaker(threads, scaladoc = true)
val cross = options.compileCross.cross.getOrElse(false)
- lazy val workingDir = options.workingDir
+ lazy val workingDir = options.sharedPublish.workingDir
.filter(_.trim.nonEmpty)
- .map(os.Path(_, Os.pwd))
+ .map(os.Path(_, os.pwd))
.getOrElse {
os.temp.dir(
prefix = "scala-cli-publish-",
@@ -138,7 +161,38 @@ object Publish extends ScalaCommand[PublishOptions] {
)
}
- if (options.watch.watch) {
+ val ivy2HomeOpt = options.sharedPublish.ivy2Home
+ .filter(_.trim.nonEmpty)
+ .map(os.Path(_, os.pwd))
+
+ doRun(
+ inputs,
+ logger,
+ initialBuildOptions,
+ compilerMaker,
+ docCompilerMaker,
+ cross,
+ workingDir,
+ ivy2HomeOpt,
+ publishLocal = false,
+ options.watch.watch
+ )
+ }
+
+ def doRun(
+ inputs: Inputs,
+ logger: Logger,
+ initialBuildOptions: BuildOptions,
+ compilerMaker: ScalaCompilerMaker,
+ docCompilerMaker: ScalaCompilerMaker,
+ cross: Boolean,
+ workingDir: => os.Path,
+ ivy2HomeOpt: Option[os.Path],
+ publishLocal: Boolean,
+ watch: Boolean
+ ): Unit = {
+
+ if (watch) {
val watcher = Build.watch(
inputs,
initialBuildOptions,
@@ -151,7 +205,7 @@ object Publish extends ScalaCommand[PublishOptions] {
postAction = () => WatchUtil.printWatchMessage()
) { res =>
res.orReport(logger).foreach { builds =>
- maybePublish(builds, workingDir, logger, allowExit = false)
+ maybePublish(builds, workingDir, ivy2HomeOpt, publishLocal, logger, allowExit = false)
}
}
try WatchUtil.waitForCtrlC()
@@ -169,7 +223,7 @@ object Publish extends ScalaCommand[PublishOptions] {
buildTests = false,
partial = None
).orExit(logger)
- maybePublish(builds, workingDir, logger, allowExit = true)
+ maybePublish(builds, workingDir, ivy2HomeOpt, publishLocal, logger, allowExit = true)
}
}
@@ -183,6 +237,8 @@ object Publish extends ScalaCommand[PublishOptions] {
private def maybePublish(
builds: Builds,
workingDir: os.Path,
+ ivy2HomeOpt: Option[os.Path],
+ publishLocal: Boolean,
logger: Logger,
allowExit: Boolean
): Unit = {
@@ -204,7 +260,7 @@ object Publish extends ScalaCommand[PublishOptions] {
val docBuilds0 = builds.allDoc.collect {
case s: Build.Successful => s
}
- val res = doPublish(builds0, docBuilds0, workingDir, logger)
+ val res = doPublish(builds0, docBuilds0, workingDir, ivy2HomeOpt, publishLocal, logger)
if (allowExit)
res.orExit(logger)
else
@@ -223,6 +279,7 @@ object Publish extends ScalaCommand[PublishOptions] {
docBuildOpt: Option[Build.Successful],
workingDir: os.Path,
now: Instant,
+ isIvy2LocalLike: Boolean,
logger: Logger
): Either[BuildException, (FileSet, String)] = either {
@@ -268,50 +325,21 @@ object Publish extends ScalaCommand[PublishOptions] {
}
}
- val dependencies = build.artifacts.userDependencies
- .map(_.toCs(build.artifacts.scalaOpt.map(_.params)))
- .sequence
- .left.map(CompositeBuildException(_))
- .orExit(logger)
- .map { dep0 =>
- val config =
- if (build.scope == Scope.Main) None
- else Some(Configuration(build.scope.name))
- (dep0.module.organization, dep0.module.name, dep0.version, config)
- }
-
- val mainClassOpt = build.options.mainClass.orElse {
- build.retainedMainClass match {
- case Left(_: NoMainClassFoundError) => None
- case Left(err) =>
- logger.debug(s"Error while looking for main class: $err")
- None
- case Right(cls) => Some(cls)
+ val mainJar = {
+ val mainClassOpt = build.options.mainClass.orElse {
+ build.retainedMainClass match {
+ case Left(_: NoMainClassFoundError) => None
+ case Left(err) =>
+ logger.debug(s"Error while looking for main class: $err")
+ None
+ case Right(cls) => Some(cls)
+ }
}
+ val content = Library.libraryJar(build, mainClassOpt)
+ val dest = workingDir / org / s"$moduleName-$ver.jar"
+ os.write(dest, content, createFolders = true)
+ dest
}
- val mainJarContent = Library.libraryJar(build, mainClassOpt)
- val mainJar = workingDir / org / s"$moduleName-$ver.jar"
- os.write(mainJar, mainJarContent, createFolders = true)
-
- val pomContent = Pom.create(
- organization = coursier.Organization(org),
- moduleName = coursier.ModuleName(moduleName),
- version = ver,
- packaging = None,
- url = publishOptions.url.map(_.value),
- name = Some(name), // ?
- dependencies = dependencies,
- description = publishOptions.description,
- license = publishOptions.license.map(_.value).map { l =>
- Pom.License(l.name, l.url)
- },
- scm = publishOptions.versionControl.map { vcs =>
- Pom.Scm(vcs.url, vcs.connection, vcs.developerConnection)
- },
- developers = publishOptions.developers.map { dev =>
- Pom.Developer(dev.id, dev.name, dev.url, dev.mail)
- }
- )
val sourceJarOpt =
if (publishOptions.sourceJar.getOrElse(true)) {
@@ -336,36 +364,132 @@ object Publish extends ScalaCommand[PublishOptions] {
else
None
- val basePath = Path(org.split('.').toSeq ++ Seq(moduleName, ver))
+ val dependencies = build.artifacts.userDependencies
+ .map(_.toCs(build.artifacts.scalaOpt.map(_.params)))
+ .sequence
+ .left.map(CompositeBuildException(_))
+ .orExit(logger)
+ .map { dep0 =>
+ val config =
+ if (build.scope == Scope.Main) None
+ else Some(Configuration(build.scope.name))
+ (dep0.module.organization, dep0.module.name, dep0.version, config)
+ }
+ val url = publishOptions.url.map(_.value)
+ val license = publishOptions.license.map(_.value).map { l =>
+ Pom.License(l.name, l.url)
+ }
+ val scm = publishOptions.versionControl.map { vcs =>
+ Pom.Scm(vcs.url, vcs.connection, vcs.developerConnection)
+ }
+ val developers = publishOptions.developers.map { dev =>
+ Pom.Developer(dev.id, dev.name, dev.url, dev.mail)
+ }
+ val description = publishOptions.description.getOrElse(moduleName)
- val mainEntries = Seq(
- (basePath / s"$moduleName-$ver.pom") -> Content.InMemory(
- now,
- pomContent.getBytes(StandardCharsets.UTF_8)
- ),
- (basePath / s"$moduleName-$ver.jar") -> Content.File(mainJar.toNIO)
+ val pomContent = Pom.create(
+ organization = coursier.Organization(org),
+ moduleName = coursier.ModuleName(moduleName),
+ version = ver,
+ packaging = None,
+ url = url,
+ name = Some(moduleName), // ?
+ dependencies = dependencies,
+ description = Some(description),
+ license = license,
+ scm = scm,
+ developers = developers
)
- val sourceJarEntries = sourceJarOpt
- .map { sourceJar =>
- (basePath / s"$moduleName-$ver-sources.jar") -> Content.File(sourceJar.toNIO)
- }
- .toSeq
+ def ivyContent = Ivy.create(
+ organization = coursier.Organization(org),
+ moduleName = coursier.ModuleName(moduleName),
+ version = ver,
+ packaging = None,
+ url = url,
+ name = Some(moduleName), // ?
+ dependencies = dependencies,
+ description = Some(description),
+ license = license,
+ scm = scm,
+ developers = developers,
+ time = LocalDateTime.ofInstant(now, ZoneOffset.UTC),
+ hasPom = true,
+ hasDoc = docJarOpt.isDefined,
+ hasSources = sourceJarOpt.isDefined
+ )
- val docJarEntries = docJarOpt
- .map { docJar =>
- (basePath / s"$moduleName-$ver-javadoc.jar") -> Content.File(docJar.toNIO)
- }
- .toSeq
+ def mavenFileSet = {
+
+ val basePath = Path(org.split('.').toSeq ++ Seq(moduleName, ver))
+
+ val mainEntries = Seq(
+ (basePath / s"$moduleName-$ver.pom") -> Content.InMemory(
+ now,
+ pomContent.getBytes(StandardCharsets.UTF_8)
+ ),
+ (basePath / s"$moduleName-$ver.jar") -> Content.File(mainJar.toNIO)
+ )
- // TODO version listings, …
- (FileSet(mainEntries ++ sourceJarEntries ++ docJarEntries), ver)
+ val sourceJarEntries = sourceJarOpt
+ .map { sourceJar =>
+ (basePath / s"$moduleName-$ver-sources.jar") -> Content.File(sourceJar.toNIO)
+ }
+ .toSeq
+
+ val docJarEntries = docJarOpt
+ .map { docJar =>
+ (basePath / s"$moduleName-$ver-javadoc.jar") -> Content.File(docJar.toNIO)
+ }
+ .toSeq
+
+ // TODO version listings, …
+ FileSet(mainEntries ++ sourceJarEntries ++ docJarEntries)
+ }
+
+ def ivy2LocalLikeFileSet = {
+
+ val basePath = Path(Seq(org, moduleName, ver))
+
+ val mainEntries = Seq(
+ (basePath / "poms" / s"$moduleName.pom") -> Content.InMemory(
+ now,
+ pomContent.getBytes(StandardCharsets.UTF_8)
+ ),
+ (basePath / "ivys" / "ivy.xml") -> Content.InMemory(
+ now,
+ ivyContent.getBytes(StandardCharsets.UTF_8)
+ ),
+ (basePath / "jars" / s"$moduleName.jar") -> Content.File(mainJar.toNIO)
+ )
+
+ val sourceJarEntries = sourceJarOpt
+ .map { sourceJar =>
+ (basePath / "srcs" / s"$moduleName-sources.jar") -> Content.File(sourceJar.toNIO)
+ }
+ .toSeq
+
+ val docJarEntries = docJarOpt
+ .map { docJar =>
+ (basePath / "docs" / s"$moduleName-javadoc.jar") -> Content.File(docJar.toNIO)
+ }
+ .toSeq
+
+ FileSet(mainEntries ++ sourceJarEntries ++ docJarEntries)
+ }
+
+ val fileSet =
+ if (isIvy2LocalLike) ivy2LocalLikeFileSet else mavenFileSet
+
+ (fileSet, ver)
}
private def doPublish(
builds: Seq[Build.Successful],
docBuilds: Seq[Build.Successful],
workingDir: os.Path,
+ ivy2HomeOpt: Option[os.Path],
+ publishLocal: Boolean,
logger: Logger
): Either[BuildException, Unit] = either {
@@ -376,6 +500,90 @@ object Publish extends ScalaCommand[PublishOptions] {
else docBuilds.iterator.map(Some(_))
}
+ val publishOptions = ConfigMonoid.sum(
+ builds.map(_.options.notForBloopOptions.publishOptions)
+ )
+
+ val ec = builds.head.options.finalCache.ec
+
+ val (repo, hooks, isIvy2LocalLike) = {
+
+ lazy val es =
+ Executors.newSingleThreadScheduledExecutor(Util.daemonThreadFactory("publish-retry"))
+
+ lazy val authOpt = {
+ val userOpt = publishOptions.repoUser
+ val passwordOpt = publishOptions.repoPassword.map(_.get())
+ passwordOpt.map { password =>
+ Authentication(userOpt.fold("")(_.get().value), password.value)
+ }
+ }
+
+ def centralRepo(base: String) = {
+ val repo0 = {
+ val r = PublishRepository.Sonatype(MavenRepository(base))
+ authOpt.fold(r)(r.withAuthentication)
+ }
+ val backend = ScalaCliSttpBackend.httpURLConnection(logger)
+ val api = SonatypeApi(backend, base + "/service/local", authOpt, logger.verbosity)
+ val hooks0 = Hooks.sonatype(
+ repo0,
+ api,
+ logger.compilerOutputStream, // meh
+ logger.verbosity,
+ batch = coursier.paths.Util.useAnsiOutput(), // FIXME Get via logger
+ es
+ )
+ (repo0, hooks0, false)
+ }
+
+ def ivy2Local = {
+ val home = ivy2HomeOpt.getOrElse(os.home / ".ivy2")
+ val base = home / "local"
+ // not really a Maven repo…
+ (
+ PublishRepository.Simple(MavenRepository(base.toNIO.toUri.toASCIIString)),
+ Hooks.dummy,
+ true
+ )
+ }
+
+ if (publishLocal)
+ ivy2Local
+ else
+ publishOptions.repository match {
+ case None =>
+ value(Left(new MissingPublishOptionError(
+ "repository",
+ "--publish-repository",
+ "publish.repository"
+ )))
+ case Some("ivy2-local") =>
+ ivy2Local
+ case Some("central" | "maven-central" | "mvn-central") =>
+ centralRepo("https://oss.sonatype.org")
+ case Some("central-s01" | "maven-central-s01" | "mvn-central-s01") =>
+ centralRepo("https://s01.oss.sonatype.org")
+ case Some(repoStr) =>
+ val repo0 = RepositoryParser.repositoryOpt(repoStr)
+ .collect {
+ case m: MavenRepository =>
+ m
+ }
+ .getOrElse {
+ val url =
+ if (repoStr.contains("://")) repoStr
+ else os.Path(repoStr, Os.pwd).toNIO.toUri.toASCIIString
+ MavenRepository(url)
+ }
+ (
+ PublishRepository.Simple(repo0),
+ Hooks.dummy,
+ publishOptions.repositoryIsIvy2LocalLike.getOrElse(false)
+ )
+ }
+ }
+
val now = Instant.now()
val (fileSet0, versionOpt) = value {
it
@@ -383,7 +591,14 @@ object Publish extends ScalaCommand[PublishOptions] {
.filter(_._1.scope != Scope.Test)
.map {
case (build, docBuildOpt) =>
- buildFileSet(build, docBuildOpt, workingDir, now, logger)
+ buildFileSet(
+ build,
+ docBuildOpt,
+ workingDir,
+ now,
+ isIvy2LocalLike = isIvy2LocalLike,
+ logger
+ )
}
.sequence0
.map { l =>
@@ -393,13 +608,6 @@ object Publish extends ScalaCommand[PublishOptions] {
}
}
- val ec = builds.head.options.finalCache.ec
- lazy val es =
- Executors.newSingleThreadScheduledExecutor(Util.daemonThreadFactory("publish-retry"))
-
- val publishOptions = ConfigMonoid.sum(
- builds.map(_.options.notForBloopOptions.publishOptions)
- )
val signerOpt = publishOptions.signer.orElse {
if (publishOptions.secretKey.isDefined) Some(PSigner.BouncyCastle)
else if (publishOptions.gpgSignatureId.isDefined) Some(PSigner.Gpg)
@@ -472,59 +680,9 @@ object Publish extends ScalaCommand[PublishOptions] {
).unsafeRun()(ec)
val fileSet2 = fileSet1 ++ checksums
- val finalFileSet = fileSet2.order(ec).unsafeRun()(ec)
-
- lazy val authOpt = {
- val userOpt = publishOptions.repoUser
- val passwordOpt = publishOptions.repoPassword.map(_.get())
- passwordOpt.map { password =>
- Authentication(userOpt.fold("")(_.get().value), password.value)
- }
- }
-
- def centralRepo(base: String) = {
- val repo0 = {
- val r = PublishRepository.Sonatype(MavenRepository(base))
- authOpt.fold(r)(r.withAuthentication)
- }
- val backend = ScalaCliSttpBackend.httpURLConnection(logger)
- val api = SonatypeApi(backend, base + "/service/local", authOpt, logger.verbosity)
- val hooks0 = Hooks.sonatype(
- repo0,
- api,
- logger.compilerOutputStream, // meh
- logger.verbosity,
- batch = coursier.paths.Util.useAnsiOutput(), // FIXME Get via logger
- es
- )
- (repo0, hooks0)
- }
-
- val (repo, hooks) = publishOptions.repository match {
- case None =>
- value(Left(new MissingPublishOptionError(
- "repository",
- "--publish-repository",
- "publish.repository"
- )))
- case Some("central" | "maven-central" | "mvn-central") =>
- centralRepo("https://oss.sonatype.org")
- case Some("central-s01" | "maven-central-s01" | "mvn-central-s01") =>
- centralRepo("https://s01.oss.sonatype.org")
- case Some(repoStr) =>
- val repo0 = RepositoryParser.repositoryOpt(repoStr)
- .collect {
- case m: MavenRepository =>
- m
- }
- .getOrElse {
- val url =
- if (repoStr.contains("://")) repoStr
- else os.Path(repoStr, Os.pwd).toNIO.toUri.toASCIIString
- MavenRepository(url)
- }
- (PublishRepository.Simple(repo0), Hooks.dummy)
- }
+ val finalFileSet =
+ if (isIvy2LocalLike) fileSet2
+ else fileSet2.order(ec).unsafeRun()(ec)
val isSnapshot0 = versionOpt.exists(_.endsWith("SNAPSHOT"))
val hooksData = hooks.beforeUpload(finalFileSet, isSnapshot0).unsafeRun()(ec)
diff --git a/modules/cli/src/main/scala/scala/cli/commands/publish/PublishLocal.scala b/modules/cli/src/main/scala/scala/cli/commands/publish/PublishLocal.scala
new file mode 100644
index 0000000000..e813bd347c
--- /dev/null
+++ b/modules/cli/src/main/scala/scala/cli/commands/publish/PublishLocal.scala
@@ -0,0 +1,71 @@
+package scala.cli.commands.publish
+
+import caseapp.core.RemainingArgs
+
+import scala.build.BuildThreads
+import scala.cli.CurrentParams
+import scala.cli.commands.ScalaCommand
+import scala.cli.commands.util.SharedOptionsUtil._
+
+object PublishLocal extends ScalaCommand[PublishLocalOptions] {
+
+ override def group = "Main"
+ override def inSipScala = false
+ override def sharedOptions(options: PublishLocalOptions) =
+ Some(options.shared)
+
+ override def names = List(
+ List("publish", "local")
+ )
+
+ def run(options: PublishLocalOptions, args: RemainingArgs): Unit = {
+ maybePrintGroupHelp(options)
+
+ CurrentParams.verbosity = options.shared.logging.verbosity
+ val inputs = options.shared.inputsOrExit(args)
+ CurrentParams.workspaceOpt = Some(inputs.workspace)
+
+ val logger = options.shared.logger
+ val initialBuildOptions = Publish.mkBuildOptions(
+ options.shared,
+ options.publishParams,
+ options.sharedPublish,
+ PublishRepositoryOptions(),
+ options.mainClass,
+ None
+ ).orExit(logger)
+ val threads = BuildThreads.create()
+
+ val compilerMaker = options.shared.compilerMaker(threads)
+ val docCompilerMaker = options.shared.compilerMaker(threads, scaladoc = true)
+
+ val cross = options.compileCross.cross.getOrElse(false)
+
+ lazy val workingDir = options.sharedPublish.workingDir
+ .filter(_.trim.nonEmpty)
+ .map(os.Path(_, os.pwd))
+ .getOrElse {
+ os.temp.dir(
+ prefix = "scala-cli-publish-",
+ deleteOnExit = true
+ )
+ }
+
+ val ivy2HomeOpt = options.sharedPublish.ivy2Home
+ .filter(_.trim.nonEmpty)
+ .map(os.Path(_, os.pwd))
+
+ Publish.doRun(
+ inputs,
+ logger,
+ initialBuildOptions,
+ compilerMaker,
+ docCompilerMaker,
+ cross,
+ workingDir,
+ ivy2HomeOpt,
+ publishLocal = true,
+ options.watch.watch
+ )
+ }
+}
diff --git a/modules/integration/src/test/scala/scala/cli/integration/PublishTestsDefault.scala b/modules/integration/src/test/scala/scala/cli/integration/PublishTestsDefault.scala
index cf3c41134f..e85afc4cb8 100644
--- a/modules/integration/src/test/scala/scala/cli/integration/PublishTestsDefault.scala
+++ b/modules/integration/src/test/scala/scala/cli/integration/PublishTestsDefault.scala
@@ -4,6 +4,68 @@ import com.eed3si9n.expecty.Expecty.expect
class PublishTestsDefault extends PublishTestDefinitions(scalaVersionOpt = None) {
+ test("publish local") {
+ val testOrg = "test-local-org.sth"
+ val testName = "my-proj"
+ val testVersion = "1.5.6"
+ val inputs = TestInputs(
+ Seq(
+ os.rel / "Project.scala" ->
+ s"""//> using publish.organization "$testOrg"
+ |//> using publish.name "$testName"
+ |//> using publish.version "$testVersion"
+ |
+ |//> using scala "2.13"
+ |//> using lib "com.lihaoyi::os-lib:0.8.1"
+ |
+ |object Project {
+ | def message = "Hello"
+ |
+ | def main(args: Array[String]): Unit =
+ | println(message)
+ |}
+ |""".stripMargin
+ )
+ )
+
+ val expectedFiles = {
+ val modName = s"${testName}_2.13"
+ val base = os.rel / testOrg / modName / testVersion
+ val baseFiles = Seq(
+ base / "jars" / s"$modName.jar",
+ base / "docs" / s"$modName-javadoc.jar",
+ base / "srcs" / s"$modName-sources.jar",
+ base / "poms" / s"$modName.pom",
+ base / "ivys" / "ivy.xml"
+ )
+ baseFiles
+ .flatMap { f =>
+ val md5 = f / os.up / s"${f.last}.md5"
+ val sha1 = f / os.up / s"${f.last}.sha1"
+ Seq(f, md5, sha1)
+ }
+ .toSet
+ }
+
+ inputs.fromRoot { root =>
+ os.proc(TestUtil.cli, "publish", "local", "Project.scala", "--ivy2-home", os.rel / "ivy2")
+ .call(cwd = root)
+ val ivy2Local = root / "ivy2" / "local"
+ val foundFiles = os.walk(ivy2Local)
+ .filter(os.isFile(_))
+ .map(_.relativeTo(ivy2Local))
+ .toSet
+ val missingFiles = expectedFiles -- foundFiles
+ val unexpectedFiles = foundFiles -- expectedFiles
+ if (missingFiles.nonEmpty)
+ pprint.err.log(missingFiles)
+ if (unexpectedFiles.nonEmpty)
+ pprint.err.log(unexpectedFiles)
+ expect(missingFiles.isEmpty)
+ expect(unexpectedFiles.isEmpty)
+ }
+ }
+
test("Pure Java") {
val testOrg = "test-org.foo"
val testName = "foo"
diff --git a/modules/options/src/main/scala/scala/build/options/PublishOptions.scala b/modules/options/src/main/scala/scala/build/options/PublishOptions.scala
index 46b9c48939..08ccd74078 100644
--- a/modules/options/src/main/scala/scala/build/options/PublishOptions.scala
+++ b/modules/options/src/main/scala/scala/build/options/PublishOptions.scala
@@ -17,6 +17,7 @@ final case class PublishOptions(
scalaVersionSuffix: Option[String] = None,
scalaPlatformSuffix: Option[String] = None,
repository: Option[String] = None,
+ repositoryIsIvy2LocalLike: Option[Boolean] = None,
sourceJar: Option[Boolean] = None,
docJar: Option[Boolean] = None,
gpgSignatureId: Option[String] = None,
diff --git a/website/docs/reference/cli-options.md b/website/docs/reference/cli-options.md
index dfd519ecba..e43676b982 100644
--- a/website/docs/reference/cli-options.md
+++ b/website/docs/reference/cli-options.md
@@ -105,6 +105,7 @@ Available in commands:
- [`browse` / `metabrowse`](./commands.md#browse)
- [`package`](./commands.md#package)
- [`publish`](./commands.md#publish)
+- [`publish local`](./commands.md#publish-local)
- [`console` / `repl`](./commands.md#console)
- [`run`](./commands.md#run)
- [`setup-ide`](./commands.md#setup-ide)
@@ -201,6 +202,7 @@ Compile test scope
Available in commands:
- [`package`](./commands.md#package)
- [`publish`](./commands.md#publish)
+- [`publish local`](./commands.md#publish-local)
- [`console` / `repl`](./commands.md#console)
- [`run`](./commands.md#run)
- [`shebang`](./commands.md#shebang)
@@ -228,6 +230,7 @@ Available in commands:
- [`browse` / `metabrowse`](./commands.md#browse)
- [`package`](./commands.md#package)
- [`publish`](./commands.md#publish)
+- [`publish local`](./commands.md#publish-local)
- [`console` / `repl`](./commands.md#console)
- [`run`](./commands.md#run)
- [`github secret create` / `gh secret create`](./commands.md#github-secret-create)
@@ -273,6 +276,7 @@ Available in commands:
- [`browse` / `metabrowse`](./commands.md#browse)
- [`package`](./commands.md#package)
- [`publish`](./commands.md#publish)
+- [`publish local`](./commands.md#publish-local)
- [`console` / `repl`](./commands.md#console)
- [`run`](./commands.md#run)
- [`setup-ide`](./commands.md#setup-ide)
@@ -317,6 +321,7 @@ Available in commands:
- [`browse` / `metabrowse`](./commands.md#browse)
- [`package`](./commands.md#package)
- [`publish`](./commands.md#publish)
+- [`publish local`](./commands.md#publish-local)
- [`console` / `repl`](./commands.md#console)
- [`run`](./commands.md#run)
- [`setup-ide`](./commands.md#setup-ide)
@@ -442,6 +447,7 @@ Available in commands:
- [`pgp sign`](./commands.md#pgp-sign)
- [`pgp verify`](./commands.md#pgp-verify)
- [`publish`](./commands.md#publish)
+- [`publish local`](./commands.md#publish-local)
- [`console` / `repl`](./commands.md#console)
- [`run`](./commands.md#run)
- [`github secret create` / `gh secret create`](./commands.md#github-secret-create)
@@ -481,6 +487,7 @@ Available in commands:
- [`browse` / `metabrowse`](./commands.md#browse)
- [`package`](./commands.md#package)
- [`publish`](./commands.md#publish)
+- [`publish local`](./commands.md#publish-local)
- [`console` / `repl`](./commands.md#console)
- [`run`](./commands.md#run)
- [`setup-ide`](./commands.md#setup-ide)
@@ -595,6 +602,7 @@ Available in commands:
- [`browse` / `metabrowse`](./commands.md#browse)
- [`package`](./commands.md#package)
- [`publish`](./commands.md#publish)
+- [`publish local`](./commands.md#publish-local)
- [`console` / `repl`](./commands.md#console)
- [`run`](./commands.md#run)
- [`setup-ide`](./commands.md#setup-ide)
@@ -657,6 +665,7 @@ Available in commands:
- [`browse` / `metabrowse`](./commands.md#browse)
- [`package`](./commands.md#package)
- [`publish`](./commands.md#publish)
+- [`publish local`](./commands.md#publish-local)
- [`console` / `repl`](./commands.md#console)
- [`run`](./commands.md#run)
- [`github secret create` / `gh secret create`](./commands.md#github-secret-create)
@@ -683,6 +692,7 @@ Available in commands:
- [`export`](./commands.md#export)
- [`package`](./commands.md#package)
- [`publish`](./commands.md#publish)
+- [`publish local`](./commands.md#publish-local)
- [`run`](./commands.md#run)
- [`shebang`](./commands.md#shebang)
@@ -977,6 +987,60 @@ Available in commands:
Available in commands:
- [`publish`](./commands.md#publish)
+- [`publish local`](./commands.md#publish-local)
+
+
+
+
+#### `--working-dir`
+
+Directory where temporary files for publishing should be written
+
+#### `--scala-version-suffix`
+
+Scala version suffix to append to the module name, like "_2.13" or "_3"
+
+#### `--scala-platform-suffix`
+
+Scala platform suffix to append to the module name, like "_sjs1" or "_native0.4"
+
+#### `--sources`
+
+Whether to build and publish source JARs
+
+#### `--doc`
+
+Aliases: `--scaladoc`, `--javadoc`
+
+Whether to build and publish doc JARs
+
+#### `--gpg-key`
+
+Aliases: `-K`
+
+ID of the GPG key to use to sign artifacts
+
+#### `--signer`
+
+Method to use to sign artifacts
+
+#### `--gpg-option`
+
+Aliases: `-G`, `--gpg-opt`
+
+gpg command-line options
+
+#### `--ivy2-home`
+
+Set Ivy 2 home directory
+
+#### `--ivy2-local-like`
+
+## Publish params options
+
+Available in commands:
+- [`publish`](./commands.md#publish)
+- [`publish local`](./commands.md#publish-local)
@@ -1021,20 +1085,6 @@ Description to put in publishing metadata
Developer(s) to add in publishing metadata, like "alex|Alex|https://alex.info" or "alex|Alex|https://alex.info|alex@alex.me"
-#### `--publish-repository`
-
-Aliases: `-R`, `--publish-repo`
-
-Repository to publish to
-
-#### `--user`
-
-User to use with publishing repository
-
-#### `--password`
-
-Password to use with publishing repository
-
#### `--secret-key`
Secret key to use to sign artifacts with BouncyCastle
@@ -1045,43 +1095,27 @@ Aliases: `--secret-key-pass`
Password of secret key to use to sign artifacts with BouncyCastle
-#### `--working-dir`
-
-Directory where temporary files for publishing should be written
-
-#### `--scala-version-suffix`
-
-Scala version suffix to append to the module name, like "_2.13" or "_3"
-
-#### `--scala-platform-suffix`
-
-Scala platform suffix to append to the module name, like "_sjs1" or "_native0.4"
-
-#### `--sources`
+## Publish repository options
-Whether to build and publish source JARs
-
-#### `--doc`
-
-Aliases: `--scaladoc`, `--javadoc`
+Available in commands:
+- [`publish`](./commands.md#publish)
-Whether to build and publish doc JARs
-#### `--gpg-key`
+
-Aliases: `-K`
+#### `--publish-repository`
-ID of the GPG key to use to sign artifacts
+Aliases: `-R`, `--publish-repo`
-#### `--signer`
+Repository to publish to
-Method to use to sign artifacts
+#### `--user`
-#### `--gpg-option`
+User to use with publishing repository
-Aliases: `-G`, `--gpg-opt`
+#### `--password`
-gpg command-line options
+Password to use with publishing repository
## Repl options
@@ -1122,6 +1156,7 @@ Available in commands:
- [`browse` / `metabrowse`](./commands.md#browse)
- [`package`](./commands.md#package)
- [`publish`](./commands.md#publish)
+- [`publish local`](./commands.md#publish-local)
- [`console` / `repl`](./commands.md#console)
- [`run`](./commands.md#run)
- [`setup-ide`](./commands.md#setup-ide)
@@ -1216,6 +1251,7 @@ Available in commands:
- [`browse` / `metabrowse`](./commands.md#browse)
- [`package`](./commands.md#package)
- [`publish`](./commands.md#publish)
+- [`publish local`](./commands.md#publish-local)
- [`console` / `repl`](./commands.md#console)
- [`run`](./commands.md#run)
- [`setup-ide`](./commands.md#setup-ide)
@@ -1276,6 +1312,7 @@ Available in commands:
- [`browse` / `metabrowse`](./commands.md#browse)
- [`package`](./commands.md#package)
- [`publish`](./commands.md#publish)
+- [`publish local`](./commands.md#publish-local)
- [`console` / `repl`](./commands.md#console)
- [`run`](./commands.md#run)
- [`setup-ide`](./commands.md#setup-ide)
@@ -1345,6 +1382,7 @@ Available in commands:
- [`browse` / `metabrowse`](./commands.md#browse)
- [`package`](./commands.md#package)
- [`publish`](./commands.md#publish)
+- [`publish local`](./commands.md#publish-local)
- [`console` / `repl`](./commands.md#console)
- [`run`](./commands.md#run)
- [`setup-ide`](./commands.md#setup-ide)
@@ -1472,6 +1510,7 @@ Available in commands:
- [`browse` / `metabrowse`](./commands.md#browse)
- [`package`](./commands.md#package)
- [`publish`](./commands.md#publish)
+- [`publish local`](./commands.md#publish-local)
- [`console` / `repl`](./commands.md#console)
- [`run`](./commands.md#run)
- [`github secret create` / `gh secret create`](./commands.md#github-secret-create)
@@ -1496,6 +1535,7 @@ Available in commands:
- [`compile`](./commands.md#compile)
- [`package`](./commands.md#package)
- [`publish`](./commands.md#publish)
+- [`publish local`](./commands.md#publish-local)
- [`console` / `repl`](./commands.md#console)
- [`run`](./commands.md#run)
- [`shebang`](./commands.md#shebang)
@@ -1528,6 +1568,7 @@ Available in commands:
- [`browse` / `metabrowse`](./commands.md#browse)
- [`package`](./commands.md#package)
- [`publish`](./commands.md#publish)
+- [`publish local`](./commands.md#publish-local)
- [`console` / `repl`](./commands.md#console)
- [`run`](./commands.md#run)
- [`setup-ide`](./commands.md#setup-ide)
diff --git a/website/docs/reference/commands.md b/website/docs/reference/commands.md
index 1470d434a8..1e9afca216 100644
--- a/website/docs/reference/commands.md
+++ b/website/docs/reference/commands.md
@@ -197,6 +197,30 @@ Accepts options:
- [logging](./cli-options.md#logging-options)
- [main class](./cli-options.md#main-class-options)
- [publish](./cli-options.md#publish-options)
+- [publish params](./cli-options.md#publish-params-options)
+- [publish repository](./cli-options.md#publish-repository-options)
+- [Scala.js](./cli-options.md#scalajs-options)
+- [Scala Native](./cli-options.md#scala-native-options)
+- [scalac](./cli-options.md#scalac-options)
+- [shared](./cli-options.md#shared-options)
+- [verbosity](./cli-options.md#verbosity-options)
+- [watch](./cli-options.md#watch-options)
+- [workspace](./cli-options.md#workspace-options)
+
+## `publish local`
+
+Accepts options:
+- [compilation server](./cli-options.md#compilation-server-options)
+- [compile cross](./cli-options.md#compile-cross-options)
+- [coursier](./cli-options.md#coursier-options)
+- [dependency](./cli-options.md#dependency-options)
+- [directories](./cli-options.md#directories-options)
+- [help group](./cli-options.md#help-group-options)
+- [jvm](./cli-options.md#jvm-options)
+- [logging](./cli-options.md#logging-options)
+- [main class](./cli-options.md#main-class-options)
+- [publish](./cli-options.md#publish-options)
+- [publish params](./cli-options.md#publish-params-options)
- [Scala.js](./cli-options.md#scalajs-options)
- [Scala Native](./cli-options.md#scala-native-options)
- [scalac](./cli-options.md#scalac-options)