diff --git a/.config b/.config index 7fd4b61..476e7bd 100644 Binary files a/.config and b/.config differ diff --git a/.template.env b/.env.template similarity index 100% rename from .template.env rename to .env.template diff --git a/.gitattributes b/.gitattributes index febccb6..4005fb0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ *.zsh diff +.config diff diff --git a/README.md b/README.md index fd5eb70..99d027a 100644 --- a/README.md +++ b/README.md @@ -28,12 +28,9 @@ Check out [Meta Scwrypts](./zsh/scwrypts) to quickly set up environments and adj ### No Install / API Usage Alternatively, the `scwrypts` API can be used directly: ```zsh -./scwrypts (environment-name) (...script-patterns) +./scwrypts [--env environment-name] (...script-name-patterns...) [-- ...passthrough arguments... ] ``` -If not already set with `$SCWRYPTS_ENV`, Scwrypts will try to load `$1` as an environment. -If no environment with the name `$1` is found, `$1` is assumed to be a script pattern. - Given one or more script patterns, Scwrypts will filter the commands by pattern conjunction. If only one command is found which matches the pattern(s), it will immediately begin execution. If multiple commands match, the user will be prompted to select from the filtered list. @@ -44,6 +41,16 @@ Given no script patterns, Scwrypts becomes an interactive CLI, prompting the use After determining which script to run, if no environment has been specified, Scwrypts prompts the user to choose one. +### Using in CI/CD or Automated Workflows +Set environment variable `CI=true` (and use the no install method) to run in an automated pipeline. +There are a few notable changes to this runtime: +- **The Scwrypts sandbox environment will not load.** All variables will be read from context. + - The underscore-prefixed `_AWS_(PROFILE|REGION|ACCOUNT)` variables will be read from the standard `AWS_` variables +- User yes/no prompts will **always be YES** +- Other user input will default to an empty string +- Logs will not be captured + + ## Contributing Before contributing an issue, idea, or pull request, check out the [super-brief contributing guide](./docs/CONTRIBUTING.md) diff --git a/py/.gitignore b/py/.gitignore index 1b058a1..4534f5c 100644 --- a/py/.gitignore +++ b/py/.gitignore @@ -1,4 +1,3 @@ __pycache__/ *.py[cod] *.so -.env/ diff --git a/py/redis/interactive.py b/py/redis/interactive.py index aa41450..51f84cd 100755 --- a/py/redis/interactive.py +++ b/py/redis/interactive.py @@ -1,16 +1,15 @@ #!/usr/bin/env python -from os import getenv from py.redis.client import Client -from py.scwrypts import interactive +from py.scwrypts import interactive, getenv @interactive def main(): r = Client - print(''' - r = StrictRedis("{getenv("REDIS_HOST")}") + print(f''' +>>> r = StrictRedis({getenv("REDIS_HOST")}:{getenv("REDIS_PORT")}) ''') return locals() diff --git a/py/scwrypts/__init__.py b/py/scwrypts/__init__.py index aa7178d..9f5e6a0 100644 --- a/py/scwrypts/__init__.py +++ b/py/scwrypts/__init__.py @@ -1,2 +1,3 @@ from py.scwrypts.getenv import getenv from py.scwrypts.interactive import interactive +from py.scwrypts.run import run diff --git a/py/scwrypts/getenv.py b/py/scwrypts/getenv.py index 1fbbb9a..c82f512 100644 --- a/py/scwrypts/getenv.py +++ b/py/scwrypts/getenv.py @@ -1,23 +1,16 @@ from os import getenv as os_getenv -from pathlib import Path -from subprocess import run from py.scwrypts.exceptions import MissingVariableError +from py.scwrypts.run import run def getenv(name, required=True): value = os_getenv(name, None) if value == None: - ZSH_COMMAND = Path(__file__).parents[2] / 'zsh/scwrypts/environment/stage-variables' + run('zsh/scwrypts/environment/stage-variables', name) - run( - f'{ZSH_COMMAND} {name}', - shell=True, - executable='/bin/zsh', - ) - - if required: - raise MissingVariableError(name) + if required and not value: + raise MissingVariableError(name) return value diff --git a/py/scwrypts/interactive.py b/py/scwrypts/interactive.py index 259a4da..53d86fc 100644 --- a/py/scwrypts/interactive.py +++ b/py/scwrypts/interactive.py @@ -3,7 +3,9 @@ def interactive(function): def main(*args, **kwargs): + print('preparing interactive environment...') local_vars = function(*args, **kwargs) + print('environment ready; user, GO! :)') embed(local_vars) return main diff --git a/py/scwrypts/run.py b/py/scwrypts/run.py new file mode 100644 index 0000000..9bf8dd2 --- /dev/null +++ b/py/scwrypts/run.py @@ -0,0 +1,17 @@ +from os import getenv +from pathlib import Path +from subprocess import run as subprocess_run + + +def run(scwrypt_name, *args): + DEPTH = int(getenv('SUBSCWRYPT', '0')) + DEPTH += 1 + + print(f'\n {"--"*DEPTH} ({DEPTH}) BEGIN SUBSCWRYPT : {Path(scwrypt_name).name}') + subprocess_run( + f'SUBSCWRYPT={DEPTH} {Path(__file__).parents[2] / "scwrypts"} {scwrypt_name} -- {" ".join([str(x) for x in args])}', + shell=True, + executable='/bin/zsh', + ) + + print(f' {"--"*DEPTH} ({DEPTH}) END SUBSCWRYPT : {Path(scwrypt_name).name}\n') diff --git a/scwrypts b/scwrypts index 4ad5a80..a0c45c8 100755 --- a/scwrypts +++ b/scwrypts @@ -1,32 +1,117 @@ #!/bin/zsh SCWRYPTS_ROOT="${0:a:h}" -source "$SCWRYPTS_ROOT/zsh/common.zsh" +source "$SCWRYPTS_ROOT/zsh/common.zsh" || exit 42 ##################################################################### __RUN() { cd "$SCWRYPTS_ROOT" + local ENV_NAME="$SCWRYPTS_ENV" + local SEARCH_PATTERNS=() + + while [[ $# -gt 0 ]] + do + case $1 in + -e|--env ) + [ $ENV_NAME ] && __WARNING 'overwriting session environment' + ENV_NAME="$2" + __STATUS "using CLI environment '$ENV_NAME'" + shift 2 + ;; + -- ) + shift 1 + break # pass arguments after '--' to the scwrypt + ;; + * ) + SEARCH_PATTERNS+=$1 + shift 1 + ;; + esac + done + ########################################## - ### parse arguments ###################### + + local SCRIPT=$(__SELECT_SCRIPT $SEARCH_PATTERNS) + [ ! $SCRIPT ] && exit 2 + + local ENV_REQUIRED=$(__CHECK_ENV_REQUIRED && echo 1 || echo 0) + + [[ $ENV_REQUIRED -eq 1 ]] && { + [ ! $ENV_NAME ] && ENV_NAME=$(__SELECT_ENV) + local ENV_FILE=$(__GET_ENV_FILE $ENV_NAME) + + [ -f "$ENV_FILE" ] && source "$ENV_FILE" \ + || __FAIL 5 "missing or invalid environment '$ENV_NAME'" + + export ENV_NAME + } + + [ ! $SUBSCWRYPT ] \ + && [[ $ENV_NAME =~ prod ]] \ + && { __VALIDATE_UPSTREAM_TIMELINE || __ABORT; } + + local RUN_STRING=$(__GET_RUN_STRING $SCRIPT $ENV_NAME) + [ ! $RUN_STRING ] && exit 3 + + [ -f $_VIRTUALENV ] && source $_VIRTUALENV + ########################################## - local ENV_NAME - [ $SCWRYPTS_ENV ] && ENV_NAME="$SCWRYPTS_ENV" || { - [ $1 ] && [ -f $(__GET_ENV_FILE $1) ] && { - ENV_NAME="$1" - shift 1 - } + local LOGFILE=$(__GET_LOGFILE $SCRIPT) + + local HEADER=$( + [ $SUBSCWRYPT ] && return 0 + echo '=====================================================================' + echo "script : $SCRIPT" + echo "run at : $(date)" + echo "config : $ENV_NAME" + [ ! $LOGFILE ] && echo '\033[1;33m------------------------------------------\033[0m' + ) + + [ ! $LOGFILE ] && { + [ $HEADER ] && echo $HEADER + eval $RUN_STRING $@ /dev/tty 2>&1 + exit $? } + { + [ $HEADER ] && echo $HEADER + echo '\033[1;33m--- BEGIN OUTPUT -------------------------\033[0m' + eval $RUN_STRING $@ + EXIT_CODE=$? + echo '\033[1;33m--- END OUTPUT ---------------------------\033[0m' + + [[ $EXIT_CODE -eq 0 ]] && EXIT_COLOR='32m' || EXIT_COLOR='31m' + + echo "terminated with\\033[1;$EXIT_COLOR code $EXIT_CODE\\033[0m" + } 2>&1 | tee --append "$LOGFILE" + + exit $(\ + sed -n 's/^terminated with.*code \([0-9]*\).*$/\1/p' $LOGFILE \ + | tail -n1 + ) +} + +##################################################################### + +__SELECT_SCRIPT() { local SCRIPT local SCRIPTS=$(__GET_AVAILABLE_SCRIPTS) + local SEARCH=($@) + + [[ ${#SEARCH[@]} -eq 0 ]] && { + SCRIPT=$(echo $SCRIPTS | __FZF 'select a script') + } + + [[ ${#SEARCH[@]} -eq 1 ]] && [ -f ./$SEARCH ] && { + SCRIPT=$SEARCH + } - [ $1 ] && { - for PATTERN in $* + [ ! $SCRIPT ] && [[ ${#SEARCH[@]} -gt 0 ]] && { + SCRIPT=$SCRIPTS + for PATTERN in $SEARCH do - shift 1 - [[ $PATTERN =~ ^--$ ]] && break - SCRIPT=$(echo $SCRIPTS | grep $PATTERN) + SCRIPT=$(echo $SCRIPT | grep $PATTERN) done [ ! $SCRIPT ] && __FAIL 2 "no script found by name '$@'" @@ -35,108 +120,91 @@ __RUN() { __STATUS "more than one script matched '$@'" SCRIPT=$(echo $SCRIPT | __FZF 'select a script') } - true - } || SCRIPT=$(echo $SCRIPTS | __FZF 'select a script') - - [ ! $SCRIPT ] && exit 2 + } - ########################################## - ### check type and min dependencies ###### - ########################################## + echo $SCRIPT +} - local ENV_REQUIRED=1 - local RUN_STRING="./$SCRIPT" +__GET_RUN_STRING() { + local SCRIPT="$1" + local ENV_NAME="$2" local TYPE=$(echo $SCRIPT | sed 's/\/.*$//') - local VIRTUALENV="$SCWRYPTS_ROOT/$TYPE/.env/bin/activate" - [ -f $VIRTUALENV ] && source $VIRTUALENV + local RUN_STRING + + local _VIRTUALENV="$SCWRYPTS_VIRTUALENV_PATH/$TYPE/bin/activate" + [ -f $_VIRTUALENV ] && source $_VIRTUALENV case $TYPE in - py ) __CHECK_DEPENDENCY python || exit 3 + py ) __CHECK_DEPENDENCY python || return 1 + RUN_STRING="python -m $(echo $SCRIPT | sed 's/\//./g; s/\.py$//; s/\.\.//')" - python --version | grep -q '3.[91]' || { - __WARNING 'only tested on python>=3.9' + CURRENT_PYTHON_VERSION=$(python --version | sed 's/^[^0-9]*\(3\.[^.]*\).*$/\1/') + + echo $__PREFERRED_PYTHON_VERSIONS | grep -q $CURRENT_PYTHON_VERSION || { + __WARNING "only tested on the following python versions: $(printf ', %s.x' ${__PREFERRED_PYTHON_VERSIONS[@]} | sed 's/^, //')" __WARNING 'compatibility may vary' } - - RUN_STRING="python -m $(echo $SCRIPT | sed 's/\//./g; s/\.py$//; s/\.\.//')" ;; - zsh ) __CHECK_DEPENDENCY zsh || exit 3 - echo $SCRIPT | grep -q 'scwrypts' && ENV_REQUIRED=0 - + zsh ) __CHECK_DEPENDENCY zsh || return 1 RUN_STRING="./$SCRIPT" ;; - zx ) __CHECK_DEPENDENCY zx || exit 3 - + zx ) __CHECK_DEPENDENCY zx || return 1 RUN_STRING="FORCE_COLOR=3 ./$SCRIPT.mjs" ;; - * ) __FAIL 4 "unsupported script type '$SCRIPT_TYPE'" ;; + * ) __ERROR "unsupported script type '$SCRIPT_TYPE'" + return 2 + ;; esac - ########################################## - ### load scwrypts env and virtualenv ##### - ########################################## + RUN_STRING="SCWRYPTS_ENV='$ENV_NAME' $RUN_STRING" + [ -f $_VIRTUALENV ] && RUN_STRING="source '$_VIRTUALENV'; $RUN_STRING" - [[ $ENV_REQUIRED -eq 1 ]] && { - [ ! $ENV_NAME ] && ENV_NAME=$(__SELECT_ENV) - [ ! $ENV_NAME ] && __ABORT + echo $RUN_STRING +} - local ENV_FILE=$(__GET_ENV_FILE $ENV_NAME) +__CHECK_ENV_REQUIRED() { + [ $CI ] && return 1 - [ -f "$ENV_FILE" ] \ - && source "$ENV_FILE" \ - && export ENV_NAME \ - || __FAIL 5 "missing or invalid environment '$ENV_NAME'" + echo $SCRIPT | grep -q 'zsh/scwrypts/logs' && return 1 - [[ $ENV_NAME =~ prod ]] && { - __STATUS "on '$ENV_NAME'; checking diff against origin/main" - - git fetch --quiet origin main \ - && git diff --exit-code origin/main -- . >&2 \ - && __SUCCESS 'up-to-date with main!' \ - || { - __WARNING - __WARNING 'your branch differs from origin/main' - __WARNING 'in '$ENV_NAME', being out-of-sync with main may have BAD CONSEQUENCES' - __WARNING - __yN 'continue?' || __ABORT - } - } - } + return 0 +} - ########################################## - ### run the scwrypt ###################### - ########################################## +__VALIDATE_UPSTREAM_TIMELINE() { + __STATUS "on '$ENV_NAME'; checking diff against origin/main" - local HEADER=$( - echo '=====================================================================' - echo "script : $SCRIPT" - echo "run at : $(date)" - echo "config : $ENV_NAME" - echo '------------------------------------------' - ) + git fetch --quiet origin main + local SYNC_STATUS=$? + + git diff --exit-code origin/main -- . >&2 + local DIFF_STATUS=$? - echo $SCRIPT | grep -q 'interactive' && { - echo $HEADER - eval $RUN_STRING $@ /dev/tty 2>&1; exit $? + [[ $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 } +} - local LOGFILE="$SCWRYPTS_LOG_PATH/$(echo $SCRIPT | sed 's/^\.\///; s/\//\%/g').log" - [[ $SCRIPT =~ scwrypts/logs ]] && LOGFILE=/dev/null - { - echo $HEADER - echo '--- BEGIN OUTPUT--------------------------' - eval $RUN_STRING $@; local EXIT_CODE="$?" - echo '--- END OUTPUT ---------------------------' +__GET_LOGFILE() { + local SCRIPT="$1" - local C - [[ $EXIT_CODE -eq 0 ]] && C='32m' || C='31m'; + [ $CI ] \ + || [ $SUBSCWRYPT ] \ + || [[ $SCRIPT =~ scwrypts/logs ]] \ + || [[ $SCRIPT =~ interactive ]] \ + && return 0 - echo "terminated with\\033[1;$C code $EXIT_CODE\\033[0m" - } 2>&1 | tee --append "$LOGFILE" + echo "$SCWRYPTS_LOG_PATH/$(echo $SCRIPT | sed 's/^\.\///; s/\//\%/g').log" } ##################################################################### diff --git a/scwrypts.plugin.zsh b/scwrypts.plugin.zsh index 298db43..af4a44c 100644 Binary files a/scwrypts.plugin.zsh and b/scwrypts.plugin.zsh differ diff --git a/zsh/README.md b/zsh/README.md index 49a4000..7690c07 100644 --- a/zsh/README.md +++ b/zsh/README.md @@ -4,6 +4,7 @@ [![Generic Badge](https://img.shields.io/badge/junegunn-fzf-informational.svg)](https://github.com/junegunn/fzf) [![Generic Badge](https://img.shields.io/badge/mikefarah-yq-informational.svg)](https://github.com/mikefarah/yq) [![Generic Badge](https://img.shields.io/badge/stedolan-jq-informational.svg)](https://github.com/stedolan/jq) +[![Generic Badge](https://img.shields.io/badge/dbcli-pgcli-informational.svg)](https://github.com/dbcli/pgcli)
Since they emulate direct user interaction, shell scripts are often the straightforward choice for task automation. diff --git a/zsh/aws/rds/common.zsh b/zsh/aws/rds/common.zsh index 98e14ea..f4e44c4 100644 --- a/zsh/aws/rds/common.zsh +++ b/zsh/aws/rds/common.zsh @@ -1,6 +1,22 @@ -_DEPENDENCIES+=( - psql -) +_DEPENDENCIES+=() _REQUIRED_ENV+=() source ${0:a:h}/../common.zsh ##################################################################### + +__SELECT_CONNECTOR() { + local DB_TYPE="$1" + + CLIENTS_postgresql=(pgcli psql) + + local C CLIENT=none + for C in $(eval 'echo $CLIENTS_'$DB_TYPE) + do + __CHECK_DEPENDENCY $C >/dev/null 2>&1 && { + CLIENT=$C + __STATUS "detected '$CLIENT' for $DB_TYPE" + break + } + done + + echo $CLIENT +} diff --git a/zsh/aws/rds/interactive-login b/zsh/aws/rds/interactive-login new file mode 100755 index 0000000..88bbea0 --- /dev/null +++ b/zsh/aws/rds/interactive-login @@ -0,0 +1,121 @@ +#!/bin/zsh +_DEPENDENCIES+=() +_REQUIRED_ENV+=() +source ${0:a:h}/common.zsh +##################################################################### + +__CONNECT_TO_RDS() { + local DATABASE=$(__SELECT_DATABASE) + [ ! $DATABASE ] && __ABORT + + local DB_HOST DB_USER DB_PORT DB_NAME DB_AUTH DB_TYPE + + DB_HOST=$(echo $DATABASE | jq -r '.host') + DB_USER=$(echo $DATABASE | jq -r '.user') + DB_PORT=$(echo $DATABASE | jq -r '.port') + DB_TYPE=$(echo $DATABASE | jq -r '.type') + + [[ $DB_PORT =~ null ]] && DB_PORT=5432 + DB_NAME=postgres + + local AUTH_METHODS=(iam secretsmanager user-input) + local AUTH_METHOD=$(\ + echo $AUTH_METHODS | sed 's/\s\+/\n/g' \ + | __FZF 'select an authentication method' \ + ) + + [ ! $AUTH_METHOD ] && __ABORT + + case $AUTH_METHOD in + iam ) + DB_AUTH=$(\ + _AWS rds generate-db-auth-token \ + --hostname $DB_HOST \ + --port $DB_PORT \ + --username $DB_USER \ + ) + ;; + secretsmanager ) + CREDENTIALS=$(__GET_SECRETSMANAGER_CREDENTIALS) + echo $CREDENTIALS | jq -e '.pass' >/dev/null 2>&1 \ + && DB_AUTH=$(echo $CREDENTIALS | jq -r '.pass') + + echo $CREDENTIALS | jq -e '.password' >/dev/null 2>&1 \ + && DB_AUTH=$(echo $CREDENTIALS | jq -r '.password') + + echo $CREDENTIALS | jq -e '.user' >/dev/null 2>&1 \ + && DB_USER=$(echo $CREDENTIALS | jq -r '.user') + + echo $CREDENTIALS | jq -e '.username' >/dev/null 2>&1 \ + && DB_USER=$(echo $CREDENTIALS | jq -r '.username') + + echo $CREDENTIALS | jq -e '.name' >/dev/null 2>&1 \ + && DB_NAME=$(echo $CREDENTIALS | jq -r '.name') + + echo $CREDENTIALS | jq -e '.dbname' >/dev/null 2>&1 \ + && DB_NAME=$(echo $CREDENTIALS | jq -r '.dbname') + ;; + user-input ) + ;; + esac + + __STATUS + __STATUS "host : $DB_HOST" + __STATUS "type : $DB_TYPE" + __STATUS "port : $DB_PORT" + __STATUS "database : $DB_NAME" + __STATUS "username : $DB_USER" + __STATUS + + __RUN_SCWRYPT 'zsh/db/interactive/postgres' -- \ + --host $DB_HOST \ + --port $DB_PORT \ + --name $DB_NAME \ + --user $DB_USER \ + --pass $DB_AUTH \ + ; +} + +__SELECT_DATABASE() { + local DATABASES=$(__GET_AVAILABLE_DATABASES) + [ ! $DATABASES ] && __FAIL 1 'no databases available' + + local ID=$(\ + echo $DATABASES | jq -r '.instance + " @ " + .cluster' \ + | __FZF 'select a database (instance@cluster)' \ + ) + [ ! $ID ] && __ABORT + + local INSTANCE=$(echo $ID | sed 's/ @ .*$//') + local CLUSTER=$(echo $ID | sed 's/^.* @ //') + + echo $DATABASES | jq "select (.instance == \"$INSTANCE\" and .cluster == \"$CLUSTER\")" +} + +__GET_AVAILABLE_DATABASES() { + _AWS rds describe-db-instances \ + | jq -r '.[] | .[] | { + instance: .DBInstanceIdentifier, + cluster: .DBClusterIdentifier, + type: .Engine, + host: .Endpoint.Address, + port: .Endpoint.Port, + user: .MasterUsername, + database: .DBName + }' +} + +__GET_SECRETSMANAGER_CREDENTIALS() { + local ID=$(\ + _AWS secretsmanager list-secrets \ + | jq -r '.[] | .[] | .Name' \ + | __FZF 'select a secret' \ + ) + [ ! $ID ] && return 1 + + _AWS secretsmanager get-secret-value --secret-id "$ID" \ + | jq -r '.SecretString' | jq +} + +##################################################################### +__CONNECT_TO_RDS diff --git a/zsh/common.zsh b/zsh/common.zsh index f1c2b85..9fabd01 100644 --- a/zsh/common.zsh +++ b/zsh/common.zsh @@ -2,25 +2,20 @@ [ ! $SCWRYPTS_ROOT ] && SCWRYPTS_ROOT="$(dirname ${0:a:h})" -source $SCWRYPTS_ROOT/.config -[ -f $SCWRYPTS_CONFIG_PATH/config ] && source $SCWRYPTS_CONFIG_PATH/config - -[ ! -d $SCWRYPTS_CONFIG_PATH ] && mkdir -p $SCWRYPTS_CONFIG_PATH -[ ! -d $SCWRYPTS_ENV_PATH ] && mkdir -p $SCWRYPTS_ENV_PATH -[ ! -d $SCWRYPTS_LOG_PATH ] && mkdir -p $SCWRYPTS_LOG_PATH - __PREFERRED_PYTHON_VERSIONS=(3.10 3.9) __NODE_VERSION=18.0.0 +__ENV_TEMPLATE=$SCWRYPTS_ROOT/.env.template -##################################################################### +__SCWRYPT=1 -source ${0:a:h}/utils/utils.zsh +source $SCWRYPTS_ROOT/.config +source ${0:a:h}/utils/utils.module.zsh || { + [ $DONT_EXIT ] && return 1 || exit 1 +} ##################################################################### -__ENV_TEMPLATE=$SCWRYPTS_ROOT/.template.env - -__GET_ENV_FILES() { find $SCWRYPTS_CONFIG_PATH/env -maxdepth 1 -type f; } +__GET_ENV_FILES() { find $SCWRYPTS_CONFIG_PATH/env -maxdepth 1 -type f | sort -r } [ ! "$(__GET_ENV_FILES)" ] && { cp $__ENV_TEMPLATE "$SCWRYPTS_CONFIG_PATH/env/dev" cp $__ENV_TEMPLATE "$SCWRYPTS_CONFIG_PATH/env/local" @@ -42,3 +37,22 @@ __GET_AVAILABLE_SCRIPTS() { | sed 's/^\.\///; s/\.[^.]*$//' \ ; } + +##################################################################### + +__RUN_SCWRYPT() { + # run a scwrypt inside a scwrypt w/stack-depth indicators + ((SUBSCWRYPT+=1)) + printf ' '; printf '--%.0s' {1..$SUBSCWRYPT}; printf " ($SUBSCWRYPT) " + echo " BEGIN SUBSCWRYPT : $(basename $1)" + + SUBSCWRYPT=$SUBSCWRYPT SCWRYPTS_ENV=$ENV_NAME \ + "$SCWRYPTS_ROOT/scwrypts" $@ + EXIT_CODE=$? + + printf ' '; printf '--%.0s' {1..$SUBSCWRYPT}; printf " ($SUBSCWRYPT) " + echo " END SUBSCWRYPT : $(basename $1)" + ((SUBSCWRYPT-=1)) + + return $EXIT_CODE +} diff --git a/zsh/db/common.zsh b/zsh/db/common.zsh new file mode 100644 index 0000000..1191a72 --- /dev/null +++ b/zsh/db/common.zsh @@ -0,0 +1,4 @@ +_DEPENDENCIES+=() +_REQUIRED_ENV+=() +source ${0:a:h}/../common.zsh +##################################################################### diff --git a/zsh/db/interactive/common.zsh b/zsh/db/interactive/common.zsh new file mode 100644 index 0000000..1191a72 --- /dev/null +++ b/zsh/db/interactive/common.zsh @@ -0,0 +1,4 @@ +_DEPENDENCIES+=() +_REQUIRED_ENV+=() +source ${0:a:h}/../common.zsh +##################################################################### diff --git a/zsh/db/interactive/postgres b/zsh/db/interactive/postgres new file mode 100755 index 0000000..2333492 --- /dev/null +++ b/zsh/db/interactive/postgres @@ -0,0 +1,45 @@ +#!/bin/zsh +_DEPENDENCIES+=( + pgcli +) +_REQUIRED_ENV+=() +source ${0:a:h}/common.zsh +##################################################################### + +_LOGIN_POSTGRES() { + local _HOST _NAME _PASS _PORT _USER + + while [[ $# -gt 0 ]] + do + case $1 in + --host | -h ) _HOST="$2"; shift 2 ;; + --name | -d ) _NAME="$2"; shift 2 ;; + --pass | -w ) _PASS="$2"; shift 2 ;; + --port | -p ) _PORT="$2"; shift 2 ;; + --user | -U ) _USER="$2"; shift 2 ;; + * ) shift 1 ;; + esac + done + + [ ! $_HOST ] && _HOST=127.0.0.1 + [ ! $_NAME ] && _NAME=postgres + [ ! $_PORT ] && _PORT=5432 + [ ! $_USER ] && _USER=postgres + + local DATA_DIR="$SCWRYPTS_DATA_PATH/db/$_HOST" + [ ! -d $DATA_DIR ] && mkdir -p $DATA_DIR + cd $DATA_DIR + + __STATUS "performing login : $_USER@$_HOST:$_PORT/$_NAME" + __STATUS "working directory : $DATA_DIR" + + PGPASSWORD="$_PASS" pgcli \ + --host $_HOST \ + --port $_PORT \ + --user $_USER \ + --dbname $_NAME \ + ; +} + +##################################################################### +_LOGIN_POSTGRES $@ diff --git a/zsh/scwrypts/README.md b/zsh/scwrypts/README.md index edb947c..0879d88 100644 --- a/zsh/scwrypts/README.md +++ b/zsh/scwrypts/README.md @@ -15,12 +15,21 @@ This will immediately open your custom configuration file and reload any necessa If you use Scwrypts, **you should use these commands all the time**. This is your gateway to managing scwrypts sandboxed environments. -Command | Description -------------- | --------------------------------------------------------------------------------------- -`edit` | edit an existing environment; synchronizes environments if new variables are added -`copy` | copy an existing environment to a new one -`delete` | permanently delete an environment by name -`synchronize` | uses [template](../../.template.env) to add missing and remove extemporaneous variables +Command | Description +----------------- | --------------------------------------------------------------------------------------- +`edit` | edit an existing environment +`copy` | create and edit a new environment from an existing one +`delete` | permanently delete an environment by name +`stage-variables` | stage missing variables; [helpful for non-ZSH scwrypts](../../py/scwrypts/getenv.py) +`synchronize` | uses [template](../../.env.template) to add missing and remove extemporaneous variables + +### Environment Inheritance +You can make a child environment by naming an environment `.`. +Children inherit all parent-set values, and **parent-set values overwrite child-set values**. +Remember that synchronize runs *every time you edit an environment*, so changes propagate to children immediately. +Inherited values are denoted by `# inherited from ` in the environment file. + +Nested children will inherit values from all parents. ## Logs Quickly view or clear Scwrypts logs. diff --git a/zsh/scwrypts/configure b/zsh/scwrypts/configure index fcd68f1..af30673 100755 --- a/zsh/scwrypts/configure +++ b/zsh/scwrypts/configure @@ -6,12 +6,20 @@ source ${0:a:h}/common.zsh [ ! -f $SCWRYPTS_CONFIG_PATH/config ] && { __STATUS 'first-time setup detected; creating local configuration override...' - cp $SCWRYPTS_ROOT/.config $SCWRYPTS_CONFIG_PATH/config \ + touch $SCWRYPTS_CONFIG_PATH/config \ && __SUCCESS 'created!' \ || __FAIL 1 "unable to create config at '$SCWRYPTS_CONFIG_PATH/config'" + { + echo '#' + echo '# configuration for scwrypts' + echo '#' + sed -n '1d; /^###/q; p' $SCWRYPTS_ROOT/.config | sed '$d' + } > $SCWRYPTS_CONFIG_PATH/config + + __EDIT $SCWRYPTS_CONFIG_PATH/config __STATUS 'attempting to build virtual environments' - $SCWRYPTS_ROOT/zsh/scwrypts/virutalenv/update-all \ + __RUN_SCWRYPT zsh/scwrypts/virtualenv/update-all \ && __SUCCESS 'finished updating virtualenvs' \ || __WARNING 'unable to create one or more virtualenv (see above)' \ ; @@ -20,16 +28,12 @@ source ${0:a:h}/common.zsh __REMINDER 'use "zsh/scwrypts/virtualenv/update-all" to update environments' __REMINDER '(equivalent to "npm install" or "pip install -r requirements.txt")' __REMINDER +} || { + __STATUS 'opening local config for editing' + __EDIT $SCWRYPTS_CONFIG_PATH/config + __STATUS 'finished editing!' } -__STATUS 'opening local config for editing' -__EDIT $SCWRYPTS_CONFIG_PATH/config -__STATUS 'finished editing!' - -which __SCWRYPTS >/dev/null 2>&1 && { - __STATUS 'reloading configuration for current session' - source $SCWRYPTS_ROOT/zsh/common.zsh \ - || __FAIL 2 'unable to reload configuration :c' -} __SUCCESS 'saved new configuration' +__REMINDER 'changes which affect the hot-key plugin will require a ZSHRC reload' diff --git a/zsh/scwrypts/environment/common.zsh b/zsh/scwrypts/environment/common.zsh index 1191a72..8cf1c5e 100644 --- a/zsh/scwrypts/environment/common.zsh +++ b/zsh/scwrypts/environment/common.zsh @@ -2,3 +2,10 @@ _DEPENDENCIES+=() _REQUIRED_ENV+=() source ${0:a:h}/../common.zsh ##################################################################### + +_SORT_ENV() { + local ENV_FILE="$1" + + sed -i "s/^[A-Z]/export &/; s/^[^#=]\\+$/&=/" "$ENV_FILE" + LC_COLLATE=C sort -uo "$ENV_FILE" "$ENV_FILE" +} diff --git a/zsh/scwrypts/environment/copy b/zsh/scwrypts/environment/copy index 7587f5e..2b0fb99 100755 --- a/zsh/scwrypts/environment/copy +++ b/zsh/scwrypts/environment/copy @@ -11,7 +11,7 @@ TEMPLATE_ENV_NAME=$(__SELECT_ENV) __STATUS "selected '$TEMPLATE_ENV_NAME'" __PROMPT 'enter new environment name' -ENV_NAME=$(__FZF_HEAD 'new environment') +ENV_NAME=$(echo '' | __FZF_HEAD 'new environment') [ ! $ENV_NAME ] && __ABORT TEMPLATE_ENV_FILE=$(__GET_ENV_FILE $TEMPLATE_ENV_NAME) @@ -19,7 +19,19 @@ ENV_FILE=$(__GET_ENV_FILE $ENV_NAME) [ -f "$ENV_FILE" ] && __FAIL 2 "'$ENV_NAME' already exists" -__STATUS "creating environment" +__STATUS "creating environment '$ENV_NAME'" cp "$TEMPLATE_ENV_FILE" "$ENV_FILE" \ && __SUCCESS "created '$ENV_NAME'" \ || __FAIL 3 "unable to create '$ENV_NAME'" + +__STATUS 'stripping inherited values' +sed -i 's/ # inherited from.*$//' "$ENV_FILE" 2>/dev/null + +__RUN_SCWRYPT zsh/scwrypts/environment/synchronize -- --no-prompt \ + || __FAIL 4 'failed to run environment sync' + +__RUN_SCWRYPT zsh/scwrypts/environment/edit -- $ENV_NAME \ + || __FAIL 4 'failed to edit new environment' + ; + +__SUCCESS "finished copy environment '$TEMPLATE_ENV_NAME > $ENV_NAME'" diff --git a/zsh/scwrypts/environment/edit b/zsh/scwrypts/environment/edit index 3b2d2af..b711710 100755 --- a/zsh/scwrypts/environment/edit +++ b/zsh/scwrypts/environment/edit @@ -4,9 +4,13 @@ _REQUIRED_ENV+=() source ${0:a:h}/common.zsh ##################################################################### -[ $SCWRYPTS_ENV ] \ - && ENV_NAME=$SCWRYPTS_ENV \ - || ENV_NAME=$(__SELECT_OR_CREATE_ENV) +[ $1 ] && ENV_NAME="$1" + +[ ! $1 ] && { + [ $SCWRYPTS_ENV ] \ + && ENV_NAME=$SCWRYPTS_ENV \ + || ENV_NAME=$(__SELECT_OR_CREATE_ENV) +} [ ! $ENV_NAME ] && __ABORT ENV_FILE=$(__GET_ENV_FILE $ENV_NAME) @@ -33,18 +37,8 @@ do } done < $ENV_FILE -[ $NEW_VAR ] && { - LC_COLLATE=C sort -uo $__ENV_TEMPLATE $__ENV_TEMPLATE - NOPROMPT=1 ${0:a:h}/synchronize - git add $__ENV_TEMPLATE \ - && __SUCCESS "auto-staged the $(basename $__ENV_TEMPLATE) changes" \ - || { - __WARNING "unable to stage $(basename $__ENV_TEMPLATE) changes" - __REMINDER "don't forget to commit changes to $(basename $__ENV_TEMPLATE)" - } - true -} || { - __STATUS 'no new environment variables' -} +__RUN_SCWRYPT zsh/scwrypts/environment/synchronize -- --no-prompt \ + || __FAIL 4 'failed to run environment sync' \ + ; __SUCCESS "environment '$ENV_NAME' successfully modified" diff --git a/zsh/scwrypts/environment/stage-variables b/zsh/scwrypts/environment/stage-variables index 455eba0..afc38e0 100755 --- a/zsh/scwrypts/environment/stage-variables +++ b/zsh/scwrypts/environment/stage-variables @@ -4,4 +4,4 @@ _REQUIRED_ENV+=() source ${0:a:h}/common.zsh ##################################################################### -__CHECK_ENV_VARS $@ || NOPROMPT=1 $SCWRYPTS_ROOT/zsh/scwrypts/environment/synchronize +__CHECK_REQUIRED_ENV $@ diff --git a/zsh/scwrypts/environment/synchronize b/zsh/scwrypts/environment/synchronize index e0538d5..3f36ae0 100755 --- a/zsh/scwrypts/environment/synchronize +++ b/zsh/scwrypts/environment/synchronize @@ -1,47 +1,130 @@ -#!/bin/zsh +#!/bin/zsh _DEPENDENCIES+=() _REQUIRED_ENV+=() source ${0:a:h}/common.zsh ##################################################################### -[ ! $NOPROMPT ] && { - __yN 'change the template before sync?' && __EDIT $__ENV_TEMPLATE - sed -i "s/^[A-Z]/export &/; s/^[^#=]\\+$/&=/; s/=.*$/=/" $__ENV_TEMPLATE - LC_COLLATE=C sort -uo $__ENV_TEMPLATE $__ENV_TEMPLATE - git add $__ENV_TEMPLATE >/dev/null 2>&1 +_SYNCHRONIZE() { + while [[ $# -gt 0 ]] + do + case $1 in + --no-prompt ) SLIENT=1; shift 1 ;; + + * ) __WARNING "argument '$1' not recognized" + shift 1 ;; + esac + done + + [ ! $SLIENT ] && { + __yN 'change the template before sync?' && __EDIT $__ENV_TEMPLATE + _SORT_ENV "$__ENV_TEMPLATE" + git add $__ENV_TEMPLATE >/dev/null 2>&1 + } + + ENVIRONMENTS=$(__GET_ENV_NAMES | sort -r) + + _CLEAR_INHERITED_VARIABLES + _INSERT_NEW_VARIABLES + _REMOVE_OLD_VARIABLES + _SORT_AND_CASCADE + + __SUCCESS 'finished sync!' +} + +##################################################################### + +_CLEAR_INHERITED_VARIABLES() { + for ENV_NAME in $(echo $ENVIRONMENTS) + do + ENV_FILE=$(__GET_ENV_FILE $ENV_NAME) + sed -i 's/ # inherited from.*//' "$ENV_FILE" + done +} + +_INSERT_NEW_VARIABLES() { + __STATUS 'inserting new environment variables...' + + local ENV_NAME ENV_FILE line + while read line + do + for ENV_NAME in $(echo $ENVIRONMENTS) + do + ENV_FILE=$(__GET_ENV_FILE $ENV_NAME) + grep -q "$line" $ENV_FILE || { + echo $line >> $ENV_FILE && __STATUS "added '$line' to '$ENV_NAME'" + } + done + done < <(sed -n '/^./p' "$__ENV_TEMPLATE") +} + +_REMOVE_OLD_VARIABLES() { + __STATUS 'removing old environment variables...' + + local ENV_NAME ENV_FILE line + for ENV_NAME in $(echo $ENVIRONMENTS) + do + ENV_FILE=$(__GET_ENV_FILE $ENV_NAME) + while read line + do + ENV_VAR=$(echo "$line" | sed 's/=.*/=/') + grep -q "$ENV_VAR" "$__ENV_TEMPLATE" || { + sed -i "\\%$ENV_VAR%d" "$ENV_FILE" + echo "$ENV_VAR" | grep -qv '^#' \ + && __WARNING "removed unwanted '$ENV_VAR' from '$ENV_NAME'" + } + done < $ENV_FILE + done } -ENVIRONMENTS=$(__GET_ENV_FILES) - -__STATUS 'inserting new environment variables...' -while read line -do - for ENV_FILE in $(echo $ENVIRONMENTS) - do - grep -q "^$line" $ENV_FILE || { - echo $line >> $ENV_FILE && __STATUS "added '$line' to '$ENV_FILE'" - } - done -done < <(sed -n '/^./p' $__ENV_TEMPLATE) - -__STATUS 'removing old environment variables...' -for ENV_FILE in $(echo $ENVIRONMENTS) -do - while read line - do - ENV_VAR=$(echo "$line" | sed 's/=.*/=/') - grep -q "$ENV_VAR" $__ENV_TEMPLATE || { - sed -i "\\%$ENV_VAR%d" $ENV_FILE - __WARNING "removed unwanted '$ENV_VAR' from '$ENV_FILE'" - } - done < $ENV_FILE -done - -for ENV_FILE in $(echo $ENVIRONMENTS) -do - sed -i "s/^[A-Z]/export &/; s/^[^#=]\\+$/&=/" $ENV_FILE - LC_COLLATE=C sort -uo $ENV_FILE $ENV_FILE -done -LC_COLLATE=C sort -uo $__ENV_TEMPLATE $__ENV_TEMPLATE - -__SUCCESS 'finished sync!' +_SORT_AND_CASCADE() { + local ENV_NAM ENV_FILE + + for ENV_NAME in $(echo $ENVIRONMENTS) + do + ENV_FILE=$(__GET_ENV_FILE $ENV_NAME) + _CASCADE_ENVIRONMENT $ENV_NAME + done + + for ENV_NAME in $(echo $ENVIRONMENTS) + do + ENV_FILE=$(__GET_ENV_FILE $ENV_NAME) + _SORT_ENV "$ENV_FILE" + done +} + +_CASCADE_ENVIRONMENT() { + local PARENT_NAME="$1" + local PARENT_FILE=$(__GET_ENV_FILE $PARENT_NAME) + + local CHILD_NAMES=$(echo $ENVIRONMENTS | grep "^$PARENT_NAME\\.") + [ ! $CHILD_NAMES ] && return 0 + + __STATUS "cascading '$PARENT_NAME' to children" + for CHILD_NAME in $(echo $CHILD_NAMES) + do + __SUCCESS "detected child '$CHILD_NAME'" + done + + local PARENT_VAR VAR_PATTERN CHILD_NAME CHILD_FILE + + while read PARENT_VAR + do + VAR_PATTERN=$(echo "$PARENT_VAR" | sed 's/=.*/=/; s/\//\/\//g') + __STATUS "propagating '$(echo $VAR_PATTERN | sed 's/^export \([^=]*\)=/\1/')' to children" + + PARENT_VAR+=" # inherited from $PARENT_NAME" + + for CHILD_NAME in $(echo $CHILD_NAMES) + do + CHILD_FILE=$(__GET_ENV_FILE $CHILD_NAME) + + sed -i "/^$VAR_PATTERN/d" "$CHILD_FILE" + echo $PARENT_VAR >> "$CHILD_FILE" + done + done < <(sed -n '/^[^#][^=]*=[^#]\+$/p' "$PARENT_FILE") + + __SUCCESS "finished '$PARENT_NAME' propagation" +} + +##################################################################### +_SYNCHRONIZE $@ diff --git a/zsh/scwrypts/logs/view b/zsh/scwrypts/logs/view index e251a31..a39ffcf 100755 --- a/zsh/scwrypts/logs/view +++ b/zsh/scwrypts/logs/view @@ -6,7 +6,7 @@ source ${0:a:h}/common.zsh cd $SCWRYPTS_ROOT __PROMPT 'select a script log' -LOG_FILE=$(ls $SCWRYPTS_LOG_PATH | __FZF 'logfile') +LOG_FILE=$(ls -t $SCWRYPTS_LOG_PATH | __FZF 'logfile') [ ! $LOG_FILE ] && { __ERROR 'user abort'; exit 1; } __STATUS 'opening logfile' diff --git a/zsh/scwrypts/virtualenv/common.zsh b/zsh/scwrypts/virtualenv/common.zsh index cbb459e..55fdcf4 100644 --- a/zsh/scwrypts/virtualenv/common.zsh +++ b/zsh/scwrypts/virtualenv/common.zsh @@ -42,11 +42,11 @@ __UPDATE_VIRTUALENV() { return 1 } - cd $VIRTUALENV_PATH/../ + cd $SCWRYPTS_ROOT local UPDATE_CODE=0 case $TYPE in - python ) pip install -r requirements.txt; UPDATE_CODE=$? ;; - node ) npm install ; + python ) cd py; pip install -r requirements.txt; UPDATE_CODE=$? ;; + node ) cd zx; npm install ;; esac UPDATE_CODE=$? [[ $UPDATE_CODE -eq 0 ]] \ @@ -78,8 +78,8 @@ __DELETE_VIRTUALENV() { __GET_VIRTUALENV_PATH() { local TYPE="$1" case $TYPE in - python ) echo "$SCWRYPTS_ROOT/py/.env" ;; - node ) echo "$SCWRYPTS_ROOT/zx/.env" ;; + python ) echo "$SCWRYPTS_VIRTUALENV_PATH/py" ;; + node ) echo "$SCWRYPTS_VIRTUALENV_PATH/zx" ;; esac } diff --git a/zsh/utils/README.md b/zsh/utils/README.md new file mode 100644 index 0000000..4c73d64 --- /dev/null +++ b/zsh/utils/README.md @@ -0,0 +1,71 @@ +# ZSH Utilities + +A shell-scripting utilities module made for ZSH. +This module is definitely a major component of Scwrypts, but is also standalone and can be sourced by any ZSH script to utilize (almost) all of the features. + +## Usage +Import `utils.module.zsh` to activate all of the features. +Doing so will *also* check for path dependencies and required environment variables (see [Dependencies](#dependencies) and [Environment](#environment) below). + + +```shell +#!/bin/zsh +source ./path/to/utils.plugin.zsh +__SUCCESS 'ZSH utilities online!' +``` + +Checkout [io](./io.zsh) and [os](./os.zsh) for available simple functions. + +### Dependencies +Ensures dependent programs are available for execution. +Specify a simple name to check the current `PATH`, or give a fully-qualified path for arbitrary dependency inclusion. + +Include a dependency by adding to the `_DEPENDENCIES` array. +*Always using `+=` makes your dependencies extensible to other scripts :)* + +If any dependencies are missing, `source utils.module.zsh` will return an error code and count the number of missing dependencies in the variable `DEP_ERROR_COUNT`. + +```shell +#!/bin/zsh +_DEPENDENCIES+=( + path-executable-1 + path-executable-2 + /path/to/arbitrary/program +) +source ./path/to/utils.plugin.zsh +echo "missing $DEP_ERROR required dependencies" +``` + +### Environment +Similar to [Dependencies](#dependencies), `environment.zsh` ensures a list of environment variables are *set to non-empty values*. + +Include an environment variable by adding to the `_REQUIRED_ENV` array. +*Something something use `+=` here too ;)* + +If any environment variables are missing, `source utils.module.zsh` will return an error code and count the number of missing variables in `ENV_ERROR_COUNT`. + +Missing environment variables will be added to the environment template (*exclusive to Scwrypts*). + +```shell +#!/bin/zsh +_REQUIRED_ENV+=( + AWS_PROFILE + AWS_REGION +) +source ./path/to/utils.plugin.zsh +echo "missing $ENV_ERROR_COUNT required environment variables" +``` + +io.zsh +os.zsh + +## Basic Utilities + +One of my biggest pet-peeves with scripting is when every line of a *(insert-language-here)* program is escaped to shell. +This kind of program, which doesn't use language features, should be a shell script. +While there are definitely unavoidable limitations to shell scripting, we can minimize a variety of problems with a modern shell and shared utilities library. + +Loaded by `common.zsh`, the [`utils/` library](./utils) provides: +- common function wrappers to unify flags and context +- lazy dependency and environment variable validation +- consistent (and pretty) user input / output diff --git a/zsh/utils/credits.zsh b/zsh/utils/credits.zsh index 8e1a379..afdc9de 100644 --- a/zsh/utils/credits.zsh +++ b/zsh/utils/credits.zsh @@ -1,5 +1,5 @@ __CREDITS() { - # only applicable within scwrypts ("credits" pulled from README files) + # scwrypts exclusive ("credits" pulled from README files) [ ! $SCWRYPTS_ROOT ] && return 0 local COMMAND="$1" diff --git a/zsh/utils/environment.zsh b/zsh/utils/environment.zsh index 73f67a5..8a7b0e5 100644 --- a/zsh/utils/environment.zsh +++ b/zsh/utils/environment.zsh @@ -1,38 +1,37 @@ __CHECK_REQUIRED_ENV() { local VAR ERROR=0 - for VAR in $*; do __CHECK_ENV_VAR $VAR_NAME || ((ERROR+=1)); done + for VAR in $*; do __CHECK_ENV_VAR $VAR || ((ERROR+=1)); done return $ERROR } __CHECK_ENV_VAR() { local NAME="$1" + [ ! $NAME ] && return 1 + local OPTIONAL="$2" local DEFAULT_VALUE="$3" local VALUE=$(eval echo '$'$NAME) [ $VALUE ] && return 0 - local LINE="export $NAME=" - local TEMPLATE="$SCWRYPTS_ROOT/.template.env" - grep -q -- "^$LINE" "$TEMPLATE" || { - __STATUS 'staging new variable in template' + [ $__SCWRYPT ] && { + # scwrypts exclusive (missing vars staged in env.template) + local LINE="export $NAME=" + + grep -q -- "^$LINE" "$__ENV_TEMPLATE" || { + __STATUS 'staging new variable in template' - echo "$LINE" >> "$TEMPLATE" \ - && NOPROMPT=1 $SCWRYPTS_ROOT/zsh/scwrypts/environment/synchronize \ - && git add $TEMPLATE >/dev/null 2>&1 \ - && __SUCCESS "staged '$NAME'" \ - || { - __WARNING "failed to stage '$NAME'" - __REMINDER "add/commit '$NAME' to template manually" - } + echo "$LINE" >> "$__ENV_TEMPLATE" \ + && __RUN_SCWRYPT zsh/scwrypts/environment/synchronize -- --no-prompt + } } [ $OPTIONAL ] && { - __ERROR "'$NAME' required" - return 1 - } || { [ $DEFAULT_VALUE ] && $NAME="$DEFAULT_VALUE" return 0 + } || { + __ERROR "'$NAME' required" + return 1 } } diff --git a/zsh/utils/io.zsh b/zsh/utils/io.zsh index f29fe62..a14e69f 100644 --- a/zsh/utils/io.zsh +++ b/zsh/utils/io.zsh @@ -3,6 +3,7 @@ __SUCCESS() { echo "\\033[1;32mSUCCESS ✔ : $@\\033[0m" >&2; } __WARNING() { echo "\\033[1;33mWARNING  : $@\\033[0m" >&2; } __STATUS() { echo "\\033[1;34mSTATUS : $@\\033[0m" >&2; } __REMINDER() { echo "\\033[1;35mREMINDER  : $@\\033[0m" >&2; } + __PROMPT() { echo "\\033[1;36mPROMPT  : $@\\033[0m" >&2 printf "\\033[1;36mUSER  : \\033[0m" >&2 @@ -10,12 +11,16 @@ __PROMPT() { __Yn() { __PROMPT "$@ [Yn]" + [ $CI ] && { echo y; return 0; } + local Yn; __READ -k Yn; echo [[ $Yn =~ [nN] ]] && return 1 || return 0 } __yN() { __PROMPT "$@ [yN]" + [ $CI ] && { echo y; return 0; } + local yN; __READ -k yN; echo [[ $yN =~ [yY] ]] && return 0 || return 1 } @@ -35,10 +40,29 @@ __GETSUDO() { __LESS() { less -R $@ /dev/tty; } -__FZF() { fzf -i --height=30% --layout=reverse --prompt "$@ : "; } -__FZF_HEAD() { fzf -i --height=30% --layout=reverse --print-query --prompt "$@ : " | head -n1; } -__FZF_TAIL() { fzf -i --height=30% --layout=reverse --print-query --prompt "$@ : " | tail -n1; } +__FZF() { + [ $CI ] && { + __ERROR 'currently in CI, but __FZF requires user input' + exit 1 + } -__READ() { read $@ /dev/tty; } +__READ() { + [ $CI ] && { + __ERROR 'currently in CI, but __READ explicitly requires terminal input' + return 1 + } + read $@ /dev/tty +} diff --git a/zsh/utils/utils.zsh b/zsh/utils/utils.module.zsh similarity index 54% rename from zsh/utils/utils.zsh rename to zsh/utils/utils.module.zsh index 4518b93..eabec20 100644 --- a/zsh/utils/utils.zsh +++ b/zsh/utils/utils.module.zsh @@ -13,17 +13,33 @@ source ${0:a:h}/credits.zsh IMPORT_ERROR=0 +[ $CI ] && { + export _AWS_PROFILE="$AWS_PROFILE" + export _AWS_ACCOUNT="$AWS_ACCOUNT" + export _AWS_REGION="$AWS_REGION" +} + source ${0:a:h}/dependencies.zsh +_DEP_ERROR=0 _DEPENDENCIES=($(echo $_DEPENDENCIES | sort -u)) -__CHECK_DEPENDENCIES $_DEPENDENCIES || ((IMPORT_ERROR+=$?)) +__CHECK_DEPENDENCIES $_DEPENDENCIES || _DEP_ERROR=$? source ${0:a:h}/environment.zsh -_REQUIRED_ENV=($(echo $__CHECK_REQUIRED_ENV | sort -u)) -__CHECK_REQUIRED_ENV $_REQUIRED_ENV || ((IMPORT_ERROR+=$?)) +_ENV_ERROR=0 +_REQUIRED_ENV=($(echo $_REQUIRED_ENV | sort -u)) +__CHECK_REQUIRED_ENV $_REQUIRED_ENV || _ENV_ERROR=$? + +[[ $_ENV_ERROR -ne 0 ]] && { + __REMINDER 'to update missing environment variables, run:' + __REMINDER "'scwrypts zsh/scwrypts/environment/edit'" +} + +((IMPORT_ERROR+=$_DEP_ERROR)) +((IMPORT_ERROR+=$_ENV_ERROR)) -[[ $IMPORT_ERROR -eq 0 ]] || { +[[ $IMPORT_ERROR -ne 0 ]] && { __ERROR "encountered $IMPORT_ERROR import error(s)" - return 1 } ##################################################################### +[[ $IMPORT_ERROR -eq 0 ]] diff --git a/zx/.gitignore b/zx/.gitignore index c11a089..c2658d7 100644 --- a/zx/.gitignore +++ b/zx/.gitignore @@ -1,2 +1 @@ node_modules/ -.env/