diff options
Diffstat (limited to 'scripts/utils.inc')
| -rw-r--r-- | scripts/utils.inc | 273 | 
1 files changed, 273 insertions, 0 deletions
| diff --git a/scripts/utils.inc b/scripts/utils.inc new file mode 100644 index 00000000..0465de8f --- /dev/null +++ b/scripts/utils.inc @@ -0,0 +1,273 @@ +# This file provides: +# - a default control flow +#   * initializes the environment +#   * able to mock "git push" in your script and in all sub scripts +#   * call a function in your script based on the arguments +# - named argument parsing and automatic generation of the "usage" for your script +# - intercepting "git push" in your script and all sub scripts +# - utility functions +# +# Usage: +# - define the variable ARGS_DEF (see below) with the arguments for your script +# - include this file using `source utils.inc` at the end of your script. +# +# Default control flow: +# 0. Set the current directory to the directory of the script. By this +#    the script can be called from anywhere. +# 1. Parse the named arguments +# 2. If the parameter "git_push_dryrun" is set, all calls the `git push` in this script +#    or in child scripts will be intercepted so that the `--dry-run` and `--porcelain` is added +#    to show what the push would do but not actually do it. +# 3. If the parameter "verbose" is set, the `-x` flag will be set in bash. +# 4. The function "init" will be called if it exists +# 5. If the parameter "action" is set, it will call the function with the name of that parameter. +#    Otherwise the function "run" will be called. +# +# Named Argument Parsing: +# - The variable ARGS_DEF defines the valid command arguments +#   * Required args syntax: --paramName=paramRegex +#   * Optional args syntax: [--paramName=paramRegex] +#   * e.g. ARG_DEFS=("--required_param=(.+)" "[--optional_param=(.+)]") +# - Checks that: +#   * all arguments match to an entry in ARGS_DEF +#   * all required arguments are present +#   * all arguments match their regex +# - Afterwards, every paramter value will be stored in a variable +#   with the name of the parameter in upper case (with dash converted to underscore). +# +# Special arguments that are always available: +# - "--action=.*": This parameter will be used to dispatch to a function with that name when the +#   script is started +# - "--git_push_dryrun=true": This will intercept all calls to `git push` in this script +#   or in child scripts so that the `--dry-run` and `--porcelain` is added +#   to show what the push would do but not actually do it. +# - "--verbose=true": This will set the `-x` flag in bash so that all calls will be logged +# +# Utility functions: +# - readJsonProp +# - replaceJsonProp +# - resolveDir +# - getVar +# - serVar +# - isFunction + + +function usage { +  echo "Usage: ${0} ${ARG_DEFS[@]}" +  exit 1 +} + + +function parseArgs { +  local REQUIRED_ARG_NAMES=() + +  # -- helper functions +  function varName { +    # everything to upper case and dash to underscore +    echo ${1//-/_} | tr '[:lower:]' '[:upper:]' +  } + +  function readArgDefs { +    local ARG_DEF +    local AD_OPTIONAL +    local AD_NAME +    local AD_RE + +    # -- helper functions +    function parseArgDef { +      local ARG_DEF_REGEX="(\[?)--([^=]+)=(.*)" +      if [[ ! $1 =~ $ARG_DEF_REGEX ]]; then +        echo "Internal error: arg def has wrong format: $ARG_DEF" +        exit 1 +      fi +      AD_OPTIONAL="${BASH_REMATCH[1]}" +      AD_NAME="${BASH_REMATCH[2]}" +      AD_RE="${BASH_REMATCH[3]}" +      if [[ $AD_OPTIONAL ]]; then +        # Remove last bracket for optional args. +        # Can't put this into the ARG_DEF_REGEX somehow... +        AD_RE=${AD_RE%?} +      fi +    } + +    # -- run +    for ARG_DEF in "${ARG_DEFS[@]}" +    do +      parseArgDef $ARG_DEF + +      local AD_NAME_UPPER=$(varName $AD_NAME) +      setVar "${AD_NAME_UPPER}_OPTIONAL" "$AD_OPTIONAL" +      setVar "${AD_NAME_UPPER}_RE" "$AD_RE" +      if [[ ! $AD_OPTIONAL ]]; then +        REQUIRED_ARG_NAMES+=($AD_NAME) +      fi +    done +  } + +  function readAndValidateArgs { +    local ARG_NAME +    local ARG_VALUE +    local ARG_NAME_UPPER + +    # -- helper functions +    function parseArg { +      local ARG_REGEX="--([^=]+)=?(.*)" + +      if [[ ! $1 =~ $ARG_REGEX ]]; then +        echo "Can't parse argument $i" +        usage +      fi + +      ARG_NAME="${BASH_REMATCH[1]}" +      ARG_VALUE="${BASH_REMATCH[2]}" +      ARG_NAME_UPPER=$(varName $ARG_NAME) +    } + +    function validateArg { +      local AD_RE=$(getVar ${ARG_NAME_UPPER}_RE) + +      if [[ ! $AD_RE ]]; then +        echo "Unknown option: $ARG_NAME" +        usage +      fi + +      if [[ ! $ARG_VALUE =~ ^${AD_RE}$ ]]; then +        echo "Wrong format: $ARG_NAME" +        usage; +      fi + +      # validate that the "action" option points to a valid function +      if [[ $ARG_NAME == "action" ]] && ! isFunction $ARG_VALUE; then +        echo "No action $ARG_VALUE defined in this script" +        usage; +      fi +    } + +    # -- run +    for i in "$@" +    do +      parseArg $i +      validateArg +      setVar "${ARG_NAME_UPPER}" "$ARG_VALUE" +    done +  } + +  function checkMissingArgs { +    local ARG_NAME +    for ARG_NAME in "${REQUIRED_ARG_NAMES[@]}" +    do +      ARG_VALUE=$(getVar $(varName $ARG_NAME)) + +      if [[ ! $ARG_VALUE ]]; then +        echo "Missing: $ARG_NAME" +        usage; +      fi +    done +  } + +  # -- run +  readArgDefs +  readAndValidateArgs "$@" +  checkMissingArgs + +} + +# getVar(varName) +function getVar { +  echo ${!1} +} + +# setVar(varName, varValue) +function setVar { +  eval "$1=\"$2\"" +} + +# isFunction(name) +# - to be used in an if, so return 0 if successful and 1 if not! +function isFunction { +  if [[ $(type -t $1) == "function" ]]; then +    return 0 +  else +    return 1 +  fi +} + +# readJsonProp(jsonFile, property) +# - restriction: property needs to be on an own line! +function readJsonProp { +  echo $(sed -En 's/.*"'$2'"[ ]*:[ ]*"(.*)".*/\1/p' $1) +} + +# replaceJsonProp(jsonFile, propertyRegex, valueRegex, replacePattern) +# - note: propertyRegex will be automatically placed into a +#   capturing group! -> all other groups start at index 2! +function replaceJsonProp { +  sed -i .tmp -E 's/"('$2')"[ ]*:[ ]*"'$3'"/"\1": "'$4'"/' $1 +  rm $1.tmp +} + +# resolveDir(relativeDir) +# - resolves a directory relative to the current script +function resolveDir { +  echo $(cd $SCRIPT_DIR; cd $1; pwd) +} + +function git_push_dryrun_proxy { +  echo "## git push dryrun proxy enabled!" +  export ORIGIN_GIT=$(which git) + +  function git { +	  local var +	  ARGS=("$@") +	  if [[ $1 == "push" ]]; then +	    ARGS+=("--dry-run" "--porcelain") +	    echo "####### START GIT PUSH DRYRUN #######" +      echo "${ARGS[@]}" +	  fi +	  if [[ $1 == "commit" ]]; then +      echo "${ARGS[@]}" +	  fi +	  $ORIGIN_GIT "${ARGS[@]}" +	  if [[ $1 == "push" ]]; then +	    echo "####### END GIT PUSH DRYRUN #######" +	  fi +  } + +  export -f git +} + +function main { +  # normalize the working dir to the directory of the script +  cd $(dirname $0);SCRIPT_DIR=$(pwd) + +  ARG_DEFS+=("[--git-push-dryrun=true]" "[--verbose=true]") +  parseArgs "$@" + +  # --git_push_dryrun argument +  if [[ $GIT_PUSH_DRYRUN ]]; then +    git_push_dryrun_proxy +  fi + +  # stop on errors +  set -e + +  # --verbose argument +  if [[ $VERBOSE ]]; then +    set -x +  fi + +  if isFunction init; then +    init "$@" +  fi + +  # jump to the function denoted by the --action argument, +  # otherwise call the "run" function +  if [[ $ACTION ]]; then +    $ACTION "$@" +  else +    run "$@" +  fi +} + + +main "$@" | 
