sbt plugin to make sbt builds more reproducible.
For general information on 'Reproducible Builds', see https://reproducible-builds.org/
This plugin provides a number of features:
- Strip 'accidental' sources of nondeterminism (e.g. timestamps) from the packaged jar
- Produce a 'buildinfo' file describing the build environment used and containing a cryptographic hash of the resulting artifacts.
- Upload this (signed) certification when publishing
- Upload this (signed) certification to a reproducible-builds-certification-repository instance
- Check your local build against already-uploaded certifications (WiP)
This is a pre-1.0 version: you are welcome to use it, but there are no guarantees, and syntax, behaviour and default configuration may still change in future versions.
Then add to your project/plugins.sbt
:
addSbtPlugin("net.bzzt" % "sbt-reproducible-builds" % "0.32")
And to build.sbt
:
enablePlugins(ReproducibleBuildsPlugin)
After sbt package
, the stripped artifact can be found under target/scala-${scalaBinaryVersion}/stripped/${reproducibleBuildsPackageName}-${version}.jar
. The generated buildinfo that will be published along with your release will look something like this:
name=simple
group-id=net.bzzt
artifact-id=simple_2.12
version=0.1.0-SNAPSHOT
java.version=1.8.0_191
os.name=Linux
build-tool=sbt
sbt.version=1.2.3
scala.version=2.12.7
scala.binary-version=2.12
date=1546548168000
outputs.0.filename=simple_2.12-0.1.0-SNAPSHOT.pom
outputs.0.length=783
outputs.0.checksums.sha512=a24500ee9a44c55abb0b4461d4f405e4c2d988e43479a0385943226dd2487faf65a28e121b7f539b764df21ad27debed5bbf7fd07df34d413a81def2af589f1b
outputs.1.filename=simple_2.12-0.1.0-SNAPSHOT.jar
outputs.1.length=2933
outputs.1.checksums.sha512=ea059170bba44d3ecdcdcc2feee91be9fe7aa33de36ab03e0934d2455b0aa6c57c20db5e1e51f88da97007a5aa8100761d71cae83a28a34ee61f755653bf612f
As this plugin as well as the sbt-osgi plugin redefine the packageBin
task, it is necessary to re-apply the plugin settings after the application of the osgi settings. You can find an example under src/sbt-test/sbt-reproducible-builds/osgi/build.sbt
.
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
and either simply publishLocal
, or, for example if you have publishCertification := false
,
ReproducibleBuilds/publishLocal
.
If you are a (3rd-party or 'official') rebuilder, you can use the
ReproducibleBuilds/publish
task to publish the buildinfo to a
reproducible-builds-certification-repository instance.
Especially if you're already deploying from Travis, it can be a great start to publish certifications from Travis as well. For this, you should give Travis its own gpg key pair to sign those certifications.
An even better way might be to use the key generated by travis for this repository itself following https://docs.travis-ci.com/user/encryption-keys/
If you want to use your own key, however,
start by generating a keypair with gpg --gen-key
, naming it something like
"Arnout Engelen (via Travis) [email protected]". Then export public and private
key with gpg --export -a "Arnout Engelen (via Travis)" > public.key
and
gpg --export-secret-key -a "Arnout Engelen (via Travis)" > private.key
.
Now encrypt the private key so only Travis can read it with
travis encrypt-file .travis/private.key
and follow the instructions to
unencrypt. Finally, gpg --import private.key public.key
and
sbt ReproducibleBuilds/publish
to sign and upload the
certification from Travis.
You can check your certification against other uploaded
certifications with reproducibleBuildsCheckCertification
.
This feature is still a work in progress.
Checking your certification with the 'official' published certification is currently not yet implemented.
You can check your local artifacts against published ones with
sbt clean reproducibleBuildsCheck
. If you want to check
against a different repository (such as a staging repository),
you can set the reproducibleBuildsCheckResolver
:
import net.bzzt.reproduciblebuilds.ReproducibleBuildsPlugin._
ThisBuild / reproducibleBuildsCheckResolver := "repository-apache-org-staging" at "https://repository.apache.org/content/groups/staging"
From version 0.3 onwards, sbt-reproducible-builds
should itself be
reproducible in the sense that building the same git tag should produce the
exact same binary.
When this is not the case, this is to be considered a bug and a bug report with the binary, the buildinfo and any additional information about any peculiarities of the build system would be greatly appreciated!
When using Ivy-style publishing (which is notably common for sbt plugins), the ivy.xml is not currently part of the published buildinfo. This is a problem because this file contains the transitive dependencies for the artifact, so a backdoor can be introduced by adding a rogue transitive dependency to this file. This issue is tracked under #84.
This plugin fetches artifacts and attestations to compare against the built artifacts from the configured repositories.
Some further recommendations to make your builds more reproducible:
- Specify
scalaVersion
(and if applicablecrossScalaVersions
) in your build configuration - Use sbt-strict-scala-versions to ensure always using those Scala versions (
addSbtPlugin("net.bzzt" % "sbt-strict-scala-versions" % "0.0.1")
)
If you find a security issue in this project, please email [email protected] . Those reports will be treated confidentially.