#!/bin/bash
#==============================================================================
# Copyright and license info is available in the LICENSE file included with
# the Server Deployment Package (SDP), and also available online:
# https://swarm.workshop.perforce.com/projects/perforce-software-sdp/view/main/LICENSE
#------------------------------------------------------------------------------
set -u

# This script acquires Perforce Helix binaries from the Perforce FTP server.
# For documentation, run: get_helix_binaries.sh -man

#==============================================================================
# Declarations and Environment

declare DefaultPerforceHelixVersion=r20.1
declare DefaultBinList="p4 p4d p4broker p4p"

declare ThisScript=${0##*/}
declare Version=1.1.1
declare -i NoOp=0
declare -i ErrorCount=0
declare -i WarningCount=0
declare -i RetryCount=0
declare -i RetryMax=2
declare -i RetryDelay=2
declare -i RetryOK=0
declare PerforceHelixVersion=
declare BinList=
declare Platform=linux26x86_64
# declare PerforceFTPBaseURL="https://ftp.perforce.com/perforce"
declare PerforceFTPBaseURL="http://cdist2.perforce.com/perforce"
declare BinURL=
declare Cmd=

function msg () { echo -e "$*"; }
function errmsg () { msg "\\nError: ${1:-Unknown Error}\\n"; ErrorCount+=1; }
function warnmsg () { msg "\\nWarning: ${1:-Unknown Warning}\\n"; WarningCount+=1; }
function bail () { errmsg "${1:-Unkonwn Error}"; exit "${2:-1}"; }

#------------------------------------------------------------------------------
# Function: usage (required function)
#
# Input:
# $1 - style, either -h (for short form) or -man (for man-page like format).
# The default is -h.
#
# $2 - error message (optional).  Specify this if usage() is called due to
# user error, in which case the given message displayed first, followed by the
# standard usage message (short or long depending on $1).  If displaying an
# errror, usually $1 should be -h so that the longer usage message doesn't
# obsure the error message.
#
# Sample Usage:
# usage 
# usage -man
# usage -h "Incorrect command line usage."
#
# This last example generates a usage error message followed by the short
# '-h' usage summary.
#------------------------------------------------------------------------------
function usage {
   declare style=${1:--h}
   declare errorMessage=${2:-Unset}

   if [[ $errorMessage != Unset ]]; then
      msg "\\n\\nUsage Error:\\n\\n$errorMessage\\n\\n"
   fi

msg "USAGE for $ThisScript v$Version:

$ThisScript [-r <HelixVersion>] [-b <Binary1>,<Binary2>,...]

   or

$ThisScript -h|-man"
   if [[ $style == -man ]]; then
      msg "
DESCRIPTION:
	This script acquires Perforce Helix binaries from the Perforce FTP server.

	The four Helix binaries acquired are:

	* p4, the command line client
	* p4d, the Helix Core server
	* p4p, the Helix Proxy
	* p4broker, the Helix Broker

	This script gets the latest patch of binaries for the current server version.
	It is intended to acquire the latest patch for an existing install, or to
	get initial binaries for a fresh new install.

	When a newer major version of Helix binares is needed, this script should not
	be modified directly. Instead, the recommended approach is to upgrade the SDP
	to get the latest version of SDP first, which will included a newer version of
	this script. This helps ensure you have a version of the SDP that works with
	the version of p4d and other binaries.

EXAMPLES:
	All examples assume the SDP is in the standard location, /hxdepots/sdp.

	Example 1 - Typical Usage with no arguments:

	cd /hxdepots/sdp/helix_binaries
	./get_helix_binaries.sh

	This acquires the latest patch of all 4 binaries for the $DefaultPerforceHelixVersion
	release (aka 20${DefaultPerforceHelixVersion#r}).

	Example 2 - Specifying the major version:

	cd /hxdepots/sdp/helix_binaries
	./get_helix_binaries.sh -r r19.2

	This gets the latest patch of for the 2019.2 release of all 4 binaries.

	Note: Only supported Helix binares are guaranteed to be available from the
	Perforce FTP server.

	Note: Only the latest patch of any given binary is available from the Perforce
	FTP server.

	Example 3 - Sample getting r20.1 and skipping the proxy binary (p4p):

	cd /hxdepots/sdp/helix_binaries
	./get_helix_binaries.sh -r r20.1 -b p4,p4d,p4broker

DEPENDENCIES:
	This script requires outbound internet access. Depending on your environment,
	it may also require HTTPS_PROXY to be defined, or may not work at all.

	If this script doesn't work due to lack of outbound internet access, it is
	still useful illustrating the locations on the Perforce FTP server where
	Helix Core binaries can be found.


OPTIONS:
 -n	Specify the '-n' (No Operation) option to show the commands needed
	to fetch the Helix binaries from the Perforce FTP server without attempting
	to execute them.

 -D	Set extreme debugging verbosity using bash 'set -x' mode.

HELP OPTIONS:
 -h	Display short help message
 -man	Display this manual page

EXAMPLES:

EXIT CODES:
	An exit code of 0 indicates no errors were encounted. An
	non-zero exit code indicates errors were encounterd.
"
   fi

   exit 1
}

#==============================================================================
# Command Line Processing

declare -i shiftArgs=0

set +u
while [[ $# -gt 0 ]]; do
   case $1 in
      (-h) usage -h;;
      (-man) usage -man;;
      (-r) PerforceHelixVersion="${2:-}"; shiftArgs=1;;
      (-b) BinList="${2:-}"; shiftArgs=1;;
      (-n) NoOp=1;;
      (-D) set -x;; # Debug; use 'set -x' mode.
      (*) usage -h "Unknown command line fragment [$1].";;
   esac

   # Shift (modify $#) the appropriate number of times.
   shift; while [[ $shiftArgs -gt 0 ]]; do
      [[ $# -eq 0 ]] && usage -h "Incorrect number of arguments."
      shiftArgs=$shiftArgs-1
      shift
   done
done
set -u

[[ -n "$PerforceHelixVersion" ]] || PerforceHelixVersion="$DefaultPerforceHelixVersion"
[[ -n "$BinList" ]] || BinList="$DefaultBinList"

#==============================================================================
# Command Line Verification

#==============================================================================
# Main Program

msg "\\nStarted $ThisScript v$Version as $USER@${HOSTNAME%%.*} at $(date)."

for binary in $(echo "$BinList"|tr ',' ' '); do
   msg "\\nGetting $binary ..."
   BinURL="${PerforceFTPBaseURL}/${PerforceHelixVersion}/bin.${Platform}/$binary"
   if [[ -f "$binary" ]]; then
      chmod +x "$binary"
      msg "Old version of $binary: $("./$binary" -V | grep Rev)"

      if [[ "$NoOp" -eq 0 ]]; then
         rm -f "$binary"
      fi
   fi

   Cmd="curl -s -k -O $BinURL"

   if [[ "$NoOp" -eq 1 ]]; then
      msg "NoOp: Would run: $Cmd"
      continue
   else
      msg "Running: $Cmd"
   fi

   if $Cmd; then
      chmod +x "$binary"
      msg "New version of $binary: $("./$binary" -V | grep Rev)"
   else
      # Replace the '-s' silent flag with '-v' after we have had an error, to
      # help with debugging.
      Cmd="curl -v -k -O $BinURL"
      warnmsg "Failed to download $binary with this URL: $BinURL\\nRetrying ..."
      RetryCount+=0

      while [[ "$RetryCount" -le "$RetryMax" ]]; do
         RetryCount+=1
         sleep "$RetryDelay"
         msg "Retry $RetryCount of $binary with command: $Cmd"
         if $Cmd; then
            chmod +x "$binary"
            msg "New version of $binary: $("./$binary" -V | grep Rev)"
            RetryOK=1
            break
         else
            warnmsg "Retry $RetryCount failed again to download $binary with this URL: $BinURL"
         fi
      done

      if [[ "$RetryOK" -eq 0 ]]; then
         errmsg "Failed to download $binary with this URL: $BinURL"
         rm -f "$binary"
      fi
   fi
done

if [[ "$ErrorCount" -eq 0 ]]; then
   msg "\\nDownloading of Perforce Helix binaries completed OK."
else
   errmsg "\\There were $ErrorCount errors attempting to download Perforce Helix binaries."
fi

exit "$ErrorCount"
