Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make gpg passphrase optional in publish method. #345

Closed
Baccata opened this issue May 21, 2018 · 8 comments
Closed

Make gpg passphrase optional in publish method. #345

Baccata opened this issue May 21, 2018 · 8 comments
Labels
solved The issue was fixed/resolved
Milestone

Comments

@Baccata
Copy link
Contributor

Baccata commented May 21, 2018

My team uses a private artifact repository (artifactory) which exposes the same API as sonatype.

However, the signing of the artifacts is not required. I'd like to make the gpg passphrase optional in PublishModule#publish, or have another task (publishWithoutSigning) that would not require the passphrase.

Thoughts ?

@rockjam
Copy link
Collaborator

rockjam commented May 21, 2018

What happens when you pass null?

@Baccata
Copy link
Contributor Author

Baccata commented May 21, 2018

What happens when you pass null?

1 targets failed
scribe.recursion.publish java.lang.NullPointerException
    java.lang.ProcessBuilder.start(ProcessBuilder.java:1012)
    ammonite.ops.Shellout$.executeInteractive(Shellout.scala:27)
    ammonite.ops.Shellout$.$anonfun$$percent$1(Shellout.scala:14)
    ammonite.ops.Shellout$.$anonfun$$percent$1$adapted(Shellout.scala:14)
    ammonite.ops.Command.applyDynamic(Shellout.scala:118)
    mill.scalalib.publish.SonatypePublisher.poorMansSign(SonatypePublisher.scala:141)

@rockjam
Copy link
Collaborator

rockjam commented May 21, 2018

I think that having optional gpg passphrase is reasonable

@lihaoyi
Copy link
Member

lihaoyi commented May 22, 2018

Maybe we could give publish a separate flag --signed false? The reason is that there are three cases here:

  • Signed with passphrase
  • Signed without passphrase
  • Unsigned

If we're going to change this we might as well make sure we handle all three cases

rockjam pushed a commit that referenced this issue May 23, 2018
* Solves 345 : optional signing

* Made gpgPassphrase optional for publishing
* Added a flag to remove signing of published artifacts altogether

* Handle optional value using null as default param

better than using empty string as default param.
@Baccata
Copy link
Contributor Author

Baccata commented May 24, 2018

Closing this as the problem it describes is solved. There is still a problem with publishing to artifactory with the default PublishModule. I need to have a closer look at how it's API differs from sonatype's API to formulate a new issue.

@Baccata Baccata closed this as completed May 24, 2018
@ddelautre
Copy link

@Baccata Did you succeed publishing to Artifactory?

@Baccata
Copy link
Contributor Author

Baccata commented Dec 11, 2018

@ddelautre, yeah, here's a snippet I used. It might need updating to the latest mill version, but it should do the trick :

import java.math.BigInteger
import java.security.MessageDigest

import ammonite.ops._
import mill.scalalib.publish.{ SonatypeHttpApi, _ }
import mill.util.Logger
import scalaj.http.HttpResponse

// Basically a copy of https://github.com/lihaoyi/mill/blob/0.2.2/scalalib/src/mill/scalalib/publish/SonatypePublisher.scala
// to avoid requiring a gpg passphrase.
class ArtifactoryPublisher(uri: String, snapshotUri: String, credentials: String, log: Logger) {

  private val api = new SonatypeHttpApi(uri, credentials)

  def publish(fileMapping: Seq[(Path, String)], artifact: Artifact): Unit = {
    publishAll(release = true, fileMapping -> artifact)
  }
  def publishAll(release: Boolean, artifacts: (Seq[(Path, String)], Artifact)*): Unit = {

    val mappings = for ((fileMapping0, artifact) <- artifacts) yield {
      val publishPath = Seq(
        artifact.group.replace(".", "/"),
        artifact.id,
        artifact.version
      ).mkString("/")
      val fileMapping = fileMapping0.map { case (file, name) => (file, publishPath + "/" + name) }

      artifact -> fileMapping.flatMap {
        case (file, name) =>
          val content = read.bytes(file)

          Seq(
            name             -> content,
            (name + ".md5")  -> md5hex(content),
            (name + ".sha1") -> sha1hex(content)
          )
      }
    }

    val (snapshots, releases) = mappings.partition(_._1.isSnapshot)
    if (snapshots.nonEmpty) {
      doPublish(snapshots.flatMap(_._2), snapshots.map(_._1), snapshotUri)
    }
    val releaseGroups = releases.groupBy(_._1.group)
    for ((group, groupReleases) <- releaseGroups) {
      doPublish(groupReleases.flatMap(_._2), releases.map(_._1), uri)
    }
  }

  private def doPublish(
    payloads: Seq[(String, Array[Byte])],
    artifacts: Seq[Artifact],
    uri: String
  ): Unit = {

    val publishResults = payloads.map {
      case (fileName, data) =>
        log.info(s"Uploading $fileName")
        val resp = api.upload(s"$uri/$fileName", data)
        resp
    }
    reportPublishResults(publishResults, artifacts)
  }

  private def reportPublishResults(
    publishResults: Seq[HttpResponse[String]],
    artifacts: Seq[Artifact]
  ) = {
    if (publishResults.forall(_.is2xx)) {
      log.info(s"Published ${artifacts.map(_.id).mkString(", ")} to Sonatype")
    } else {
      val errors = publishResults.filterNot(_.is2xx).map { response =>
        s"Code: ${response.code}, message: ${response.body}"
      }
      throw new RuntimeException(
        s"Failed to publish ${artifacts.map(_.id).mkString(", ")} to Sonatype. Errors: \n${errors.mkString("\n")}"
      )
    }
  }

  private def awaitRepoStatus(status: String, stagingRepoId: String, attempts: Int = 20): Unit = {
    def isRightStatus =
      api.getStagingRepoState(stagingRepoId).equalsIgnoreCase(status)
    var attemptsLeft = attempts

    while (attemptsLeft > 0 && !isRightStatus) {
      Thread.sleep(3000)
      attemptsLeft -= 1
      if (attemptsLeft == 0) {
        throw new RuntimeException(s"Couldn't wait for staging repository to be ${status}. Failing")
      }
    }
  }

  private def md5hex(bytes: Array[Byte]): Array[Byte] =
    hexArray(md5.digest(bytes)).getBytes

  private def sha1hex(bytes: Array[Byte]): Array[Byte] =
    hexArray(sha1.digest(bytes)).getBytes

  private def md5 = MessageDigest.getInstance("md5")

  private def sha1 = MessageDigest.getInstance("sha1")

  private def hexArray(arr: Array[Byte]) =
    String.format("%0" + (arr.length << 1) + "x", new BigInteger(1, arr))

}

@lefou lefou added this to the 0.2.3 milestone May 9, 2019
@lefou lefou added the solved The issue was fixed/resolved label Jul 3, 2019
@lefou
Copy link
Member

lefou commented Mar 3, 2020

For those who came here in need to publish to Artifactory. Mill > 0.6.1 now has a artifactory module. See #783

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
solved The issue was fixed/resolved
Projects
None yet
Development

No branches or pull requests

5 participants