#!/usr/bin/env bash ########################################################################### # Packaging script which creates debian and RPM packages. It optionally # tags the repo with the given version. # # Requirements: GOPATH must be set. 'fpm' must be on the path, and the AWS # CLI tools must also be installed. # # https://github.com/jordansissel/fpm # http://aws.amazon.com/cli/ # # Packaging process: to package a build, simple execute: # # package.sh # # where is the desired version. If generation of a debian and RPM # package is successful, the script will offer to tag the repo using the # supplied version string. # # See package.sh -h for options # # AWS upload: the script will also offer to upload the packages to S3. If # this option is selected, the credentials should be present in the file # ~/aws.conf. The contents should be of the form: # # [default] # aws_access_key_id= # aws_secret_access_key= # region = us-east-1 # # Trim the leading spaces when creating the file. The script will exit if # S3 upload is requested, but this file does not exist. [ -z $DEBUG ] || set -x AWS_FILE=~/aws.conf INSTALL_ROOT_DIR=/usr/bin INFLUXDB_LOG_DIR=/var/log/influxdb INFLUXDB_DATA_DIR=/var/lib/influxdb INFLUXDB_SCRIPT_DIR=/usr/lib/influxdb CONFIG_ROOT_DIR=/etc/influxdb LOGROTATE_DIR=/etc/logrotate.d SAMPLE_CONFIGURATION=etc/config.sample.toml INITD_SCRIPT=scripts/init.sh SYSTEMD_SCRIPT=scripts/influxdb.service POSTINSTALL_SCRIPT=scripts/post-install.sh PREINSTALL_SCRIPT=scripts/pre-install.sh POSTUNINSTALL_SCRIPT=scripts/post-uninstall.sh LOGROTATE=scripts/logrotate TMP_WORK_DIR=`mktemp -d` POST_INSTALL_PATH=`mktemp` POST_UNINSTALL_PATH=`mktemp` ARCH=`uname -i` LICENSE=MIT URL=influxdb.com MAINTAINER=support@influxdb.com VENDOR=Influxdb DESCRIPTION="Distributed time-series database" # Allow path to FPM to be set by environment variables. Some execution contexts # like cron don't have PATH set correctly to pick it up. if [ -z "$FPM" ]; then FPM=`which fpm` fi GO_VERSION="go1.4.3" GOPATH_INSTALL= BINS=( influxd influx influx_stress influx_tsm influx_inspect ) ########################################################################### # Helper functions. # usage prints simple usage information. usage() { cat << EOF >&2 $0 [-h] [-p|-w] [-t ] [-r ] should be a dotted version such as 0.9.5. -r release candidate number, if any. Example: -r 7 -p just build packages -x build with race-detection enabled -w build packages for current working directory imply -p -t build package for can be rpm, tar or deb can have multiple -t Examples: $0 0.9.5 -r 9 # Creates 0.9.5-rc9 $0 0.9.4 # Creates 0.9.4 EOF cleanup_exit $1 } # full_version echoes the full version string, given a version and an optiona;l # RC number. If the just the version is present, that is echoed. If the RC is # also provided, then "rc" and the number is concatenated with the version. # For example, 0.9.4rc4 would be returned if version was 0.9.4 and the RC number # was 4. full_version() { version=$1 rc=$2 if [ -z "$rc" ]; then echo $version else echo ${version}-rc${rc} fi } # rpm_release echoes the RPM release or "iteration" given an RC number. rpm_release() { rc=$1 if [ -z "$rc" ]; then echo 1 else echo 0.1.rc${rc} fi } # cleanup_exit removes all resources created during the process and exits with # the supplied returned code. cleanup_exit() { rm -r $TMP_WORK_DIR rm $POST_INSTALL_PATH rm $POST_UNINSTALL_PATH exit $1 } # current_branch echos the current git branch. current_branch() { echo `git rev-parse --abbrev-ref HEAD` } # check_gopath sanity checks the value of the GOPATH env variable, and determines # the path where build artifacts are installed. GOPATH may be a colon-delimited # list of directories. check_gopath() { [ -z "$GOPATH" ] && echo "GOPATH is not set." && cleanup_exit 1 GOPATH_INSTALL=`echo $GOPATH | cut -d ':' -f 1` [ ! -d "$GOPATH_INSTALL" ] && echo "GOPATH_INSTALL is not a directory." && cleanup_exit 1 echo "GOPATH ($GOPATH) looks sane, using $GOPATH_INSTALL for installation." } check_gvm() { if [ -n "$GOPATH" ]; then existing_gopath=$GOPATH fi source $HOME/.gvm/scripts/gvm which gvm if [ $? -ne 0 ]; then echo "gvm not found -- aborting." cleanup_exit $1 fi gvm use $GO_VERSION if [ $? -ne 0 ]; then echo "gvm cannot find Go version $GO_VERSION -- aborting." cleanup_exit $1 fi # Keep any existing GOPATH set. if [ -n "$existing_gopath" ]; then GOPATH=$existing_gopath fi } # check_clean_tree ensures that no source file is locally modified. check_clean_tree() { modified=$(git ls-files --modified | wc -l) if [ $modified -ne 0 ]; then echo "The source tree is not clean -- aborting." cleanup_exit 1 fi echo "Git tree is clean." } # update_tree ensures the tree is in-sync with the repo. update_tree() { git pull origin $TARGET_BRANCH if [ $? -ne 0 ]; then echo "Failed to pull latest code -- aborting." cleanup_exit 1 fi git fetch --tags if [ $? -ne 0 ]; then echo "Failed to fetch tags -- aborting." cleanup_exit 1 fi echo "Git tree updated successfully." } # check_tag_exists checks if the existing release already exists in the tags. check_tag_exists () { version=$1 git tag | grep -q "^v$version$" if [ $? -eq 0 ]; then echo "Proposed version $version already exists as a tag -- aborting." cleanup_exit 1 fi } # make_dir_tree creates the directory structure within the packages. make_dir_tree() { work_dir=$1 version=$2 mkdir -p $work_dir/$INSTALL_ROOT_DIR mkdir -p $work_dir/$INFLUXDB_SCRIPT_DIR/scripts if [ $? -ne 0 ]; then echo "Failed to create script directory -- aborting." cleanup_exit 1 fi mkdir -p $work_dir/$CONFIG_ROOT_DIR if [ $? -ne 0 ]; then echo "Failed to create configuration directory -- aborting." cleanup_exit 1 fi mkdir -p $work_dir/$LOGROTATE_DIR if [ $? -ne 0 ]; then echo "Failed to create logrotate directory -- aborting." cleanup_exit 1 fi } # do_build builds the code. The version and commit must be passed in. do_build() { for b in ${BINS[*]}; do rm -f $GOPATH_INSTALL/bin/$b done if [ -n "$WORKING_DIR" ]; then STASH=`git stash create -a` if [ $? -ne 0 ]; then echo "WARNING: failed to stash uncommited local changes" fi git reset --hard fi go get -u -f -d ./... if [ $? -ne 0 ]; then echo "WARNING: failed to 'go get' packages." fi git checkout $TARGET_BRANCH # go get switches to master, so ensure we're back. if [ -n "$WORKING_DIR" ]; then git stash apply $STASH if [ $? -ne 0 ]; then #and apply previous uncommited local changes echo "WARNING: failed to restore uncommited local changes" fi fi version=$1 commit=`git rev-parse HEAD` branch=`current_branch` if [ $? -ne 0 ]; then echo "Unable to retrieve current commit -- aborting" cleanup_exit 1 fi date=`date -u --iso-8601=seconds` go install $RACE -a -ldflags="-X main.version=$version -X main.branch=$branch -X main.commit=$commit -X main.buildTime=$date" ./... if [ $? -ne 0 ]; then echo "Build failed, unable to create package -- aborting" cleanup_exit 1 fi echo "Build completed successfully." } ########################################################################### # Process options while : do case $1 in -h | --help) usage 0 ;; -p | --packages-only) PACKAGES_ONLY="PACKAGES_ONLY" shift ;; -t | --target) case "$2" in 'tar') TAR_WANTED="gz" ;; 'deb') DEB_WANTED="deb" ;; 'rpm') RPM_WANTED="rpm" ;; *) echo "Unknown target distribution $2" usage 1 ;; esac shift 2 ;; -r) RC=$2 if [ -z "$RC" ]; then echo "RC number required" fi shift 2 ;; -x) RACE="-race" shift ;; -w | --working-directory) PACKAGES_ONLY="PACKAGES_ONLY" WORKING_DIR="WORKING_DIR" shift ;; -*) echo "Unknown option $1" usage 1 ;; ?*) if [ -z $VERSION ]; then VERSION=$1 shift else echo "$1 : aborting version already set to $VERSION" usage 1 fi echo $VERSION | grep -i '[r|rc]' 2>&1 >/dev/null if [ $? -ne 1 -a -z "$NIGHTLY_BUILD" ]; then echo echo "$VERSION contains reference to RC - specify RC separately" echo usage 1 fi ;; *) break esac done if [ -z "$DEB_WANTED$RPM_WANTED$TAR_WANTED" ]; then TAR_WANTED="gz" DEB_WANTED="deb" RPM_WANTED="rpm" fi if [ -z "$VERSION" ]; then echo -e "Missing version" usage 1 fi ########################################################################### # Start the packaging process. echo -e "\nStarting package process...\n" # Ensure the current is correct. TARGET_BRANCH=`current_branch` if [ -z "$NIGHTLY_BUILD" -a -z "$PACKAGES_ONLY" ]; then echo -n "Current branch is $TARGET_BRANCH. Start packaging this branch? [Y/n] " read response response=`echo $response | tr 'A-Z' 'a-z'` if [ "x$response" == "xn" ]; then echo "Packaging aborted." cleanup_exit 1 fi fi check_gvm check_gopath if [ -z "$NIGHTLY_BUILD" -a -z "$PACKAGES_ONLY" ]; then check_clean_tree update_tree check_tag_exists `full_version $VERSION $RC` fi do_build `full_version $VERSION $RC` make_dir_tree $TMP_WORK_DIR `full_version $VERSION $RC` ########################################################################### # Copy the assets to the installation directories. for b in ${BINS[*]}; do cp $GOPATH_INSTALL/bin/$b $TMP_WORK_DIR/$INSTALL_ROOT_DIR/ if [ $? -ne 0 ]; then echo "Failed to copy binaries to packaging directory ($TMP_WORK_DIR/$INSTALL_ROOT_DIR/) -- aborting." cleanup_exit 1 fi done echo "${BINS[*]} copied to $TMP_WORK_DIR/$INSTALL_ROOT_DIR/" cp $INITD_SCRIPT $TMP_WORK_DIR/$INFLUXDB_SCRIPT_DIR/$INITD_SCRIPT if [ $? -ne 0 ]; then echo "Failed to copy init.d script to packaging directory ($TMP_WORK_DIR/$INFLUXDB_SCRIPT_DIR/) -- aborting." cleanup_exit 1 fi echo "$INITD_SCRIPT copied to $TMP_WORK_DIR/$INFLUXDB_SCRIPT_DIR" cp $SYSTEMD_SCRIPT $TMP_WORK_DIR/$INFLUXDB_SCRIPT_DIR/$SYSTEMD_SCRIPT if [ $? -ne 0 ]; then echo "Failed to copy systemd script to packaging directory -- aborting." cleanup_exit 1 fi echo "$SYSTEMD_SCRIPT copied to $TMP_WORK_DIR/$INFLUXDB_SCRIPT_DIR" cp $SAMPLE_CONFIGURATION $TMP_WORK_DIR/$CONFIG_ROOT_DIR/influxdb.conf if [ $? -ne 0 ]; then echo "Failed to copy $SAMPLE_CONFIGURATION to packaging directory -- aborting." cleanup_exit 1 fi install -m 644 $LOGROTATE $TMP_WORK_DIR/$LOGROTATE_DIR/influxdb if [ $? -ne 0 ]; then echo "Failed to copy logrotate configuration to packaging directory -- aborting." cleanup_exit 1 fi ########################################################################### # Create the actual packages. if [ -z "$NIGHTLY_BUILD" -a -z "$PACKAGES_ONLY" ]; then echo -n "Commence creation of $ARCH packages, version `full_version $VERSION $RC`? [Y/n] " read response response=`echo $response | tr 'A-Z' 'a-z'` if [ "x$response" == "xn" ]; then echo "Packaging aborted." cleanup_exit 1 fi fi COMMON_FPM_ARGS="\ --log error \ -C $TMP_WORK_DIR \ --vendor $VENDOR \ --url $URL \ --license $LICENSE \ --maintainer $MAINTAINER \ --after-install $POSTINSTALL_SCRIPT \ --before-install $PREINSTALL_SCRIPT \ --after-remove $POSTUNINSTALL_SCRIPT \ --name influxdb${RACE} \ --config-files $CONFIG_ROOT_DIR \ --config-files $LOGROTATE_DIR" if [ -n "$DEB_WANTED" ]; then $FPM -s dir -t deb $deb_args --description "$DESCRIPTION" $COMMON_FPM_ARGS --version `full_version $VERSION $RC` . if [ $? -ne 0 ]; then echo "Failed to create Debian package -- aborting." cleanup_exit 1 fi echo "Debian package created successfully." fi if [ -n "$TAR_WANTED" ]; then if [ -n "$RACE" ]; then # Tweak race prefix for tarball. race="race_" fi $FPM -s dir -t tar --prefix influxdb_$race`full_version $VERSION $RC`_${ARCH} -p influxdb_$race`full_version $VERSION $RC`_${ARCH}.tar.gz --description "$DESCRIPTION" $COMMON_FPM_ARGS --version `full_version $VERSION $RC ` . if [ $? -ne 0 ]; then echo "Failed to create Tar package -- aborting." cleanup_exit 1 fi echo "Tar package created successfully." fi if [ -n "$RPM_WANTED" ]; then $rpm_args $FPM -s dir -t rpm --description "$DESCRIPTION" $COMMON_FPM_ARGS --depends coreutils --version $VERSION --iteration `rpm_release $RC` . if [ $? -ne 0 ]; then echo "Failed to create RPM package -- aborting." cleanup_exit 1 fi echo "RPM package created successfully." fi ########################################################################### # Offer to tag the repo. if [ -z "$NIGHTLY_BUILD" -a -z "$PACKAGES_ONLY" ]; then echo -n "Tag source tree with v`full_version $VERSION $RC` and push to repo? [y/N] " read response response=`echo $response | tr 'A-Z' 'a-z'` if [ "x$response" == "xy" ]; then echo "Creating tag v`full_version $VERSION $RC` and pushing to repo" git tag v`full_version $VERSION $RC` if [ $? -ne 0 ]; then echo "Failed to create tag v`full_version $VERSION $RC` -- aborting" cleanup_exit 1 fi echo "Tag v`full_version $VERSION $RC` created" git push origin v`full_version $VERSION $RC` if [ $? -ne 0 ]; then echo "Failed to push tag v`full_version $VERSION $RC` to repo -- aborting" cleanup_exit 1 fi echo "Tag v`full_version $VERSION $RC` pushed to repo" else echo "Not creating tag v`full_version $VERSION $RC`." fi fi ########################################################################### # Offer to publish the packages. if [ -z "$NIGHTLY_BUILD" -a -z "$PACKAGES_ONLY" ]; then echo -n "Publish packages to S3? [y/N] " read response response=`echo $response | tr 'A-Z' 'a-z'` fi if [ "x$response" == "xy" -o -n "$NIGHTLY_BUILD" ]; then echo "Publishing packages to S3." if [ ! -e "$AWS_FILE" ]; then echo "$AWS_FILE does not exist -- aborting." cleanup_exit 1 fi for filepath in `ls *.{$DEB_WANTED,$RPM_WANTED,$TAR_WANTED} 2> /dev/null`; do filename=`basename $filepath` sum=`md5sum $filename | cut -d ' ' -f 1` if [ -n "$NIGHTLY_BUILD" ]; then # Replace the version string in the filename with "nightly". v=`full_version $VERSION $RC` v_underscored=`echo "$v" | tr - _` v_rpm=$VERSION-`rpm_release $RC` # It's ok to run each of these since only 1 will match, leaving # filename untouched otherwise. filename=`echo $filename | sed s/$v/nightly/` filename=`echo $filename | sed s/$v_underscored/nightly/` filename=`echo $filename | sed s/$v_rpm/nightly-1/` fi AWS_CONFIG_FILE=$AWS_FILE aws s3 cp $filepath s3://influxdb/$filename --acl public-read --region us-east-1 if [ $? -ne 0 ]; then echo "Upload failed ($filename) -- aborting". cleanup_exit 1 fi echo "$filename uploaded, MD5 checksum is $sum" done else echo "Not publishing packages to S3." fi ########################################################################### # All done. echo -e "\nPackaging process complete." cleanup_exit 0