Skip to content

Commit

Permalink
Ensures source tarballs are reproducible
Browse files Browse the repository at this point in the history
When building tarballs dynamically, let's take the time to ensure that
they're fully reproducible. We still run 'python setup.py sdist', but
since that tool doesn't (yet) support SOURCE_DATE_EPOCH, we'll manually
repack the archive with native tar & gzip, forcing predictable
timestamps from the git info, resulting in a deterministic build.
  • Loading branch information
Conor Schaefer committed Aug 5, 2020
1 parent 2a03bcc commit c9f3ce7
Showing 1 changed file with 34 additions and 3 deletions.
37 changes: 34 additions & 3 deletions scripts/build-debianpackage
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ if [[ -z "${PKG_NAME:-}" ]]; then
fi


# Look up most recent release from GitHub repo
function find_latest_version() {
repo_url="https://github.com/freedomofpress/${PKG_NAME}/releases"
curl -s "$repo_url" \
Expand All @@ -58,15 +59,45 @@ fi
# Copy over the debian directory (including new changelog) from repo
cp -r "$CUR_DIR/$PKG_NAME/" "$TOP_BUILDDIR/"

# Ensures that a given git tag is signed with the prod release key
# If "rc" is in the tag name, this will fail.
function verify_git_tag() {
local d
local t
d="$1"
t="$2"
prod_fingerprint="22245C81E3BAEB4138B36061310F561200F4AD77"
git -C "$build_dir" tag --verify "$PKG_VERSION" 2>&1 \
| grep -q -F "using RSA key $prod_fingerprint"
}

# Dynamically generate a tarball, from the Python source code,
# that is byte-for-byte reproducible. Use timestamps from the git tag.
function build_source_tarball() {
repo_url="https://github.com/freedomofpress/${PKG_NAME}"
build_dir="/tmp/${PKG_NAME}"
rm -rf "$build_dir"
git clone "$repo_url" "$build_dir"
git -C "$build_dir" tag --verify "$PKG_VERSION" 1>&2
git -C "$build_dir" checkout "$PKG_VERSION" 1>&2

# Verify tag, using only the prod key
verify_git_tag "$build_dir" "$PKG_VERSION" || exit 1

# Tag is verified, proceed with checkout
git -C "$build_dir" checkout "$PKG_VERSION" 1>&2 || exit 1
(cd "$build_dir" && python setup.py sdist 1>&2)
find "${build_dir}/dist/" | grep -P '\.tar.gz$' | head -n1

# Initial tarball will contain timestamps from NOW, let's repack
# with timestamps from the signed tag.
raw_tarball="$(find "${build_dir}/dist/" | grep -P '\.tar.gz$' | head -n1)"
tag_time="$(git -C "$build_dir" log --format="%ai" --no-patch -n1 "$PKG_VERSION")"
(cd "$build_dir" && tar -xzf "dist/$(basename $raw_tarball)" 1>&2)
tarball_basename="$(basename "$raw_tarball")"
# Repack with tar only, so env vars are respected
(cd "$build_dir" && tar -cf "${tarball_basename%.gz}" --mode=go=rX,u+rw,a-s --mtime="$tag_time" --sort=name --owner=root:0 --group=root:0 "${tarball_basename%.tar.gz}" 1>&2)
# Then gzip it separately, so we can pass args
(cd "$build_dir" && gzip --no-name "${tarball_basename%.gz}" 1>&2)
(cd "$build_dir" && mv "$tarball_basename" dist/ 1>&2)
echo "$raw_tarball"
}

# If the package is contained in the list, it should be a python package. In
Expand Down

0 comments on commit c9f3ce7

Please sign in to comment.