#!/bin/zsh export EXECUTION_DIR=$(pwd) source "${0:a:h}/zsh/lib/import.driver.zsh" || exit 42 ##################################################################### () { cd "$SCWRYPTS_ROOT__scwrypts" GIT_SCWRYPTS() { git -C "$SCWRYPTS_ROOT__scwrypts" $@; } local ERRORS=0 local USAGE=' usage: scwrypts [...options...] [...patterns...] -- [...script options...] options: selection -m, --name only run the script if there is an exact match (requires type and group) -g, --group only use scripts from the indicated group -t, --type only use scripts of the indicated type runtime -y, --yes auto-accept all [yn] prompts through current scwrypt -e, --env set environment; overwrites SCWRYPTS_ENV -n shorthand for "--log-level 0" -v, --log-level [0-4] set scwrypts log level to one of the following: 0 : only command output and critical failures; skips logfile 1 : add success / failure messages 2 : (default) include status update messages 3 : (CI default) include warning messages 4 : include debug messages alternate commands -h, --help display this message and exit -l, --list print out command list and exit --list-envs print out environment list and exit --update update scwrypts library to latest version --version print out scwrypts version and exit patterns: - a list of glob patterns to loose-match a scwrypt by name script options: - everything after "--" is forwarded to the scwrypt you run ("-- --help" will provide more information) ' ##################################################################### ### cli argument parsing and global configuration ################### ##################################################################### local ENV_NAME="$SCWRYPTS_ENV" local SEARCH_PATTERNS=() local VARSPLIT SEARCH_GROUP SEARCH_TYPE SEARCH_NAME [ ! $SCWRYPTS_LOG_LEVEL ] && { local SCWRYPTS_LOG_LEVEL [ $CI ] && SCWRYPTS_LOG_LEVEL=3 || SCWRYPTS_LOG_LEVEL=2 } while [[ $# -gt 0 ]] do case $1 in -[a-z][a-z]* ) VARSPLIT=$(echo "$1 " | sed 's/^\(-.\)\(.*\) /\1 -\2/') set -- $(echo " $VARSPLIT ") ${@:2} ;; ### alternate commands ################### -h | --help ) USAGE return 0 ;; -l | --list ) SCWRYPTS__GET_AVAILABLE_SCWRYPTS return 0 ;; --list-envs ) SCWRYPTS__GET_ENV_NAMES return 0 ;; --version ) echo scwrypts $(GIT_SCWRYPTS describe --tags) return 0 ;; --update ) GIT_SCWRYPTS fetch --quiet origin main GIT_SCWRYPTS fetch --quiet origin main --tags local SYNC_STATUS=$? GIT_SCWRYPTS diff --exit-code origin/main -- . >/dev/null 2>&1 local DIFF_STATUS=$? [[ $SYNC_STATUS -eq 0 ]] && [[ $DIFF_STATUS -eq 0 ]] && { SUCCESS 'already up-to-date with origin/main' } || { GIT_SCWRYPTS rebase --autostash origin/main \ && SUCCESS 'up-to-date with origin/main' \ && GIT_SCWRYPTS log -n1 \ || { GIT_SCWRYPTS rebase --abort ERROR 'unable to update scwrypts; please try manual upgrade' REMINDER "installation in '$(pwd)'" } } return 0 ;; ### scwrypts filters ##################### -m | --name ) [ $2 ] || { ERROR "missing value for argument $1"; break; } SEARCH_NAME=$2 shift 1 ;; -g | --group ) [ $2 ] || { ERROR "missing value for argument $1"; break; } SEARCH_GROUP=$2 shift 1 ;; -t | --type ) [ $2 ] || { ERROR "missing value for argument $1"; break; } SEARCH_TYPE=$2 shift 1 ;; ### runtime settings ##################### -y | --yes ) export __SCWRYPTS_YES=1 ;; -n | --no-log ) SCWRYPTS_LOG_LEVEL=0 [[ $1 =~ ^--no-log$ ]] && WARNING 'the --no-log flag is deprecated and will be removed in scwrypts v4.2' ;; -v | --log-level ) [[ $2 =~ ^[0-4]$ ]] || ERROR "invalid setting for log-level '$2'" SCWRYPTS_LOG_LEVEL=$2 shift 1 ;; -e | --env ) [ $2 ] || { ERROR "missing value for argument $1"; break; } [ $ENV_NAME ] && DEBUG 'overwriting session environment' ENV_NAME="$2" STATUS "using CLI environment '$ENV_NAME'" shift 1 ;; ########################################## -- ) shift 1; break ;; # pass arguments after '--' to the scwrypt --* ) ERROR "unrecognized argument '$1'" ;; * ) SEARCH_PATTERNS+=($1) ;; esac shift 1 done [ $SEARCH_NAME ] && { [ $SEARCH_TYPE ] || ERROR '--name requires --type argument' [ $SEARCH_GROUP ] || ERROR '--name requires --group argument' } CHECK_ERRORS ##################################################################### ### scwrypts selection / filtering ################################## ##################################################################### local SCWRYPTS_AVAILABLE SCWRYPTS_AVAILABLE=$(SCWRYPTS__GET_AVAILABLE_SCWRYPTS) ########################################## [ $SEARCH_NAME ] && SCWRYPTS_AVAILABLE=$({ echo $SCWRYPTS_AVAILABLE | head -n1 echo $SCWRYPTS_AVAILABLE | sed -e 's/\x1b\[[0-9;]*m//g' | grep "^$SEARCH_NAME *$SEARCH_TYPE *$SEARCH_GROUP\$" }) || { [ $SEARCH_TYPE ] && { SCWRYPTS_AVAILABLE=$(\ { echo $SCWRYPTS_AVAILABLE | head -n1 echo $SCWRYPTS_AVAILABLE | grep ' [^/]*'$SEARCH_TYPE'[^/]* ' } \ | awk '{$2=""; print $0;}' \ | sed 's/ \+$/'$(printf $__COLOR_RESET)'/; s/ \+/^/g' \ | column -ts '^' ) } [ $SEARCH_GROUP ] && { SCWRYPTS_AVAILABLE=$( { echo $SCWRYPTS_AVAILABLE | head -n1 echo $SCWRYPTS_AVAILABLE | grep "$SEARCH_GROUP"'[^/]*$' } \ | awk '{$NF=""; print $0;}' \ | sed 's/ \+$/'$(printf $__COLOR_RESET)'/; s/ \+/^/g' \ | column -ts '^' ) } [[ ${#SEARCH_PATTERNS[@]} -gt 0 ]] && { POTENTIAL_ERROR+="\n PATTERNS : $SEARCH_PATTERNS" local P for P in ${SEARCH_PATTERNS[@]} do SCWRYPTS_AVAILABLE=$( { echo $SCWRYPTS_AVAILABLE | head -n1 echo $SCWRYPTS_AVAILABLE | grep $P } ) done } } [[ $(echo $SCWRYPTS_AVAILABLE | wc -l) -lt 2 ]] && { FAIL 1 "$(echo " no such scwrypt exists NAME : '$SEARCH_NAME' TYPE : '$SEARCH_TYPE' GROUP : '$SEARCH_GROUP' PATTERNS : '$SEARCH_PATTERNS' " | sed "1d; \$d; /''$/d")" } ########################################## [[ $(echo $SCWRYPTS_AVAILABLE | wc -l) -eq 2 ]] \ && SCWRYPT_SELECTION=$(echo $SCWRYPTS_AVAILABLE | tail -n1) \ || SCWRYPT_SELECTION=$(echo $SCWRYPTS_AVAILABLE | FZF "select a script to run" --header-lines 1) \ ; [ $SCWRYPT_SELECTION ] || exit 2 ########################################## local NAME TYPE GROUP SCWRYPTS__SEPARATE_SCWRYPT_SELECTION $SCWRYPT_SELECTION export SCWRYPT_NAME=$NAME export SCWRYPT_TYPE=$TYPE export SCWRYPT_GROUP=$GROUP ##################################################################### ### environment variables and configuration validation ############## ##################################################################### local ENV_REQUIRED=true \ && [ ! $CI ] \ && [[ ! $SCWRYPT_NAME =~ scwrypts/logs ]] \ && [[ ! $SCWRYPT_NAME =~ scwrypts/environment ]] \ || ENV_REQUIRED=false local REQUIRED_ENVIRONMENT_REGEX=$(eval echo '$SCWRYPTS_REQUIRED_ENVIRONMENT_REGEX__'$SCWRYPT_GROUP) [[ $ENV_REQUIRED =~ true ]] && { [ ! $ENV_NAME ] && ENV_NAME=$(SCWRYPTS__SELECT_ENV) for GROUP in ${SCWRYPTS_GROUPS[@]} do local ENV_FILE=$(SCWRYPTS__GET_ENV_FILE "$ENV_NAME" "$GROUP") source "$ENV_FILE" || FAIL 5 "missing or invalid environment '$GROUP/$ENV_NAME'" for f in $(eval 'echo $SCWRYPTS_STATIC_CONFIG__'$GROUP) do source "$f" || FAIL 5 "invalid static config '$f'" done done export ENV_NAME } ########################################## [ $REQUIRED_ENVIRONMENT_REGEX ] && { [[ $ENV_NAME =~ $REQUIRED_ENVIRONMENT_REGEX ]] \ || FAIL 5 "group '$SCWRYPT_GROUP' requires current environment name to match '$REQUIRED_ENVIRONMENT_REGEX' (currently $ENV_NAME)" } ########################################## [ ! $SUBSCWRYPT ] && [[ $ENV_NAME =~ prod ]] && { STATUS "on '$ENV_NAME'; checking diff against origin/main" GIT_SCWRYPTS fetch --quiet origin main local SYNC_STATUS=$? GIT_SCWRYPTS diff --exit-code origin/main -- . >&2 local DIFF_STATUS=$? [[ $SYNC_STATUS -eq 0 ]] && [[ $DIFF_STATUS -eq 0 ]] && { SUCCESS 'up-to-date with origin/main' } || { SCWRYPTS_LOG_LEVEL=3 WARNING "you are trying to run in ${__BRIGHT_RED}production${__YELLOW} but $([[ $SYNC_STATUS -ne 0 ]] && echo 'I am unable to verify your scwrypts version')$([[ $DIFF_STATUS -ne 0 ]] && echo 'your scwrypts is out-of-date (diff listed above)')" yN 'continue?' || { REMINDER "you can use 'scwrypts --update' to quickly update scwrypts to latest" ABORT } } } ########################################## local RUN_STRING=$(SCWRYPTS__GET_RUNSTRING $SCWRYPT_NAME $SCWRYPT_TYPE $SCWRYPT_GROUP) [ "$RUN_STRING" ] || return 42 ##################################################################### ### logging and pretty header/footer setup ########################## ##################################################################### local LOGFILE \ && [[ $SCWRYPTS_LOG_LEVEL -gt 0 ]] \ && [ ! $SUBSCWRYPT ] \ && [[ ! $SCWRYPT_NAME =~ scwrypts/logs ]] \ && [[ ! $SCWRYPT_NAME =~ interactive ]] \ && LOGFILE="$SCWRYPTS_LOG_PATH/$(echo $GROUP/$TYPE/$NAME | sed 's/^\.\///; s/\//\%/g').log" \ || LOGFILE='/dev/null' \ ; local RUN_MODE=normal [[ $LOGFILE =~ ^/dev/null$ ]] && RUN_MODE=no-logfile [[ $SCWRYPT_NAME =~ interactive ]] && RUN_MODE=interactive local HEADER FOOTER [[ $SCWRYPTS_LOG_LEVEL -ge 2 ]] && { HEADER=$( echo " ===================================================================== script : $SCWRYPT_GROUP $SCWRYPT_TYPE $SCWRYPT_NAME run at : $(date) config : $ENV_NAME log level : $SCWRYPTS_LOG_LEVEL \\033[1;33m--- SCWRYPT BEGIN ---------------------------------------------------\\033[0m " | sed 's/^\s\+//; 1d' ) FOOTER="\\033[1;33m--- SCWRYPT END ---------------------------------------------------\\033[0m" } [ $SUBSCWRYPT ] && { HEADER="\\033[0;33m--- ($SUBSCWRYPT) BEGIN $SCWRYPT_GROUP $SCWRYPT_TYPE $SCWRYPT_NAME ---" FOOTER="\\033[0;33m--- ($SUBSCWRYPT) END $SCWRYPT_GROUP $SCWRYPT_TYPE $SCWRYPT_NAME ---" } ##################################################################### ### run the scwrypt ################################################# ##################################################################### [ ! $SUBSCWRYPT ] && export SUBSCWRYPT=0 set -o pipefail { [ $HEADER ] && echo $HEADER case $RUN_MODE in normal ) (eval "$RUN_STRING $(printf "%q " "$@")") EXIT_CODE=$? ;; no-logfile ) eval "$RUN_STRING $(printf "%q " "$@")" EXIT_CODE=$? ;; interactive ) eval "$RUN_STRING $(printf "%q " "$@")" /dev/tty 2>&1 EXIT_CODE=$? ;; esac [ $FOOTER ] && echo $FOOTER [[ $EXIT_CODE -eq 0 ]] && EXIT_COLOR='32m' || EXIT_COLOR='31m' [[ $SCWRYPTS_LOG_LEVEL -ge 2 ]] && [ ! $SUBSCWRYPT ] \ && echo "terminated with\\033[1;$EXIT_COLOR code $EXIT_CODE\\033[0m" return $EXIT_CODE } 2>&1 | tee --append "$LOGFILE" } $@