diff --git a/docs/pages/1 - Intro to Mill.md b/docs/pages/1 - Intro to Mill.md index e4ba6d46fca..ab5a88adc0e 100644 --- a/docs/pages/1 - Intro to Mill.md +++ b/docs/pages/1 - Intro to Mill.md @@ -740,13 +740,15 @@ your sonatype credentials (e.g. `lihaoyi:foobarbaz`) and GPG password as inputs: ```bash $ mill foo.publish -Missing arguments: (--sonatypeCreds: String, --gpgPassphrase: String, --release: Boolean) +Missing arguments: (--sonatypeCreds: String, --release: Boolean) Arguments provided did not match expected signature: publish --sonatypeCreds String (format: "username:password") - --gpgPassphrase String + --gpgPassphrase String (default null) + --gpgKeyName String (default null) + --signed Boolean (default true) --release Boolean ``` diff --git a/scalalib/src/PublishModule.scala b/scalalib/src/PublishModule.scala index 588781f448e..cd1c436a51b 100644 --- a/scalalib/src/PublishModule.scala +++ b/scalalib/src/PublishModule.scala @@ -73,6 +73,7 @@ trait PublishModule extends JavaModule { outer => def publish(sonatypeCreds: String, gpgPassphrase: String = null, + gpgKeyName: String = null, signed: Boolean = true, release: Boolean): define.Command[Unit] = T.command { val PublishModule.PublishData(artifactInfo, artifacts) = publishArtifacts() @@ -81,6 +82,7 @@ trait PublishModule extends JavaModule { outer => sonatypeSnapshotUri, sonatypeCreds, Option(gpgPassphrase), + Option(gpgKeyName), signed, T.ctx().log ).publish(artifacts.map{case (a, b) => (a.path, b)}, artifactInfo, release) @@ -96,6 +98,7 @@ object PublishModule extends ExternalModule { def publishAll(sonatypeCreds: String, gpgPassphrase: String = null, + gpgKeyName: String = null, publishArtifacts: mill.main.Tasks[PublishModule.PublishData], release: Boolean = false, sonatypeUri: String = "https://oss.sonatype.org/service/local", @@ -110,6 +113,7 @@ object PublishModule extends ExternalModule { sonatypeSnapshotUri, sonatypeCreds, Option(gpgPassphrase), + Option(gpgKeyName), signed, T.ctx().log ).publishAll( diff --git a/scalalib/src/publish/SonatypePublisher.scala b/scalalib/src/publish/SonatypePublisher.scala index 1843943b452..6dcadd5b8c1 100644 --- a/scalalib/src/publish/SonatypePublisher.scala +++ b/scalalib/src/publish/SonatypePublisher.scala @@ -4,13 +4,14 @@ import java.math.BigInteger import java.security.MessageDigest import mill.api.Logger - +import os.Shellable import scalaj.http.HttpResponse class SonatypePublisher(uri: String, snapshotUri: String, credentials: String, gpgPassphrase: Option[String], + gpgKeyName: Option[String], signed: Boolean, log: Logger) { @@ -19,6 +20,7 @@ class SonatypePublisher(uri: String, def publish(fileMapping: Seq[(os.Path, String)], artifact: Artifact, release: Boolean): Unit = { publishAll(release, fileMapping -> artifact) } + def publishAll(release: Boolean, artifacts: (Seq[(os.Path, String)], Artifact)*): Unit = { val mappings = for ((fileMapping0, artifact) <- artifacts) yield { @@ -27,10 +29,10 @@ class SonatypePublisher(uri: String, artifact.id, artifact.version ).mkString("/") - val fileMapping = fileMapping0.map{ case (file, name) => (file, publishPath+"/"+name) } + val fileMapping = fileMapping0.map { case (file, name) => (file, publishPath + "/" + name) } val signedArtifacts = if (signed) fileMapping.map { - case (file, name) => poorMansSign(file, gpgPassphrase) -> s"$name.asc" + case (file, name) => poorMansSign(file, gpgPassphrase, gpgKeyName) -> s"$name.asc" } else Seq() artifact -> (fileMapping ++ signedArtifacts).flatMap { @@ -46,11 +48,11 @@ class SonatypePublisher(uri: String, } val (snapshots, releases) = mappings.partition(_._1.isSnapshot) - if(snapshots.nonEmpty) { + if (snapshots.nonEmpty) { publishSnapshot(snapshots.flatMap(_._2), snapshots.map(_._1)) } val releaseGroups = releases.groupBy(_._1.group) - for((group, groupReleases) <- releaseGroups){ + for ((group, groupReleases) <- releaseGroups) { publishRelease(release, groupReleases.flatMap(_._2), group, releases.map(_._1)) } } @@ -122,6 +124,7 @@ class SonatypePublisher(uri: String, attempts: Int = 20): Unit = { def isRightStatus = api.getStagingRepoState(stagingRepoId).equalsIgnoreCase(status) + var attemptsLeft = attempts while (attemptsLeft > 0 && !isRightStatus) { @@ -135,16 +138,15 @@ class SonatypePublisher(uri: String, } // http://central.sonatype.org/pages/working-with-pgp-signatures.html#signing-a-file - private def poorMansSign(file: os.Path, maybePassphrase: Option[String]): os.Path = { + private def poorMansSign(file: os.Path, maybePassphrase: Option[String], maybeKeyName: Option[String]): os.Path = { val fileName = file.toString - maybePassphrase match { - case Some(passphrase) => - os.proc("gpg", "--passphrase", passphrase, "--batch", "--yes", "-a", "-b", fileName) - .call(stdin = os.Inherit, stdout = os.Inherit, stderr = os.Inherit) - case None => - os.proc("gpg", "--batch", "--yes", "-a", "-b", fileName) - .call(stdin = os.Inherit, stdout = os.Inherit, stderr = os.Inherit) - } + val optionFlag = (flag: String, ov: Option[String]) => ov.map(flag :: _ :: Nil).getOrElse(Nil) + val command = "gpg" :: + optionFlag("--passphrase", maybePassphrase) ++ optionFlag("-u", maybeKeyName) ++ + Seq("--batch", "--yes", "-a", "-b", fileName) + + os.proc(command.map(v => v: Shellable)) + .call(stdin = os.Inherit, stdout = os.Inherit, stderr = os.Inherit) os.Path(fileName + ".asc") }