From 43015ac14270229a36133cd2549b4699c25ffdc8 Mon Sep 17 00:00:00 2001 From: Olivier Melois Date: Tue, 22 May 2018 08:47:39 +0100 Subject: [PATCH] Solves 345 : optional signing * Made gpgPassphrase optional for publishing * Added a flag to remove signing of published artifacts altogether --- .../src/mill/scalalib/PublishModule.scala | 76 +++++++++++-------- .../scalalib/publish/SonatypePublisher.scala | 18 +++-- 2 files changed, 57 insertions(+), 37 deletions(-) diff --git a/scalalib/src/mill/scalalib/PublishModule.scala b/scalalib/src/mill/scalalib/PublishModule.scala index 9626f1c28ce..7827ec61154 100644 --- a/scalalib/src/mill/scalalib/PublishModule.scala +++ b/scalalib/src/mill/scalalib/PublishModule.scala @@ -3,9 +3,9 @@ package scalalib import ammonite.ops._ import mill.define.{ExternalModule, Task} -import mill.eval.{PathRef, Result} +import mill.eval.PathRef import mill.scalalib.publish.{Artifact, SonatypePublisher} -import mill.util.Loose.Agg + /** * Configuration necessary for publishing a Scala module to Maven Central or similar */ @@ -17,17 +17,18 @@ trait PublishModule extends JavaModule { outer => def pomSettings: T[PomSettings] def publishVersion: T[String] - def publishSelfDependency = T{ - Artifact(pomSettings().organization, artifactId(), publishVersion()), + def publishSelfDependency = T { + Artifact(pomSettings().organization, artifactId(), publishVersion()) } - def publishXmlDeps = T.task{ + def publishXmlDeps = T.task { val ivyPomDeps = ivyDeps().map(resolvePublishDependency().apply(_)) val modulePomDeps = Task.sequence(moduleDeps.map(_.publishSelfDependency))() ivyPomDeps ++ modulePomDeps.map(Dependency(_, Scope.Compile)) } def pom = T { - val pom = Pom(artifactMetadata(), publishXmlDeps(), artifactId(), pomSettings()) + val pom = + Pom(artifactMetadata(), publishXmlDeps(), artifactId(), pomSettings()) val pomPath = T.ctx().dest / s"${artifactId()}-${publishVersion()}.pom" write.over(pomPath, pom) PathRef(pomPath) @@ -57,9 +58,10 @@ trait PublishModule extends JavaModule { outer => def sonatypeUri: String = "https://oss.sonatype.org/service/local" - def sonatypeSnapshotUri: String = "https://oss.sonatype.org/content/repositories/snapshots" + def sonatypeSnapshotUri: String = + "https://oss.sonatype.org/content/repositories/snapshots" - def publishArtifacts = T{ + def publishArtifacts = T { val baseName = s"${artifactId()}-${publishVersion()}" PublishModule.PublishData( artifactMetadata(), @@ -73,49 +75,61 @@ trait PublishModule extends JavaModule { outer => } def publish(sonatypeCreds: String, - gpgPassphrase: String, + gpgPassphrase: String = "", + signed: Boolean = true, release: Boolean): define.Command[Unit] = T.command { val PublishModule.PublishData(artifactInfo, artifacts) = publishArtifacts() new SonatypePublisher( sonatypeUri, sonatypeSnapshotUri, sonatypeCreds, - gpgPassphrase, + Some(gpgPassphrase).filter(_.nonEmpty), + signed, T.ctx().log - ).publish(artifacts.map{case (a, b) => (a.path, b)}, artifactInfo, release) + ).publish(artifacts.map { case (a, b) => (a.path, b) }, + artifactInfo, + release) } } -object PublishModule extends ExternalModule{ +object PublishModule extends ExternalModule { case class PublishData(meta: Artifact, payload: Seq[(PathRef, String)]) - object PublishData{ - implicit def jsonify: upickle.default.ReadWriter[PublishData] = upickle.default.macroRW + object PublishData { + implicit def jsonify: upickle.default.ReadWriter[PublishData] = + upickle.default.macroRW } def publishAll(sonatypeCreds: String, - gpgPassphrase: String, + gpgPassphrase: String = "", + signed: Boolean = true, publishArtifacts: mill.main.Tasks[PublishModule.PublishData], release: Boolean = false, sonatypeUri: String = "https://oss.sonatype.org/service/local", - sonatypeSnapshotUri: String = "https://oss.sonatype.org/content/repositories/snapshots") = T.command{ - - val x: Seq[(Seq[(Path, String)], Artifact)] = Task.sequence(publishArtifacts.value)().map{ - case PublishModule.PublishData(a, s) => (s.map{case (p, f) => (p.path, f)}, a) + sonatypeSnapshotUri: String = + "https://oss.sonatype.org/content/repositories/snapshots") = + T.command { + + val x: Seq[(Seq[(Path, String)], Artifact)] = + Task.sequence(publishArtifacts.value)().map { + case PublishModule.PublishData(a, s) => + (s.map { case (p, f) => (p.path, f) }, a) + } + new SonatypePublisher( + sonatypeUri, + sonatypeSnapshotUri, + sonatypeCreds, + Some(gpgPassphrase).filter(_.nonEmpty), + signed, + T.ctx().log + ).publishAll( + release, + x: _* + ) } - new SonatypePublisher( - sonatypeUri, - sonatypeSnapshotUri, - sonatypeCreds, - gpgPassphrase, - T.ctx().log - ).publishAll( - release, - x:_* - ) - } implicit def millScoptTargetReads[T] = new mill.main.Tasks.Scopt[T]() - lazy val millDiscover: mill.define.Discover[this.type] = mill.define.Discover[this.type] + lazy val millDiscover: mill.define.Discover[this.type] = + mill.define.Discover[this.type] } diff --git a/scalalib/src/mill/scalalib/publish/SonatypePublisher.scala b/scalalib/src/mill/scalalib/publish/SonatypePublisher.scala index 4f8f42b84f3..47ec8dbf1c4 100644 --- a/scalalib/src/mill/scalalib/publish/SonatypePublisher.scala +++ b/scalalib/src/mill/scalalib/publish/SonatypePublisher.scala @@ -11,7 +11,8 @@ import scalaj.http.HttpResponse class SonatypePublisher(uri: String, snapshotUri: String, credentials: String, - gpgPassphrase: String, + gpgPassphrase: Option[String], + signed: Boolean, log: Logger) { private val api = new SonatypeHttpApi(uri, credentials) @@ -29,11 +30,11 @@ class SonatypePublisher(uri: String, ).mkString("/") val fileMapping = fileMapping0.map{ case (file, name) => (file, publishPath+"/"+name) } - val signedArtifacts = fileMapping ++ fileMapping.map { + val signedArtifacts = if (signed) fileMapping.map { case (file, name) => poorMansSign(file, gpgPassphrase) -> s"$name.asc" - } + } else Seq() - artifact -> signedArtifacts.flatMap { + artifact -> (fileMapping ++ signedArtifacts).flatMap { case (file, name) => val content = read.bytes(file) @@ -135,10 +136,15 @@ class SonatypePublisher(uri: String, } // http://central.sonatype.org/pages/working-with-pgp-signatures.html#signing-a-file - private def poorMansSign(file: Path, passphrase: String): Path = { + private def poorMansSign(file: Path, maybePassphrase: Option[String]): Path = { val fileName = file.toString import ammonite.ops.ImplicitWd._ - %("gpg", "--passphrase", passphrase, "--batch", "--yes", "-a", "-b", fileName) + maybePassphrase match { + case Some(passphrase) => + %("gpg", "--passphrase", passphrase, "--batch", "--yes", "-a", "-b", fileName) + case None => + %("gpg", "--batch", "--yes", "-a", "-b", fileName) + } Path(fileName + ".asc") }