Files
percona-toolkit/util/build-packages
2015-09-11 14:47:51 +02:00

529 lines
15 KiB
Bash
Executable File

#!/usr/bin/env bash
# This script builds tar, rpm, and deb packages for a new release. The
# packages are created in the release/ directory (which is created if it
# does not exist).
#
# There's only one command line option: VERISON. It must be newer than
# the last version in the Changelog; see check_version(). Do not include
# a leading 'v', just `build-packages 1.0.8' for example.
#
# These environment variables control what the script does:
# CHECK=0|1 - Do (not) check the branch, version, etc.
# UPDATE=0|1 - Do (not) update changelogs, versions, etc.
# BUILD=0|1 - Do (not) build any packages
# BUILD_TAR=0|1 - Do (not) build the .tar.gz package
# BUILD_RPM=0|1 - Do (not) build the .rpm package
# BUILD_DEB=0|1 - Do (not) build the .deb package
# All of these env vars are true by default. If, for example, you just want
# to build the branch as-is: CHECK=0 UPDATE=0 build-packages VERSION
# Otherwise, this script is pretty strict and tries to ensure a good build.
#
# These environment variables are for special builds:
# UPDATE_DOCS=0|1 - Do (not) update docs
# BETA=1 - UPDATE_DOCS=0 BUILD_RPM=0 BUILD_DEB=0
#
# A few more things you should know:
# * You'll need rpmbuild and dpkg-buildpackage to build the rpm and deb pkgs
# * Output (STDOUT and STDERR) for some stuff is saved to files in tmpdir
# * All dates/times are UTC
# * No pkgs are signed (TODO)
# ############################################################################
# Standard startup, find the branch's root directory
# ############################################################################
set -ue # bail out on errors, be strict
exit_status=0
die() {
echo "$1" >&2
exit 1
}
warn() {
echo "$1" >&2
exit_status=1
}
cwd=$PWD
PERCONA_TOOLKIT_BRANCH=${PERCONA_TOOLKIT_BRANCH:-""}
if [ -n "$PERCONA_TOOLKIT_BRANCH" ]; then
BRANCH=$PERCONA_TOOLKIT_BRANCH
cd $BRANCH
else
while [ ! -f Makefile.PL ] && [ $PWD != "/" ]; do
cd ..
done
if [ ! -f Makefile.PL ]; then
die "Cannot find the root directory of the Percona Toolkit branch"
exit 1
fi
BRANCH=`pwd`
fi
cd $cwd
# ############################################################################
# Paths
# ############################################################################
DOCS_DIR=$BRANCH/docs
SPHINX_CONFIG_DIR=$BRANCH/config/sphinx-build
DEB_CONFIG_DIR=$BRANCH/config/deb
RPM_CONFIG_DIR=$BRANCH/config/rpm
RELEASE_DIR=$BRANCH/release
# ############################################################################
# Programs and their options
# ############################################################################
TAR=${TAR:-tar}
# ############################################################################
# Subroutines
# ############################################################################
check_branch() {
echo -n "Checking branch... "
local clean_branch=$(git status --porcelain|wc -l)
if [ "$clean_branch" -gt 0 ]; then
die "The branch has uncommitted changes or unknown files"
fi
echo "OK"
}
check_version() {
echo -n "Checking new version $VERSION... "
cd $BRANCH
local current_version=$(expr `cat Makefile.PL | grep VERSION | awk '{print $3}'` : "'\([0-9.]*\)'")
if ! [ "$VERSION" '>' "$current_version" ]; then
die "New version $VERSION is not greater than current version $current_version"
fi
echo "OK"
}
check_changelog() {
echo -n "Checking Changelog... "
cd $BRANCH
first_line=$(head -n 3 Changelog | tail -n 1)
if [ $(expr "$first_line" : "v[0-9]") -gt 0 ]; then
die "No changes since $first_line"
fi
if [ -n "$(grep "^v$VERSION" Changelog)" ]; then
die "Entries for v$VERSION already exist"
fi
echo "OK"
}
check_rel_notes() {
echo -n "Checking release_notes.rst... "
cd $DOCS_DIR
if [ -n "$(grep "^v$VERSION" release_notes.rst)" ]; then
die "Entries for v$VERSION already exist"
fi
echo "OK"
}
update_version() {
echo -n "Updating version in tools... "
cd $BRANCH/bin
for tool_file in *; do
sed -i'.bak' -e "s/^$tool_file [0-9]\.[0-9][^ ]\+/$tool_file $VERSION/" $tool_file
if [ $? -ne 0 ]; then
die "Error updating version in $tool_file"
fi
rm "$tool_file.bak"
done
local new_versions=$(grep --no-filename '^pt-[^ ]\+ [0-9]\.' * | cut -d' ' -f2 | sort -u)
if [ "$new_versions" != "$VERSION" ]; then
die "The version in some tools did not update correctly"
fi
echo "OK"
echo -n "Updating version in Makefile.PL... "
cd $BRANCH
sed -i'.bak' -e "s/'[0-9.]*'/'$VERSION'/" Makefile.PL
if [ $? -ne 0 ]; then
die "Error updating version in Makefile.PL"
fi
rm "Makefile.PL.bak"
echo "OK"
echo -n "Updating version in percona-toolkit.pod... "
cd $DOCS_DIR
sed -i'.bak' -e "s/^Percona Toolkit v.*/Percona Toolkit v$VERSION released $DATE/" percona-toolkit.pod
if [ $? -ne 0 ]; then
die "Error updating version in percona-toolkit.pod"
fi
rm "percona-toolkit.pod.bak"
echo "OK"
echo -n "Updating version in sphinx-build/conf.py... "
# What Sphinx calls verison, we call series; what it calls release we
# call version.
cd $SPHINX_CONFIG_DIR
sed -i'.bak' -e "s/^version = .*/version = '$SERIES'/" conf.py
if [ $? -ne 0 ]; then
die "Error updating version in conf.py"
fi
rm "conf.py.bak"
sed -i'.bak' -e "s/^release = .*/release = '$VERSION'/" conf.py
if [ $? -ne 0 ]; then
die "Error updating release in conf.py"
fi
rm "conf.py.bak"
echo "OK"
}
update_copyright_year() {
echo -n "Updating copyright year in tools... "
cd $BRANCH/bin
for tool_file in *; do
local copyright="$(grep "[0-9] Percona LLC and/or its affiliates" $tool_file)"
local new_copyright="$(../util/new-copyright-year "$YEAR" "$copyright")"
if [ $? -ne 0 ]; then
die "Error parsing copyright year in $tool_file"
fi
sed -i'.bak' -e "s#^$copyright#$new_copyright#" $tool_file
if [ $? -ne 0 ]; then
die "Error updating copyright year in $tool_file"
fi
rm "$tool_file.bak"
done
echo "OK"
echo -n "Updating copyright year in percona-toolkit.pod... "
local pod=$DOCS_DIR/percona-toolkit.pod
local copyright="$(grep "[0-9] Percona LLC and/or its affiliates" $pod)"
local new_copyright="$(../util/new-copyright-year "$YEAR" "$copyright")"
if [ $? -ne 0 ]; then
die "Error parsing copyright year in percona-toolkit.pod"
fi
sed -i'.bak' -e "s#^$copyright#$new_copyright#" $pod
if [ $? -ne 0 ]; then
die "Error updating copyright year in percona-toolkit.pod"
fi
rm $pod.bak
echo "OK"
}
update_manifest() {
echo -n "Updating MANIFEST... "
cd $BRANCH
echo -n > MANIFEST
for file in * bin/* docs/*.pod; do
if [ -f $file ]; then
echo $file >> MANIFEST
fi
done
echo "OK"
}
update_percona_toolkit_pod() {
echo -n "Updating TOOLS section in percona-toolkit.pod... "
cd $BRANCH/bin
local pod=$DOCS_DIR/percona-toolkit.pod
local tool_list=/tmp/percona-tool-list.pod
echo "=head1 TOOLS
This release of Percona Toolkit includes the following tools:
=over
" > $tool_list
for tool in *; do
desc=$(grep -A 2 '^=head1 NAME' $tool | tail -n 1 | sed 's/ - /:/' | cut -d':' -f2)
echo "=item $tool
$desc
" >> $tool_list
done
echo "=back
For more free, open-source software developed Percona, visit
L<http://www.percona.com/software/>.
" >> $tool_list
cat $pod | ../util/replace-text -v from='^=head1 TOOLS' -v file=$tool_list -v to='^=head1' > $pod.tmp
rm $tool_list
if [ -z "$(podchecker $pod.tmp 2>&1 | grep -i 'pod syntax OK')" ]; then
die "POD syntax errors; run podchecker $pod.tmp"
fi
mv $pod.tmp $pod
echo "OK"
}
update_changelog() {
echo -n "Updating Changelog... "
cd $BRANCH
head -n 2 Changelog > /tmp/changelog.tmp
echo "v$VERSION released $DATE" >> /tmp/changelog.tmp
echo >> /tmp/changelog.tmp
n_lines=$(wc -l Changelog | awk '{print $1}')
tail -n $((n_lines - 2)) Changelog >> /tmp/changelog.tmp
mv /tmp/changelog.tmp $BRANCH/Changelog
echo "OK"
echo -n "Updating Debian changelog... "
cd $DEB_CONFIG_DIR
echo "percona-toolkit ($VERSION-1) unstable; urgency=low
" > /tmp/changelog.tmp
cat $BRANCH/Changelog | $BRANCH/util/log-entries $VERSION >> /tmp/changelog.tmp
echo >> /tmp/changelog.tmp
echo " -- Percona Toolkit Developers <toolkit-dev@percona.com> $DEB_DATE
" >> /tmp/changelog.tmp
cat changelog >> /tmp/changelog.tmp
mv /tmp/changelog.tmp changelog
echo "OK"
}
update_rel_notes() {
echo -n "Updating release_notes.rst... "
cd $DOCS_DIR
head -n 3 release_notes.rst > /tmp/release_notes.tmp
local line="v$VERSION released $DATE"
local len=${#line}
local ul="$(printf "%${len}s" | tr [:space:] '=')"
echo "$line" >> /tmp/release_notes.tmp
echo "$ul" >> /tmp/release_notes.tmp
echo >> /tmp/release_notes.tmp
(cd $cwd && cat $REL_NOTES >> /tmp/release_notes.tmp)
echo >> /tmp/release_notes.tmp
echo "Changelog" >> /tmp/release_notes.tmp
echo "---------" >> /tmp/release_notes.tmp
echo >> /tmp/release_notes.tmp
cat $BRANCH/Changelog | $BRANCH/util/log-entries $VERSION \
| sed -e 's/^ *//g' \
>> /tmp/release_notes.tmp
echo >> /tmp/release_notes.tmp
tail -n +4 release_notes.rst >> /tmp/release_notes.tmp
mv /tmp/release_notes.tmp release_notes.rst
echo "OK"
}
update_user_docs() {
echo -n "Updating user docs... "
$BRANCH/util/write-user-docs $BRANCH/bin/* > /tmp/sphinx-build.output 2>&1
if [ $? -ne 0 ]; then
warn "Error updating user docs:"
cat /tmp/sphinx-build.output >&2
exit 1
fi
rm /tmp/sphinx-build.output
echo "OK"
}
prep_release_dir() {
echo -n "Preparing release directory... "
cd $BRANCH
if [ ! -d $RELEASE_DIR ]; then
mkdir $RELEASE_DIR
elif [ -d $RELEASE_DIR/$PKG ]; then
rm -rf $RELEASE_DIR/$PKG
fi
(
cd $RELEASE_DIR
mkdir -p $PKG $PKG/bin $PKG/docs $PKG/lib
)
for file in `cat MANIFEST`; do
cp $file $RELEASE_DIR/$PKG/$file
done
echo "OK"
}
build_tar() {
echo -n "Building $PKG.tar.gz... "
cd $RELEASE_DIR
$TAR czf "$PKG.tar.gz" $PKG
echo "OK"
}
build_rpm() {
echo -n "Building $PKG-1.noarch.rpm... "
cd $RELEASE_DIR
if [ ! -f "$PKG.tar.gz" ]; then
die "Cannot build RPM because $PKG.tar.gz does not exist"
fi
mkdir -p rpm rpm/BUILD rpm/SOURCES rpm/RPMS rpm/SRPMS
mkdir -p RPM
cd rpm
local topdir=`pwd`
# Build RPM package from the tarball.
rpmbuild -bb --clean $RPM_CONFIG_DIR/percona-toolkit.spec \
--quiet \
--define "_topdir $PWD" \
--define "_sourcedir $RELEASE_DIR" \
--define "version $VERSION" \
--define "release 1" > $tmpdir/rpmbuild 2>&1
if [ $? -ne 0 ]; then
warn "rpmbuild has warnings; see $tmpdir/rpmbuild"
fi
if [ ! -f "RPMS/noarch/$PKG-1.noarch.rpm" ]; then
die "RPMS/noarch/$PKG-1.noarch.rpm did not build"
fi
mv "RPMS/noarch/$PKG-1.noarch.rpm" $RELEASE_DIR/RPM
rm -rf $RELEASE_DIR/rpm
echo "OK"
}
build_deb() {
local deb_pkg="percona-toolkit_$VERSION-1_all.deb"
echo -n "Building $deb_pkg... "
cd $RELEASE_DIR
if [ ! -f "$PKG.tar.gz" ]; then
die "Cannot build RPM because $PKG.tar.gz does not exist"
fi
# Copy debian pkg files.
if [ ! -d "$RELEASE_DIR/$PKG/debian" ]; then
mkdir $RELEASE_DIR/$PKG/debian
else
rm -rf * $RELEASE_DIR/$PKG/debian
fi
cp $BRANCH/config/deb/* $RELEASE_DIR/$PKG/debian/
# Build Debian binary and source packages.
cd $RELEASE_DIR/$PKG
dpkg-buildpackage -us -uc >$tmpdir/dpkg-buildpackage 2>&1
if [ $? -ne 0 ]; then
warn "dpkg-buildpackage has warnings; see $tmpdir/dpkg-buildpackage"
fi
rm -rf debian/ build-stamp >/dev/null
make distclean >/dev/null
cd $RELEASE_DIR
rm -rf *.changes >/dev/null
mkdir -p deb
mv percona-toolkit_* deb/
echo "OK"
}
# ############################################################################
# Script starts here
# ############################################################################
if [ $# -lt 2 ]; then
die "Usage: $0 VERSION RELEASE_NOTES"
fi
VERSION=$1
REL_NOTES=$2
if [ ! -f $REL_NOTES ]; then
die "$REL_NOTES does not exist"
fi
# My machine's language is not English. This affects DEB_DATE because
# date %a is language-sensitive. We want abbreviated day names in English.
# Setting LANG as such works for me; hopefully it works for you, too.
LANG='en_US.UTF-8'
SERIES=$(echo $VERSION | sed 's/\.[0-9][0-9]*$//')
YEAR=$(date -u +'%Y') # for updating copyright year
DATE=$(date -u +'%F') # for updating release date
DEB_DATE=$(date -u +'%a, %d %b %Y %T %z') # for updating deb/changelog
PKG="percona-toolkit-$VERSION" # what we're building
# mktemp -d doesn't work on Mac OS X, so we'll do it the old-fashioned way.
tmpdir="/tmp/build-percona-toolkit-$VERSION"
rm -rf $tmpdir >/dev/null 2>&1
mkdir $tmpdir
BETA=${BETA:-0}
if [ $BETA -eq 1 ]; then
UPDATE_DOCS=0
BUILD_RPM=0
BUILD_DEB=0
fi
# This script does not check that you've done pre-release tasks like running
# the test suite, updating Changelog entries, etc. You're responsible for
# that. These checks are for the sanity of package building.
CHECK=${CHECK:-1}
if [ $CHECK -eq 1 ]; then
check_branch
check_version
check_changelog
check_rel_notes
fi
# These items need to be updated automatically for each release.
UPDATE=${UPDATE:-1}
if [ $UPDATE -eq 1 ]; then
update_version
update_copyright_year
update_manifest
UPDATE_DOCS=${UPDATE_DOCS:-1}
if [ $UPDATE_DOCS -eq 1 ]; then
update_percona_toolkit_pod
update_changelog
update_rel_notes
update_user_docs
fi
fi
# Now that those ^ items are updated, you need to commit and push one more
# time before the release packages are built. This script can't do that
# because your branch could non-standard.
BUILD=${BUILD:-1}
if [ $BUILD -eq 1 ]; then
cat <<MSG
Branch verified and updated; ready to build $PKG,
but first you must:
1. git diff and review the changes (Changelog, percon-toolkit.pod, etc.)
2. git add . (from root dir of project)
3. git commit -m "Build $PKG"
4. git push origin <release-branch>
Press any key to continue... (or Ctrl-C to abort)
MSG
read
prep_release_dir
BUILD_TAR=${BUILD_TAR:-1}
if [ $BUILD_TAR -eq 1 ]; then
build_tar
fi
BUILD_RPM=${BUILD_RPM:-1}
if [ $BUILD_RPM -eq 1 ]; then
build_rpm
fi
BUILD_DEB=${BUILD_DEB:-1}
if [ $BUILD_DEB -eq 1 ]; then
build_deb
fi
if [ -d $RELEASE_DIR/$PKG ]; then
rm -rf $RELEASE_DIR/$PKG
fi
echo "Done building $PKG. Packages are in $RELEASE_DIR"
fi
exit $exit_status