lnd.xprv/scripts/release.sh
Oliver Gugger 2686391c31
scripts: package release artifacts deterministically
A number of tricks need to be applied to the tar and zip commands
respectively to make sure they always produce deterministic archives.
This includes setting the file timestamps to a fixed date and setting
specific ownership attributes (tar) or no attributes at all (zip).
2021-01-08 15:03:20 +01:00

228 lines
6.2 KiB
Bash
Executable File

#!/bin/bash
# Simple bash script to build basic lnd tools for all the platforms
# we support with the golang cross-compiler.
#
# Copyright (c) 2016 Company 0, LLC.
# Use of this source code is governed by the ISC
# license.
set -e
LND_VERSION_REGEX="lnd version (.+) commit"
PKG="github.com/lightningnetwork/lnd"
PACKAGE=lnd
# Needed for setting file timestamps to get reproducible archives.
BUILD_DATE="2020-01-01 00:00:00"
BUILD_DATE_STAMP="202001010000.00"
# reproducible_tar_gzip creates a reproducible tar.gz file of a directory. This
# includes setting all file timestamps and ownership settings uniformly.
function reproducible_tar_gzip() {
local dir=$1
local tar_cmd=tar
# MacOS has a version of BSD tar which doesn't support setting the --mtime
# flag. We need gnu-tar, or gtar for short to be installed for this script to
# work properly.
tar_version=$(tar --version)
if [[ ! "$tar_version" =~ "GNU tar" ]]; then
if ! command -v "gtar"; then
echo "GNU tar is required but cannot be found!"
echo "On MacOS please run 'brew install gnu-tar' to install gtar."
exit 1
fi
# We have gtar installed, use that instead.
tar_cmd=gtar
fi
# Pin down the timestamp time zone.
export TZ=UTC
find "${dir}" -print0 | LC_ALL=C sort -r -z | $tar_cmd \
"--mtime=${BUILD_DATE}" --no-recursion --null --mode=u+rw,go+r-w,a+X \
--owner=0 --group=0 --numeric-owner -c -T - | gzip -9n > "${dir}.tar.gz"
rm -r "${dir}"
}
# reproducible_zip creates a reproducible zip file of a directory. This
# includes setting all file timestamps.
function reproducible_zip() {
local dir=$1
# Pin down file name encoding and timestamp time zone.
export TZ=UTC
# Set the date of each file in the directory that's about to be packaged to
# the same timestamp and make sure the same permissions are used everywhere.
chmod -R 0755 "${dir}"
touch -t "${BUILD_DATE_STAMP}" "${dir}"
find "${dir}" -print0 | LC_ALL=C sort -r -z | xargs -0r touch \
-t "${BUILD_DATE_STAMP}"
find "${dir}" | LC_ALL=C sort -r | zip -o -X -r -@ "${dir}.zip"
rm -r "${dir}"
}
# green prints one line of green text (if the terminal supports it).
function green() {
echo -e "\e[0;32m${1}\e[0m"
}
# red prints one line of red text (if the terminal supports it).
function red() {
echo -e "\e[0;31m${1}\e[0m"
}
# check_tag_correct makes sure the given git tag is checked out and the git tree
# is not dirty.
# arguments: <version-tag>
function check_tag_correct() {
local tag=$1
# For automated builds we can skip this check as they will only be triggered
# on tags.
if [[ "$SKIP_VERSION_CHECK" -eq "1" ]]; then
green "skipping version check, assuming automated build"
exit 0
fi
# If a tag is specified, ensure that that tag is present and checked out.
if [[ $tag != $(git describe --tags) ]]; then
red "tag $tag not checked out"
exit 1
fi
# Build lnd to extract version.
go build ${PKG}/cmd/lnd
# Extract version command output.
lnd_version_output=$(./lnd --version)
# Use a regex to isolate the version string.
if [[ $lnd_version_output =~ $LND_VERSION_REGEX ]]; then
# Prepend 'v' to match git tag naming scheme.
lnd_version="v${BASH_REMATCH[1]}"
green "version: $lnd_version"
# If tag contains a release candidate suffix, append this suffix to the
# lnd reported version before we compare.
RC_REGEX="-rc[0-9]+$"
if [[ $tag =~ $RC_REGEX ]]; then
lnd_version+=${BASH_REMATCH[0]}
fi
# Match git tag with lnd version.
if [[ $tag != "${lnd_version}" ]]; then
red "lnd version $lnd_version does not match tag $tag"
exit 1
fi
else
red "malformed lnd version output"
exit 1
fi
}
# build_release builds the actual release binaries.
# arguments: <version-tag> <build-system(s)> <build-tags> <ldflags>
function build_release() {
local tag=$1
local sys=$2
local buildtags=$3
local ldflags=$4
green " - Packaging vendor"
go mod vendor
reproducible_tar_gzip vendor
maindir=$PACKAGE-$tag
mkdir -p $maindir
mv vendor.tar.gz "${maindir}/"
# Don't use tag in source directory, otherwise our file names get too long and
# tar starts to package them non-deterministically.
package_source="${PACKAGE}-source"
# The git archive command doesn't support setting timestamps and file
# permissions. That's why we unpack the tar again, then use our reproducible
# method to create the final archive.
git archive -o "${maindir}/${package_source}.tar" HEAD
cd "${maindir}"
mkdir -p ${package_source}
tar -xf "${package_source}.tar" -C ${package_source}
rm "${package_source}.tar"
reproducible_tar_gzip ${package_source}
mv "${package_source}.tar.gz" "${package_source}-$tag.tar.gz"
for i in $sys; do
os=$(echo $i | cut -f1 -d-)
arch=$(echo $i | cut -f2 -d-)
arm=
if [[ $arch == "armv6" ]]; then
arch=arm
arm=6
elif [[ $arch == "armv7" ]]; then
arch=arm
arm=7
fi
dir="${PACKAGE}-${i}-${tag}"
mkdir "${dir}"
pushd "${dir}"
green " - Building: ${os} ${arch} ${arm} with build tags '${buildtags}'"
env CGO_ENABLED=0 GOOS=$os GOARCH=$arch GOARM=$arm go build -v -trimpath -ldflags="${ldflags}" -tags="${buildtags}" ${PKG}/cmd/lnd
env CGO_ENABLED=0 GOOS=$os GOARCH=$arch GOARM=$arm go build -v -trimpath -ldflags="${ldflags}" -tags="${buildtags}" ${PKG}/cmd/lncli
popd
if [[ $os == "windows" ]]; then
reproducible_zip "${dir}"
else
reproducible_tar_gzip "${dir}"
fi
done
sha256sum * >manifest-$tag.txt
}
# usage prints the usage of the whole script.
function usage() {
red "Usage: "
red "release.sh check-tag <version-tag>"
red "release.sh build-release <version-tag> <build-system(s)> <build-tags> <ldflags>"
}
# Whatever sub command is passed in, we need at least 2 arguments.
if [ "$#" -lt 2 ]; then
usage
exit 1
fi
# Extract the sub command and remove it from the list of parameters by shifting
# them to the left.
SUBCOMMAND=$1
shift
# Call the function corresponding to the specified sub command or print the
# usage if the sub command was not found.
case $SUBCOMMAND in
check-tag)
green "Checking if version tag exists"
check_tag_correct "$@"
;;
build-release)
green "Building release"
build_release "$@"
;;
*)
usage
exit 1
;;
esac