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

Builds source tarballs dynamically #185

Merged
merged 4 commits into from
Aug 19, 2020
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 65 additions & 6 deletions scripts/build-debianpackage
Original file line number Diff line number Diff line change
Expand Up @@ -27,29 +27,90 @@ TOP_BUILDDIR="$HOME/debbuild/packaging"
mkdir -p "$TOP_BUILDDIR"
rm -rf "${TOP_BUILDDIR:?}/${PKG_NAME}"
mkdir -p "${TOP_BUILDDIR}/${PKG_NAME}"
# Move changelog into place (we have separate changelogs for each platform)
PLATFORM="$(lsb_release -sc)"

# Validate required args.
if [[ -z "${PKG_NAME:-}" ]]; then
echo "Set PKG_NAME of the build";
exit 1
fi


# Look up most recent release from GitHub repo
function find_latest_version() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a good idea. will your change still support supplied version numbers in case we're building a dev release?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, fully backwards compatible in that PKG_VERSION & PKG_PATH can still be provided as overrides, they're just not required. The verification logic needs some work before it's ready for full review—at least:

  1. use a dedicated keyring so that only the prod signing key is used to validate tags
  2. detect if an "rc" tag is being used, and if so, don't expect a prod sig

Then it should be ready to bang around on.

repo_url="https://github.com/freedomofpress/${PKG_NAME}/releases"
curl -s "$repo_url" \
| perl -nE '$_ =~ m#/releases/tag/(v?[\d\.]+)\"# and say $1' \
| head -n 1
}

if [[ -z "${PKG_VERSION:-}" ]]; then
echo "Set PKG_VERSION of the build";
exit 1
echo "PKG_VERSION not set, inferring from recent releases..."
PKG_VERSION="$(find_latest_version)"
if [[ -z "$PKG_VERSION" ]]; then
echo "Failed to infer version"
exit 1
else
echo "Using PKG_VERSION: $PKG_VERSION"
fi
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 \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we please add some error messages here somehow? At least for missing key or expired key :)
I was wondering what is wrong for some time with the following output.

✦ ❯ make securedrop-client
		PKG_NAME="securedrop-client" ./scripts/build-debianpackage
	gpg: key 310F561200F4AD77: public key "SecureDrop Release Signing Key" imported
	gpg: Total number processed: 1
	gpg:               imported: 1
	gpg: Signature made Tuesday 28 April 2020 07:57:53 PM IST
	gpg:                using RSA key 22245C81E3BAEB4138B36061310F561200F4AD77
	gpg: Good signature from "SecureDrop Release Signing Key" [unknown]
	gpg:                 aka "SecureDrop Release Signing Key <securedrop-release-key@freedom.press>" [unknown]
	gpg: WARNING: This key is not certified with a trusted signature!
	gpg:          There is no indication that the signature belongs to the owner.
	Primary key fingerprint: 2224 5C81 E3BA EB41 38B3  6061 310F 5612 00F4 AD77
	Checking that SHA256SUMs from mirror match signed file...  OK
	PKG_VERSION not set, inferring from recent releases...
	Using PKG_VERSION: 0.2.1
	securedrop-client is a Python package
	PKG_PATH not set, building from source (version 0.2.1)...
	Cloning into '/tmp/securedrop-client'...
	remote: Enumerating objects: 64, done.
	remote: Counting objects: 100% (64/64), done.
	remote: Compressing objects: 100% (48/48), done.
	remote: Total 9276 (delta 32), reused 26 (delta 16), pack-reused 9212
	Receiving objects: 100% (9276/9276), 6.95 MiB | 2.51 MiB/s, done.
	Resolving deltas: 100% (6863/6863), done.
	make: *** [Makefile:9: securedrop-client] Error 1

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great point, @kushaldas. Added. Try again and confirm that an error message displays for you.

| grep -q -F "using RSA key $prod_fingerprint"
}

# Dynamically generate a tarball, from the Python source code,
# that is byte-for-byte reproducible. Infers timestamp
# from the changelog, same as for the deb package.
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"

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

# Tag is verified, proceed with checkout
git -C "$build_dir" checkout "$PKG_VERSION"
(cd "$build_dir" && LC_ALL="C.UTF-8" python setup.py sdist)

# Initial tarball will contain timestamps from NOW, let's repack
# with timestamps from the changelog, which is static.
raw_tarball="$(find "${build_dir}/dist/" | grep -P '\.tar.gz$' | head -n1)"
dch_time="$(date "+%Y-%m-%d %H:%M:%S %z" -d@$(dpkg-parsechangelog --file $PKG_NAME/debian/changelog-$PLATFORM -STimestamp)) "
(cd "$build_dir" && tar -xzf "dist/$(basename $raw_tarball)")
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="$dch_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}")
(cd "$build_dir" && mv "$tarball_basename" dist/)
echo "$raw_tarball"
}

# If the package is contained in the list, it should be a python package. In
# that case, we should extract tarball, and validate wheel hashes.
if [[ "${PKG_NAME}" =~ ^(securedrop-client|securedrop-proxy|securedrop-export|securedrop-log)$ ]]; then
echo "${PKG_NAME} is a Python package"

if [[ -z "${PKG_PATH:-}" ]]; then
# Try to find tarball in a reasonable location
candidate_pkg_path="$(realpath "${CUR_DIR}/../${PKG_NAME}/dist/${PKG_NAME}-${PKG_VERSION}.tar.gz")"
# Build from source
echo "PKG_PATH not set, building from source (version $PKG_VERSION)..."
build_source_tarball
candidate_pkg_path="$(find /tmp/$PKG_NAME/dist -type f -iname '*.tar.gz')"
if [[ -f "$candidate_pkg_path" ]]; then
PKG_PATH="$candidate_pkg_path"
echo "Found tarball at $PKG_PATH, override with PKG_PATH..."
Expand Down Expand Up @@ -80,8 +141,6 @@ fi

printf "Building package '%s' from version '%s'...\\n" "$PKG_NAME" "$PKG_VERSION"

# Move changelog into place (we have separate changelogs for each platform)
PLATFORM="$(lsb_release -sc)"
echo "$TOP_BUILDDIR/$PKG_NAME/"
mv "$TOP_BUILDDIR/$PKG_NAME/debian/changelog-$PLATFORM" "$TOP_BUILDDIR/$PKG_NAME/debian/changelog"

Expand Down