#!/bin/zsh export EXECUTION_DIR=$(pwd) source "$(dirname $(readlink -f "$0"))/zsh/lib/import.driver.zsh" || exit 42 ##################################################################### () { cd "$SCWRYPTS_ROOT__scwrypts" GIT_SCWRYPTS() { git -C "$SCWRYPTS_ROOT__scwrypts" $@; } local INSTALLATION_TYPE [ ! -d "$SCWRYPTS_ROOT__scwrypts/.git" ] && [ ! -f "$SCWRYPTS_ROOT__scwrypts/.git" ] \ && INSTALLATION_TYPE=$(cat "$SCWRYPTS_ROOT__scwrypts/MANAGED_BY" 2>/dev/null) \ || INSTALLATION_TYPE=manual \ ; 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 incremental scwrypts log level to one of the following: 0 : only command output and critical failures; skips logfile 1 : include success / failure messages 2 : include status update messages 3 : (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=3 while [[ $# -gt 0 ]] do case $1 in -[a-z][a-z]* ) VARSPLIT=$(echo "$1 " | sed 's/^\(-.\)\(.*\) /\1 -\2/') set -- throw-away $(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 ) case $INSTALLATION_TYPE in manual ) echo "scwrypts $(GIT_SCWRYPTS describe --tags) (via GIT)" ;; * ) echo scwrypts $(cat "$SCWRYPTS_ROOT__scwrypts/VERSION") ;; esac return 0 ;; --update ) case $INSTALLATION_TYPE in aur ) SCWRYPTS_LOG_LEVEL=3 REMINDER " This installation is built from the AUR. Update through 'makepkg' or use your preferred AUR package management tool (e.g. 'yay -Syu scwrypts') " ;; manual ) 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 '$SCWRYPTS_ROOT__scwrypts'" } } ;; * ) SCWRYPTS_LOG_LEVEL=3 REMINDER " This is a managed installation of scwrypts. Please update through your system package manager. " ;; esac 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_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)" } [[ $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 ] && export SUBSCWRYPT=0 [[ $INSTALLATION_TYPE =~ ^manual$ ]] && { [[ $SUBSCWRYPT -eq 0 ]] && [[ $ENV_NAME =~ prod ]] && [[ $SCWRYPTS_LOG_LEVEL -gt 0 ]] && { STATUS "on '$ENV_NAME'; checking diff against origin/main" local WARNING_MESSAGE [ ! $WARNING_MESSAGE ] && { GIT_SCWRYPTS fetch --quiet origin main \ || WARNING_MESSAGE='I am unable to verify your scwrypts version' } [ ! $WARNING_MESSAGE ] && { GIT_SCWRYPTS diff --exit-code origin/main -- . >/dev/null 2>&1 \ || WARNING_MESSAGE='your scwrypts is currently out-of-date' } [ $WARNING_MESSAGE ] && { [[ $SCWRYPTS_LOG_LEVEL -lt 3 ]] && { REMINDER "you are running in ${__BRIGHT_RED}production${__BRIGHT_MAGENTA} and $WARNING_MESSAGE" } || { GIT_SCWRYPTS diff --exit-code origin/main -- . >&2 WARNING "you are trying to run in ${__BRIGHT_RED}production${__YELLOW} but $WARNING_MESSAGE (relevant diffs and errors 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 -eq 0 ]] \ && [[ ! $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 -eq 0 ]] || { 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 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" } $@