From 05694ed0226976331b0cf337fb779f2ed15d1500 Mon Sep 17 00:00:00 2001 From: yage Date: Mon, 12 Feb 2024 23:39:56 -0700 Subject: [PATCH] bring some much-needed tender love and care to the scwrypts runner --- run | 311 ++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 166 insertions(+), 145 deletions(-) diff --git a/run b/run index 2b7c8e2..89f82be 100755 --- a/run +++ b/run @@ -1,12 +1,13 @@ #!/bin/zsh export EXECUTION_DIR=$(pwd) source "${0:a:h}/zsh/lib/import.driver.zsh" || exit 42 - ##################################################################### - -__RUN() { +() { + cd "$SCWRYPTS_ROOT__scwrypts" + GIT_SCWRYPTS() { git -C "$SCWRYPTS_ROOT__scwrypts" $@; } + local ERRORS=0 local USAGE=' - usage: scwrypts [... options ...] [patterns] -- [...script options...] + usage: scwrypts [...options...] [...patterns...] -- [...script options...] options: selection @@ -32,27 +33,28 @@ __RUN() { --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 - (usually "-- --help" will provide more information) + ("-- --help" will provide more information) ' - cd "$SCWRYPTS_ROOT" + + ##################################################################### + ### cli argument parsing and global configuration ################### + ##################################################################### local ENV_NAME="$SCWRYPTS_ENV" local SEARCH_PATTERNS=() local VARSPLIT SEARCH_GROUP SEARCH_TYPE SEARCH_NAME - local ALLOW_LOGFILE=1 - local SCWRYPTS_LOG_LEVEL=2 - - [ $CI ] && [ ! $SCWRYPTS_CI_FORCE_NON_VERBOSE ] && SCWRYPTS_LOG_LEVEL=3 - - local ERROR=0 + [ ! $SCWRYPTS_LOG_LEVEL ] && { + local SCWRYPTS_LOG_LEVEL + [ $CI ] && SCWRYPTS_LOG_LEVEL=3 || SCWRYPTS_LOG_LEVEL=2 + } while [[ $# -gt 0 ]] do @@ -62,30 +64,44 @@ __RUN() { set -- $(echo " $VARSPLIT ") ${@:2} ;; - -h | --help ) USAGE; return 0 ;; - -l | --list ) SCWRYPTS__GET_AVAILABLE_SCWRYPTS; return 0 ;; - --list-envs ) SCWRYPTS__GET_ENV_NAMES; return 0 ;; + ### 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 -C "$SCWRYPTS__ROOT__scwrypts" describe --tags) + echo scwrypts $(GIT_SCWRYPTS describe --tags) return 0 ;; --update ) - cd "$SCWRYPTS__ROOT__scwrypts" - git fetch --quiet origin main - git fetch --quiet origin main --tags + GIT_SCWRYPTS fetch --quiet origin main + GIT_SCWRYPTS fetch --quiet origin main --tags local SYNC_STATUS=$? - git diff --exit-code origin/main -- . >&2 + 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 rebase --autostash origin/main \ + GIT_SCWRYPTS rebase --autostash origin/main \ && SUCCESS 'up-to-date with origin/main' \ + && GIT_SCWRYPTS log -n1 \ || { - git rebase --abort + GIT_SCWRYPTS rebase --abort ERROR 'unable to update scwrypts; please try manual upgrade' REMINDER "installation in '$(pwd)'" } @@ -93,40 +109,52 @@ __RUN() { return 0 ;; + ### scwrypts filters ##################### + -m | --name ) - [ ! $2 ] && ERROR "missing value for argument $1" && break + [ $2 ] || { ERROR "missing value for argument $1"; break; } SEARCH_NAME=$2 shift 1 ;; -g | --group ) - [ ! $2 ] && ERROR "missing value for argument $1" && break + [ $2 ] || { ERROR "missing value for argument $1"; break; } SEARCH_GROUP=$2 shift 1 ;; -t | --type ) - [ ! $2 ] && ERROR "missing value for argument $1" && break + [ $2 ] || { ERROR "missing value for argument $1"; break; } SEARCH_TYPE=$2 shift 1 ;; - -y | --yes ) export __SCWRYPTS_YES=1 ;; - -n | --no-log ) SCWRYPTS_LOG_LEVEL=0 ;; - -v | --verbosity ) SCWRYPTS_LOG_LEVEL=$2 ;; + ### 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 | --verbosity ) + [[ $2 =~ ^[0-4]$ ]] || ERROR "invalid setting for verbosity '$2'" + SCWRYPTS_LOG_LEVEL=$2 + shift 1 + ;; -e | --env ) - [ ! $2 ] && ERROR "missing value for argument $1" && break - [ ! $SUBSCWRYPTS ] \ - && [ $ENV_NAME ] \ - && WARNING 'overwriting session environment' \ - ; + [ $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'" ;; @@ -142,26 +170,20 @@ __RUN() { CHECK_ERRORS - ########################################## + ##################################################################### + ### scwrypts selection / filtering ################################## + ##################################################################### local SCWRYPTS_AVAILABLE - local POTENTIAL_ERROR="no such scwrypt exists:" - SCWRYPTS_AVAILABLE=$(SCWRYPTS__GET_AVAILABLE_SCWRYPTS) - [ $SEARCH_NAME ] && { - POTENTIAL_ERROR+="\n NAME : '$SEARCH_NAME'" - POTENTIAL_ERROR+="\n TYPE : '$SEARCH_TYPE'" - POTENTIAL_ERROR+="\n GROUP : '$SEARCH_GROUP'" - 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_NAME ] && { + [ $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 ] && { - POTENTIAL_ERROR+="\n TYPE : '$SEARCH_TYPE'" SCWRYPTS_AVAILABLE=$(\ { echo $SCWRYPTS_AVAILABLE | head -n1 @@ -174,7 +196,6 @@ __RUN() { } [ $SEARCH_GROUP ] && { - POTENTIAL_ERROR+="\n GROUP : '$SEARCH_GROUP'" SCWRYPTS_AVAILABLE=$( { echo $SCWRYPTS_AVAILABLE | head -n1 @@ -201,40 +222,47 @@ __RUN() { } } - [[ $(echo $SCWRYPTS_AVAILABLE | wc -l) -lt 2 ]] && ERROR "$POTENTIAL_ERROR" - - CHECK_ERRORS + [[ $(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")" + } ########################################## - local NAME="$SEARCH_NAME" - local TYPE="$SEARCH_TYPE" - local GROUP="$SEARCH_GROUP" + [[ $(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) \ + ; - [[ $(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 ENV_REQUIRED=$(__CHECK_ENV_REQUIRED && echo 1 || echo 0) local REQUIRED_ENVIRONMENT_REGEX=$(eval echo '$SCWRYPTS_REQUIRED_ENVIRONMENT_REGEX__'$SCWRYPT_GROUP) - [ $REQUIRED_ENVIRONMENT_REGEX ] && { - [[ $ENV_NAME =~ $REQUIRED_ENVIRONMENT_REGEX ]] \ - || FAIL 5 "group '$SCWRYPT_GROUP' requires current environment to match '$REQUIRED_ENVIRONMENT_REGEX' (currently $ENV_NAME)" - } - - [[ $ENV_REQUIRED -eq 1 ]] && { + [[ $ENV_REQUIRED =~ true ]] && { [ ! $ENV_NAME ] && ENV_NAME=$(SCWRYPTS__SELECT_ENV) for GROUP in ${SCWRYPTS_GROUPS[@]} @@ -251,104 +279,97 @@ __RUN() { export ENV_NAME } + ########################################## + [ $REQUIRED_ENVIRONMENT_REGEX ] && { [[ $ENV_NAME =~ $REQUIRED_ENVIRONMENT_REGEX ]] \ - || FAIL 5 "group '$SCWRYPT_GROUP' requires current environment to match '$REQUIRED_ENVIRONMENT_REGEX' (currently $ENV_NAME)" + || FAIL 5 "group '$SCWRYPT_GROUP' requires current environment name to match '$REQUIRED_ENVIRONMENT_REGEX' (currently $ENV_NAME)" } ########################################## - : \ - && [ ! $SUBSCWRYPT ] \ - && [[ $ENV_NAME =~ prod ]] \ - && { __VALIDATE_UPSTREAM_TIMELINE || ABORT; } + [ ! $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 ] && exit 3 + [ "$RUN_STRING" ] || return 42 - ########################################## + ##################################################################### + ### logging and pretty header/footer setup ########################## + ##################################################################### - local LOGFILE=$(__GET_LOGFILE) + 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 HEADER=$( - [[ $SCWRYPTS_LOG_LEVEL -ge 2 ]] || return 0 - [ $SUBSCWRYPT ] && return 0 - echo '=====================================================================' - echo "script : $SCWRYPT_GROUP $SCWRYPT_TYPE $SCWRYPT_NAME" - echo "run at : $(date)" - echo "config : $ENV_NAME" - echo "verbosity : $SCWRYPTS_LOG_LEVEL" - [ ! $LOGFILE ] && echo '\033[1;33m------------------------------------------\033[0m' - ) + local HEADER FOOTER - [ ! $LOGFILE ] && { - [ $HEADER ] && echo $HEADER - [ $SUBSCWRYPT ] && { - eval "$RUN_STRING $(printf "%q " "$@")" - exit $? - } || { - eval "$RUN_STRING $(printf "%q " "$@")" /dev/tty 2>&1 - exit $? - } + [[ $SCWRYPTS_LOG_LEVEL -ge 2 ]] && { + HEADER=$( + echo " + ===================================================================== + script : $SCWRYPT_GROUP $SCWRYPT_TYPE $SCWRYPT_NAME + run at : $(date) + config : $ENV_NAME + verbosity : $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 ################################################# + ##################################################################### + set -o pipefail { [ $HEADER ] && echo $HEADER - [[ $SCWRYPTS_LOG_LEVEL -ge 2 ]] && echo '\033[1;33m--- BEGIN OUTPUT -------------------------\033[0m' - (eval "$RUN_STRING $(printf "%q " "$@")") - export EXIT_CODE=$? - [[ $SCWRYPTS_LOG_LEVEL -ge 2 ]] && echo '\033[1;33m--- END OUTPUT ---------------------------\033[0m' - + [[ $LOGFILE =~ ^/dev/null$ ]] && { + eval "$RUN_STRING $(printf "%q " "$@")" /dev/tty 2>&1 + EXIT_CODE=$? + } || { + (eval "$RUN_STRING $(printf "%q " "$@")") + EXIT_CODE=$? + } + [ $FOOTER ] && echo $FOOTER [[ $EXIT_CODE -eq 0 ]] && EXIT_COLOR='32m' || EXIT_COLOR='31m' - [[ $SCWRYPTS_LOG_LEVEL -ge 2 ]] && echo "terminated with\\033[1;$EXIT_COLOR code $EXIT_CODE\\033[0m" + + [[ $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" -} -##################################################################### - -__CHECK_ENV_REQUIRED() { - [ $CI ] && return 1 - - echo $SCWRYPT_NAME | grep -q 'scwrypts/logs/' && return 1 - echo $SCWRYPT_NAME | grep -q 'scwrypts/environment/' && return 1 - - return 0 -} - -__VALIDATE_UPSTREAM_TIMELINE() { - STATUS "on '$ENV_NAME'; checking diff against origin/main" - - git fetch --quiet origin main - local SYNC_STATUS=$? - - git diff --exit-code origin/main -- . >&2 - local DIFF_STATUS=$? - - [[ $SYNC_STATUS -eq 0 ]] && [[ $DIFF_STATUS -eq 0 ]] && { - SUCCESS 'up-to-date with origin/main' - } || { - WARNING - [[ $SYNC_STATUS -ne 0 ]] && WARNING 'unable to synchronize with origin/main' - [[ $DIFF_STATUS -ne 0 ]] && WARNING 'your branch differs from origin/main (diff listed above)' - WARNING - - yN 'continue?' || return 1 - } -} - -__GET_LOGFILE() { - [ $SUBSCWRYPT ] && return 0 - [[ $SCWRYPTS_LOG_LEVEL -eq 0 ]] && return 0 - [[ $SCWRYPT_NAME =~ scwrypts/logs ]] && return 0 - [[ $SCWRYPT_NAME =~ interactive ]] && return 0 - - echo "$SCWRYPTS_LOG_PATH/$(echo $GROUP/$TYPE/$NAME | sed 's/^\.\///; s/\//\%/g').log" -} - -##################################################################### -__RUN $@ +} $@