aboutsummaryrefslogtreecommitdiff
package io.crashbox.gpg

import sbt.{AutoPlugin, Def, _}
import sbt.Keys._
import sbt.plugins.JvmPlugin

object SbtGpg extends AutoPlugin {

  override def requires = JvmPlugin
  override def trigger = allRequirements

  object autoImport {

    val gpgWarnOnFailure = settingKey[Boolean](
      "If true, only issue a warning when signing fails. If false, error " +
        "and fail the build. Defaults to true in publishLocal, false in publish.")

    val gpgCommand = settingKey[String]("Path to GnuPG executable.")

    val gpgOptions =
      settingKey[Seq[String]]("Additional global options to pass to gpg.")

    val gpgKey = taskKey[Option[String]](
      "Key ID used to sign artifacts. Setting this to None will " +
        "cause sbt-gpg to fall back to using gpg's default key. When set, " +
        "it is equivalent to gpg's `--local-user` option.")

    val gpg =
      taskKey[Gpg]("Utility wrapper to the underlying gpg executable.")
  }

  def packagedArtifactsImpl(
      arts: Map[Artifact, File],
      gpg: Gpg,
      warnOnFailure: Boolean)(warn: String => Unit): Map[Artifact, File] = {

    val (signatures, failure) = arts.foldLeft((Map[Artifact, File](), false)) {
      case ((acc, false), (art, file)) =>
        gpg.sign(file) match {
          case Some(signed) =>
            (acc + (art.withExtension(art.extension + ".asc") -> signed), false)

          case None =>
            val report: String => Unit =
              if (warnOnFailure) warn else sys.error(_)

            report("GPG reported an error. Artifacts won't be signed.")
            (acc, true)
        }

      case (pair @ (_, true), _) => pair
    }

    // if we fail the signing part-way through, we throw out *all* the signatures
    if (failure) arts else signatures ++ arts
  }

  import autoImport._

  lazy val gpgSettings: Seq[Setting[_]] = Seq(
    gpgWarnOnFailure := false,
    publishLocal / gpgWarnOnFailure := true,
    gpgCommand := "gpg",
    gpgOptions := Seq("--yes"),
    gpgKey := Credentials.forHost(credentials.value, "gpg").map(_.userName),
    gpg := {
      val log = streams.value.log
      new Gpg(gpgCommand.value, gpgOptions.value, gpgKey.value)(log.info(_),
                                                                log.warn(_))
    }
  )

  lazy val signingSettings: Seq[Setting[_]] = Seq(
    publish / packagedArtifacts := {
      packagedArtifactsImpl(
        (publish / packagedArtifacts).value,
        gpg.value,
        (publish / gpgWarnOnFailure).value)(streams.value.log.warn(_))
    },
    publishLocal / packagedArtifacts := {
      packagedArtifactsImpl(
        (publishLocal / packagedArtifacts).value,
        gpg.value,
        (publishLocal / gpgWarnOnFailure).value)(streams.value.log.warn(_))

    }
  )

  override lazy val projectSettings
    : Seq[Def.Setting[_]] = gpgSettings ++ signingSettings

}