diff --git a/bash/habitual/git.functions b/bash/habitual/git.functions index 83e1665..5d517f0 100644 --- a/bash/habitual/git.functions +++ b/bash/habitual/git.functions @@ -1,3 +1,4 @@ +#!/usr/bin/env bash # vim: et sr sw=4 ts=4 smartindent syntax=sh: # # @overview @@ -18,7 +19,7 @@ GIT_SHA_LEN=${GIT_SHA_LEN:-8} # __cd() - if user passed a path, cd to it. __cd() { local d="$1" - [[ ! -z "$d" ]] && ! cd $d 2>/dev/null && red_e "couldn't cd to $d" && return 1; + [[ -n "$d" ]] && ! cd "$d" 2>/dev/null && red_e "couldn't cd to $d" && return 1; return 0 } @@ -43,7 +44,7 @@ git_repo() { # @desc Prints sha of current commit - up to $GIT\_SHA\_LEN chars. git_sha() { - ${GIT} rev-parse --short=${GIT_SHA_LEN} --verify HEAD + ${GIT} rev-parse --short="$GIT_SHA_LEN" --verify HEAD } # @desc Prints out the git-tag on the current commit (exact match only) @@ -67,10 +68,12 @@ git_email() { # @desc Prints user.name user.email (from git config) # Returns 1 if user.name not set. git_id() { - local u=$(git_user) - local e=$(git_email) + local u="" e="" + + u=$(git_user) + e=$(git_email) [[ -z "$u" ]] && echo "" && return 1 - echo $u $e + echo "$u" "$e" } # @desc Outputs a str formed of repo, sha1, tag and branch info. @@ -91,8 +94,13 @@ git_info_str() { local branch="" repo="" sha="" tag="" ( - __cd $d || exit 1 - ! in_git_clone && e "can't get git info for $(pwd) - not a git dir" && return 1 + __cd "$d" || exit 1 + # No need to call `cd` by sending $d in here for sure, + # but, shellcheck complains about SC2120 if you never + # call the function with an argument. + # It thinks you're confused about script's $1 and + # functions $1. So, no harm in this + ! in_git_clone "$d" && e "can't get git info for $(pwd) - not a git dir" && return 1 repo="$(git_repo)" sha="$(git_sha)" @@ -143,13 +151,20 @@ git_vars() { ! in_git_clone && e "... $PWD is not inside a git repo" && return 1 GIT_BRANCH="$(git_branch)" || return 1 ; export GIT_BRANCH - export GIT_TAG="$(git_tag)" - export GIT_REPO="$(git_repo)" - export GIT_SHA="$(git_sha)" - export GIT_USER="$(git_user)" - export GIT_EMAIL="$(git_email)" - export GIT_ID="$(git_id)" # git user and email, space separated - export GIT_INFO=$(_git_info_str "$GIT_REPO" "$GIT_SHA" "$GIT_TAG" "$GIT_BRANCH") + GIT_TAG="$(git_tag)" + export GIT_TAG + GIT_REPO="$(git_repo)" + export GIT_REPO + GIT_SHA="$(git_sha)" + export GIT_SHA + GIT_USER="$(git_user)" + export GIT_USER + GIT_EMAIL="$(git_email)" + export GIT_EMAIL + GIT_ID="$(git_id)" # git user and email, space separated + export GIT_ID + GIT_INFO=$(_git_info_str "$GIT_REPO" "$GIT_SHA" "$GIT_TAG" "$GIT_BRANCH") + export GIT_INFO } @@ -170,10 +185,12 @@ git_vars() { # DEVMODE=true no_unpushed_changes || exit 1 # no_unpushed_changes() { - [[ ! -z "${DEVMODE}" ]] && yellow_i "DEVMODE - skipping git checks" && return 0; + [[ -n "${DEVMODE}" ]] && yellow_i "DEVMODE - skipping git checks" && return 0; - local d=$(pwd) - local sha=$(git_sha) + local d="" sha="" + + d=$(pwd) + sha=$(git_sha) check_for_changes "$d" || return 1 sha_in_origin "$sha" || return 1 @@ -195,7 +212,7 @@ check_for_changes() { local d="${1:-$(pwd)}" i "... checking for uncommitted changes in $d" ( - __cd $d || exit 1 + __cd "$d" || exit 1 ! in_git_clone && red_e "$(pwd) is not a git dir" && return 1; git &>/dev/null --no-pager status # make sure index is up-to-date @@ -238,7 +255,8 @@ sha_in_origin() { return 1 fi - b=$(git branch -r --contains ${sha} 2>/dev/null) + b=$(git branch -r --contains "$sha" 2>/dev/null) + #shellcheck disable=SC2181 if [[ $? -ne 0 ]] || [[ -z "$b" ]]; then red_e "This commit ($sha) does not exist on origin." red_e "Did you push these changes?" @@ -264,7 +282,7 @@ in_git_clone() { local rc=0 local d="$1" ( - __cd $d || exit 1 + __cd "$d" || exit 1 git --no-pager rev-parse --is-inside-work-tree >/dev/null 2>&1 ) || rc=1 return $rc diff --git a/bash/habitual/std.functions b/bash/habitual/std.functions index 6fd2ed3..b6d30ed 100644 --- a/bash/habitual/std.functions +++ b/bash/habitual/std.functions @@ -1,3 +1,4 @@ +#!/usr/bin/env bash # vim: et sr sw=4 ts=4 smartindent syntax=sh: # @overview @@ -28,11 +29,12 @@ # source_files() { local files=("$@") - local f="" i="" rc=0 + local f="" rc=0 for f in "${files[@]}"; do if [[ -e "$f" ]]; then d "... sourcing $f" + #shellcheck disable=SC1090 ! . "$f" && e "... can not source $f" && rc=1 else if [[ -z "$IGNORE_MISSING" ]]; then @@ -91,7 +93,7 @@ required_vars() { local required_vars="$1" local this_var="" for this_var in $required_vars; do - if ! check_var_defined $this_var + if ! check_var_defined "$this_var" then failed="${failed}\$$this_var " rc=1 @@ -111,7 +113,7 @@ required_vars() { # # ... test to see $FOO and $BAR are non-empty. # check_var_defined "FOO" || echo "FOO is empty or not defined" # -check_var_defined() { [[ ! -z "${!1}" ]] ; } +check_var_defined() { [[ -n "${!1}" ]] ; } # @desc Trim leading and trailing whitespace from a string # @@ -181,16 +183,20 @@ std::render_tmpl() { fi cmd="cat <<$delim"$'\n'"$d"$'\n'"$delim" # creates a heredoc - eval "$cmd" # ... check eval worked syntactically ... - [[ $? -ne 0 ]] && e "... failed to render file $file_tmpl" && return 1 + if ! eval "$cmd" + then + e "... failed to render file $file_tmpl" + return 1 + fi return 0 } _stop_breakouts_in_render() { local d="$1" + #shellcheck disable=SC2016 echo "$d" \ | sed \ -e 's/\([^\\]\)`/\1\\`/g' `# catch backticks preceded by non-backslash` \ @@ -262,6 +268,7 @@ str_to_safe_chars() { local s="$1" # str to transform local r="${2:-_}" # replacement char local p="${3:-$(safe_chars_def_list)}" # allowed chars (or with leading ! disallowed) + local sed_rc="" [[ -z "$1" ]] && red_e "... you must pass a str to transform" && return 1 [[ "${#r}" -ne 1 ]] && red_e "... replacement must be one UTF-8 char, not '$r'" && return 1 @@ -275,16 +282,20 @@ str_to_safe_chars() { # GNU tr does not handle UTF8 correctly, so sed is used instead # ... user may pass '/' to $r which would mess with default sed delimiter. if [[ "$r" == '/' ]]; then + #shellcheck disable=SC2001 echo "$s" | sed -e "s#$sed_class#$r#g" + sed_rc="$?" else + #shellcheck disable=SC2001 echo "$s" | sed -e "s/$sed_class/$r/g" + sed_rc="$?" fi - if [[ $? -eq 0 ]]; then - return 0 - else + + if [[ $sed_rc -ne 0 ]]; then e "... unable to make safe\nstr: $s\nreplacement char: $r\nchar list: $p\ncl:$sed_class" return 1 fi + return 0 } # @desc Prints default list of allowed chars for @@ -361,7 +372,7 @@ semver_a_ge_b() { if [[ "$a" =~ \- ]] && [[ "$b" =~ \- ]]; then # ... both are prerelease, just compare originals with out leading vVs - _semver_a_gt_b ${a##[vV]} ${b##[vV]} + _semver_a_gt_b "${a##[vV]}" "${b##[vV]}" return $? elif [[ "$a" =~ \- ]] ; then @@ -371,7 +382,7 @@ semver_a_ge_b() { fi fi - _semver_a_gt_b $va $vb + _semver_a_gt_b "$va" "$vb" } @@ -397,7 +408,7 @@ _semver_a_gt_b() { # possible to construct it. # export_build_url() { - if [[ ! -z "$CIRCLE_BUILD_URL" ]]; then + if [[ -n "$CIRCLE_BUILD_URL" ]]; then BUILD_URL="$CIRCLE_BUILD_URL" elif [[ "$TRAVIS" == "true" ]]; then if required_vars "TRAVIS_REPO_SLUG TRAVIS_JOB_ID" @@ -405,7 +416,7 @@ export_build_url() { BUILD_URL="https://travis-ci.org/$TRAVIS_REPO_SLUG/jobs/$TRAVIS_JOB_ID" fi fi - [[ ! -z "$BUILD_URL" ]] && export BUILD_URL + [[ -n "$BUILD_URL" ]] && export BUILD_URL } ##################################################################### @@ -459,6 +470,8 @@ set_log_prefix() { echo "${0#-}" else local src="" f="" func="" + # I don't think the one below is correct but suspect it's a bug + #shellcheck disable=SC2086 if [[ "$(caller ${FROM_STACKFRAME:-1})" =~ $__CALLER_REGX ]]; then src="${BASH_REMATCH[3]}" func_i=$(( $(( ${FROM_STACKFRAME:-1} + 1 )) )) @@ -469,13 +482,13 @@ set_log_prefix() { # ... if the cwd is changed, realpath may not work, so fall back to just $src f=$(realpath -- "$src" 2>/dev/null); [[ -z "$f" ]] && f=$src - if [[ ! -z "$DEBUG_ABS_PATHS" ]]; then + if [[ -n "$DEBUG_ABS_PATHS" ]]; then str=$f else str=$(basename "$f") fi - [[ ! -z "$func" ]] && str="$str:$func()" - echo $str + [[ -n "$func" ]] && str="$str:$func()" + echo "$str" fi } @@ -502,12 +515,14 @@ set_log_prefix() { # e() { local IFS='' - local pre="ERROR $(set_log_prefix)" - while read line ; do + local pre + + pre="ERROR $(set_log_prefix)" + while read -r line ; do echo -e "$pre: $line" >&2 done < <(echo -e "$*\n") - while read line; do + while read -r line; do echo -e "$pre: ${__TRED}$line${__TRESET}" >&2 done < <(echo -e "TRACE:\n$(__stacktrace)") } @@ -527,10 +542,12 @@ e() { # # INFO script.sh:main(): ... line 3 # i() { - [[ ! -z "$QUIET" ]] && return 0 + [[ -n "$QUIET" ]] && return 0 local IFS='' - local pre="INFO $(set_log_prefix)" - while read line ; do + local pre + + pre="INFO $(set_log_prefix)" + while read -r line ; do echo -e "$pre: $line" done < <(echo -e "$*") } @@ -551,11 +568,13 @@ i() { # @example # d "msg line 1" "line 2\nline3" d() { - [[ ! -z "$QUIET" ]] && return 0 + [[ -n "$QUIET" ]] && return 0 [[ -z "$DEBUG" ]] && return 0 local IFS='' - local pre="DEBUG $(set_log_prefix)" - while read line ; do + local pre + + pre="DEBUG $(set_log_prefix)" + while read -r line ; do echo -e "$pre: ${__TDEBUG}$line${__TRESET}" >&2 done < <(echo -e "$*") } @@ -563,52 +582,62 @@ d() { # @desc as with e(), but msg text is coloured red_e() { local IFS='' - local pre="ERROR $(set_log_prefix)" - while read line ; do + local pre + + pre="ERROR $(set_log_prefix)" + while read -r line ; do echo -e "$pre: ${__TRED}$line${__TRESET}" >&2 done < <(echo -e "$*\n") - while read line; do + while read -r line; do echo -e "$pre: ${__TRED}$line${__TRESET}" >&2 done < <(echo -e "TRACE:\n$(__stacktrace)") } # @desc as with i(), but msg text is highlighted bold_i() { - [[ ! -z "$QUIET" ]] && return 0 + [[ -n "$QUIET" ]] && return 0 local IFS='' - local pre="INFO $(set_log_prefix)" - while read line ; do + local pre + + pre="INFO $(set_log_prefix)" + while read -r line ; do echo -e "$pre: ${__TBOLD}$line${__TRESET}" done < <(echo -e "$*") } # @desc as with i(), but msg text is coloured. yellow_i() { - [[ ! -z "$QUIET" ]] && return 0 + [[ -n "$QUIET" ]] && return 0 local IFS='' - local pre="INFO $(set_log_prefix)" - while read line ; do + local pre + + pre="INFO $(set_log_prefix)" + while read -r line ; do echo -e "$pre: ${__TYELLOW}$line${__TRESET}" done < <(echo -e "$*") } # @desc as with i(), but msg text is coloured. green_i() { - [[ ! -z "$QUIET" ]] && return 0 + [[ -n "$QUIET" ]] && return 0 local IFS='' - local pre="INFO $(set_log_prefix)" - while read line ; do + local pre + + pre="INFO $(set_log_prefix)" + while read -r line ; do echo -e "$pre: ${__TGREEN}$line${__TRESET}" done < <(echo -e "$*") } # @desc as with i(), but msg text is coloured. blue_i() { - [[ ! -z "$QUIET" ]] && return 0 + [[ -n "$QUIET" ]] && return 0 local IFS='' - local pre="INFO $(set_log_prefix)" - while read line ; do + local pre + + pre="INFO $(set_log_prefix)" + while read -r line ; do echo -e "$pre: ${__TBLUE}$line${__TRESET}" done < <(echo -e "$*") } @@ -628,17 +657,18 @@ __stacktrace() { local frame=$start_index str="" while true; do - local st="" ln="" func="" file="" + local st="" ln="" func="" file="" indent="" tmpl="" - st=$(caller $frame) || break + st=$(caller "$frame") || break [[ "$st" =~ $__CALLER_REGX ]] \ && ln="${BASH_REMATCH[1]}" \ && func="${BASH_REMATCH[2]}" \ && file="${BASH_REMATCH[3]}" - local tmpl="%$(( $(( frame - $start_index )) * 2 ))s" - local indent="$(printf $tmpl)" + tmpl="%$(( $(( frame - start_index )) * 2 ))s" + #shellcheck disable=SC2059 + indent="$(printf $tmpl)" str_fmt="${indent}${func}() (file: $file, line: $ln)\n" str="$str$str_fmt" diff --git a/bash/terraform/terraform_run.functions b/bash/terraform/terraform_run.functions index 59b97ec..871693c 100644 --- a/bash/terraform/terraform_run.functions +++ b/bash/terraform/terraform_run.functions @@ -1,3 +1,4 @@ +#!/usr/bin/env bash # vim: et sr sw=4 ts=4 smartindent syntax=sh: # # @overview @@ -127,8 +128,8 @@ tf() { # terraform_version() { [[ -z "$TERRAFORM" ]] && TERRAFORM=$(tf) - $TERRAFORM --version | grep -Po '(?<=Terraform v)[\d\.]+' - if [[ $? -ne 0 ]] ; then + + if ! $TERRAFORM --version | grep -Po '(?<=Terraform v)[\d\.]+' ; then red_e "... could not determine version: used $TERRAFORM --version" return 1 fi @@ -142,7 +143,7 @@ __special_modes() { _disclaimer_devmode() { local v='DEVMODE' - if [[ ! -z "${!v}" ]] ; then + if [[ -n "${!v}" ]] ; then yellow_i "=================================================" yellow_i "= ACTIVATED: \$${v}" yellow_i "=================================================" @@ -155,7 +156,7 @@ _disclaimer_devmode() { _disclaimer_keep_plugins() { local v='KEEP_PLUGINS' - if [[ ! -z "${!v}" ]] ; then + if [[ -n "${!v}" ]] ; then yellow_i "=================================================" yellow_i "= ACTIVATED: \$${v}" yellow_i "=================================================" @@ -167,7 +168,7 @@ _disclaimer_keep_plugins() { _disclaimer_no_apply() { local v='NO_APPLY' - if [[ ! -z "${!v}" ]] ; then + if [[ -n "${!v}" ]] ; then yellow_i "=================================================" yellow_i "= ACTIVATED: \$${v}" yellow_i "=================================================" @@ -204,8 +205,9 @@ terraform_cleanup() { # terraform_init() { local init_opts="$TERRAFORM_INIT_OPTS" # init / remote config opts - local get_opts="$TERRAFORM_GET_OPTS" - local tv=$(terraform_version) ; [[ -z "$tv" ]] && e "version of terrform not found" && return 1 + local tv + + tv=$(terraform_version) ; [[ -z "$tv" ]] && e "version of terrform not found" && return 1 # ... for terraform 0.9.0+ local init_cmd="init" get_cmd="" @@ -219,12 +221,12 @@ terraform_init() { get_cmd="get" # need to get modules first. fi - if [[ ! -z "$DEVMODE" ]]; then - if [[ ! -z "$get_cmd" ]]; then + if [[ -n "$DEVMODE" ]]; then + if [[ -n "$get_cmd" ]]; then yellow_i "=================================================" yellow_i "... will run '$get_cmd' to fetch any modules" fi - if [[ ! -z "$init_opts" ]]; then + if [[ -n "$init_opts" ]]; then yellow_i "=================================================" yellow_i "= terraform $init_cmd opts:" yellow_i "=================================================" @@ -234,7 +236,7 @@ terraform_init() { fi # ... run terraform get if needed - [[ ! -z $get_cmd ]] && ! eval "$TERRAFORM get $TERRAFORM_GET_OPTS" && return 1 + [[ -n $get_cmd ]] && ! eval "$TERRAFORM get $TERRAFORM_GET_OPTS" && return 1 # ... now run init eval "$TERRAFORM $init_cmd $init_opts" } @@ -273,7 +275,7 @@ __skip_on() { local rc=1 for var_name in $flags; do - if [[ ! -z "${!var_name}" ]]; then + if [[ -n "${!var_name}" ]]; then d "... env var \$$var_name set." rc=0 fi @@ -364,7 +366,7 @@ __skip_on() { # terraform_run "/my/tf/dir" || exit 1 # unless DEVMODE, will apply terraform and switch dns. # terraform_run() { - local wd="$1" tf="" + local wd="$1" __special_modes @@ -376,7 +378,7 @@ terraform_run() { ( if [[ "$wd" ]]; then i "... changing to working dir $wd" - ! cd $wd && red_e "... could not change to $wd" && exit 1 + ! cd "$wd" && red_e "... could not change to $wd" && exit 1 fi terraform_cleanup || exit 1 diff --git a/shippable.build.sh b/shippable.build.sh index 211914e..c48143a 100644 --- a/shippable.build.sh +++ b/shippable.build.sh @@ -7,9 +7,19 @@ NVM_DIR=$CIUSER_HOME/.nvm install_tools() { sudo apt-get update - sudo apt-get install -y coreutils realpath + sudo apt-get install -y coreutils realpath curl xz-utils sort --help | grep -q -- '--version-sort' || return 1 # ...verify coreutils realpath $PWD >/dev/null || return 1 # ... verify realpath + # install the latest version of shellcheck + ( + cd /tmp \ + && curl -o shellcheck.tar.xz \ + https://storage.googleapis.com/shellcheck/shellcheck-stable.linux.x86_64.tar.xz \ + && tar xvf shellcheck.tar.xz \ + && mv shellcheck-stable/shellcheck /usr/bin/ \ + && chmod +x /usr/bin/shellcheck + ) || return 1 + shellcheck --version | grep -E '^version:' || return 1 # ... verify shellcheck and print version } # workarounds because of quirky behaviour when building diff --git a/shippable.test.sh b/shippable.test.sh index 7d9b27e..22e9eb9 100644 --- a/shippable.test.sh +++ b/shippable.test.sh @@ -25,6 +25,7 @@ run_bash_tests() { local rc=0 for lib in $(find_libs_to_test); do run_bash_test_file "$lib" || rc=1 + shellcheck "$lib" || rc=1 done return $rc }