Skip to content

Commit

Permalink
Optional sbt-gpg integration (#75)
Browse files Browse the repository at this point in the history
  • Loading branch information
raboof authored Jan 3, 2019
1 parent 86632af commit 92d0731
Show file tree
Hide file tree
Showing 8 changed files with 40 additions and 46 deletions.
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,19 @@ As this plugin as well as the sbt-osgi plugin redefine the `packageBin` task, it

### Describe your build as a 'buildinfo' certification

You can now generate a signed description of the build environment with the
sbt task `signedReproducibleBuildsCertification`. This certification will
You can now generate a description of the build environment with the
sbt task `reproducibleBuildsCertification`. This certification will
also be included when publishing your project. If you want to disable this,
you can set `publishCertification := false`.

To sign the certification, configure [sbt-gpg](https://github.com/jodersky/sbt-gpg)
and either simply `publishLocal`, or, for example if you have `publishCertification := false`,
`reproducible-builds:publishLocal`.

### Sharing certifications

If you are a (3rd-party or 'official') rebuilder, you can use the
'reproducible-builds:publish' task to publish the buildinfo to a
`reproducible-builds:publish` task to publish the buildinfo to a
[reproducible-builds-certification-repository](http://github.com/raboof/reproducible-builds-certification-repository) instance.

#### Uploading certifications from Travis
Expand Down
6 changes: 2 additions & 4 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ enablePlugins(SbtPlugin)
enablePlugins(ScriptedPlugin)

libraryDependencies += "net.bzzt" % "reproducible-builds-jvm-stripper" % "0.9"
libraryDependencies += "com.jsuereth" % "sbt-pgp" % sbtPgpVersion
libraryDependencies += "io.spray" %% "spray-json" % "1.3.5"

libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.5" % "test"

// Optional integration:
addSbtPlugin("com.typesafe.sbt" %% "sbt-native-packager" % "1.3.15" % Provided)
addSbtPlugin("io.crashbox" %% "sbt-gpg" % "0.2.0" % Provided)
// addSbtPlugin("com.jsuereth" % "sbt-pgp" % sbtPgpVersion % Provided)

// Dogfood^WChampagne time!
import net.bzzt.reproduciblebuilds.ReproducibleBuildsPlugin._
Expand All @@ -35,6 +36,3 @@ scriptedLaunchOpts := { scriptedLaunchOpts.value ++
Seq("-Xmx1024M", "-Dplugin.version=" + version.value)
}
scriptedBufferLog := false

// Transitive plugin dependency:
addSbtPlugin("com.jsuereth" % "sbt-pgp" % sbtPgpVersion)
18 changes: 18 additions & 0 deletions src/main/scala/GpgHelpers.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package net.bzzt.reproduciblebuilds

import io.crashbox.gpg.SbtGpg.autoImport.{gpg, gpgWarnOnFailure}
import io.crashbox.gpg.SbtGpg.packagedArtifactsImpl
import sbt.Keys.{packagedArtifacts, streams}
import sbt.Setting

object GpgHelpers {
val settings: Seq[Setting[_]] =
Seq(
packagedArtifacts := {
packagedArtifactsImpl(
packagedArtifacts.value,
gpg.value,
gpgWarnOnFailure.value)(streams.value.log.warn(_))
}
)
}
44 changes: 9 additions & 35 deletions src/main/scala/ReproducibleBuildsPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,11 @@ import java.nio.charset.Charset
import java.nio.file.Files

import scala.concurrent.duration._

import gigahorse.GigahorseSupport
import sbt.{io => _, _}
import sbt.Keys._
import sbt.Classpaths._
import sbt.plugins.JvmPlugin
import com.typesafe.sbt.pgp
import com.typesafe.sbt.pgp.PgpSigner
import com.typesafe.sbt.pgp.PgpKeys._
import com.typesafe.sbt.pgp.PgpSettings.{pgpPassphrase => _, pgpSecretRing => _, pgpSigningKey => _, useGpgAgent => _, _}
import io.github.zlika.reproducible._
import org.apache.ivy.core.IvyPatternHelper
import sbt.io.syntax.{URI, uri}
Expand All @@ -32,6 +27,9 @@ object ReproducibleBuildsPlugin extends AutoPlugin {
val universalPluginOnClasspath =
Try(getClass.getClassLoader.loadClass("com.typesafe.sbt.packager.universal.UniversalPlugin")).isSuccess

val gpgPluginOnClasspath =
Try(getClass.getClassLoader.loadClass("io.crashbox.gpg.SbtGpg")).isSuccess

override def requires: Plugins = JvmPlugin

val ReproducibleBuilds = config("reproducible-builds")
Expand All @@ -41,7 +39,6 @@ object ReproducibleBuildsPlugin extends AutoPlugin {
val hostname = settingKey[String]("The hostname to include when publishing 3rd-party attestations")

val reproducibleBuildsCertification = taskKey[File]("Create a Reproducible Builds certification")
val signedReproducibleBuildsCertification = taskKey[File]("Create a signed Reproducible Builds certification")
val reproducibleBuildsCheckCertification = taskKey[Unit]("Download and compare Reproducible Builds certifications")

val bzztNetResolver = Resolver.url("repo.bzzt.net", url("http://repo.bzzt.net:8000"))(Patterns().withArtifactPatterns(Vector(
Expand Down Expand Up @@ -99,11 +96,6 @@ object ReproducibleBuildsPlugin extends AutoPlugin {

if (publishCertification.value) generatedArtifact else Map.empty[Artifact, File]
},
signedReproducibleBuildsCertification := {
val file = reproducibleBuildsCertification.value
val signer = new CleartextCommandLineGpgSigner(gpgCommand.value, useGpgAgent.value, pgpSigningKey.value, pgpPassphrase.value)
signer.sign(file, new File(file.getAbsolutePath + pgp.gpgExtension), streams.value)
},
reproducibleBuildsCheckCertification := {
val ours = Certification(
organization.value,
Expand Down Expand Up @@ -167,7 +159,8 @@ object ReproducibleBuildsPlugin extends AutoPlugin {
generatedArtifact
artifacts.map { case (key, value) => (key.withExtraAttributes(key.extraAttributes ++ Map("host"->hostname.value, "timestamp" -> (System.currentTimeMillis() / 1000l).toString)), value) }
},
publishTo := Some(bzztNetResolver),
publishTo := Some(bzztNetResolver)
) ++ gpgPluginSettings ++ Seq(
publishConfiguration := {
publishConfig(
publishMavenStyle.value,
Expand Down Expand Up @@ -209,6 +202,10 @@ object ReproducibleBuildsPlugin extends AutoPlugin {
publishM2 := publishTask(publishM2Configuration).value
))

private def gpgPluginSettings =
if (gpgPluginOnClasspath) GpgHelpers.settings
else Seq.empty

def postProcessJar(jar: File): File = postProcessWith(jar, new ZipStripper()
.addFileStripper("META-INF/MANIFEST.MF", new ManifestStripper())
.addFileStripper("META-INF/maven/\\S*/pom.properties", new PomPropertiesStripper()))
Expand All @@ -232,29 +229,6 @@ object ReproducibleBuildsPlugin extends AutoPlugin {
}
}

/**
* A GpgSigner that uses the command-line to run gpg.
*
* Taken from sbt-pgp, but:
* * changed '--detach-sign' to '--clearsign'
* (needs to be '--clearsign' rather than '--clear-sign' to support gnupg 1.4.x)
* * removed 'secRing' (see https://github.com/sbt/sbt-pgp/issues/126)
*/
private class CleartextCommandLineGpgSigner(command: String, agent: Boolean, optKey: Option[Long], optPassphrase: Option[Array[Char]]) extends PgpSigner {
def sign(file: File, signatureFile: File, s: TaskStreams): File = {
if (signatureFile.exists) IO.delete(signatureFile)
val passargs: Seq[String] = (optPassphrase map { passArray => passArray mkString "" } map { pass => Seq("--passphrase", pass) }) getOrElse Seq.empty
val keyargs: Seq[String] = optKey map (k => Seq("--default-key", "0x%x" format (k))) getOrElse Seq.empty
val args = passargs ++ Seq("--clearsign", "--armor") ++ (if (agent) Seq("--use-agent") else Seq.empty) ++ keyargs
sys.process.Process(command, args ++ Seq("--output", signatureFile.getAbsolutePath, file.getAbsolutePath)) !< match {
case 0 => ()
case n => sys.error("Failure running gpg --clearsign. Exit code: " + n)
}
signatureFile
}
override val toString: String = "RB GPG-Command(" + command + ")"
}

/**
* Determine the target filename.
*
Expand Down
1 change: 0 additions & 1 deletion src/sbt-test/sbt-reproducible-builds/native-packager/test
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@ $ must-mirror target/scala-2.12/stripped/native-packager_2.12-0.1.0-SNAPSHOT.jar
> reproducibleBuildsCertification
$ exists target/scala-2.12/native-packager_2.12-0.1.0-SNAPSHOT.buildinfo
# Not on travis:
#> signedReproducibleBuildsCertification
#> reproducibleBuildsCheckCertification
1 change: 0 additions & 1 deletion src/sbt-test/sbt-reproducible-builds/osgi/test
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@ $ must-mirror target/scala-2.12/stripped/osgi_2.12-0.1.0-SNAPSHOT.jar expected/o
> reproducibleBuildsCertification
$ exists target/scala-2.12/osgi_2.12-0.1.0-SNAPSHOT.buildinfo
# Not on travis:
#> signedReproducibleBuildsCertification
#> reproducibleBuildsCheckCertification
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@ sys.props.get("plugin.version") match {
}

// Included but not used, to catch problems with that combination:
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.15")
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.15")

// to easily test sbt-gpg integration manually
addSbtPlugin("io.crashbox" % "sbt-gpg" % "0.2.0")
1 change: 0 additions & 1 deletion src/sbt-test/sbt-reproducible-builds/simple/test
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@ $ must-mirror target/scala-2.12/stripped/simple_2.12-0.1.0-SNAPSHOT.jar expected
> reproducibleBuildsCertification
$ exists target/scala-2.12/simple_2.12-0.1.0-SNAPSHOT.buildinfo
# Not on travis:
#> signedReproducibleBuildsCertification
#> reproducibleBuildsCheckCertification

0 comments on commit 92d0731

Please sign in to comment.