=====================================================================

Subscwrypts + Environment Inheritance

--- Release Notes ------------------------

- added support for environment inheritance
- added support for arbitrarily nested scripts (subscwrypts)
- added support for CI mode

- improved modularity of zsh/utils module

- refactored to move some data from ~/.config/scwrypts to ~/.local/share/scwrypts

- refactored various scripts to use new subscwrypt api

--- New Scripts --------------------------

zsh )
  - db/interactive/postgres
  - aws/rds/interactive-login
This commit is contained in:
2022-06-22 12:17:19 -06:00
parent 2dcf94199b
commit eaefc99774
34 changed files with 741 additions and 231 deletions

View File

@ -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)
<br>
Since they emulate direct user interaction, shell scripts are often the straightforward choice for task automation.

View File

@ -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
}

121
zsh/aws/rds/interactive-login Executable file
View File

@ -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

View File

@ -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 $SCWRYPTS_ROOT/.config
source ${0:a:h}/utils/utils.module.zsh || {
[ $DONT_EXIT ] && return 1 || exit 1
}
#####################################################################
source ${0:a:h}/utils/utils.zsh
#####################################################################
__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
}

4
zsh/db/common.zsh Normal file
View File

@ -0,0 +1,4 @@
_DEPENDENCIES+=()
_REQUIRED_ENV+=()
source ${0:a:h}/../common.zsh
#####################################################################

View File

@ -0,0 +1,4 @@
_DEPENDENCIES+=()
_REQUIRED_ENV+=()
source ${0:a:h}/../common.zsh
#####################################################################

45
zsh/db/interactive/postgres Executable file
View File

@ -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 $@

View File

@ -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 `<parent-name>.<child-name>`.
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 <parent-name>` in the environment file.
Nested children will inherit values from all parents.
## Logs
Quickly view or clear Scwrypts logs.

View File

@ -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'

View File

@ -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"
}

View File

@ -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'"

View File

@ -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"

View File

@ -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 $@

View File

@ -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!'
}
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)
_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
}
__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
_INSERT_NEW_VARIABLES() {
__STATUS 'inserting new environment variables...'
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
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")
}
__SUCCESS 'finished sync!'
_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
}
_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 $@

View File

@ -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'

View File

@ -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
}

71
zsh/utils/README.md Normal file
View File

@ -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

View File

@ -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"

View File

@ -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="
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"
}
grep -q -- "^$LINE" "$__ENV_TEMPLATE" || {
__STATUS 'staging new variable in template'
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
}
}

View File

@ -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 >/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; }
fzf -i --height=30% --layout=reverse --prompt "$1 : " ${@:2}
}
__FZF_HEAD() { __FZF $@ --print-query | sed '/^$/d' | head -n1; } # prefer user input over selected
__FZF_TAIL() { __FZF $@ --print-query | sed '/^$/d' | tail -n1; } # prefer selected over user input
__EDIT() { $EDITOR $@ </dev/tty >/dev/tty; }
__READ() {
[ $CI ] && {
__ERROR 'currently in CI, but __READ explicitly requires terminal input'
return 1
}
read $@ </dev/tty
}
__EDIT() {
[ $CI ] && {
__ERROR 'currently in CI, but __EDIT explicitly requires terminal input'
return 1
}
$EDITOR $@ </dev/tty >/dev/tty
}

View File

@ -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=$?
[[ $IMPORT_ERROR -eq 0 ]] || {
[[ $_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 -ne 0 ]] && {
__ERROR "encountered $IMPORT_ERROR import error(s)"
return 1
}
#####################################################################
[[ $IMPORT_ERROR -eq 0 ]]