#!/bin/bash
set -u
# For documetation, run this script with the '-man' option:
# build.sh -man
#==============================================================================
# Declarations an Environment
declare CmdLine="$0 $*"
declare ThisScript="${0##*/}"
declare Version=1.0.2
declare ThisUser=
declare ThisHost=${HOSTNAME%%.*}
declare SDPCommonSiteLib=/p4/common/site/lib
declare SDPInstance=
declare Changelist=
# Color support.
declare GREEN=
declare RED=
declare YELLOW=
declare RESET=
declare H1="=============================================================================="
#declare H2="------------------------------------------------------------------------------"
declare -i Debug=${SDP_DEBUG:-0}
declare -i ErrorCount=0
#declare -i WarningCount=0
declare -i SilentMode=0
declare LogsDir=
declare LogLink="${ThisScript%.sh}.log"
declare Log=
#==============================================================================
# Local Functions
# shellcheck disable=SC2317
function msg_green { msg "${GREEN}$*${RESET}"; }
# shellcheck disable=SC2317
function msg_green { msg "${GREEN}$*${RESET}"; }
# shellcheck disable=SC2317
function msg () { echo -e "$*"; }
# shellcheck disable=SC2317
function msg_green { msg "${GREEN}$*${RESET}"; }
# shellcheck disable=SC2317
function msgn () { echo -n -e "$*"; }
# shellcheck disable=SC2317
function msg_green { msg "${GREEN}$*${RESET}"; }
# shellcheck disable=SC2317
function msg_yellow { msg "${YELLOW}$*${RESET}"; }
# shellcheck disable=SC2317
function msg_green { msg "${GREEN}$*${RESET}"; }
# shellcheck disable=SC2317
function msg_red { msg "${RED}$*${RESET}"; }
function dbg () { [[ "$Debug" -eq 0 ]] || echo -e "DEBUG: $*" >&2; }
# shellcheck disable=SC2317
function dbg2 () { [[ "$Debug" -ge 2 ]] && echo -e "DEBUG: $*" >&2; }
function errmsg () { msg_red "\\nError: ${1:-Unknown Error}\\n"; ErrorCount+=1; }
# shellcheck disable=SC2317
#function warnmsg () { msg_yellow "\\nWarning: ${1:-Unknown Warning}\\n"; WarningCount+=1; }
function bail () { errmsg "${1:-Unknown 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
# error, usually $1 should be -h so that the longer usage message doesn't
# obscure the error message.
#
# Sample Usage:
# usage
# usage -h
# usage -man
# usage -h "Incorrect command line usage."
#------------------------------------------------------------------------------
function usage
{
local style=${1:--h}
local usageErrorMessage=${2:-}
[[ -n "$usageErrorMessage" ]] && msg "\\n\\nUsage Error:\\n\\n$usageErrorMessage\\n\\n"
msg "USAGE for $ThisScript v$Version:
$ThisScript -c <changelist> [-d|-D]
OR
$ThisScript [-h|-man|-V]
"
if [[ $style == -man ]]; then
msg "
DESCRIPTION:
This script is intended to build a changelist.
INSTALLATION:
This is to be installed in the P4 Triggers table with a trigger like so:
Triggers:
Build change-commit //jam/main/... \"/p4/common/site/bin/triggers/build.sh -c %changelist%\"
OPTIONS:
-L <log>
Specify the path to a log file, or the special value 'off' to disable
logging. By default, all output (stdout and stderr) goes to a log file
pointed to by a symlink:
$LogLink
The symlink is for convenience. It refers to the log from the most recent
run of the script.
Each time this script is run, a new timestamped log is started, and
the symlink updated to reference the new/latest log during startup. Log
files have timestamps that go to the second (or millisecond if needed)
to differentiate logs.
NOTE: This script is self-logging. Output displayed on the screen is
simultaneously captured in the log file. Using redirection operators like
'> log' or '2>&1' are unnecessary, as is using 'tee' (though using 'tee'
or redirects is safe and harmless).
-d
Display debug messages.
-D
Set extreme debugging verbosity using bash 'set -x' mode. Implies -d.
-si
Silent Mode. No output is displayed to the terminal (except for usage errors
on startup). Output is captured in the log. The '-si' cannot be used with
'-L off'.
HELP OPTIONS:
-h Display short help message.
-man Display man-style help message.
-V Display script name and version.
FILES:
/etc/systemd/system/${ThisScript%.sh}.service
/etc/systemd/system/${ThisScript%.sh}.timer
TO DO:
A future version of this script may preserve the crontab of the OSUSER.
"
fi
exit 2
}
#==============================================================================
# SDP Library Functions
declare -a BashLibList=(logging.lib p4triggers.lib)
if [[ -d "$SDPCommonSiteLib" ]]; then
for BashLib in "${BashLibList[@]}"; do
dbg "Loading lib $BashLib with: source \"$SDPCommonSiteLib/$BashLib\""
# shellcheck disable=SC1090
source "$SDPCommonSiteLib/$BashLib" ||\
bail "Failed to load bash lib [$SDPCommonSiteLib/$BashLib]. Aborting."
done
fi
#==============================================================================
# Command Line Processing
declare -i shiftArgs=0
set +u
while [[ $# -gt 0 ]]; do
case "$1" in
(-h) usage -h;;
(-man) usage -man;;
(-V|-version|--version) msg "$ThisScript version $Version"; exit 0;;
(-L) Log="$2"; shiftArgs=1;;
(-c) Changelist="$2"; shiftArgs=1;;
(-si) SilentMode=1;;
(-d) Debug=1;;
(-D) Debug=1; set -x;; # Use bash 'set -x' extreme debug mode.
(-*) usage -h "Unknown option ($1).";;
(*) usage -h "Unknown parameter ($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
#==============================================================================
# Command Line Verification
[[ -n "$Changelist" ]] || usage -h "The '-c <changelist>' argument is required."
[[ "$Changelist" =~ ^[0-9]+$ ]] || usage -h "Changelist must be numeric; invalid value: $Changelist."
[[ "$SilentMode" -eq 1 && "$Log" == off ]] && \
usage -h "The '-si' option cannot be used with '-Log off'."
[[ -z "$SDPInstance" ]] && SDPInstance="${SDP_INSTANCE:-}"
[[ -z "$SDPInstance" ]] && usage -h "The SDP instance parameter is required unless SDP_INSTANCE is set. To set SDP_INSTANCE, do:\\n\\tsource /p4/common/bin/p4_vars INSTANCE\\n\\nreplacing INSTANCE with your SDP instance name."
SDPInstanceVars="/p4/common/config/p4_${SDPInstance}.vars"
[[ -r "$SDPInstanceVars" ]] || \
usage -h "The SDP instance specified [$SDPInstance] is missing Instance Vars file: $SDPInstanceVars"
# shellcheck disable=SC1090 disable=SC1091
source /p4/common/bin/p4_vars "$SDPInstance" ||\
bail "Could not do: source /p4/common/bin/p4_vars \"$SDPInstance\""
LogsDir="$LOGS"
#==============================================================================
# Main Program
trap terminate EXIT SIGINT SIGTERM
# Detect support for colors
if [[ $SilentMode -eq 0 ]] \
&& command -v tput >/dev/null 2>&1 \
&& [[ -t 1 ]] \
&& [[ "$(tput colors)" -ge 8 ]]; then
RED="$(tput setaf 1)"
GREEN="$(tput setaf 2)"
YELLOW="$(tput setaf 3)"
RESET="$(tput sgr0)"
else
RED=; GREEN=; YELLOW=; RESET=
fi
[[ -n "$Log" ]] || Log="${LogsDir}/${ThisScript%.sh}.$Changelist.$(date +'%Y-%m-%d-%H%M%S').log"
if [[ "$Log" != off ]]; then
# Set LogsDir to the directory containing $Log. While LogsDir is defined above,
# we need to reset it here in case the user used the -L option.
LogsDir="${Log%/*}"
if [[ ! -d "$LogsDir" ]]; then
mkdir -p "$LogsDir" || bail "Couldn't do: mkdir -p \"$LogsDir\""
fi
touch "$Log" || bail "Couldn't touch new log file [$Log]."
# Use a subshell so the 'cd' doesn't persist.
( cd "$LOGS"; ln -s "${Log##*/}" "${LogLink##*/}"; ) ||\
bail "Couldn't initialize log symlink; tried: ln -s \"$Log\" \"$LogLink\""
# Redirect stdout and stderr to a log file.
if [[ "$SilentMode" -eq 0 ]]; then
if [[ -n "$GREEN" ]]; then
exec > >( tee \
>(sed -r \
-e 's/\x1B\[[0-9;]*[a-zA-Z]//g' \
-e 's/\x1B\(B//g' >>"$Log"))
else
exec > >(tee "$Log")
fi
exec 2>&1
else
exec >"$Log"
exec 2>&1
fi
msg "${H1}\\nLog is: $Log"
fi
ThisUser=$(id -n -u)
msg "Starting $ThisScript v$Version as $ThisUser@$ThisHost on $(date) with \\n$CmdLine"
msg "Building change @$Changelist ..."
#------------------------------------------------------------------------------
# See the terminate() function where this script really exits.
exit "$ErrorCount"