bin/sym.symit.bash in sym-2.8.1 vs bin/sym.symit.bash in sym-2.8.2

- old
+ new

@@ -5,16 +5,16 @@ # MIT License, distributed as part of `sym` ruby gem. # • https://github.com/kigster/sym # #============================================================================== # -# The purpose of this script is to transparently edit application secrets in a -# Rails apps or other repos. It simplifies the process of key import, as well -# as the direct editing. +# The purpose of this script is to transparently edit application secrets in +# Rails apps or other projects. It simplifies the process of key import, as well +# as the direct editing, as well as multi-file encryption/decryption routines. # -# If you set some or (ideally) ALL variables below to values specific to your -# system, things will get easy. +# The idea is that you set some of the variables below to values specific to your +# system and working with encrypted files will become very easy. # # SYMIT__FOLDER is a relative folder to your project root, under which you # might keep ALL of your encrypted files. Alternatively, if you keep encrypted # files sprinkled around your project, just leave it out, because it defaults # to "." — the current folder, and search anything beneath. @@ -23,11 +23,11 @@ # # # only search ./config folder # export SYMIT__FOLDER="config" # # # this will be the name of your key in OS-X KeyChain -# export SYMIT__KEY="MY_KEYCHAIN_NAME" +# export SYMIT__KEY="my-org.engineering.dev" # just a name # # # This is the extension given to the encrypted files. Ideally, leave it # # be as ".enc" # export SYMIT__EXTENSION=".enc" # @@ -41,19 +41,45 @@ # # ...and vola! You are editing the encrypted file with sym from the root of # your Rails application. Neat, no? # -( [[ -n $ZSH_EVAL_CONTEXT && $ZSH_EVAL_CONTEXT =~ :file$ ]] || \ + +# Check if we are being sourced in, or run as a script: +( [[ -n ${ZSH_EVAL_CONTEXT} && ${ZSH_EVAL_CONTEXT} =~ :file$ ]] || \ [[ -n $BASH_VERSION && $0 != "$BASH_SOURCE" ]]) && _s_=1 || _s_=0 (( $_s_ )) && _is_sourced=1 (( $_s_ )) || _is_sourced=0 -function __lib::color::setup() { - if [[ -z "${setup_colors_loaded}" ]]; then +# Set all the defaults +function __symit::init() { + export SYMIT__EXTENSION=${SYMIT__EXTENSION:-'.enc'} + export SYMIT__FOLDER=${SYMIT__FOLDER:-'.'} + export SYMIT__KEY=${SYMIT__KEY} + export SYMIT__MIN_VERSION='latest' +} +# Returns name of the current shell, eg 'bash' +function __lib::shell::name() { + echo $(basename $(printf $SHELL)) +} + +# Returns 'yes' if current shell is BASH +function __lib::shell::is_bash() { + [[ $(__lib::shell::name) == "bash" ]] && echo yes +} + +# Returns a number representing shell version, eg. +# 3 or 4 for BASH v3 and v4 respectively. +function __lib::bash::version_number() { + echo $BASH_VERSION | awk 'BEGIN{FS="."}{print $1}' +} + +# Enable all colors, but only if the STDOUT is a terminal +function __lib::color::setup() { + if [[ -t 1 ]]; then export txtblk='\e[0;30m' # Black - Regular export txtred='\e[0;31m' # Red export txtgrn='\e[0;32m' # Green export txtylw='\e[0;33m' # Yellow export txtblu='\e[0;34m' # Blue @@ -89,246 +115,423 @@ export bakwht='\e[47m' # White export clr='\e[0m' # Text Reset export txtrst='\e[0m' # Text Reset export rst='\e[0m' # Text Reset - export GREP_COLOR=32 - export setup_colors_loaded=1 + fi +} + +# Unset all the colors, in case we a being piped into +# something else. +function __lib::color::reset() { + export txtblk= + export txtred= + export txtgrn= + export txtylw= + export txtblu= + export txtpur= + export txtcyn= + export txtwht= + + export bldblk= + export bldred= + export bldgrn= + export bldylw= + export bldblu= + export bldpur= + export bldcyn= + export bldwht= + + export unkblk= + export undred= + export undgrn= + export undylw= + export undblu= + export undpur= + export undcyn= + export undwht= + + export bakblk= + export bakred= + export bakgrn= + export bakylw= + export bakblu= + export bakpur= + export bakcyn= + export bakwht= + + export clr= + export txtrst= + export rst= +} + +# Enable or disable the colors based on whether the STDOUT +# is a proper terminal, or a pipe. +function __lib::stdout::configure() { + if [[ -t 1 ]]; then + __lib::color::setup else - [[ -n ${DEBUG} ]] && echo "colors already loaded..." + __lib::color::reset fi } -((${setup_colors_loaded})) ||__lib::color::setup +__lib::stdout::configure +# Check if we are being run as a script, and if so — bail. (( $_s_ )) || { printf "${bldred}This script is meant to be sourced into your environment,\n" printf "not run on a command line.${clr} \n\n" printf "Please add 'source $0' to your BASH initialization file,\n" printf "or run the following command:\n\n" printf " \$ ${bldgrn}sym -B ~/.bash_profile${clr}\n\n" - + printf "${bldblu}Thanks for using Sym!${clr}\n" exit 1 } +# Horizontal line, width of the full terminal function __lib::color::hr() { local cols=${1:-${COLUMNS}} local char=${2:-"—"} local color=${3:-${txtylw}} printf "${color}" eval "printf \"%0.s${char}\" {1..${cols}}" printf "${clr}\n" } +# Large header, all caps function __lib::color::h1() { local title=$(echo "$*" | tr 'a-z' 'A-Z') len=${#title} printf "${bldylw}${title}\n" - __lib::color::hr ${len} '—' + __lib::color::hr ${len} '─' } +# Smaller header function __lib::color::h2() { printf "${bldpur}$*${clr}\n" } -function __lib::color::cursor_to_col() { +# Shift cursor by N positions to the right +function __lib::color::cursor-right-by() { position=$1 - echo -en "\e[${position}C" + printf "\e[${position}C" } -function __lib::color::cursor_to_row() { +# Shift cursor by N positions to the left +function __lib::color::cursor-left-by() { position=$1 - echo -en "\e[${position}H" + printf "\e[${position}D" } -function __symit::init() { - export SYMIT__EXTENSION=${SYMIT__EXTENSION:-'.enc'} - export SYMIT__FOLDER=${SYMIT__FOLDER:-'.'} - export SYMIT__KEY=${SYMIT__KEY} +# Shift cursor by N positions up +function __lib::color::cursor-up-by() { + position=$1 + printf "\e[${position}A" } + +# Shift cursor by N positions down +function __lib::color::cursor-down-by() { + position=$1 + printf "\e[${position}B" +} + +# Convert a version string such as "1.50.17" to an integer +# 101050017 for numeric comparison: +function __lib::ver-to-i() { + version=${1} + echo ${version} | awk 'BEGIN{FS="."}{ printf "1%02d%03.3d%03.3d", $1, $2, $3}' +} + +# Convert a result of __lib::ver-to-i() back to a regular version. +function __lib::i-to-ver() { + version=${1} + /usr/bin/env ruby -e "ver='${version}'; printf %Q{%d.%d.%d}, ver[1..2].to_i, ver[3..5].to_i, ver[6..8].to_i" +} + +# Prints Usage function __symit::usage() { - __lib::color::setup + echo __lib::color::h1 "symit" + printf " - This a BASH wrapper for the encryption tool (ruby gem) 'Sym'. It streamlines - editing encrypted of files, importing and securing your key, and other - actions. The wrapper can be configured with ENV variables, or CLI flags.\n" + ${bldylw}symit${bldgrn} is a powerful BASH helper, that enhances the CLI encryption + tool called ${bldred}Sym${clr}, which is a Ruby Gem. + Sym has an extensive CLI interface, but it only handles one + encryption/decryption operation per invocation. With this script, you can + auto decrypt all files in a given folder, you can import the key in a + simpler way, and you can save into the environment sym configuration that + will be used. It also streamlines editing of encrypted files in a given + folder. Symit can be configured either with the ENV variables, or using + the CLI flags.\n" + printf " - The easiest way to take advantage of this wrapper is to set the following + The recommended way to use ${bldred}symit${clr} is to set the following environment variables, which removes the need to pass these values via the - flags. These variables default to the shown values if not set elsewhere:${txtylw} + flags. These variables default to the shown values if not set elsewhere: - export SYMIT__EXTENSION='${SYMIT__EXTENSION}' - export SYMIT__FOLDER='${SYMIT__FOLDER}' - export SYMIT__KEY='${SYMIT__KEY}' + Perhaps the most critically important variable to set is ${txtylw}SYMIT__KEY${clr}: + ${txtylw} + export SYMIT__KEY='my-org.my-app.dev' + eg: export SYMIT__KEY='github.web.development' + ${clr} + The ${txtcya}key${clr} can resolve to a file name, or a name of ENV variable, + a keychain entry, or be the actual key (not recommended!). See the following + link for more info: + + ${undblu}https://github.com/kigster/sym#resolving-the--k-argument${clr} + + Additional configuration is available through these variables: + ${txtylw} + export SYMIT__EXTENSION='${SYMIT__EXTENSION}' + export SYMIT__FOLDER='${SYMIT__FOLDER}' + export SYMIT__MIN_VERSION='latest' + ${clr} + The last variable defines the minimum Sym version desired. Set it to + 'latest' to have symit auto-upgrade Sym every time it is invoked. + ${clr}\n" __lib::color::h2 "Usage:" - printf " ${bldgrn}symit [ action ] [ partial-file-path ] [ flags ]${clr}\n\n" + printf " ${bldgrn}symit [ action ] [ file-path/pattern ] [ flags ]${clr}\n\n" __lib::color::h2 "Actions:" printf " Action is the first word that defaults to ${bldylw}edit${clr}.\n\n" - printf " Valid actions are:\n" - printf " ${bldylw}— install ${bldblk}ensures you are on the latest gem version\n" - printf " ${bldylw}— generate ${bldblk}create a new secure key, and copies it to \n" + printf " ${bldcya}Valid actions are below, starting with the Key import or creation:${clr}\n\n" + printf " ${bldylw}— generate ${clr}create a new secure key, and copies it to the\n" printf " clipboard (if supported), otherwise prints to STDOUT\n" - printf " Key is required, and used as a name within OSX KeyChain\n\n" - printf " ${bldylw}— import [key] [insecure]\n" - printf " ${bldblk}imports the key from clipboard and adds password\n" - printf " encryption unless 'insecure' is passed in\n\n" - printf " ${bldylw}— edit ${bldblk}Finds all files, and opens them in $EDITOR\n" - printf " ${bldylw}— encrypt ${bldblk}Encrypts files matching file-path\n" - printf " ${bldylw}— decrypt ${bldblk}Adds the extension to file pattern and decrypts\n" - printf " ${bldylw}— auto ${bldblk}encrypts decrypted file, and vice versa\n" + printf " Key name (set via SYMIT__KEY or -k flag) is required,\n" + printf " and is used as the KeyChain entry name for the new key.\n\n" + printf " ${bldylw}— import [insecure]\n" + printf " ${clr}imports the key from clipboard and adds password\n" + printf " encryption unless 'insecure' is passed in. Same as above\n" + printf " in relation with the key parameter.\n\n" + printf " ${bldcya}The following actions require the file pattern/path argument:${clr}\n" + printf " ${bldylw}— edit ${clr}Finds all files, and opens them in $EDITOR\n" + printf " ${bldylw}— encrypt ${clr}Encrypts files matching file-path\n" + printf " ${bldylw}— decrypt ${clr}Adds the extension to file pattern and decrypts\n" + printf " ${bldylw}— auto ${clr}encrypts decrypted file, and vice versa\n" echo __lib::color::h2 "Flags:" - printf " -f | --folder DIR ${bldblk}Top level folder to search.${clr}\n" - printf " -k | --key KEY ${bldblk}Key identifier${clr}\n" - printf " -x | --extension EXT ${bldblk}Default extension of encrypted files.${clr}\n" - printf " -n | --dry-run ${bldblk}Print stuff, but don't do it${clr}\n" - printf " -h | --help ${bldblk}Show this help message${clr}\n" + printf " -f | --folder DIR ${clr}Top level folder to search.${clr}\n" + printf " -k | --key KEY ${clr}Key identifier${clr}\n" + printf " -x | --extension EXT ${clr}Default extension of encrypted files.${clr}\n" + printf " -n | --dry-run ${clr}Print stuff, but dont do it${clr}\n" + printf " -a | --all-files ${clr}If provided ALL FILES are operated on${clr}\n" + printf " ${clr}Use with CAUTION!${clr}\n" + printf " -v | --verbose ${clr}Print more stuff${clr}\n" + printf " -q | --quiet ${clr}Print less stuff${clr}\n" + printf " -h | --help ${clr}Show this help message${clr}\n" echo __lib::color::h2 'Encryption key identifier can be:' - printf "${clr}\ + printf "${clr}" + + printf ' 1. name of the keychain item storing the keychain (secure) 2. name of the environment variable storing the Key (*) 3. name of the file storing the key (*) - 4. the key itself (*) + 4. the key itself (*)' - ${bldred}(*) 2-4 are insecure UNLESS the key is encrypted with a password.${clr} - - Please refer to README about generating password protected keys: + echo + printf "${bldred}" + printf ' + (*) 2-4 are insecure UNLESS the key is encrypted with a password.'; echo + printf "${clr}\ + Please refer to README about generating password protected keys:\n ${bldblu}${undblu}https://github.com/kigster/sym#generating-the-key--examples${clr}\n\n" - echo __lib::color::h1 'Examples:' - printf " Ex1: To import a key securely,\n" - printf " \$ ${bldgrn}symit${bldblu} import key ${clr}\n\n" + printf " To import a key securely, first copy the key to your clipboard,\n" + printf " and then run the following command, pasting the key when asked:\n\n" + printf " ❯ ${bldgrn}symit${bldblu} import key ${clr}\n\n" - printf " Ex2.: To encrypt (or decrypt) ALL files in the 'config' directory:${clr}\n" - printf " \$ ${bldgrn}symit${bldblu} encrypt|decrypt -a -f config ${clr}\n\n" + printf " To encrypt or decrypt ALL files in the 'config' directory:${clr}\n\n" + printf " ❯ ${bldgrn}symit${bldblu} encrypt|decrypt -a -f config ${clr}\n\n" - printf " Ex3: To decrypt all *.yml.enc files in the 'config' directory:${clr}\n" - printf " \$ ${bldgrn}symit${bldblu} decrypt '*.yml' -f config ${clr}\n\n" + printf " To decrypt all *.yml.enc files in the 'config' directory:${clr}\n\n" + printf " ❯ ${bldgrn}symit${bldblu} decrypt '*.yml' -f config ${clr}\n\n" - printf " Ex4: To edit an encrypted file ${txtblu}config/application.yml.enc${clr}\n" - printf " \$ ${bldgrn}symit${bldblu} application.yml${clr}\n\n" + printf " To edit an encrypted file ${txtblu}config/application.yml.enc${clr}\n\n" + printf " ❯ ${bldgrn}symit${bldblu} application.yml${clr}\n\n" - printf " Ex5.: To auto decrypt a file ${txtblu}config/settings/crypt/pass.yml.enc${clr}\n" - printf " \$ ${bldgrn}symit${bldblu} auto config/settings/crypt/pass.yml.enc${clr}\n\n" + printf " To auto decrypt a file ${txtblu}config/settings/crypt/pass.yml.enc${clr}\n\n" + printf " ❯ ${bldgrn}symit${bldblu} auto config/settings/crypt/pass.yml.enc${clr}\n\n" - printf " Ex6.: To automatically decide to either encrypt or decrypt a file,\n" - printf " based on the file extension. First example encrypts the file, second\n" - printf " decrypts it (because file extension is .enc):${clr}\n" - printf " \$ ${bldgrn}symit${bldblu} auto config/settings/crypt/pass.yml${clr}\n" - printf " \$ ${bldgrn}symit${bldblu} auto config/settings/crypt/pass.yml.enc${clr}\n\n" + printf " To automatically decide to either encrypt or decrypt a file,\n" + printf " based on the file extension use 'auto' command. The first line below\n" + printf " encrypts the file, second decrypts it, because the file extension is .enc:${clr}\n\n" - printf " Ex7.: To encrypt a file ${txtblu}config/settings.yml${clr}\n" - printf " \$ ${bldgrn}symit${bldblu} encrypt config/settings.yml${clr}\n\n" + printf " ❯ ${bldgrn}symit${bldblu} auto config/settings/crypt/pass.yml${clr}\n" + printf " ❯ ${bldgrn}symit${bldblu} auto config/settings/crypt/pass.yml.enc${clr}\n\n" + printf " To encrypt a file ${txtblu}config/settings.yml${clr}\n" + printf " ❯ ${bldgrn}symit${bldblu} encrypt config/settings.yml${clr}\n\n" } + function __datum() { date +"%m/%d/%Y.%H:%M:%S" } +function __warn() { + __lib::color::cursor-left-by 1000 + printf "${bldylw}$* ${bldylw}\n" +} function __err() { - #__lib::color::cursor_to_col 0 - printf "${txtpur}[$(__datum)] ${bldred}ERROR: ${txterr}$* ${bldylw}\n" + __lib::color::cursor-left-by 1000 + printf "${bldred}ERROR: ${txtred}$* ${bldylw}\n" } function __inf() { - #__lib::color::cursor_to_col 0 - printf "${txtpur}[$(__datum)] ${bldgrn}INFO: ${clr}${bldblu}$*${clr}\n" + [[ ${cli__opts__quiet} ]] && return + __lib::color::cursor-left-by 1000 + printf "${txtblu}$*${clr}\n" } +function __dbg() { + [[ ${cli__opts__verbose} ]] || return + __lib::color::cursor-left-by 1000 + printf "${txtgrn}$*${clr}\n" +} + +function __lib::command::print() { + __inf "${bldylw}❯ ${bldcya}$*${clr}" +} + +function __symit::sym::installed_version() { + __lib::ver-to-i $(gem list | grep sym | awk '{print $2}' | sed 's/(//g;s/)//g') +} + +function __symit::sym::latest_version() { + __lib::ver-to-i $(gem query --remote -n '^sym$' | awk '{print $2}' | sed 's/(//g;s/)//g') +} + +function __symit::install::update() { + local desired_version=$1 + shift + local current_version=$2 + shift + local version_args=$* + + __inf "updating sym to version ${bldylw}$(__lib::i-to-ver ${desired_version})${clr}..." + printf "${bldblu}" >&1 + echo y | gem uninstall sym --force -x 2>/dev/null + printf "${clr}" >&1 + + command="gem install sym ${version_args} " + eval "${command}" >/dev/null + code=$? + printf "${clr}" >&2 + if [[ ${code} != 0 ]]; then + __err "gem install returned ${code}, with command ${bldylw}${command}" + return 127 + fi + current_version=$(__symit::sym::installed_version) + __inf "sym version ${bldylw}$(__lib::i-to-ver ${current_version}) was successfully installed." +} + function __symit::install::gem() { - __inf "Verifying current Sym version, please wait..." - if [[ -z "${_symit__installed}" ]]; then - current_version=$(gem list | grep sym | awk '{print $2}' | sed 's/(//g;s/)//g') - if [[ -z "${current_version}" ]]; then - gem install sym + if [[ -n ${__symit_last_checked_at} ]]; then + now=$(date +'%s') + if [[ $(( $now - ${__symit_last_checked_at} )) -lt 3600 ]]; then + return + fi + fi + + export __symit_last_checked_at=${now:-$(date +'%s')} + + __inf "Verifying current sym version, please wait..." + current_version=$(__symit::sym::installed_version) + if [[ -n ${SYMIT__MIN_VERSION} ]]; then + if [[ ${SYMIT__MIN_VERSION} -eq 'latest' ]]; then + desired_version=$(__symit::sym::latest_version) + version_args='' else - local help=$(sym -h 2>&1) - unset SYM_ARGS - remote_version=$(gem search sym | egrep '^sym \(' | awk '{print $2}' | sed 's/(//g;s/)//g') - if [[ "${remote_version}" != "${current_version}" ]]; then - __inf "detected an older ${bldgrn}sym (${current_version})" - __inf "installing ${bldgrn}sym (${remote_version})${clr}..." - echo y | gem uninstall sym -a 2> /dev/null - gem install sym - export _symit__installed="yes" - __inf "Installed sym version ${bldylw}$(sym --version)" - else - __inf "${bldgrn}sym${clr} ${txtblu}is on the latest version ${remote_version} already\n" - fi + desired_version=$( __lib::ver-to-i ${SYMIT__MIN_VERSION}) + version_args=" --version ${SYMIT__MIN_VERSION}" fi + + if [[ "${desired_version}" != "${current_version}" ]]; then + __symit::install::update "${desired_version}" "${current_version}" "${version_args}" + else + __inf "${bldgrn}sym${clr} ${txtblu}is on the correct version ${bldylw}$(__lib::i-to-ver ${desired_version})${txtblu} already" + fi + else + if [[ -z ${current_version} ]] ; then + __dbg "installing latest version of ${bldylw}sym..." + fi fi } function __symit::files() { eval $(__symit::files::cmd) } function __symit::files::cmd() { - if [[ -n ${cli__opts[file]} && -n ${cli__opts[extension]} ]]; then - local folder - if [[ ${cli__opts[file]} =~ '/' ]]; then - folder="${cli__opts[folder]}/$(dirname ${cli__opts[file]})" - else - folder="${cli__opts[folder]}" + if [[ -n ${cli__opts__file} && -n ${cli__opts__extension} ]]; then + + local folder=${cli__opts__folder} + local file="${cli__opts__file}" + local ext="${cli__opts__extension}" + + if [[ ${file} =~ '/' ]]; then + if [[ ${folder} == '.' ]]; then + folder="$(dirname ${file})" + else + folder="${folder}/$(dirname ${file})" + fi + file="$(basename ${file})" fi - local file="$(basename ${cli__opts[file]})" - local ext="${cli__opts[extension]}" - if [[ "${cli__opts[action]}" == "auto" || "${cli__opts[action]}" == "encrypt" ]] ; then - #find ${folder} -name "${file}" >&2 + if [[ "${cli__opts__action}" == "encrypt" ]] ; then printf "find ${folder} -name '${file}' -and -not -name '*${ext}'" - else - #find ${folder} -name "${file}${ext}" >&2 - printf "find ${folder} -name '${file}${ext}'" + elif [[ "${cli__opts__action}" == "auto" ]] ; then + printf "find ${folder} -name '${file}'" + else # edit, decrypt + [[ ${file} =~ "${ext}" ]] || file="${file}${ext}" + printf "find ${folder} -name '${file}'" fi fi } function __symit::command() { file=${1} - if [[ -n "${cli__opts[key]}" && -n "${cli__opts[extension]}" ]]; then - action="${cli__opts[action]}" - flags="${sym__actions[${action}]}" + if [[ -n "${cli__opts__key}" && -n "${cli__opts__extension}" ]]; then + action="${cli__opts__action}" + v="sym__actions__${action}" + flags="${!v}" if [[ ${action} =~ "key" ]]; then - [[ -n ${cli__opts[verbose]} ]] && printf "processing key import action ${bldylw}${action}${clr}\n" >&2 - printf "sym ${flags} ${cli__opts[key]} " + [[ -n ${cli__opts__verbose} ]] && printf "processing key import action ${bldylw}${action}${clr}\n" >&2 + printf "sym ${flags} ${cli__opts__key} " elif [[ ${action} =~ "generate" ]] ; then - [[ -n ${cli__opts[verbose]} ]] && printf "processing generate key action ${bldylw}${action}${clr}\n" >&2 + [[ -n ${cli__opts__verbose} ]] && printf "processing generate key action ${bldylw}${action}${clr}\n" >&2 if [[ -n $(which pbcopy) ]]; then out_key=/tmp/outkey - command="sym ${flags} ${cli__opts[key]} -q -o ${out_key}; cat ${out_key} | pbcopy; rm -f ${out_key}" + command="sym ${flags} ${cli__opts__key} -q -o ${out_key}; cat ${out_key} | pbcopy; rm -f ${out_key}" printf "${command}" else - printf "sym ${flags} ${cli__opts[key]} " + printf "sym ${flags} ${cli__opts__key} " fi elif [[ -n ${file} ]] ; then - ext="${cli__opts[extension]}" + ext="${cli__opts__extension}" [[ -z ${ext} ]] && ext='.enc' ext=$(echo ${ext} | sed -E 's/[\*\/,.]//g') if [[ ${action} =~ "encrypt" ]]; then - printf "sym ${flags} ${file} -ck ${cli__opts[key]} -o ${file}.${ext}" + printf "sym ${flags} ${file} -ck ${cli__opts__key} -o ${file}.${ext}" elif [[ ${action} =~ "decrypt" ]]; then new_name=$(echo ${file} | sed "s/\.${ext}//g") [[ "${new_name}" == "${file}" ]] && name="${file}.decrypted" - printf "sym ${flags} ${file} -ck ${cli__opts[key]} -o ${new_name}" + printf "sym ${flags} ${file} -ck ${cli__opts__key} -o ${new_name}" else - printf "sym ${flags} ${file} -ck ${cli__opts[key]} " + printf "sym ${flags} ${file} -ck ${cli__opts__key} " fi else printf "printf \"ERROR: not sure how to generate a correct command\\n\"" fi fi @@ -344,69 +547,66 @@ __symit::cleanup echo -n ${code} } function __symit::print_cli_args() { - local -A args=$@ - __inf "action ${bldylw}: ${cli__opts[action]}${clr}" - __inf "key ${bldylw}: ${cli__opts[key]}${clr}" - __inf "file ${bldylw}: ${cli__opts[file]}${clr}" - __inf "extension ${bldylw}: ${cli__opts[extension]}${clr}" - __inf "folder ${bldylw}: ${cli__opts[folder]}${clr}" - __inf "verbose ${bldylw}: ${cli__opts[verbose]}${clr}" - __inf "dry_run ${bldylw}: ${cli__opts[dry_run]}${clr}" + __dbg "action ${bldylw}: ${cli__opts__action}${clr}" + __dbg "key ${bldylw}: ${cli__opts__key}${clr}" + __dbg "file ${bldylw}: ${cli__opts__file}${clr}" + __dbg "extension ${bldylw}: ${cli__opts__extension}${clr}" + __dbg "folder ${bldylw}: ${cli__opts__folder}${clr}" + __dbg "verbose ${bldylw}: ${cli__opts__verbose}${clr}" + __dbg "dry_run ${bldylw}: ${cli__opts__dry_run}${clr}" } function __symit::args::needs_file() { - if [[ "${cli__opts[action]}" == 'edit' || \ - "${cli__opts[action]}" == 'auto' || \ - "${cli__opts[action]}" == 'encrypt' || \ - "${cli__opts[action]}" == 'decrypt' ]]; then + if [[ "${cli__opts__action}" == 'edit' || \ + "${cli__opts__action}" == 'auto' || \ + "${cli__opts__action}" == 'encrypt' || \ + "${cli__opts__action}" == 'decrypt' ]]; then printf 'yes' fi } function __symit::validate_args() { - if [[ -n $(__symit::args::needs_file) && -z ${cli__opts[file]} ]]; then + if [[ -n $(__symit::args::needs_file) && -z ${cli__opts__file} ]]; then __err "missing file argument, config/application.yml" return $(__symit::exit 2) fi - if [[ -z "${cli__opts[key]}" ]]; then + if [[ -z "${cli__opts__key}" ]]; then __err "Key was not defined, pass it with ${bldblu}-k KEY_ID${bldred}" __err "or set it via ${bldgrn}\$SYMIT__KEY${bldred} variable." return $(__symit::exit 4) fi - if [[ -z ${cli__opts[extension]} ]]; then - cli__opts[extension]='.enc' + if [[ -z ${cli__opts__extension} ]]; then + cli__opts__extension='.enc' fi } function __symit::run() { __symit::cleanup __symit::init - declare -A cli__opts=( - [verbose]='' - [key]=${SYMIT__KEY} - [extension]=${SYMIT__EXTENSION} - [folder]=${SYMIT__FOLDER} - [dry_run]='' - [action]=edit - [file]='' - ) + cli__opts__verbose='' + cli__opts__quiet='' + cli__opts__key=${SYMIT__KEY} + cli__opts__extension=${SYMIT__EXTENSION} + cli__opts__folder=${SYMIT__FOLDER} + cli__opts__dry_run='' + cli__opts__action=edit + cli__opts__file='' - declare -A sym__actions=( - [generate]=' -cpgx ' - [edit]=' -t ' - [encrypt]='-e -f ' - [decrypt]='-d -f ' - [auto]=' -n ' - [key_secure]=' -iqcpx ' - [key_insecure]=' -iqcx ' - ) + sym__actions__generate=' -cpgx ' + sym__actions__edit=' -t ' + sym__actions__encrypt='-e -f ' + sym__actions__decrypt='-d -f ' + sym__actions__auto=' -n ' + sym__actions__key_secure=' -iqcpx ' + sym__actions__key_insecure=' -iqcx ' + sym__actions__install='install' if [[ -z $1 ]]; then __symit::usage return $(__symit::exit 0) fi @@ -423,59 +623,64 @@ -k|--key) shift if [[ -z $1 ]]; then __err "-k/--key requires an argument" && return $(__symit::exit 1) else - cli__opts[key]=$1 + cli__opts__key=$1 shift fi ;; -x|--extension) shift if [[ -z $1 ]]; then __err "-x/--extension requires an argument" && return $(__symit::exit 1) else - cli__opts[extension]=${1} + cli__opts__extension=${1} shift fi ;; -f|--folder) shift if [[ -z $1 ]]; then __err "-f/--folder requires an argument" && return $(__symit::exit 1) else - cli__opts[folder]=${1} + cli__opts__folder=${1} shift fi ;; -a|--all-files) shift - cli__opts[file]="'*'" + cli__opts__file="'*'" ;; -n|--dry-run) shift - cli__opts[dry_run]="yes" + cli__opts__dry_run="yes" ;; -v|--verbose) shift - cli__opts[verbose]="yes" + cli__opts__verbose="yes" ;; + -q|--quiet) + shift + cli__opts__quiet="yes" + ;; + import|key) shift - cli__opts[action]="key_secure" + cli__opts__action="key_secure" ;; insecure) shift - if [[ "${cli__opts[action]}" == 'key_secure' ]] ; then - cli__opts[action]="key_insecure" + if [[ "${cli__opts__action}" == 'key_secure' ]] ; then + cli__opts__action="key_insecure" fi ;; --) # End of all options. shift @@ -489,68 +694,88 @@ ;; ?*) param=$1 - if [[ -n "${sym__actions[${param}]}" ]]; then - cli__opts[action]=${param} + v="sym__actions__${param}" + if [[ ! ${param} =~ '.' && -n "${!v}" ]]; then + __dbg "Action ${bldylw}${param}${clr} is a valid action." + cli__opts__action=${param} else - cli__opts[file]=${1} + __dbg "Parameter ${bldylw}${param}${clr} is not a valid action," + __dbg "therefore it must be a file pattern." + cli__opts__file=${1} fi shift ;; *) # Default case: If no more options then break out of the loop. break shift esac done - [[ -n ${cli__opts[verbose]} ]] &&__symit::print_cli_args + [[ -n "${cli__opts__verbose}" ]] && __symit::print_cli_args - if [[ "${cli__opts[action]}" == 'install' ]]; then - if [[ -n ${cli__opts[dry_run]} ]]; then - __inf "This command verifies that Sym is properly installed," - __inf "and if not found — installs it." + if [[ "${cli__opts__action}" == 'install' ]]; then + if [[ -n ${cli__opts__dry_run} ]]; then + __dbg "This command verifies that Sym is properly installed," + __dbg "and if not found — installs it." return $(__symit::exit 0) else __symit::install::gem return $(__symit::exit 0) fi fi __symit::validate_args + code=$? + if [[ ${code} != 0 ]]; then + return $(__symit::exit ${code}) + fi + + __symit::install::gem + changed_count=0 - if [[ -n "${cli__opts[dry_run]}" ]] ; then - __lib::color::h1 "Dry Run — printing commands that would be run:" + if [[ -n "${cli__opts__dry_run}" ]] ; then + __lib::color::h1 "DRY RUN" for file in $(__symit::files); do printf " \$ ${bldblu}$(__symit::command ${file})${clr}\n" done else - if [[ -n "${cli__opts[file]}" ]]; then - [[ -n ${cli__opts[verbose]} ]] && __inf $(__symit::files) + if [[ -n "${cli__opts__file}" ]]; then + [[ -n ${cli__opts__verbose} ]] && __dbg $(__symit::files) declare -a file_list + for file in $(__symit::files); do - file_list=(${file} "${file_list[*]}") - __inf "❯ ${bldblu}$(__symit::command ${file})${clr}" - eval $(__symit::command ${file}) - code=$?; [[ ${code} != 0 ]] && __err "sym returned non-zero code ${code}" + local cmd="$(__symit::command ${file})" + __lib::command::print "${cmd}" + eval "${cmd}" + code=$?; [[ ${code} != 0 ]] && __err "command '${bldblu}${cmd}${bldred}' exited with code ${bldylw}${code}" + changed_count=$(( ${changed_count} + 1)) done - if [[ ${#file_list} == 0 ]]; then - __inf "No files matched your specification. The following 'find' command" - __inf "ran to find them: \n" - __inf " ${bldylw}$(__symit::files::cmd)${clr}\n\n" + + if [[ ${changed_count} == 0 ]]; then + printf "${undylw}Bad news:${clr}\n\n" + __warn " No files matched your specification. The following 'find' command" + __warn " ran to find the file you requested. Please change the name, and " + __warn " try again.\n" + __warn " ${bldblu}$(__symit::files::cmd)${clr}\n\n" return $(__symit::exit 5) fi - else - [[ -n ${cli__opts[verbose]} ]] && __inf $(__symit::command) - eval $(__symit::command) - code=$?; [[ ${code} != 0 ]] && return $(__symits::exit ${code}) + + else # opts[file] + cmd=$(__symit::command) + __lib::command::print "${cmd}" + eval "${cmd}" + code=$?; [[ ${code} != 0 ]] && return $(__symit::exit ${code}) + changed_count=$(( ${changed_count} + 1)) fi fi } function symit() { + __lib::stdout::configure __symit::run $@ }