You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@yetus.apache.org by aw...@apache.org on 2018/11/11 22:19:25 UTC

[09/17] yetus git commit: YETUS-15. build environment

http://git-wip-us.apache.org/repos/asf/yetus/blob/6ebaa111/precommit/src/main/shell/test-patch.d/maven.sh
----------------------------------------------------------------------
diff --git a/precommit/src/main/shell/test-patch.d/maven.sh b/precommit/src/main/shell/test-patch.d/maven.sh
new file mode 100755
index 0000000..12fb239
--- /dev/null
+++ b/precommit/src/main/shell/test-patch.d/maven.sh
@@ -0,0 +1,761 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+declare -a MAVEN_ARGS
+
+if [[ -z "${MAVEN_HOME:-}" ]]; then
+  MAVEN=mvn
+else
+  MAVEN=${MAVEN_HOME}/bin/mvn
+fi
+
+MAVEN_CUSTOM_REPOS=false
+MAVEN_CUSTOM_REPOS_DIR="@@@WORKSPACE@@@/yetus-m2"
+MAVEN_DEPENDENCY_ORDER=true
+
+add_test_type mvnsite
+add_build_tool maven
+
+## @description  Add the given test type as requiring a mvn install during the branch phase
+## @audience     public
+## @stability    stable
+## @replaceable  yes
+## @param        test
+function maven_add_install
+{
+    yetus_add_entry MAVEN_NEED_INSTALL "${1}"
+}
+
+## @description  Remove the given test type as requiring a mvn install
+## @audience     public
+## @stability    stable
+## @replaceable  yes
+## @param        test
+function maven_delete_install
+{
+  yetus_delete_entry MAVEN_NEED_INSTALL "${1}"
+}
+
+## @description  replace the custom repo with either home or workspace if jenkins.
+## @description  is configured. this gets called in a few places since different
+## @description  circumstances dictate a few places where it may be needed.
+## @audience     private
+## @stability    evolving
+function maven_ws_replace
+{
+  declare previous=${MAVEN_CUSTOM_REPOS_DIR}
+
+  if [[ ${JENKINS} == true ]] && [[ -n "${WORKSPACE}" ]]; then
+    MAVEN_CUSTOM_REPOS_DIR=$(echo "${MAVEN_CUSTOM_REPOS_DIR}" | "${SED}" -e "s,@@@WORKSPACE@@@,${WORKSPACE},g" )
+  else
+    MAVEN_CUSTOM_REPOS_DIR=$(echo "${MAVEN_CUSTOM_REPOS_DIR}" | "${SED}" -e "s,@@@WORKSPACE@@@,${HOME},g" )
+  fi
+  if [[ "${previous}" != "${MAVEN_CUSTOM_REPOS_DIR}" ]]; then
+    # put this in the array so that if docker is run, this is already resolved
+    USER_PARAMS=("${USER_PARAMS[@]}" "--mvn-custom-repos-dir=${MAVEN_CUSTOM_REPOS_DIR}")
+  fi
+}
+
+function maven_usage
+{
+  maven_ws_replace
+  yetus_add_option "--mvn-cmd=<cmd>" "The 'mvn' command to use (default \${MAVEN_HOME}/bin/mvn, or 'mvn')"
+  yetus_add_option "--mvn-custom-repos" "Use per-project maven repos"
+  yetus_add_option "--mvn-custom-repos-dir=dir" "Location of repos, default is '${MAVEN_CUSTOM_REPOS_DIR}'"
+  yetus_add_option "--mvn-deps-order=<bool>" "Disable maven's auto-dependency module ordering (Default: '${MAVEN_DEPENDENCY_ORDER}')"
+  yetus_add_option "--mvn-settings=file" "File to use for settings.xml"
+}
+
+## @description  parse maven build tool args
+## @replaceable  yes
+## @audience     public
+## @stability    stable
+function maven_parse_args
+{
+  local i
+
+  for i in "$@"; do
+    case ${i} in
+      --mvn-cmd=*)
+        MAVEN=${i#*=}
+      ;;
+      --mvn-custom-repos)
+        MAVEN_CUSTOM_REPOS=true
+      ;;
+      --mvn-custom-repos-dir=*)
+        MAVEN_CUSTOM_REPOS_DIR=${i#*=}
+      ;;
+      --mvn-deps-order=*)
+        MAVEN_DEPENDENCY_ORDER=${i#*=}
+      ;;
+      --mvn-settings=*)
+        MAVEN_SETTINGS=${i#*=}
+        if [[ -f ${MAVEN_SETTINGS} ]]; then
+          MAVEN_ARGS=("${MAVEN_ARGS[@]}" "--settings=${MAVEN_SETTINGS}")
+        else
+          yetus_error "WARNING: ${MAVEN_SETTINGS} not found. Ignoring."
+        fi
+      ;;
+    esac
+  done
+
+  if [[ ${OFFLINE} == "true" ]]; then
+    MAVEN_ARGS=("${MAVEN_ARGS[@]}" --offline)
+  fi
+
+  maven_ws_replace
+}
+
+## @description  initialize the maven build tool
+## @replaceable  yes
+## @audience     public
+## @stability    stable
+function maven_initialize
+{
+  # we need to do this before docker does it as root
+
+  maven_add_install compile
+  maven_add_install mvnsite
+  maven_add_install unit
+
+  # Tell the reaper about the maven surefire plugin
+  reaper_add_name surefirebooter
+
+  # we need to do this before docker does it as root
+  maven_ws_replace
+
+  if [[ ! ${MAVEN_CUSTOM_REPOS_DIR} =~ ^/ ]]; then
+    yetus_error "ERROR: --mvn-custom-repos-dir must be an absolute path."
+    return 1
+  fi
+
+  if [[ ${MAVEN_CUSTOM_REPOS} = true ]]; then
+    MAVEN_LOCAL_REPO="${MAVEN_CUSTOM_REPOS_DIR}"
+    if [[ -e "${MAVEN_CUSTOM_REPOS_DIR}"
+       && ! -d "${MAVEN_CUSTOM_REPOS_DIR}" ]]; then
+      yetus_error "ERROR: ${MAVEN_CUSTOM_REPOS_DIR} is not a directory."
+      return 1
+    elif [[ ! -d "${MAVEN_CUSTOM_REPOS_DIR}" ]]; then
+      yetus_debug "Creating ${MAVEN_CUSTOM_REPOS_DIR}"
+      mkdir -p "${MAVEN_CUSTOM_REPOS_DIR}"
+    fi
+  fi
+
+  if [[ -e "${HOME}/.m2"
+     && ! -d "${HOME}/.m2" ]]; then
+    yetus_error "ERROR: ${HOME}/.m2 is not a directory."
+    return 1
+  elif [[ ! -e "${HOME}/.m2" ]]; then
+    yetus_debug "Creating ${HOME}/.m2"
+    mkdir -p "${HOME}/.m2"
+  fi
+}
+
+## @audience     private
+## @stability    stable
+function mvnsite_precheck
+{
+  if ! verify_plugin_enabled 'maven'; then
+    yetus_error "ERROR: to run the mvnsite test you must ensure the 'maven' plugin is enabled."
+    return 1
+  fi
+}
+
+## @audience     private
+## @stability    stable
+function maven_precheck
+{
+  declare logfile="${PATCH_DIR}/mvnrepoclean.log"
+  declare line
+  declare maven_version
+
+  if ! verify_plugin_enabled 'maven'; then
+    yetus_error "ERROR: you can't specify maven as the buildtool if you don't enable the plugin."
+    return 1
+  fi
+
+  if ! verify_command maven "${MAVEN}"; then
+    add_vote_table -1 maven "ERROR: maven was not available."
+    return 1
+  fi
+
+  if [[ ! ${MAVEN_CUSTOM_REPOS_DIR} =~ ^/ ]]; then
+    yetus_error "ERROR: --mvn-custom-repos-dir must be an absolute path."
+    return 1
+  fi
+
+  MAVEN_ARGS=("${MAVEN_ARGS[@]}" "--batch-mode")
+
+  if [[ ${MAVEN_CUSTOM_REPOS} = true ]]; then
+    MAVEN_LOCAL_REPO="${MAVEN_CUSTOM_REPOS_DIR}/${PROJECT_NAME}-${PATCH_BRANCH}-${BUILDMODE}-${INSTANCE}"
+    if [[ -e "${MAVEN_LOCAL_REPO}"
+       && ! -d "${MAVEN_LOCAL_REPO}" ]]; then
+      yetus_error "ERROR: ${MAVEN_LOCAL_REPO} is not a directory."
+      return 1
+    fi
+
+    if [[ ! -d "${MAVEN_LOCAL_REPO}" ]]; then
+      yetus_debug "Creating ${MAVEN_LOCAL_REPO}"
+      mkdir -p "${MAVEN_LOCAL_REPO}"
+      if [[ $? -ne 0 ]]; then
+        yetus_error "ERROR: Unable to create ${MAVEN_LOCAL_REPO}"
+        return 1
+      fi
+    fi
+    touch "${MAVEN_LOCAL_REPO}"
+
+    # if we have a local settings.xml file, we copy it.
+    if [[ -f "${HOME}/.m2/settings.xml" ]]; then
+      cp -p "${HOME}/.m2/settings.xml" "${MAVEN_LOCAL_REPO}"
+    fi
+    MAVEN_ARGS=("${MAVEN_ARGS[@]}" "-Dmaven.repo.local=${MAVEN_LOCAL_REPO}")
+
+    # let's do some cleanup while we're here
+
+    find "${MAVEN_CUSTOM_REPOS_DIR}" \
+      -name '*-*-*' \
+      -type d \
+      -mtime +30 \
+      -maxdepth 1 \
+      -print \
+        > "${logfile}"
+
+    while read -r line; do
+      echo "Removing old maven repo ${line}"
+      rm -rf "${line}"
+    done < "${logfile}"
+  fi
+
+  # finally let folks know what version they'll be dealing with.
+  maven_version=$(${MAVEN} --offline --version 2>/dev/null | head -n 1 2>/dev/null)
+  add_footer_table maven "version: ${maven_version}"
+}
+
+function maven_filefilter
+{
+  declare filename=$1
+
+  if [[ ${filename} =~ pom\.xml$ ]]; then
+    yetus_debug "tests/compile: ${filename}"
+    add_test compile
+  fi
+}
+
+function maven_buildfile
+{
+  echo "pom.xml"
+}
+
+function maven_executor
+{
+  echo "${MAVEN}" "${MAVEN_ARGS[@]}"
+}
+
+function mvnsite_filefilter
+{
+  local filename=$1
+
+  if [[ ${BUILDTOOL} = maven ]]; then
+    if [[ ${filename} =~ src/site ]]; then
+      yetus_debug "tests/mvnsite: ${filename}"
+      add_test mvnsite
+    fi
+  fi
+}
+
+## @description  maven version of the modules_worker routine
+## @audience     public
+## @stability    stable
+function maven_modules_worker
+{
+  declare repostatus=$1
+  declare tst=$2
+  declare maven_unit_test_filter
+
+  maven_unit_test_filter="$(maven_unit_test_filter)"
+  # shellcheck disable=SC2034
+  UNSUPPORTED_TEST=false
+
+  case ${tst} in
+    findbugs)
+      modules_workers "${repostatus}" findbugs test-compile findbugs:findbugs -DskipTests=true
+    ;;
+    compile)
+      modules_workers "${repostatus}" compile clean test-compile -DskipTests=true
+    ;;
+    distclean)
+      modules_workers "${repostatus}" distclean clean -DskipTests=true
+    ;;
+    javadoc)
+      modules_workers "${repostatus}" javadoc clean javadoc:javadoc -DskipTests=true
+    ;;
+    scaladoc)
+      modules_workers "${repostatus}" scaladoc clean scala:doc -DskipTests=true
+    ;;
+    unit)
+      if [[ -n "${maven_unit_test_filter}" ]]; then
+        modules_workers "${repostatus}" unit clean test -fae "${maven_unit_test_filter}"
+      else
+        modules_workers "${repostatus}" unit clean test -fae
+      fi
+    ;;
+    *)
+      # shellcheck disable=SC2034
+      UNSUPPORTED_TEST=true
+      if [[ ${repostatus} = patch ]]; then
+        add_footer_table "${tst}" "not supported by the ${BUILDTOOL} plugin"
+      fi
+      yetus_error "WARNING: ${tst} is unsupported by ${BUILDTOOL}"
+      return 1
+    ;;
+  esac
+}
+
+function maven_javac_logfilter
+{
+  declare input=$1
+  declare output=$2
+
+  ${GREP} -E '\[(ERROR|WARNING)\] /.*\.java:' "${input}" > "${output}"
+}
+
+## @description  Helper for check_patch_javadoc
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @return       0 on success
+## @return       1 on failure
+function maven_javadoc_logfilter
+{
+  declare input=$1
+  declare output=$2
+
+  ${GREP} -E '\[(ERROR|WARNING)\] /.*\.java:' "${input}" > "${output}"
+}
+
+## @description  handle diffing maven javac errors
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @param        branchlog
+## @param        patchlog
+## @return       differences
+function maven_javac_calcdiffs
+{
+  declare orig=$1
+  declare new=$2
+  declare tmp=${PATCH_DIR}/pl.$$.${RANDOM}
+  declare j
+
+  # first, strip :[line
+  # this keeps file,column in an attempt to increase
+  # accuracy in case of multiple, repeated errors
+  # since the column number shouldn't change
+  # if the line of code hasn't been touched
+  # shellcheck disable=SC2016
+  ${SED} -e 's#:\[[0-9]*,#:#' "${orig}" > "${tmp}.branch"
+  # shellcheck disable=SC2016
+  ${SED} -e 's#:\[[0-9]*,#:#' "${new}" > "${tmp}.patch"
+
+  # compare the errors, generating a string of line
+  # numbers. Sorry portability: GNU diff makes this too easy
+  ${DIFF} --unchanged-line-format="" \
+     --old-line-format="" \
+     --new-line-format="%dn " \
+     "${tmp}.branch" \
+     "${tmp}.patch" > "${tmp}.lined"
+
+  # now, pull out those lines of the raw output
+  # shellcheck disable=SC2013
+  for j in $(cat "${tmp}.lined"); do
+    # shellcheck disable=SC2086
+    head -${j} "${new}" | tail -1
+  done
+
+  rm "${tmp}.branch" "${tmp}.patch" "${tmp}.lined" 2>/dev/null
+}
+
+## @description  handle diffing maven javadoc errors
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @param        branchlog
+## @param        patchlog
+## @return       differences
+function maven_javadoc_calcdiffs
+{
+  declare orig=$1
+  declare new=$2
+  declare tmp=${PATCH_DIR}/pl.$$.${RANDOM}
+  declare j
+
+  # can't use the generic handler for this because of the
+  # [WARNING], etc headers.
+  # strip :linenum from the output, keeping the filename
+  # shellcheck disable=SC2016
+  ${SED} -e 's#:[0-9]*:#:#' "${orig}" > "${tmp}.branch"
+  # shellcheck disable=SC2016
+  ${SED} -e 's#:[0-9]*:#:#' "${new}" > "${tmp}.patch"
+
+  # compare the errors, generating a string of line
+  # numbers. Sorry portability: GNU diff makes this too easy
+  ${DIFF} --unchanged-line-format="" \
+     --old-line-format="" \
+     --new-line-format="%dn " \
+     "${tmp}.branch" \
+     "${tmp}.patch" > "${tmp}.lined"
+
+  # now, pull out those lines of the raw output
+  # shellcheck disable=SC2013
+  for j in $(cat "${tmp}.lined"); do
+    # shellcheck disable=SC2086
+    head -${j} "${new}" | tail -1
+  done
+
+  rm "${tmp}.branch" "${tmp}.patch" "${tmp}.lined" 2>/dev/null
+}
+
+function maven_builtin_personality_modules
+{
+  declare repostatus=$1
+  declare testtype=$2
+
+  declare module
+
+  yetus_debug "Using builtin personality_modules"
+  yetus_debug "Personality: ${repostatus} ${testtype}"
+
+  clear_personality_queue
+
+  # this always makes sure the local repo has a fresh
+  # copy of everything per pom rules.
+  if [[ ${repostatus} == branch
+        && ${testtype} == mvninstall ]] ||
+     [[ "${BUILDMODE}" = full ]];then
+    personality_enqueue_module "${CHANGED_UNION_MODULES}"
+    return
+  fi
+
+  for module in "${CHANGED_MODULES[@]}"; do
+    personality_enqueue_module "${module}"
+  done
+}
+
+function maven_builtin_personality_file_tests
+{
+  local filename=$1
+
+  yetus_debug "Using builtin mvn personality_file_tests"
+
+  if [[ ${filename} =~ src/main/webapp ]]; then
+    yetus_debug "tests/webapp: ${filename}"
+  elif [[ ${filename} =~ \.sh
+       || ${filename} =~ \.cmd
+       || ${filename} =~ src/main/scripts
+       || ${filename} =~ src/test/scripts
+       ]]; then
+    yetus_debug "tests/shell: ${filename}"
+  elif [[ ${filename} =~ \.c$
+       || ${filename} =~ \.cc$
+       || ${filename} =~ \.h$
+       || ${filename} =~ \.hh$
+       || ${filename} =~ \.proto$
+       || ${filename} =~ \.cmake$
+       || ${filename} =~ CMakeLists.txt
+       ]]; then
+    yetus_debug "tests/units: ${filename}"
+    add_test cc
+    add_test unit
+  elif [[ ${filename} =~ \.scala$
+       || ${filename} =~ src/scala ]]; then
+    add_test scalac
+    add_test scaladoc
+    add_test unit
+  elif [[ ${filename} =~ build.xml$
+       || ${filename} =~ pom.xml$
+       || ${filename} =~ \.java$
+       || ${filename} =~ src/main
+       ]]; then
+      yetus_debug "tests/javadoc+units: ${filename}"
+      add_test javac
+      add_test javadoc
+      add_test unit
+  fi
+
+  if [[ ${filename} =~ src/test ]]; then
+    yetus_debug "tests"
+    add_test unit
+  fi
+
+  if [[ ${filename} =~ \.java$ ]]; then
+    add_test findbugs
+  fi
+}
+
+## @description  Maven unit test filter file string
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @return       string
+function maven_unit_test_filter()
+{
+  declare filtered
+
+  if [[ ! -z "${UNIT_TEST_FILTER_FILE}" ]]; then
+    while read -r line || [[ -n "${line}" ]]; do
+      if [[ -z $line ]]; then
+        continue
+      fi
+
+      filtered="${filtered}${line},"
+    done < "${UNIT_TEST_FILTER_FILE}"
+  fi
+
+  if [[ -z "${filtered}" ]]; then
+    printf "%s" ""
+  else
+    printf "%s" "-Dtest=${filtered%,}"
+  fi
+}
+
+## @description  Confirm site pre-patch
+## @audience     private
+## @stability    stable
+## @replaceable  no
+## @return       0 on success
+## @return       1 on failure
+function mvnsite_postcompile
+{
+  declare repostatus=$1
+  declare result=0
+
+  if [[ ${BUILDTOOL} != maven ]]; then
+    return 0
+  fi
+
+  if ! verify_needed_test mvnsite; then
+    return 0
+  fi
+
+  if [[ "${repostatus}" = branch ]]; then
+    big_console_header "maven site verification: ${PATCH_BRANCH}"
+  else
+    big_console_header "maven site verification: ${BUILDMODE}"
+  fi
+
+  personality_modules "${repostatus}" mvnsite
+  modules_workers "${repostatus}" mvnsite clean site site:stage
+  result=$?
+  modules_messages "${repostatus}" mvnsite true
+  if [[ ${result} != 0 ]]; then
+    return 1
+  fi
+  return 0
+}
+
+## @description  maven precompile phase
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @return       0 on success
+## @return       1 on failure
+function maven_precompile
+{
+  declare repostatus=$1
+  declare result=0
+  declare need=false
+
+  if [[ ${BUILDTOOL} != maven ]]; then
+    return 0
+  fi
+
+  # not everything needs a maven install
+  # but quite a few do ...
+  # shellcheck disable=SC2086
+  for index in ${MAVEN_NEED_INSTALL}; do
+    if verify_needed_test "${index}"; then
+      need=true
+    fi
+  done
+
+  if [[ "${need}" = false ]]; then
+    return 0
+  fi
+
+  if [[ "${repostatus}" = branch ]]; then
+    big_console_header "maven install: ${PATCH_BRANCH}"
+  else
+    big_console_header "maven install: ${BUILDMODE}"
+  fi
+
+  personality_modules "${repostatus}" mvninstall
+  modules_workers "${repostatus}" mvninstall -fae \
+    clean install \
+    -DskipTests=true -Dmaven.javadoc.skip=true \
+    -Dcheckstyle.skip=true -Dfindbugs.skip=true
+  result=$?
+  modules_messages "${repostatus}" mvninstall true
+  if [[ ${result} != 0 ]]; then
+    return 1
+  fi
+  return 0
+}
+
+function maven_docker_support
+{
+  DOCKER_EXTRAARGS=("${DOCKER_EXTRAARGS[@]}" "-v" "${HOME}/.m2:/home/${USER_NAME}/.m2")
+
+  if [[ ${MAVEN_CUSTOM_REPOS} = true ]]; then
+    DOCKER_EXTRAARGS=("${DOCKER_EXTRAARGS[@]}" "-v" "${MAVEN_CUSTOM_REPOS_DIR}:${MAVEN_CUSTOM_REPOS_DIR}")
+  fi
+}
+
+## @description  worker for maven reordering. MAVEN_DEP_LOG is set to the log file name
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @param        repostatus
+## @return       0 = success
+## @return       1 = failure
+function maven_reorder_module_process
+{
+  declare repostatus=$1
+  declare module
+  declare line
+  declare indexm
+  declare indexn
+  declare -a newlist
+  declare fn
+  declare needroot=false
+  declare found
+  declare ret
+
+  for module in "${CHANGED_MODULES[@]}"; do
+    if [[ "${module}" = \. ]]; then
+      needroot=true
+    fi
+  done
+
+  fn=$(module_file_fragment "${CHANGED_UNION_MODULES}")
+  pushd "${BASEDIR}/${CHANGED_UNION_MODULES}" >/dev/null
+
+  # get the module directory list in the correct order based on maven dependencies
+  # shellcheck disable=SC2046
+  echo_and_redirect "${PATCH_DIR}/maven-${repostatus}-dirlist-${fn}.txt" \
+    $("${BUILDTOOL}_executor") "-fae" "-q" "exec:exec" "-Dexec.executable=pwd" "-Dexec.args=''"
+  MAVEN_DEP_LOG="maven-${repostatus}-dirlist-${fn}.txt"
+  ret=$?
+
+  while read -r line; do
+    for indexm in "${CHANGED_MODULES[@]}"; do
+      if [[ ${line} == "${BASEDIR}/${indexm}" ]]; then
+        yetus_debug "mrm: placing ${indexm} from dir: ${line}"
+        newlist=("${newlist[@]}" "${indexm}")
+        break
+      fi
+    done
+  done < "${PATCH_DIR}/maven-${repostatus}-dirlist-${fn}.txt"
+  popd >/dev/null
+
+  if [[ "${needroot}" = true ]]; then
+    newlist=("${newlist[@]}" ".")
+  fi
+
+  indexm="${#CHANGED_MODULES[@]}"
+  indexn="${#newlist[@]}"
+
+  if [[ ${indexm} -ne ${indexn} ]]; then
+    yetus_debug "mrm: Missed a module"
+    for indexm in "${CHANGED_MODULES[@]}"; do
+      found=false
+      for indexn in "${newlist[@]}"; do
+        if [[ "${indexn}" = "${indexm}" ]]; then
+          found=true
+          break
+        fi
+      done
+      if [[ ${found} = false ]]; then
+        yetus_debug "mrm: missed ${indexm}"
+        newlist=("${newlist[@]}" "${indexm}")
+      fi
+    done
+  fi
+
+  CHANGED_MODULES=("${newlist[@]}")
+  return "${ret}"
+}
+
+## @description  take a stab at reordering modules based upon
+## @description  maven dependency order
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @param        repostatus
+## @param        module
+function maven_reorder_modules
+{
+  declare repostatus=$1
+  declare index
+  declare ret
+
+  if [[ "${MAVEN_DEPENDENCY_ORDER}" != "true" ]]; then
+    return
+  fi
+
+  # don't bother if there is only one
+  index="${#CHANGED_MODULES[@]}"
+  if [[ ${index} -eq 1 ]]; then
+    return
+  fi
+
+  big_console_header "Determining Maven Dependency Order (downloading dependencies in the process)"
+
+  start_clock
+
+  maven_reorder_module_process "${repostatus}"
+  ret=$?
+
+  yetus_debug "Maven: finish re-ordering modules"
+  yetus_debug "Finished list: ${CHANGED_MODULES[*]}"
+
+  # build some utility module lists for maven modules
+  for index in "${CHANGED_MODULES[@]}"; do
+    if [[ -d "${index}/src" ]]; then
+      MAVEN_SRC_MODULES=("${MAVEN_SRC_MODULES[@]}" "${index}")
+      if [[ -d "${index}/src/test" ]]; then
+        MAVEN_SRCTEST_MODULES=("${MAVEN_SRCTEST_MODULES[@]}" "${index}")
+      fi
+    fi
+  done
+
+  if [[ "${BUILDMODE}" = patch ]]; then
+    if [[ ${ret} == 0 ]]; then
+      add_vote_table 0 mvndep "Maven dependency ordering for ${repostatus}"
+    else
+      add_vote_table -1 mvndep "Maven dependency ordering for ${repostatus}"
+      add_footer_table mvndep "${MAVEN_DEP_LOG}"
+    fi
+  else
+    if [[ ${ret} == 0 ]]; then
+      add_vote_table 0 mvndep "Maven dependency ordering"
+    else
+      add_vote_table -1 mvndep "Maven dependency ordering"
+      add_footer_table mvndep "${MAVEN_DEP_LOG}"
+    fi
+  fi
+
+  echo "Elapsed: $(clock_display $(stop_clock))"
+}

http://git-wip-us.apache.org/repos/asf/yetus/blob/6ebaa111/precommit/src/main/shell/test-patch.d/nobuild.sh
----------------------------------------------------------------------
diff --git a/precommit/src/main/shell/test-patch.d/nobuild.sh b/precommit/src/main/shell/test-patch.d/nobuild.sh
new file mode 100755
index 0000000..cf5a2ad
--- /dev/null
+++ b/precommit/src/main/shell/test-patch.d/nobuild.sh
@@ -0,0 +1,54 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+add_build_tool nobuild
+
+function nobuild_buildfile
+{
+  echo
+}
+
+function nobuild_executor
+{
+  echo "true"
+}
+
+function nobuild_modules_worker
+{
+  local status=$1
+  local testtype=$2
+  modules_workers "${status}" "${testtype}"
+}
+
+function nobuild_builtin_personality_modules
+{
+  local status=$1
+  local testtype=$2
+  yetus_debug "built-in personality for no build system: ${status} ${testtype}"
+
+  clear_personality_queue
+  for module in "${CHANGED_MODULES[@]}"; do
+    personality_enqueue_module "${module}"
+  done
+}
+
+function nobuild_builtin_personality_file_tests
+{
+  local filename=$1
+
+  yetus_debug "Using built-in no build system personality_file_tests."
+  yetus_debug "    given file ${filename}"
+}

http://git-wip-us.apache.org/repos/asf/yetus/blob/6ebaa111/precommit/src/main/shell/test-patch.d/pathlen.sh
----------------------------------------------------------------------
diff --git a/precommit/src/main/shell/test-patch.d/pathlen.sh b/precommit/src/main/shell/test-patch.d/pathlen.sh
new file mode 100755
index 0000000..a013adb
--- /dev/null
+++ b/precommit/src/main/shell/test-patch.d/pathlen.sh
@@ -0,0 +1,126 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+add_test_type pathlen
+
+PATHLEN_SIZE=240
+
+## @description  pathlen usage hook
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+function pathlen_usage
+{
+  yetus_add_option "--pathlen-size=<int>" "reject patches with this size of paths (default: ${PATHLEN_SIZE}"
+
+}
+
+## @description  pathlen parse args hook
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+function pathlen_parse_args
+{
+  declare i
+
+  for i in "$@"; do
+    case ${i} in
+      --pathlen-size=*)
+        PATHLEN_SIZE="${i#*=}"
+      ;;
+    esac
+  done
+}
+
+## @description  helper function to count long pathnames
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @return       0 on success
+## @return       1 on failure
+function pathlen_generic
+{
+  declare size
+  declare i
+  declare msg
+  declare counter
+
+  counter=0
+
+  if [[ "${BUILDMODE}" = full ]]; then
+    msg="source tree"
+  else
+    msg="patch"
+  fi
+
+  for i in "${CHANGED_FILES[@]}"; do
+    size=${#i}
+    if [[ ${size} -gt ${PATHLEN_SIZE} ]]; then
+      ((counter = counter + 1 ))
+      echo "${i}" >>  "${PATCH_DIR}/pathlen.txt"
+    fi
+  done
+
+  # shellcheck disable=SC2016
+  echo "${counter} files in the ${msg} with paths longer that ${PATHLEN_SIZE}."
+  if [[ ${counter} -gt 0 ]] ; then
+    add_vote_table -1 pathlen \
+      "${BUILDMODEMSG} appears to contain ${counter} files with names longer than ${PATHLEN_SIZE}"
+    add_footer_table pathlen "@@BASE@@/pathlen.txt"
+    return 1
+  fi
+  return 0
+}
+
+## @description  Check the current patchfile for @pathlen tags
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @return       0 on success
+## @return       1 on failure
+## @param        patchfile
+function pathlen_patchfile
+{
+  if [[ "${BUILDMODE}" != patch ]]; then
+    return
+  fi
+
+  big_console_header "Checking for long paths: ${BUILDMODE}"
+
+  start_clock
+
+  pathlen_generic
+}
+
+
+## @description  Check the current directory for @pathlen tags
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @return       0 on success
+## @return       1 on failure
+function pathlen_postcompile
+{
+  if [[ "${BUILDMODE}" != full ]]; then
+    return
+  fi
+
+  big_console_header "Checking for long paths: ${BUILDMODE}"
+
+  start_clock
+
+  pathlen_generic
+}

http://git-wip-us.apache.org/repos/asf/yetus/blob/6ebaa111/precommit/src/main/shell/test-patch.d/perlcritic.sh
----------------------------------------------------------------------
diff --git a/precommit/src/main/shell/test-patch.d/perlcritic.sh b/precommit/src/main/shell/test-patch.d/perlcritic.sh
new file mode 100755
index 0000000..291e94e
--- /dev/null
+++ b/precommit/src/main/shell/test-patch.d/perlcritic.sh
@@ -0,0 +1,171 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+add_test_type perlcritic
+
+PERLCRITIC_TIMER=0
+
+PERLCRITIC=${PERLCRITIC:-$(which perlcritic 2>/dev/null)}
+
+function perlcritic_usage
+{
+  yetus_add_option "--perlcritic=<path>" "path to perlcritic executable"
+}
+
+function perlcritic_parse_args
+{
+  local i
+
+  for i in "$@"; do
+    case ${i} in
+    --perlcritic=*)
+      PERLCRITIC=${i#*=}
+    ;;
+    esac
+  done
+}
+
+function perlcritic_filefilter
+{
+  local filename=$1
+
+  if [[ ${filename} =~ \.p[lm]$ ]]; then
+    add_test perlcritic
+  fi
+}
+
+function perlcritic_precheck
+{
+  if ! verify_command "Perl::Critic" "${PERLCRITIC}"; then
+    add_vote_table 0 perlcritic "Perl::Critic was not available."
+    delete_test perlcritic
+  fi
+}
+
+
+function perlcritic_preapply
+{
+  local i
+
+  if ! verify_needed_test perlcritic; then
+    return 0
+  fi
+
+  big_console_header "Perl::Critic plugin: ${PATCH_BRANCH}"
+
+  start_clock
+
+  echo "Running perlcritic against identified perl scripts/modules."
+  pushd "${BASEDIR}" >/dev/null
+  for i in "${CHANGED_FILES[@]}"; do
+    if [[ ${i} =~ \.p[lm]$ && -f ${i} ]]; then
+      ${PERLCRITIC} -1 --verbose 1 "${i}" 2>/dev/null >> "${PATCH_DIR}/branch-perlcritic-result.txt"
+    fi
+  done
+  popd >/dev/null
+  # keep track of how much as elapsed for us already
+  PERLCRITIC_TIMER=$(stop_clock)
+  return 0
+}
+
+## @description  Wrapper to call column_calcdiffs
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @param        branchlog
+## @param        patchlog
+## @return       differences
+function perlcritic_calcdiffs
+{
+  column_calcdiffs "$@"
+}
+
+function perlcritic_postapply
+{
+  declare i
+  declare numPrepatch
+  declare numPostpatch
+  declare diffPostpatch
+  declare fixedpatch
+  declare statstring
+
+  if ! verify_needed_test perlcritic; then
+    return 0
+  fi
+
+  big_console_header "Perl::Critic plugin: ${BUILDMODE}"
+
+  start_clock
+
+  # add our previous elapsed to our new timer
+  # by setting the clock back
+  offset_clock "${PERLCRITIC_TIMER}"
+
+  echo "Running perlcritic against identified perl scripts/modules."
+  # we re-check this in case one has been added
+  pushd "${BASEDIR}" >/dev/null
+  for i in "${CHANGED_FILES[@]}"; do
+    if [[ ${i} =~ \.p[lm]$ && -f ${i} ]]; then
+      ${PERLCRITIC} -1 --verbose 1 "${i}" 2>/dev/null >> "${PATCH_DIR}/patch-perlcritic-result.txt"
+    fi
+  done
+  popd >/dev/null
+
+  PERLCRITIC_VERSION=$(${PERLCRITIC} --version 2>/dev/null)
+  add_footer_table perlcritic "v${PERLCRITIC_VERSION}"
+
+  calcdiffs \
+    "${PATCH_DIR}/branch-perlcritic-result.txt" \
+    "${PATCH_DIR}/patch-perlcritic-result.txt" \
+    perlcritic \
+    > "${PATCH_DIR}/diff-patch-perlcritic.txt"
+
+  # shellcheck disable=SC2016
+  numPrepatch=$(wc -l "${PATCH_DIR}/branch-perlcritic-result.txt" | ${AWK} '{print $1}')
+
+  # shellcheck disable=SC2016
+  numPostpatch=$(wc -l "${PATCH_DIR}/patch-perlcritic-result.txt" | ${AWK} '{print $1}')
+
+  # shellcheck disable=SC2016
+  diffPostpatch=$(wc -l "${PATCH_DIR}/diff-patch-perlcritic.txt" | ${AWK} '{print $1}')
+
+  ((fixedpatch=numPrepatch-numPostpatch+diffPostpatch))
+
+  statstring=$(generic_calcdiff_status "${numPrepatch}" "${numPostpatch}" "${diffPostpatch}" )
+
+  if [[ ${diffPostpatch} -gt 0 ]]; then
+    add_vote_table -1 perlcritic "${BUILDMODEMSG} ${statstring}"
+    add_footer_table perlcritic "@@BASE@@/diff-patch-perlcritic.txt"
+    return 1
+  elif [[ ${fixedpatch} -gt 0 ]]; then
+    add_vote_table +1 perlcritic "${BUILDMODEMSG} ${statstring}"
+    return 0
+  fi
+
+  add_vote_table +1 perlcritic "There were no new perlcritic issues."
+  return 0
+}
+
+function perlcritic_postcompile
+{
+  declare repostatus=$1
+
+  if [[ "${repostatus}" = branch ]]; then
+    perlcritic_preapply
+  else
+    perlcritic_postapply
+  fi
+}

http://git-wip-us.apache.org/repos/asf/yetus/blob/6ebaa111/precommit/src/main/shell/test-patch.d/pylint.sh
----------------------------------------------------------------------
diff --git a/precommit/src/main/shell/test-patch.d/pylint.sh b/precommit/src/main/shell/test-patch.d/pylint.sh
new file mode 100755
index 0000000..facba6c
--- /dev/null
+++ b/precommit/src/main/shell/test-patch.d/pylint.sh
@@ -0,0 +1,183 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+add_test_type pylint
+
+PYLINT_TIMER=0
+
+PYLINT=${PYLINT:-$(which pylint 2>/dev/null)}
+PYLINT_OPTIONS=${PYLINT_OPTIONS:-}
+
+function pylint_usage
+{
+  yetus_add_option "--pylint=<path>" "path to pylint executable"
+  yetus_add_option "--pylint-options=<path>" "pylint options other than output-format and reports"
+}
+
+function pylint_parse_args
+{
+  local i
+
+  for i in "$@"; do
+    case ${i} in
+    --pylint=*)
+      PYLINT=${i#*=}
+    ;;
+    --pylint-options=*)
+      PYLINT_OPTIONS=${i#*=}
+    ;;
+    esac
+  done
+}
+
+function pylint_filefilter
+{
+  local filename=$1
+
+  if [[ ${filename} =~ \.py$ ]]; then
+    add_test pylint
+  fi
+}
+
+function pylint_precheck
+{
+  if ! verify_command "Pylint" "${PYLINT}"; then
+    add_vote_table 0 pylint "Pylint was not available."
+    delete_test pylint
+  fi
+}
+
+
+function pylint_preapply
+{
+  local i
+  local count
+  local pylintStderr=branch-pylint-stderr.txt
+
+  if ! verify_needed_test pylint; then
+    return 0
+  fi
+
+  big_console_header "pylint plugin: ${PATCH_BRANCH}"
+
+  start_clock
+
+  echo "Running pylint against identified python scripts."
+  pushd "${BASEDIR}" >/dev/null
+  for i in "${CHANGED_FILES[@]}"; do
+    if [[ ${i} =~ \.py$ && -f ${i} ]]; then
+      # shellcheck disable=SC2086
+      eval "${PYLINT} ${PYLINT_OPTIONS} --msg-template='{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}' --reports=n ${i}" \
+        2>>${PATCH_DIR}/${pylintStderr} | ${AWK} '1<NR' >> "${PATCH_DIR}/branch-pylint-result.txt"
+    fi
+  done
+  if [[ -f ${PATCH_DIR}/${pylintStderr} ]]; then
+    count=$(${GREP}  -Evc "^(No config file found|Using config file)" "${PATCH_DIR}/${pylintStderr}")
+    if [[ ${count} -gt 0 ]]; then
+      add_footer_table pylint "${PATCH_BRANCH} stderr: @@BASE@@/${pylintStderr}"
+      return 1
+    fi
+  fi
+  rm "${PATCH_DIR}/${pylintStderr}" 2>/dev/null
+  popd >/dev/null
+  # keep track of how much as elapsed for us already
+  PYLINT_TIMER=$(stop_clock)
+  return 0
+}
+
+function pylint_postapply
+{
+  declare i
+  declare count
+  declare numPrepatch
+  declare numPostpatch
+  declare diffPostpatch
+  declare pylintStderr=patch-pylint-stderr.txt
+  declare fixedpatch
+  declare statstring
+
+  if ! verify_needed_test pylint; then
+    return 0
+  fi
+
+  big_console_header "pylint plugin: ${BUILDMODE}"
+
+  start_clock
+
+  # add our previous elapsed to our new timer
+  # by setting the clock back
+  offset_clock "${PYLINT_TIMER}"
+
+  echo "Running pylint against identified python scripts."
+  # we re-check this in case one has been added
+  pushd "${BASEDIR}" >/dev/null
+  for i in "${CHANGED_FILES[@]}"; do
+    if [[ ${i} =~ \.py$ && -f ${i} ]]; then
+      # shellcheck disable=SC2086
+      eval "${PYLINT} ${PYLINT_OPTIONS} --msg-template='{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}' --reports=n ${i}" \
+        2>>${PATCH_DIR}/${pylintStderr} | ${AWK} '1<NR' >> "${PATCH_DIR}/patch-pylint-result.txt"
+    fi
+  done
+  if [[ -f ${PATCH_DIR}/${pylintStderr} ]]; then
+    count=$(${GREP}  -Evc "^(No config file found|Using config file)" "${PATCH_DIR}/${pylintStderr}")
+    if [[ ${count} -gt 0 ]]; then
+      add_vote_table -1 pylint "Something bad seems to have happened in running pylint. Please check pylint stderr files."
+      add_footer_table pylint "${BUILDMODEMSG} stderr: @@BASE@@/${pylintStderr}"
+      return 1
+    fi
+  fi
+  rm "${PATCH_DIR}/${pylintStderr}" 2>/dev/null
+  popd >/dev/null
+
+  # shellcheck disable=SC2016
+  PYLINT_VERSION=$(${PYLINT} --version 2>/dev/null | ${GREP} pylint | ${AWK} '{print $NF}')
+  add_footer_table pylint "v${PYLINT_VERSION%,}"
+
+  calcdiffs "${PATCH_DIR}/branch-pylint-result.txt" \
+            "${PATCH_DIR}/patch-pylint-result.txt" \
+            pylint > "${PATCH_DIR}/diff-patch-pylint.txt"
+  numPrepatch=$(${GREP} -c "^.*:.*: \[.*\] " "${PATCH_DIR}/branch-pylint-result.txt")
+  numPostpatch=$(${GREP} -c "^.*:.*: \[.*\] " "${PATCH_DIR}/patch-pylint-result.txt")
+  # Exclude Pylint messages from the information category to avoid false positives (see YETUS-309).
+  diffPostpatch=$(${GREP} -c "^.*:.*: \[[^I].*\] " "${PATCH_DIR}/diff-patch-pylint.txt")
+
+  ((fixedpatch=numPrepatch-numPostpatch+diffPostpatch))
+
+  statstring=$(generic_calcdiff_status "${numPrepatch}" "${numPostpatch}" "${diffPostpatch}" )
+
+  if [[ ${diffPostpatch} -gt 0 ]] ; then
+    add_vote_table -1 pylint "${BUILDMODEMSG} ${statstring}"
+    add_footer_table pylint "@@BASE@@/diff-patch-pylint.txt"
+    return 1
+  elif [[ ${fixedpatch} -gt 0 ]]; then
+    add_vote_table +1 pylint "${BUILDMODEMSG} ${statstring}"
+    return 0
+  fi
+
+  add_vote_table +1 pylint "There were no new pylint issues."
+  return 0
+}
+
+function pylint_postcompile
+{
+  declare repostatus=$1
+
+  if [[ "${repostatus}" = branch ]]; then
+    pylint_preapply
+  else
+    pylint_postapply
+  fi
+}

http://git-wip-us.apache.org/repos/asf/yetus/blob/6ebaa111/precommit/src/main/shell/test-patch.d/rubocop.sh
----------------------------------------------------------------------
diff --git a/precommit/src/main/shell/test-patch.d/rubocop.sh b/precommit/src/main/shell/test-patch.d/rubocop.sh
new file mode 100755
index 0000000..828bea1
--- /dev/null
+++ b/precommit/src/main/shell/test-patch.d/rubocop.sh
@@ -0,0 +1,170 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+add_test_type rubocop
+
+RUBOCOP_TIMER=0
+
+RUBOCOP=${RUBOCOP:-$(which rubocop 2>/dev/null)}
+
+function rubocop_usage
+{
+  yetus_add_option "--rubocop=<path>" "path to rubocop executable"
+}
+
+function rubocop_parse_args
+{
+  local i
+
+  for i in "$@"; do
+    case ${i} in
+    --rubocop=*)
+      RUBOCOP=${i#*=}
+    ;;
+    esac
+  done
+}
+
+function rubocop_filefilter
+{
+  local filename=$1
+
+  if [[ ${filename} =~ \.rb$ ]]; then
+    add_test rubocop
+  fi
+}
+
+function rubocop_precheck
+{
+  if ! verify_command rubocop "${RUBOCOP}"; then
+    add_vote_table 0 rubocop "rubocop was not available."
+    delete_test rubocop
+  fi
+}
+
+
+function rubocop_preapply
+{
+  local i
+
+  if ! verify_needed_test rubocop; then
+    return 0
+  fi
+
+  big_console_header "rubocop plugin: ${PATCH_BRANCH}"
+
+  start_clock
+
+  echo "Running rubocop against identified ruby scripts."
+  pushd "${BASEDIR}" >/dev/null
+  for i in "${CHANGED_FILES[@]}"; do
+    if [[ ${i} =~ \.rb$ && -f ${i} ]]; then
+      ${RUBOCOP} -f e "${i}" | ${AWK} '!/[0-9]* files? inspected/' >> "${PATCH_DIR}/branch-rubocop-result.txt"
+    fi
+  done
+  popd >/dev/null
+  # keep track of how much as elapsed for us already
+  RUBOCOP_TIMER=$(stop_clock)
+  return 0
+}
+
+## @description  Wrapper to call column_calcdiffs
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @param        branchlog
+## @param        patchlog
+## @return       differences
+function rubocop_calcdiffs
+{
+  column_calcdiffs "$@"
+}
+
+function rubocop_postapply
+{
+  declare i
+  declare numPrepatch
+  declare numPostpatch
+  declare diffPostpatch
+  declare fixedpatch
+  declare statstring
+
+  if ! verify_needed_test rubocop; then
+    return 0
+  fi
+
+  big_console_header "rubocop plugin: ${BUILDMODE}"
+
+  start_clock
+
+  # add our previous elapsed to our new timer
+  # by setting the clock back
+  offset_clock "${RUBOCOP_TIMER}"
+
+  echo "Running rubocop against identified ruby scripts."
+  # we re-check this in case one has been added
+  pushd "${BASEDIR}" >/dev/null
+  for i in "${CHANGED_FILES[@]}"; do
+    if [[ ${i} =~ \.rb$ && -f ${i} ]]; then
+      ${RUBOCOP} -f e "${i}" | ${AWK} '!/[0-9]* files? inspected/' >> "${PATCH_DIR}/patch-rubocop-result.txt"
+    fi
+  done
+  popd >/dev/null
+
+  # shellcheck disable=SC2016
+  RUBOCOP_VERSION=$(${RUBOCOP} -v | ${AWK} '{print $NF}')
+  add_footer_table rubocop "v${RUBOCOP_VERSION}"
+
+  calcdiffs \
+    "${PATCH_DIR}/branch-rubocop-result.txt" \
+    "${PATCH_DIR}/patch-rubocop-result.txt" \
+    rubocop \
+      > "${PATCH_DIR}/diff-patch-rubocop.txt"
+  diffPostpatch=$(${AWK} -F: 'BEGIN {sum=0} 4<NF {sum+=1} END {print sum}' "${PATCH_DIR}/diff-patch-rubocop.txt")
+
+  # shellcheck disable=SC2016
+  numPrepatch=$(${AWK} -F: 'BEGIN {sum=0} 4<NF {sum+=1} END {print sum}' "${PATCH_DIR}/branch-rubocop-result.txt")
+
+  # shellcheck disable=SC2016
+  numPostpatch=$(${AWK} -F: 'BEGIN {sum=0} 4<NF {sum+=1} END {print sum}' "${PATCH_DIR}/patch-rubocop-result.txt")
+
+  ((fixedpatch=numPrepatch-numPostpatch+diffPostpatch))
+
+  statstring=$(generic_calcdiff_status "${numPrepatch}" "${numPostpatch}" "${diffPostpatch}" )
+
+  if [[ ${diffPostpatch} -gt 0 ]] ; then
+    add_vote_table -1 rubocop "${BUILDMODEMSG} ${statstring}"
+    add_footer_table rubocop "@@BASE@@/diff-patch-rubocop.txt"
+    return 1
+  elif [[ ${fixedpatch} -gt 0 ]]; then
+    add_vote_table +1 rubocop "${BUILDMODEMSG} ${statstring}"
+    return 0
+  fi
+
+  add_vote_table +1 rubocop "There were no new rubocop issues."
+  return 0
+}
+
+function rubocop_postcompile
+{
+  declare repostatus=$1
+
+  if [[ "${repostatus}" = branch ]]; then
+    rubocop_preapply
+  else
+    rubocop_postapply
+  fi
+}

http://git-wip-us.apache.org/repos/asf/yetus/blob/6ebaa111/precommit/src/main/shell/test-patch.d/ruby-lint.sh
----------------------------------------------------------------------
diff --git a/precommit/src/main/shell/test-patch.d/ruby-lint.sh b/precommit/src/main/shell/test-patch.d/ruby-lint.sh
new file mode 100755
index 0000000..78be7b6
--- /dev/null
+++ b/precommit/src/main/shell/test-patch.d/ruby-lint.sh
@@ -0,0 +1,200 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+add_test_type ruby_lint
+
+RUBY_LINT_TIMER=0
+
+RUBY_LINT=${RUBY_LINT:-$(which ruby-lint 2>/dev/null)}
+
+function ruby_lint_usage
+{
+  yetus_add_option "--ruby-lint=<path>" "path to ruby-lint executable"
+}
+
+function ruby_lint_parse_args
+{
+  local i
+
+  for i in "$@"; do
+    case ${i} in
+    --ruby-lint=*)
+      RUBY_LINT=${i#*=}
+    ;;
+    esac
+  done
+}
+
+function ruby_lint_filefilter
+{
+  local filename=$1
+
+  if [[ ${filename} =~ \.rb$ ]]; then
+    add_test ruby_lint
+  fi
+}
+
+function ruby_lint_precheck
+{
+  if ! verify_command "Ruby-lint" "${RUBY_LINT}"; then
+    add_vote_table 0 ruby-lint "Ruby-lint was not available."
+    delete_test ruby_lint
+  fi
+}
+
+function ruby_lint_preapply
+{
+  local i
+
+  if ! verify_needed_test ruby_lint; then
+    return 0
+  fi
+
+  big_console_header "ruby-lint plugin: ${PATCH_BRANCH}"
+
+  start_clock
+
+  echo "Running ruby-lint against identified ruby scripts."
+  pushd "${BASEDIR}" >/dev/null
+  for i in "${CHANGED_FILES[@]}"; do
+    if [[ ${i} =~ \.rb$ && -f ${i} ]]; then
+      ${RUBY_LINT} -p syntastic "${i}" | sort -t : -k 1,1 -k 3,3n -k 4,4n >> "${PATCH_DIR}/branch-ruby-lint-result.txt"
+    fi
+  done
+  popd >/dev/null
+  # keep track of how much as elapsed for us already
+  RUBY_LINT_TIMER=$(stop_clock)
+  return 0
+}
+
+## @description  Calculate the differences between the specified files
+## @description  using columns and output it to stdout
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @param        branchlog
+## @param        patchlog
+## @return       differences
+function ruby_lint_calcdiffs
+{
+  declare orig=$1
+  declare new=$2
+  declare tmp=${PATCH_DIR}/pl.$$.${RANDOM}
+  declare j
+
+  # first, strip filenames:line:
+  # this keeps column: in an attempt to increase
+  # accuracy in case of multiple, repeated errors
+  # since the column number shouldn't change
+  # if the line of code hasn't been touched
+  # shellcheck disable=SC2016
+  cut -f4- -d: "${orig}" > "${tmp}.branch"
+  # shellcheck disable=SC2016
+  cut -f4- -d: "${new}" > "${tmp}.patch"
+
+  # compare the errors, generating a string of line
+  # numbers. Sorry portability: GNU diff makes this too easy
+  ${DIFF} --unchanged-line-format="" \
+     --old-line-format="" \
+     --new-line-format="%dn " \
+     "${tmp}.branch" \
+     "${tmp}.patch" > "${tmp}.lined"
+
+  # now, pull out those lines of the raw output
+  # shellcheck disable=SC2013
+  for j in $(cat "${tmp}.lined"); do
+    # shellcheck disable=SC2086
+    head -${j} "${new}" | tail -1
+  done
+
+  rm "${tmp}.branch" "${tmp}.patch" "${tmp}.lined" 2>/dev/null
+}
+
+function ruby_lint_postapply
+{
+  declare i
+  declare numPrepatch
+  declare numPostpatch
+  declare diffPostpatch
+  declare fixedpatch
+  declare statstring
+
+  if ! verify_needed_test ruby_lint; then
+    return 0
+  fi
+
+  big_console_header "ruby-lint plugin: ${BUILDMODE}"
+
+  start_clock
+
+  # add our previous elapsed to our new timer
+  # by setting the clock back
+  offset_clock "${RUBY_LINT_TIMER}"
+
+  echo "Running ruby-lint against identified ruby scripts."
+  # we re-check this in case one has been added
+  pushd "${BASEDIR}" >/dev/null
+  for i in "${CHANGED_FILES[@]}"; do
+    if [[ ${i} =~ \.rb$ && -f ${i} ]]; then
+      ${RUBY_LINT} -p syntastic "${i}" | sort -t : -k 1,1 -k 3,3n -k 4,4n >> "${PATCH_DIR}/patch-ruby-lint-result.txt"
+    fi
+  done
+  popd >/dev/null
+
+  # shellcheck disable=SC2016
+  RUBY_LINT_VERSION=$(${RUBY_LINT} -v | ${AWK} '{print $2}')
+  add_footer_table ruby-lint "${RUBY_LINT_VERSION}"
+
+  calcdiffs \
+    "${PATCH_DIR}/branch-ruby-lint-result.txt" \
+    "${PATCH_DIR}/patch-ruby-lint-result.txt" \
+      ruby_lint \
+      > "${PATCH_DIR}/diff-patch-ruby-lint.txt"
+  diffPostpatch=$(${AWK} -F: 'BEGIN {sum=0} 4<NF {sum+=1} END {print sum}' "${PATCH_DIR}/diff-patch-ruby-lint.txt")
+
+  # shellcheck disable=SC2016
+  numPrepatch=$(${AWK} -F: 'BEGIN {sum=0} 4<NF {sum+=1} END {print sum}' "${PATCH_DIR}/branch-ruby-lint-result.txt")
+
+  # shellcheck disable=SC2016
+  numPostpatch=$(${AWK} -F: 'BEGIN {sum=0} 4<NF {sum+=1} END {print sum}' "${PATCH_DIR}/patch-ruby-lint-result.txt")
+
+  ((fixedpatch=numPrepatch-numPostpatch+diffPostpatch))
+
+  statstring=$(generic_calcdiff_status "${numPrepatch}" "${numPostpatch}" "${diffPostpatch}" )
+
+  if [[ ${diffPostpatch} -gt 0 ]] ; then
+    add_vote_table -1 ruby-lint "${BUILDMODEMSG} ${statstring}"
+    add_footer_table ruby-lint "@@BASE@@/diff-patch-ruby-lint.txt"
+    return 1
+  elif [[ ${fixedpatch} -gt 0 ]]; then
+    add_vote_table +1 ruby-lint "${BUILDMODEMSG} ${statstring}"
+    return 0
+  fi
+
+  add_vote_table +1 ruby-lint "There were no new ruby-lint issues."
+  return 0
+}
+
+function ruby_lint_postcompile
+{
+  declare repostatus=$1
+
+  if [[ "${repostatus}" = branch ]]; then
+    ruby_lint_preapply
+  else
+    ruby_lint_postapply
+  fi
+}

http://git-wip-us.apache.org/repos/asf/yetus/blob/6ebaa111/precommit/src/main/shell/test-patch.d/scala.sh
----------------------------------------------------------------------
diff --git a/precommit/src/main/shell/test-patch.d/scala.sh b/precommit/src/main/shell/test-patch.d/scala.sh
new file mode 100755
index 0000000..f2b599a
--- /dev/null
+++ b/precommit/src/main/shell/test-patch.d/scala.sh
@@ -0,0 +1,140 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+add_test_type scalac
+add_test_type scaladoc
+
+SCALA_INITIALIZED=false
+
+function scalac_filefilter
+{
+  declare filename=$1
+
+  if [[ ${filename} =~ \.scala$ ]]; then
+   yetus_debug "tests/scalac: ${filename}"
+   add_test scalac
+   add_test compile
+  fi
+}
+
+function scaladoc_filefilter
+{
+  local filename=$1
+
+  if [[ ${filename} =~ \.scala$ ]]; then
+    yetus_debug "tests/scaladoc: ${filename}"
+    add_test scaladoc
+  fi
+}
+
+## @description  initialize the scala plug-in
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+function scala_initialize
+{
+  if [[ ${SCALA_INITIALIZED} == true ]]; then
+    return
+  else
+    SCALA_INITIALIZED=true
+  fi
+
+  if declare -f maven_add_install >/dev/null 2>&1; then
+    maven_add_install scaladoc
+    maven_add_install scalac
+  fi
+}
+
+## @description  initialize the scalac plug-in
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+function scalac_initialize
+{
+  scala_initialize
+}
+
+## @description  initialize the scaladoc plug-in
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+function scaladoc_initialize
+{
+  scala_initialize
+}
+
+## @description
+## @audience     private
+## @stability    stable
+## @replaceable  no
+## @return       0 on success
+## @return       1 on failure
+function scalac_compile
+{
+  declare codebase=$1
+  declare multijdkmode=$2
+
+  if ! verify_needed_test scalac; then
+    return 0
+  fi
+
+  if [[ ${codebase} = patch ]]; then
+    generic_postlog_compare compile scalac "${multijdkmode}"
+  fi
+}
+
+## @description  Count and compare the number of ScalaDoc warnings pre- and post- patch
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @return       0 on success
+## @return       1 on failure
+function scaladoc_rebuild
+{
+  declare codebase=$1
+
+  if [[ "${codebase}" = branch ]]; then
+    generic_pre_handler scaladoc false
+  else
+    generic_post_handler scaladoc scaladoc false true
+  fi
+}
+
+## @description  Helper for generic_logfilter
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+function scalac_logfilter
+{
+  declare input=$1
+  declare output=$2
+
+  #shellcheck disable=SC2016,SC2046
+  ${GREP} "^/.*.scala:[0-9]*:" "${input}" > "${output}"
+}
+
+## @description  Helper for generic_logfilter
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+function scaladoc_logfilter
+{
+  declare input=$1
+  declare output=$2
+
+  #shellcheck disable=SC2016,SC2046
+  ${GREP} "^/.*.scala:[0-9]*:" "${input}" > "${output}"
+}

http://git-wip-us.apache.org/repos/asf/yetus/blob/6ebaa111/precommit/src/main/shell/test-patch.d/shellcheck.sh
----------------------------------------------------------------------
diff --git a/precommit/src/main/shell/test-patch.d/shellcheck.sh b/precommit/src/main/shell/test-patch.d/shellcheck.sh
new file mode 100755
index 0000000..f0450f7
--- /dev/null
+++ b/precommit/src/main/shell/test-patch.d/shellcheck.sh
@@ -0,0 +1,263 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# no public APIs here
+# SHELLDOC-IGNORE
+
+add_test_type shellcheck
+
+SHELLCHECK_TIMER=0
+SHELLCHECK_X=true
+
+SHELLCHECK=${SHELLCHECK:-$(which shellcheck 2>/dev/null)}
+
+# files that are going to get shellcheck'd
+SHELLCHECK_CHECKFILES=()
+
+# files that are going to get shellcheck'd
+SHELLCHECK_FILTERFILES=()
+
+
+function shellcheck_filefilter
+{
+  declare filename=$1
+
+  if [[ ${filename} =~ \.sh$ ]]; then
+    # if it ends in an explicit .sh, then this is shell code.
+    add_test shellcheck
+    yetus_add_array_element SHELLCHECK_FILTERFILES "${filename}"
+  elif [[ ${BUILDTOOL} = maven && ${filename} =~ src/main/shell ]]; then
+    # if it is maven and in src/main/shell, assume it's shell code
+    add_test shellcheck
+    yetus_add_array_element SHELLCHECK_FILTERFILES "${filename}"
+  elif [[ ! ${filename} =~ \. ]]; then
+    # if it doesn't have an extension, then assume it is and
+    # we'll deal with it later
+    add_test shellcheck
+    yetus_add_array_element SHELLCHECK_FILTERFILES "${filename}"
+  fi
+}
+
+function shellcheck_precheck
+{
+  declare langs
+
+  if ! verify_command "shellcheck" "${SHELLCHECK}"; then
+    add_vote_table 0 shellcheck "Shellcheck was not available."
+    delete_test shellcheck
+  else
+    # shellcheck disable=SC2016
+    SHELLCHECK_VERSION=$("${SHELLCHECK}" --version | "${GREP}" version: | "${AWK}" '{print $NF}')
+
+    # versions less than 0.4.1 do not support -x
+    if [[ ${SHELLCHECK_VERSION} =~ 0.[0-3].[0-9] || ${SHELLCHECK_VERSION} = 0.4.0 ]]; then
+      SHELLCHECK_X=false
+    fi
+  fi
+
+  if [[ -z "${LANG}" ]]; then
+    langs=$(locale -a)
+    if [[ ${langs}  =~ C.UTF-8 ]]; then
+      yetus_error "WARNING: shellcheck needs UTF-8 locale support. Forcing C.UTF-8."
+      export LANG=C.UTF-8
+      export LC_ALL=C.UTF-8
+    elif [[ ${langs}  =~ en_US.UTF-8 ]]; then
+      yetus_error "WARNING: shellcheck needs UTF-8 locale support. Forcing en_US.UTF-8."
+      export LANG=en_US.UTF-8
+      export LC_ALL=en_US.UTF-8
+    else
+      for i in ${langs}; do
+        if [[ "${i}" =~ UTF-8 ]]; then
+          yetus_error "WARNING: shellcheck needs UTF-8 locale support. Forcing ${i}."
+          export LANG="${i}"
+          export LC_ALL="${i}"
+          break
+        fi
+      done
+    fi
+  fi
+
+  if [[ ! "${LANG}" =~ UTF-8 ]]; then
+    yetus_error "WARNING: shellcheck may fail without UTF-8 locale setting."
+  fi
+}
+
+function shellcheck_criteria
+{
+  declare fn=$1
+  declare text
+
+  if [[ ! -f "${fn}" ]]; then
+    yetus_debug "Shellcheck rejected (not exist): ${fn}"
+    return
+  fi
+
+  text=$(head -n 1 "${fn}")
+
+  # shell check requires either a bangpath or a shell check directive
+  # on the first line.  so check for a leading comment char
+  # and some sort of reference to 'sh'
+  if echo "${text}" | "${GREP}" -E -q "^#"; then
+    if echo "${text}" | "${GREP}" -q sh; then
+      yetus_add_array_element SHELLCHECK_CHECKFILES "${fn}"
+      yetus_debug "Shellcheck added: ${fn}"
+    fi
+  fi
+}
+
+function shellcheck_findscripts
+{
+  declare fn
+
+  # reset
+  SHELLCHECK_CHECKFILES=()
+
+  # run through the files our filter caught
+  # this will set SHELLCHECK_CHECKFILES elements
+  for fn in "${SHELLCHECK_FILTERFILES[@]}"; do
+    shellcheck_criteria "${fn}"
+  done
+
+  # finally, sort the array
+  yetus_sort_array SHELLCHECK_CHECKFILES
+}
+
+function shellcheck_logic
+{
+  declare repostatus=$1
+  declare i
+
+  echo "Running shellcheck against all suspected shell scripts"
+  pushd "${BASEDIR}" >/dev/null
+
+  # need to run this every time in case patch
+  # add/removed files
+  shellcheck_findscripts
+
+  for i in "${SHELLCHECK_CHECKFILES[@]}"; do
+    if [[ "${SHELLCHECK_X}" = true ]]; then
+      "${SHELLCHECK}" -x -f gcc "${i}" >> "${PATCH_DIR}/${repostatus}-shellcheck-result.txt"
+    else
+      "${SHELLCHECK}" -f gcc "${i}" >> "${PATCH_DIR}/${repostatus}-shellcheck-result.txt"
+    fi
+  done
+  popd > /dev/null
+}
+
+function shellcheck_preapply
+{
+  declare msg
+
+  if ! verify_needed_test shellcheck; then
+    return 0
+  fi
+
+  big_console_header "shellcheck plugin: ${PATCH_BRANCH}"
+
+  start_clock
+
+  shellcheck_logic branch
+
+  msg="v${SHELLCHECK_VERSION}"
+  if [[ ${SHELLCHECK_VERSION} =~ 0.[0-3].[0-5] ]]; then
+    msg="${msg} (This is an old version that has serious bugs. Consider upgrading.)"
+  fi
+  add_footer_table shellcheck "${msg}"
+
+  # keep track of how much as elapsed for us already
+  SHELLCHECK_TIMER=$(stop_clock)
+  return 0
+}
+
+## @description  Wrapper to call column_calcdiffs
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @param        branchlog
+## @param        patchlog
+## @return       differences
+function shellcheck_calcdiffs
+{
+  column_calcdiffs "$@"
+}
+
+function shellcheck_postapply
+{
+  declare i
+  declare numPrepatch
+  declare numPostpatch
+  declare diffPostpatch
+  declare fixedpatch
+  declare statstring
+
+  if ! verify_needed_test shellcheck; then
+    return 0
+  fi
+
+  big_console_header "shellcheck plugin: ${BUILDMODE}"
+
+  start_clock
+
+  # add our previous elapsed to our new timer
+  # by setting the clock back
+  offset_clock "${SHELLCHECK_TIMER}"
+
+  shellcheck_logic patch
+
+  calcdiffs \
+    "${PATCH_DIR}/branch-shellcheck-result.txt" \
+    "${PATCH_DIR}/patch-shellcheck-result.txt" \
+    shellcheck \
+      > "${PATCH_DIR}/diff-patch-shellcheck.txt"
+
+  # shellcheck disable=SC2016
+  numPrepatch=$(wc -l "${PATCH_DIR}/branch-shellcheck-result.txt" | ${AWK} '{print $1}')
+
+  # shellcheck disable=SC2016
+  numPostpatch=$(wc -l "${PATCH_DIR}/patch-shellcheck-result.txt" | ${AWK} '{print $1}')
+
+  # shellcheck disable=SC2016
+  diffPostpatch=$(wc -l "${PATCH_DIR}/diff-patch-shellcheck.txt" | ${AWK} '{print $1}')
+
+
+  ((fixedpatch=numPrepatch-numPostpatch+diffPostpatch))
+
+  statstring=$(generic_calcdiff_status "${numPrepatch}" "${numPostpatch}" "${diffPostpatch}" )
+
+  if [[ ${diffPostpatch} -gt 0 ]] ; then
+    add_vote_table -1 shellcheck "${BUILDMODEMSG} ${statstring}"
+    add_footer_table shellcheck "@@BASE@@/diff-patch-shellcheck.txt"
+    bugsystem_linecomments "shellcheck" "${PATCH_DIR}/diff-patch-shellcheck.txt"
+    return 1
+  elif [[ ${fixedpatch} -gt 0 ]]; then
+    add_vote_table +1 shellcheck "${BUILDMODEMSG} ${statstring}"
+    return 0
+  fi
+
+  add_vote_table +1 shellcheck "There were no new shellcheck issues."
+  return 0
+}
+
+function shellcheck_postcompile
+{
+  declare repostatus=$1
+
+  if [[ "${repostatus}" = branch ]]; then
+    shellcheck_preapply
+  else
+    shellcheck_postapply
+  fi
+}

http://git-wip-us.apache.org/repos/asf/yetus/blob/6ebaa111/precommit/src/main/shell/test-patch.d/shelldocs.sh
----------------------------------------------------------------------
diff --git a/precommit/src/main/shell/test-patch.d/shelldocs.sh b/precommit/src/main/shell/test-patch.d/shelldocs.sh
new file mode 100755
index 0000000..d52fa6b
--- /dev/null
+++ b/precommit/src/main/shell/test-patch.d/shelldocs.sh
@@ -0,0 +1,195 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+add_test_type shelldocs
+
+SHELLDOCS_TIMER=0
+
+SHELLDOCS=${SHELLDOCS}
+if [[ -z ${SHELLDOCS} ]]; then
+  for shelldocsexec in "${BINDIR}/shelldocs" "${BINDIR}/../shelldocs/shelldocs.py"; do
+    if [[ -f ${shelldocsexec} && -x ${shelldocsexec} ]]; then
+      SHELLDOCS=${shelldocsexec}
+      break
+    fi
+  done
+fi
+
+SHELLDOCS_SPECIFICFILES=""
+
+function shelldocs_usage
+{
+  yetus_add_option "--shelldocs=<path>" "path to shelldocs executable"
+}
+
+function shelldocs_parse_args
+{
+  declare i
+
+  for i in "$@"; do
+    case ${i} in
+    --shelldocs=*)
+      SHELLDOCS=${i#*=}
+    ;;
+    esac
+  done
+}
+
+# if it ends in an explicit .sh, then this is shell code.
+# if it doesn't have an extension, we assume it is shell code too
+function shelldocs_filefilter
+{
+  declare filename=$1
+
+  if [[ ${filename} =~ \.sh$ ]]; then
+    add_test shelldocs
+    SHELLDOCS_SPECIFICFILES="${SHELLDOCS_SPECIFICFILES} ./${filename}"
+  fi
+
+  if [[ ! ${filename} =~ \. ]]; then
+    add_test shelldocs
+  fi
+}
+
+function shelldocs_precheck
+{
+  if ! verify_command "shelldocs" "${SHELLDOCS}"; then
+    add_vote_table 0 shelldocs "Shelldocs was not available."
+    delete_test shelldocs
+  fi
+}
+
+function shelldocs_private_findbash
+{
+  declare i
+  declare value
+  declare list
+
+  while read -r line; do
+    value=$(find "${line}" ! -name '*.cmd' -type f \
+      | ${GREP} -E -v '(.orig$|.rej$)')
+
+    for i in ${value}; do
+      if [[ ! ${i} =~ \.sh(\.|$)
+          && ! $(head -n 1 "${i}") =~ ^#! ]]; then
+        yetus_debug "Shelldocs skipped: ${i}"
+        continue
+      fi
+      list="${list} ${i}"
+    done
+  done < <(find . -type d -name bin -o -type d -name sbin -o -type d -name scripts -o -type d -name libexec -o -type d -name shellprofile.d)
+  # shellcheck disable=SC2086
+  echo ${list} ${SHELLDOCS_SPECIFICFILES} | tr ' ' '\n' | sort -u
+}
+
+function shelldocs_preapply
+{
+  declare i
+
+  if ! verify_needed_test shelldocs; then
+    return 0
+  fi
+
+  big_console_header "shelldocs plugin: ${PATCH_BRANCH}"
+
+  start_clock
+
+  echo "Running shelldocs against all identifiable shell scripts"
+  pushd "${BASEDIR}" >/dev/null
+  for i in $(shelldocs_private_findbash); do
+    if [[ -f ${i} ]]; then
+      ${SHELLDOCS} --input "${i}" --lint >> "${PATCH_DIR}/branch-shelldocs-result.txt"
+    fi
+  done
+  popd > /dev/null
+
+  # keep track of how much as elapsed for us already
+  SHELLDOCS_TIMER=$(stop_clock)
+  return 0
+}
+
+function shelldocs_postapply
+{
+  declare i
+  declare numPrepatch
+  declare numPostpatch
+  declare diffPostpatch
+  declare fixedpatch
+  declare statstring
+
+  if ! verify_needed_test shelldocs; then
+    return 0
+  fi
+
+  big_console_header "shelldocs plugin: ${BUILDMODE}"
+
+  start_clock
+
+  # add our previous elapsed to our new timer
+  # by setting the clock back
+  offset_clock "${SHELLDOCS_TIMER}"
+
+  echo "Running shelldocs against all identifiable shell scripts"
+  # we re-check this in case one has been added
+  for i in $(shelldocs_private_findbash); do
+    if [[ -f ${i} ]]; then
+      ${SHELLDOCS} --input "${i}" --lint >> "${PATCH_DIR}/patch-shelldocs-result.txt"
+    fi
+  done
+
+  calcdiffs \
+    "${PATCH_DIR}/branch-shelldocs-result.txt" \
+    "${PATCH_DIR}/patch-shelldocs-result.txt" \
+    shelldocs \
+      > "${PATCH_DIR}/diff-patch-shelldocs.txt"
+
+  # shellcheck disable=SC2016
+  numPrepatch=$(wc -l "${PATCH_DIR}/branch-shelldocs-result.txt" | ${AWK} '{print $1}')
+
+  # shellcheck disable=SC2016
+  numPostpatch=$(wc -l "${PATCH_DIR}/patch-shelldocs-result.txt" | ${AWK} '{print $1}')
+
+  # shellcheck disable=SC2016
+  diffPostpatch=$(wc -l "${PATCH_DIR}/diff-patch-shelldocs.txt" | ${AWK} '{print $1}')
+
+  ((fixedpatch=numPrepatch-numPostpatch+diffPostpatch))
+
+  statstring=$(generic_calcdiff_status "${numPrepatch}" "${numPostpatch}" "${diffPostpatch}" )
+
+  if [[ ${diffPostpatch} -gt 0 ]] ; then
+    add_vote_table -1 shelldocs "${BUILDMODEMSG} ${statstring}"
+    add_footer_table shelldocs "@@BASE@@/diff-patch-shelldocs.txt"
+    bugsystem_linecomments "shelldocs" "${PATCH_DIR}/diff-patch-shelldocs.txt"
+    return 1
+  elif [[ ${fixedpatch} -gt 0 ]]; then
+    add_vote_table +1 shelldocs "${BUILDMODEMSG} ${statstring}"
+    return 0
+  fi
+
+  add_vote_table +1 shelldocs "There were no new shelldocs issues."
+  return 0
+}
+
+function shelldocs_postcompile
+{
+  declare repostatus=$1
+
+  if [[ "${repostatus}" = branch ]]; then
+    shelldocs_preapply
+  else
+    shelldocs_postapply
+  fi
+}

http://git-wip-us.apache.org/repos/asf/yetus/blob/6ebaa111/precommit/src/main/shell/test-patch.d/tap.sh
----------------------------------------------------------------------
diff --git a/precommit/src/main/shell/test-patch.d/tap.sh b/precommit/src/main/shell/test-patch.d/tap.sh
new file mode 100755
index 0000000..465b7f1
--- /dev/null
+++ b/precommit/src/main/shell/test-patch.d/tap.sh
@@ -0,0 +1,81 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+add_test_format tap
+
+TAP_FAILED_TESTS=""
+TAP_LOG_DIR="target/tap"
+
+function tap_parse_args
+{
+  declare i
+
+  for i in "$@"; do
+    case ${i} in
+      --tap-log-dir=*)
+        TAP_LOG_DIR=${i#=*}
+      ;;
+    esac
+  done
+}
+
+function tap_usage
+{
+  yetus_add_option "--tap-log-dir=<dir>" "Directory relative to the module for tap output (default: \"target/tap\")"
+}
+
+function tap_process_tests
+{
+  # shellcheck disable=SC2034
+  declare module=$1
+  # shellcheck disable=SC2034
+  declare buildlogfile=$2
+  declare filefrag=$3
+  declare result=0
+  declare module_failed_tests
+  declare filenames
+
+  if [[ -d "${TAP_LOG_DIR}" ]]; then
+    filenames=$(find "${TAP_LOG_DIR}" -type f -exec "${GREP}" -l -E "^not ok" {} \;)
+  fi
+
+  if [[ -n "${filenames}" ]]; then
+    module_failed_tests=$(echo "${filenames}" \
+      | ${SED} -e "s,${TAP_LOG_DIR},,g" -e s,^/,,g )
+    # shellcheck disable=SC2086
+    cat ${filenames} >> "${PATCH_DIR}/patch-${filefrag}.tap"
+    TAP_LOGS="${TAP_LOGS} @@BASE@@/patch-${filefrag}.tap"
+    TAP_FAILED_TESTS="${TAP_FAILED_TESTS} ${module_failed_tests}"
+    ((result=result+1))
+  fi
+
+  if [[ ${result} -gt 0 ]]; then
+    return 1
+  fi
+  return 0
+}
+
+function tap_finalize_results
+{
+  declare jdk=$1
+
+  if [[ -n "${TAP_FAILED_TESTS}" ]] ; then
+    # shellcheck disable=SC2086
+    populate_test_table "${jdk}Failed TAP tests" ${TAP_FAILED_TESTS}
+    TAP_FAILED_TESTS=""
+    add_footer_table "TAP logs" "${TAP_LOGS}"
+  fi
+}

http://git-wip-us.apache.org/repos/asf/yetus/blob/6ebaa111/precommit/src/main/shell/test-patch.d/test4tests.sh
----------------------------------------------------------------------
diff --git a/precommit/src/main/shell/test-patch.d/test4tests.sh b/precommit/src/main/shell/test-patch.d/test4tests.sh
new file mode 100755
index 0000000..6c3230c
--- /dev/null
+++ b/precommit/src/main/shell/test-patch.d/test4tests.sh
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+add_test_type test4tests
+
+## @description  Check the patch file for changed/new tests
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @return       0 on success
+## @return       1 on failure
+function test4tests_patchfile
+{
+  declare testReferences=0
+  declare i
+
+  if [[ "${BUILDMODE}" = full ]]; then
+    return
+  fi
+
+  big_console_header "Checking there are new or changed tests in the patch."
+
+  if ! verify_needed_test unit; then
+    echo "Patch does not appear to need new or modified tests."
+    return 0
+  fi
+
+  start_clock
+
+  for i in "${CHANGED_FILES[@]}"; do
+    if [[ ${i} =~ (^|/)test/ ]]; then
+      ((testReferences=testReferences + 1))
+    fi
+  done
+
+  echo "There appear to be ${testReferences} test file(s) referenced in the patch."
+  if [[ ${testReferences} == 0 ]] ; then
+    add_vote_table -1 "test4tests" \
+      "The patch doesn't appear to include any new or modified tests. " \
+      "Please justify why no new tests are needed for this patch." \
+      "Also please list what manual steps were performed to verify this patch."
+    return 1
+  fi
+  add_vote_table +1 "test4tests" \
+    "The patch appears to include ${testReferences} new or modified test files."
+  return 0
+}

http://git-wip-us.apache.org/repos/asf/yetus/blob/6ebaa111/precommit/src/main/shell/test-patch.d/unitveto.sh
----------------------------------------------------------------------
diff --git a/precommit/src/main/shell/test-patch.d/unitveto.sh b/precommit/src/main/shell/test-patch.d/unitveto.sh
new file mode 100755
index 0000000..e0d348e
--- /dev/null
+++ b/precommit/src/main/shell/test-patch.d/unitveto.sh
@@ -0,0 +1,62 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+add_test_type unitveto
+
+UNITVETO_RE=${UNITVETO_RE:-}
+
+function unitveto_filefilter
+{
+  declare filename=$1
+
+  if [[ -n "${UNITVETO_RE}"
+     && ${filename} =~ ${UNITVETO_RE} ]]; then
+    yetus_debug "unitveto: ${filename} matched"
+    add_test unitveto
+  fi
+}
+
+function unitveto_usage
+{
+  yetus_add_option "--unitveto-re=<regex>" "Regex to automatically -1 due to manual test requirements"
+}
+
+function unitveto_parse_args
+{
+  declare i
+
+  for i in "$@"; do
+    case ${i} in
+      --unitveto-re=*)
+        UNITVETO_RE=${i#*=}
+      ;;
+    esac
+  done
+}
+
+function unitveto_patchfile
+{
+  if ! verify_needed_test unit; then
+    return 0
+  fi
+
+  if ! verify_needed_test unitveto; then
+    return 0
+  fi
+
+  add_vote_table -1 unitveto "Patch requires manual testing."
+  return 1
+}

http://git-wip-us.apache.org/repos/asf/yetus/blob/6ebaa111/precommit/src/main/shell/test-patch.d/whitespace.sh
----------------------------------------------------------------------
diff --git a/precommit/src/main/shell/test-patch.d/whitespace.sh b/precommit/src/main/shell/test-patch.d/whitespace.sh
new file mode 100755
index 0000000..390f15f
--- /dev/null
+++ b/precommit/src/main/shell/test-patch.d/whitespace.sh
@@ -0,0 +1,150 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+WHITESPACE_EOL_IGNORE_LIST=
+WHITESPACE_TABS_IGNORE_LIST=Makefile
+
+add_test_type whitespace
+
+## @description  whitespace usage hook
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+function whitespace_usage
+{
+  yetus_add_option "--whitespace-eol-ignore-list=<list>" "comma-separated regex list of filenames to ignore on checking whitespaces at EOL (default '${WHITESPACE_EOL_IGNORE_LIST}')"
+  yetus_add_option "--whitespace-tabs-ignore-list=<list>" "comma-separated regex list of filenames to ignore on checking tabs in a file (default '${WHITESPACE_TABS_IGNORE_LIST}')"
+}
+
+## @description  whitespace parse args hook
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+function whitespace_parse_args
+{
+  declare i
+
+  for i in "$@"; do
+    case ${i} in
+      --whitespace-eol-ignore-list=*)
+        yetus_comma_to_array WHITESPACE_EOL_IGNORE_LIST "${i#*=}"
+      ;;
+      --whitespace-tabs-ignore-list=*)
+        yetus_comma_to_array WHITESPACE_TABS_IGNORE_LIST "${i#*=}"
+      ;;
+    esac
+  done
+}
+
+function whitespace_linecomment_reporter
+{
+  declare file=$1
+  shift
+  declare comment=$*
+  declare tmpfile="${PATCH_DIR}/wlr.$$.${RANDOM}"
+
+  while read -r line; do
+    {
+      # shellcheck disable=SC2086
+      printf "%s" "$(echo ${line} | cut -f1-2 -d:)"
+      echo "${comment}"
+    } >> "${tmpfile}"
+  done < "${file}"
+
+  bugsystem_linecomments "whitespace:" "${tmpfile}"
+  rm "${tmpfile}"
+}
+
+function whitespace_postcompile
+{
+  declare repostatus=$1
+  declare count
+  declare result=0
+  declare eolignore
+  declare tabsignore
+
+  if [[ "${repostatus}" = branch ]]; then
+    return 0
+  fi
+
+  big_console_header "Checking for whitespace issues."
+  start_clock
+
+  pushd "${BASEDIR}" >/dev/null
+
+  eolignore=$(printf -- "-e ^%s: " "${WHITESPACE_EOL_IGNORE_LIST[@]}")
+  tabsignore=$(printf -- "-e ^%s: " "${WHITESPACE_TABS_IGNORE_LIST[@]}")
+
+  case "${BUILDMODE}" in
+    patch)
+      # shellcheck disable=SC2016,SC2086
+      ${AWK} '/\t/ {print $0}' \
+          "${GITDIFFCONTENT}" \
+        | ${GREP} -v ${tabsignore} >> "${PATCH_DIR}/whitespace-tabs.txt"
+
+      # shellcheck disable=SC2086
+       ${GREP} -E '[[:blank:]]$' \
+         "${GITDIFFCONTENT}" \
+        | ${GREP} -v ${eolignore} >> "${PATCH_DIR}/whitespace-eol.txt"
+    ;;
+    full)
+      # shellcheck disable=SC2086
+      ${GIT} grep -n -I --extended-regexp '[[:blank:]]$' \
+        | "${GREP}" -v ${eolignore} \
+         >> "${PATCH_DIR}/whitespace-eol.txt"
+      # shellcheck disable=SC2086
+      ${GIT} grep -n -I $'\t' \
+        | "${GREP}" -v ${tabsignore} \
+        >> "${PATCH_DIR}/whitespace-tabs.txt"
+    ;;
+  esac
+
+  # shellcheck disable=SC2016
+  count=$(wc -l "${PATCH_DIR}/whitespace-eol.txt" | ${AWK} '{print $1}')
+
+  if [[ ${count} -gt 0 ]]; then
+    if [[ "${BUILDMODE}" = full ]]; then
+      add_vote_table -1 whitespace "${BUILDMODEMSG} has ${count} line(s) that end in whitespace."
+    else
+      add_vote_table -1 whitespace \
+        "${BUILDMODEMSG} has ${count} line(s) that end in whitespace. Use git apply --whitespace=fix <<patch_file>>. Refer https://git-scm.com/docs/git-apply"
+    fi
+
+    whitespace_linecomment_reporter "${PATCH_DIR}/whitespace-eol.txt" "end of line"
+    add_footer_table whitespace "@@BASE@@/whitespace-eol.txt"
+    ((result=result+1))
+  fi
+
+  # shellcheck disable=SC2016
+  count=$(wc -l "${PATCH_DIR}/whitespace-tabs.txt" | ${AWK} '{print $1}')
+
+  if [[ ${count} -gt 0 ]]; then
+    add_vote_table -1 whitespace "${BUILDMODEMSG} ${count}"\
+      " line(s) with tabs."
+    add_footer_table whitespace "@@BASE@@/whitespace-tabs.txt"
+    whitespace_linecomment_reporter "${PATCH_DIR}/whitespace-tabs.txt" "tabs in line"
+    ((result=result+1))
+  fi
+
+  if [[ ${result} -gt 0 ]]; then
+    popd >/dev/null
+    return 1
+  fi
+
+  popd >/dev/null
+  add_vote_table +1 whitespace "${BUILDMODEMSG} has no whitespace issues."
+  return 0
+}

http://git-wip-us.apache.org/repos/asf/yetus/blob/6ebaa111/precommit/src/main/shell/test-patch.d/xml.sh
----------------------------------------------------------------------
diff --git a/precommit/src/main/shell/test-patch.d/xml.sh b/precommit/src/main/shell/test-patch.d/xml.sh
new file mode 100755
index 0000000..1e56905
--- /dev/null
+++ b/precommit/src/main/shell/test-patch.d/xml.sh
@@ -0,0 +1,90 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+declare -a XML_FILES
+
+add_test_type xml
+
+function xml_filefilter
+{
+  declare filename=$1
+
+  if [[ ${filename} =~ \.xml$ ]]; then
+    add_test xml
+  fi
+}
+
+function xml_precheck
+{
+  if ! verify_command "jrunscript" "${JAVA_HOME}/bin/jrunscript"; then
+    add_vote_table 0 xml "jrunscript was not available."
+    delete_test xml
+  fi
+}
+
+function xml_postcompile
+{
+  declare repostatus=$1
+  declare js
+  declare i
+  declare count
+
+  if ! verify_needed_test xml; then
+    return 0
+  fi
+
+  if [[ "${repostatus}" = branch ]]; then
+    return 0
+  fi
+
+  big_console_header "XML verification: ${BUILDMODE}"
+
+  js="${JAVA_HOME}/bin/jrunscript"
+
+  start_clock
+
+  pushd "${BASEDIR}" >/dev/null
+  for i in "${CHANGED_FILES[@]}"; do
+    if [[ ${i} =~ \.xml$ && -f ${i} ]]; then
+      if ! "${js}" -e "XMLDocument(arguments[0])" "${i}" > "${PATCH_DIR}/xml.txt.tmp" 2>&1; then
+        {
+          echo ""
+          echo "${i}:"
+          echo ""
+          cat "${PATCH_DIR}/xml.txt.tmp"
+        } >> "${PATCH_DIR}/xml.txt"
+        ((count=count+1))
+        XML_FILES+=("${i}")
+      fi
+    fi
+  done
+
+  popd >/dev/null
+
+  if [[ -f "${PATCH_DIR}/xml.txt.tmp" ]]; then
+    rm "${PATCH_DIR}/xml.txt.tmp"
+  fi
+
+  if [[ ${count} -gt 0 ]]; then
+    add_vote_table -1 xml "${BUILDMODEMSG} has ${count} ill-formed XML file(s)."
+    add_footer_table xml "@@BASE@@/xml.txt"
+    populate_test_table "XML" "Parsing Error(s):" "${XML_FILES[@]}"
+    return 1
+  fi
+
+  add_vote_table +1 xml "${BUILDMODEMSG} has no ill-formed XML file."
+  return 0
+}