280 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			280 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
# 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 to `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 parameter 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 execute 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
 | 
						|
 | 
						|
# always stop on errors
 | 
						|
set -e
 | 
						|
 | 
						|
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 a single 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 {
 | 
						|
  replaceInFile $1 '"('$2')"[ ]*:[ ]*"'$3'"' '"\1": "'$4'"'
 | 
						|
}
 | 
						|
 | 
						|
# replaceInFile(file, findPattern, replacePattern)
 | 
						|
function replaceInFile {
 | 
						|
  sed -i .tmp -E "s/$2/$3/" $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 ARGS=("$@")
 | 
						|
	  local RC
 | 
						|
	  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[@]}"
 | 
						|
	  RC=$?
 | 
						|
	  if [[ $1 == "push" ]]; then
 | 
						|
	    echo "####### END GIT PUSH DRYRUN #######"
 | 
						|
	  fi
 | 
						|
	  return $RC
 | 
						|
  }
 | 
						|
 | 
						|
  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|false)]" "[--verbose=(true|false)]")
 | 
						|
  parseArgs "$@"
 | 
						|
 | 
						|
  # --git_push_dryrun argument
 | 
						|
  if [[ $GIT_PUSH_DRYRUN == "true" ]]; then
 | 
						|
    git_push_dryrun_proxy
 | 
						|
  fi
 | 
						|
 | 
						|
  # --verbose argument
 | 
						|
  if [[ $VERBOSE == "true" ]]; 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 "$@"
 |