You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by sa...@apache.org on 2018/03/15 16:32:08 UTC

lucene-solr:master: SOLR-10912: Add scripts for automatic patch validation

Repository: lucene-solr
Updated Branches:
  refs/heads/master b896fe68a -> 12372530a


SOLR-10912: Add scripts for automatic patch validation


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/12372530
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/12372530
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/12372530

Branch: refs/heads/master
Commit: 12372530a8366ab35834b8a93c39775ab87564ed
Parents: b896fe6
Author: Steve Rowe <sa...@apache.org>
Authored: Thu Mar 15 12:31:45 2018 -0400
Committer: Steve Rowe <sa...@apache.org>
Committed: Thu Mar 15 12:31:59 2018 -0400

----------------------------------------------------------------------
 build.xml                                       |   4 +-
 dev-tools/README.txt                            |  15 +-
 .../test-patch/jenkins-precommit-script.sh      |  90 +++++
 .../test-patch/lucene-solr-yetus-personality.sh | 399 +++++++++++++++++++
 dev-tools/test-patch/test-patch.sh              |  91 +++++
 lucene/CHANGES.txt                              |   4 +
 6 files changed, 594 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/12372530/build.xml
----------------------------------------------------------------------
diff --git a/build.xml b/build.xml
index 93415fb..70f0cc7 100755
--- a/build.xml
+++ b/build.xml
@@ -113,7 +113,7 @@
     </subant>
   </target>
 
-  <target name="validate" description="Validate dependencies, licenses, etc." depends="-validate-source-patterns,resolve-groovy,rat-sources-typedef,-install-forbidden-apis">
+  <target name="validate" description="Validate dependencies, licenses, etc." depends="validate-source-patterns,resolve-groovy,rat-sources-typedef,-install-forbidden-apis">
     <subant target="validate" inheritall="false" failonerror="true">
       <fileset dir="lucene" includes="build.xml" />
       <fileset dir="solr" includes="build.xml" />
@@ -124,7 +124,7 @@
     </subant>
   </target>
   
-  <target name="-validate-source-patterns" unless="disable.source-patterns" depends="resolve-groovy,rat-sources-typedef">
+  <target name="validate-source-patterns" unless="disable.source-patterns" depends="resolve-groovy,rat-sources-typedef">
     <groovy taskname="source-patterns" classpathref="rat.classpath" src="${common.dir}/tools/src/groovy/check-source-patterns.groovy"/>
   </target>
   

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/12372530/dev-tools/README.txt
----------------------------------------------------------------------
diff --git a/dev-tools/README.txt b/dev-tools/README.txt
index 404ce8b..b0b7e4a 100644
--- a/dev-tools/README.txt
+++ b/dev-tools/README.txt
@@ -6,10 +6,11 @@ as to the usefulness of the tools.
 Description of dev-tools/ contents:
 
 ./size-estimator-lucene-solr.xls -- Spreadsheet for estimating memory and disk usage in Lucene/Solr
-./doap/     -- Lucene and Solr project descriptors in DOAP RDF format.
-./eclipse/  -- Used to generate project descriptors for the Eclipse IDE.
-./git/      -- Git documentation and resources.
-./idea/     -- Used to generate project descriptors for IntelliJ's IDEA IDE.
-./maven/    -- Mavenizes the Lucene/Solr packages
-./netbeans/ -- Used to generate project descriptors for the Netbeans IDE.
-./scripts/  -- Odds and ends for building releases, etc.
+./doap/       -- Lucene and Solr project descriptors in DOAP RDF format.
+./eclipse/    -- Used to generate project descriptors for the Eclipse IDE.
+./git/        -- Git documentation and resources.
+./idea/       -- Used to generate project descriptors for IntelliJ's IDEA IDE.
+./maven/      -- Mavenizes the Lucene/Solr packages
+./netbeans/   -- Used to generate project descriptors for the Netbeans IDE.
+./scripts/    -- Odds and ends for building releases, etc.
+./test-patch/ -- Scripts for automatically validating patches

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/12372530/dev-tools/test-patch/jenkins-precommit-script.sh
----------------------------------------------------------------------
diff --git a/dev-tools/test-patch/jenkins-precommit-script.sh b/dev-tools/test-patch/jenkins-precommit-script.sh
new file mode 100644
index 0000000..155e333
--- /dev/null
+++ b/dev-tools/test-patch/jenkins-precommit-script.sh
@@ -0,0 +1,90 @@
+# 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.
+#
+# ----------------------------------------------------------------------------------
+#
+# This script is a copy of the one used by ASF Jenkins's PreCommit-LUCENE-Build job.
+#
+# See the script "test-patch.sh" in this directory for the script to use for
+# local manual patch validation.
+#
+# For other examples of scripts used to invoke Yetus, see the configuration on the
+# PreCommit jobs on ASF Jenkins: https://builds.apache.org/view/PreCommit+Builds/
+#
+# ------------>8-------------------------->8-------------------------->8------------
+ 
+#!/usr/bin/env bash
+
+# This is a modified copy of the script from Jenkins project "PreCommit-HADOOP-Build"
+
+YETUSDIR=${WORKSPACE}/yetus
+TESTPATCHBIN=${YETUSDIR}/precommit/test-patch.sh
+ARTIFACTS=${WORKSPACE}/out
+BASEDIR=${WORKSPACE}/sourcedir
+rm -rf "${ARTIFACTS}"
+mkdir -p "${ARTIFACTS}"
+
+if [[ -d /sys/fs/cgroup/pids/user.slice ]]; then
+  pids=$(cat /sys/fs/cgroup/pids/user.slice/user-910.slice/pids.max)
+  
+  if [[ ${pids} -gt 13000 ]]; then
+    echo "passed: ${pids}"
+    PIDMAX=10000
+  else
+    echo "failed: ${pids}"
+    PIDMAX=5500
+  fi
+else
+  systemctl status $$ 2>/dev/null
+  echo "passed? no limit on trusty?"
+  PIDMAX=10000
+fi
+
+# One-time operation: download and expand Yetus source release
+# TODO: when upgrading the Yetus release, remove the old tarball
+YETUS_RELEASE=0.7.0
+YETUS_TARBALL="yetus-${YETUS_RELEASE}.tar.gz"
+if [[ ! -f "${YETUS_TARBALL}" || ! -d "$YETUSDIR}}" ]]; then
+  echo "Downloading Yetus ${YETUS_RELEASE}"
+  curl -L "https://api.github.com/repos/apache/yetus/tarball/rel/${YETUS_RELEASE}" -o "${YETUS_TARBALL}"
+  if [[ -d "${YETUSDIR}" ]]; then
+    rm -rf "${YETUSDIR}"
+    mkdir -p "${YETUSDIR}"
+  fi
+  gunzip -c "${YETUS_TARBALL}" | tar xpf - -C "${YETUSDIR}" --strip-components 1
+fi
+
+
+YETUS_ARGS+=("--project=LUCENE")
+YETUS_ARGS+=("--basedir=${BASEDIR}")
+YETUS_ARGS+=("--patch-dir=${ARTIFACTS}")
+YETUS_ARGS+=("--personality=${BASEDIR}/dev-tools/test-patch/lucene-solr-yetus-personality.sh")
+YETUS_ARGS+=("--jira-user=lucenesolrqa")
+YETUS_ARGS+=("--jira-password=$JIRA_PASSWORD")
+YETUS_ARGS+=("--brief-report-file=${ARTIFACTS}/email-report.txt")
+YETUS_ARGS+=("--console-report-file=${ARTIFACTS}/console-report.txt")
+YETUS_ARGS+=("--html-report-file=${ARTIFACTS}/console-report.html")
+YETUS_ARGS+=("--proclimit=${PIDMAX}")
+YETUS_ARGS+=("--console-urls")
+YETUS_ARGS+=("--debug")
+YETUS_ARGS+=("--skip-dirs=dev-tools")
+YETUS_ARGS+=("--bugcomments=jira")
+YETUS_ARGS+=("--resetrepo")
+YETUS_ARGS+=("--run-tests")
+YETUS_ARGS+=("--contrib-guide=https://wiki.apache.org/lucene-java/HowToContribute#Contributing_your_work")
+YETUS_ARGS+=("--jenkins")
+YETUS_ARGS+=("LUCENE-${ISSUE_NUM}")
+
+/bin/bash ${TESTPATCHBIN} "${YETUS_ARGS[@]}"
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/12372530/dev-tools/test-patch/lucene-solr-yetus-personality.sh
----------------------------------------------------------------------
diff --git a/dev-tools/test-patch/lucene-solr-yetus-personality.sh b/dev-tools/test-patch/lucene-solr-yetus-personality.sh
new file mode 100644
index 0000000..e519639
--- /dev/null
+++ b/dev-tools/test-patch/lucene-solr-yetus-personality.sh
@@ -0,0 +1,399 @@
+#!/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.
+
+# This is a Yetus precommit "personality" (aka customized configuration) for Lucene/Solr.
+#
+# See the Yetus precommit documentation at https://yetus.apache.org/documentation/0.7.0/
+# and especially https://yetus.apache.org/documentation/0.7.0/precommit-advanced/.
+# See also the Yetus source code for other projects' personality examples at
+# https://git-wip-us.apache.org/repos/asf?p=yetus.git;f=precommit/personality;a=tree;hb=HEAD
+#
+# To add a new validation method (aka "plugin"):
+#   1) Add its name to the PLUGIN_LIST below
+#   2) Invoke "add_test_type" with it below
+#   3) Add a "<plugin>_filefilter" function to decide whether the plugin needs to be run based on changed files
+#   4) Add a "<plugin>_rebuild" function to call out to ant to perform the validation method.
+# See examples of the above-described function types ^^ below.
+   
+PLUGIN_LIST="ant,testoutput,jira,compile,javac,junit,unit,test4tests,checkluceneversion"
+PLUGIN_LIST+=",ratsources,checkforbiddenapis,checklicenses,validatesourcepatterns,validaterefguide"
+personality_plugins "${PLUGIN_LIST}"
+
+add_test_type "checkluceneversion"
+add_test_type "ratsources"
+add_test_type "checkforbiddenapis"
+add_test_type "checklicenses"
+add_test_type "validatesourcepatterns"
+add_test_type "validaterefguide"
+
+add_test_format "testoutput"
+
+## @description  Globals specific to this personality
+## @audience     private
+## @stability    evolving
+function personality_globals
+{
+  #shellcheck disable=SC2034
+  PATCH_BRANCH_DEFAULT=master
+  #shellcheck disable=SC2034
+  JIRA_ISSUE_RE='^(LUCENE|SOLR)-[0-9]+$'
+  #shellcheck disable=SC2034
+  JIRA_STATUS_RE='Patch Available'
+  #shellcheck disable=SC2034
+  GITHUB_REPO="apache/lucene-solr"
+  #shellcheck disable=SC2034
+  BUILDTOOL=ant
+}
+
+## @description  Queue up modules for this personality
+## @audience     private
+## @stability    evolving
+## @param        repostatus
+## @param        testtype
+function personality_modules
+{
+  local repostatus=$1
+  local testtype=$2
+
+  local module
+  local extra
+
+  local moduleType="submodules"
+
+  yetus_debug "Personality (lucene-solr): ${repostatus} ${testtype}"
+
+  clear_personality_queue
+
+  case ${testtype} in
+    clean|distclean|validatesourcepatterns)
+      moduleType="top"
+      ;;
+    checkluceneversion)
+      moduleType="solr"
+      ;;
+    ratsources)
+      moduleType="submodules"
+      ;;
+    checkforbiddenapis)
+      moduleType="both"
+      ;;
+    checklicenses)
+      moduleType="mains"
+      ;;
+    validaterefguide)
+      moduleType="solr-ref-guide"
+      ;;
+    compile)
+      moduleType="submodules"
+      extra="compile-test"
+      ;;
+    junit|unit)
+      moduleType="submodules"
+      extra="test"
+      ;;  
+    *)
+      ;;
+  esac
+
+  case ${moduleType} in
+    submodules)
+      for module in "${CHANGED_MODULES[@]}"; do
+        if [[ ! "$module" =~ ^lucene/(licenses|site) ]]; then # blacklist lucene/ dirs that aren't modules
+          if [[ "$module" =~ ^(lucene/(analysis/[^/]+|[^/]+)) ]]; then
+            lucene_module=${BASH_REMATCH[0]}
+            personality_enqueue_module "${lucene_module}" "$extra"
+          elif [[ "$module" =~ ^solr/(core|solrj|test-framework|solr-ref-guide|contrib/[^.]+) ]]; then # whitelist solr/ modules
+            solr_module=${BASH_REMATCH[0]}
+            personality_enqueue_module "${solr_module}" "$extra"
+          fi
+        fi
+      done
+      ;;
+    lucene|solr)
+      personality_enqueue_module "${moduleType}" "$extra"
+      ;;
+    top)
+      personality_enqueue_module . "$extra"
+      ;;
+    mains)
+      personality_enqueue_module "lucene" "$extra"
+      personality_enqueue_module "solr" "$extra"
+      ;;
+    both) # solr, lucene, or both
+      # personality_enqueue_module KEEPS duplicates, so de-dupe first
+      local doSolr=0,doLucene=0 
+      for module in "${CHANGED_MODULES[@]}"; do
+        if [[ "$module" =~ ^solr/ ]]; then doSolr=1; fi
+        if [[ "$module" =~ ^lucene/ ]]; then doLucene=1; fi
+      done
+      if [[ $doLucene == 1 ]]; then
+        if [[ $doSolr == 1 ]]; then 
+          personality_enqueue_module . "$extra"
+        else
+          personality_enqueue_module "lucene" "$extra"
+        fi          
+      elif [[ $doSolr == 1 ]]; then 
+        personality_enqueue_module "solr" "$extra"
+      fi
+      ;;
+    solr-ref-guide)
+      for module in "${CHANGED_MODULES[@]}"; do
+        if [[ "$module" =~ ^solr/solr-ref-guide ]]; then
+          personality_enqueue_module "solr/solr-ref-guide" "$extra"
+        fi
+      done 
+      ;;     
+    *)
+      ;;
+  esac
+}
+
+## @description  Add tests based upon personality needs
+## @audience     private
+## @stability    evolving
+## @param        filename
+function personality_file_tests
+{
+  declare filename=$1
+
+  yetus_debug "Using Lucene/Solr-specific personality_file_tests"
+
+  if [[ ${filename} =~ build\.xml$ || ${filename} =~ /src/(java|resources|test|test-files|tools) ]]; then
+    yetus_debug "tests/unit: ${filename}"
+    add_test compile
+    add_test javac
+    add_test unit
+  fi
+}
+
+## @description  hook to reroute junit folder to search test results based on the module
+## @audience     private
+## @stability    evolving
+## @param        module
+## @param        buildlogfile
+function testoutput_process_tests
+{
+  # shellcheck disable=SC2034
+  declare module=$1
+  declare buildlogfile=$2
+  if [[ "${module}" =~ ^lucene/analysis/ ]]; then
+    JUNIT_TEST_OUTPUT_DIR="../../build/${module#*/}"
+  elif [[ "${module}" =~ ^solr/contrib/extraction ]]; then
+    JUNIT_TEST_OUTPUT_DIR="../../build/contrib/solr-cell"
+  elif [[ "${module}" =~ ^solr/contrib/(.*) ]]; then
+    JUNIT_TEST_OUTPUT_DIR="../../build/contrib/solr-${BASH_REMATCH[1]}"
+  elif [[ "${module}" =~ ^(lucene|solr)/ ]]; then
+    JUNIT_TEST_OUTPUT_DIR="../build/${module#*/}"
+  fi
+  yetus_debug "Rerouting build dir for junit to ${JUNIT_TEST_OUTPUT_DIR}"
+}
+
+## @description  checkluceneversion file filter
+## @audience     private
+## @stability    evolving
+## @param        filename
+function checkluceneversion_filefilter
+{
+  local filename=$1
+  if [[ ${filename} =~ ^solr/(example|server/solr/configsets) ]]; then
+    yetus_debug "tests/checkluceneversion: ${filename}"
+    add_test checkluceneversion
+  fi
+}
+
+## @description  checkluceneversion test
+## @audience     private
+## @stability    evolving
+## @param        repostatus
+function checkluceneversion_rebuild
+{
+  local repostatus=$1
+  lucene_ant_command ${repostatus} "checkluceneversion" "check-example-lucene-match-version" "Check configsets' lucene version"
+}
+
+## @description  ratsources file filter
+## @audience     private
+## @stability    evolving
+## @param        filename
+function ratsources_filefilter
+{
+  local filename=$1
+  if [[ ${filename} =~ /src/|\.xml$ ]] ; then
+    yetus_debug "tests/ratsources: ${filename}"
+    add_test ratsources
+  fi
+}
+
+## @description  ratsources test
+## @audience     private
+## @stability    evolving
+## @param        repostatus
+function ratsources_rebuild
+{
+  local repostatus=$1
+  lucene_ant_command ${repostatus} "ratsources" "rat-sources" "Release audit (RAT)"
+}
+
+## @description  checkforbiddenapis file filter
+## @audience     private
+## @stability    evolving
+## @param        filename
+function checkforbiddenapis_filefilter
+{
+  local filename=$1
+  if [[ ${filename} =~ \.java$ ]] ; then
+    yetus_debug "tests/checkforbiddenapis: ${filename}"
+    add_test checkforbiddenapis
+  fi
+}
+
+## @description  checkforbiddenapis test
+## @audience     private
+## @stability    evolving
+## @param        repostatus
+function checkforbiddenapis_rebuild
+{
+  local repostatus=$1
+  lucene_ant_command ${repostatus} "checkforbiddenapis" "check-forbidden-apis" "Check forbidden APIs"
+}
+
+## @description  checklicenses file filter
+## @audience     private
+## @stability    evolving
+## @param        filename
+function checklicenses_filefilter
+{
+  local filename=$1
+  if [[ ${filename} =~ (lucene|solr)/licenses/|lucene/ivy-versions.properties$ ]]; then
+    yetus_debug "tests/checklicenses: ${filename}"
+    add_test checklicenses
+  fi    
+}
+
+## @description  checklicenses test
+## @audience     private
+## @stability    evolving
+## @param        repostatus
+function checklicenses_rebuild
+{
+  local repostatus=$1
+  lucene_ant_command ${repostatus} "checklicenses" "check-licenses" "Check licenses"
+}
+
+## @description  validaterefguide file filter
+## @audience     private
+## @stability    evolving
+## @param        filename
+function validaterefguide_filefilter
+{
+  local filename=$1
+  if [[ ${filename} =~ solr/solr-ref-guide ]]; then
+    yetus_debug "tests/validaterefguide: ${filename}"
+    add_test validaterefguide
+  fi
+}
+
+## @description  validaterefguide test
+## @audience     private
+## @stability    evolving
+## @param        repostatus
+function validaterefguide_rebuild
+{
+  local repostatus=$1
+  lucene_ant_command ${repostatus} "validaterefguide" "bare-bones-html-validation" "Validate ref guide"
+}
+
+## @description  validatesourcepatterns file filter
+## @audience     private
+## @stability    evolving
+## @param        filename
+function validatesourcepatterns_filefilter
+{
+  local filename=$1
+  if [[ ${filename} =~ \.(java|jflex|py|pl|g4|jj|html|js|css|xml|xsl|vm|sh|cmd|bat|policy|properties|mdtext|groovy|template|adoc|json)$ ]]; then
+    yetus_debug "tests/validatesourcepatterns: ${filename}"
+    add_test validatesourcepatterns
+  fi
+}
+
+## @description  validatesourcepatterns test
+## @audience     private
+## @stability    evolving
+## @param        repostatus
+function validatesourcepatterns_rebuild
+{
+  local repostatus=$1
+  lucene_ant_command ${repostatus} "validatesourcepatterns" "validate-source-patterns" "Validate source patterns"
+}
+
+function lucene_ant_command
+{
+  declare repostatus=$1
+  declare testname=$2
+  declare antcommand=$3
+  declare title=$4
+
+  declare result=0
+  declare i=0
+  declare module
+  declare fn
+  declare result
+
+  if [[ "${repostatus}" = branch ]]; then
+    return 0
+  fi
+
+  if ! verify_needed_test ${testname}; then
+    echo "${BUILDMODEMSG} does not need ${testname} testing."
+    return 0
+  fi
+
+  big_console_header "${title}"
+  personality_modules $repostatus $testname
+  until [[ ${i} -eq ${#MODULE[@]} ]]; do
+    if [[ ${MODULE_STATUS[${i}]} == -1 ]]; then
+      ((result=result+1))
+      ((i=i+1))
+      continue
+    fi
+    ANT_ARGS=${antcommand}
+
+    start_clock
+    module=${MODULE[$i]}
+    fn=$(module_file_fragment "${module}")
+    logfilename="${repostatus}-${antcommand}-${fn}.txt";
+    logfile="${PATCH_DIR}/${logfilename}"
+    buildtool_cwd "${i}"
+    echo_and_redirect "${logfile}" $(ant_executor)
+
+    if [[ $? == 0 ]] ; then
+      module_status ${i} +1 "${logfilename}" "${title}" "${antcommand} passed"
+    else
+      module_status ${i} -1 "${logfilename}" "${title}" "${antcommand} failed"
+      ((result=result+1))
+    fi
+    ((i=i+1))
+    savestop=$(stop_clock)
+    MODULE_STATUS_TIMER[${i}]=${savestop}
+  done
+  ANT_ARGS=""
+  if [[ ${result} -gt 0 ]]; then
+    modules_messages ${repostatus} "${title}" false
+    return 1
+  fi
+  modules_messages ${repostatus} "${title}" true
+  return 0
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/12372530/dev-tools/test-patch/test-patch.sh
----------------------------------------------------------------------
diff --git a/dev-tools/test-patch/test-patch.sh b/dev-tools/test-patch/test-patch.sh
new file mode 100644
index 0000000..3d7bb38
--- /dev/null
+++ b/dev-tools/test-patch/test-patch.sh
@@ -0,0 +1,91 @@
+#!/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.
+
+
+# Invoke Yetus locally to validate a patch against Lucene/Solr, and 
+# (optionally) post a validation report comment on the passed-in JIRA issue
+# from which the patch was downloaded.
+#
+# NB 1: The Lucene/Solr Yetus personality currently performs the equivalent
+# of "ant precommit" and "ant test" in modified modules; instead of using
+# this script to test your changes, please consider invoking those targets
+# directly.
+#
+# NB 2: The Jenkins job "PreCommit-Admin" automatically detects new patches
+# posted to LUCENE and SOLR JIRA issues that are in the "Patch Available"
+# state, and then queues the appropriate "PreCommit-LUCENE-Build" or 
+# "PreCommit-SOLR-Build" job pointing to the JIRA hosting the new patch.
+# Those jobs perform the same checks as this script, and like this script,
+# will post a comment on the JIRA issue.  As a result, manual invocation
+# (e.g. via this script) should ordinarily not be necessary.
+# 
+# Environment variable ${YETUS_HOME} must point to the separately installed
+# Yetus home directory, e.g. the "rel/0.7.0" tag checked out from a local
+# Yetus Git repository. 
+#
+# Environment variable ${PROJECT_DIR} must point to a local Lucene/Solr git
+# workspace dir.
+#
+# The sole cmdline param can be a JIRA issue, a local patch file, 
+# or a URL to a patch file.
+#
+# If the cmdline param is a JIRA issue, the patch to download and validate
+# will be the most recently uploaded patch on the issue.  See the patch
+# naming schema that Yetus recognizes: 
+# https://yetus.apache.org/documentation/in-progress/precommit-patchnames/ 
+#
+# If the cmdline param is a JIRA issue and you provide JIRA user/password via
+# environment variables ${JIRA_USER} and ${JIRA_PASSWORD}, a patch validation
+# report will be posted as a comment on the JIRA issue.
+
+help () {
+  echo "Usage 1: [ JIRA_USER=xxx JIRA_PASSWORD=yyy ] PROJECT_DIR=/path/to/lucene-solr YETUS_HOME=/path/to/yetus $0 SOLR-12345"
+  echo "Usage 2: [ JIRA_USER=xxx JIRA_PASSWORD=yyy ] PROJECT_DIR=/path/to/lucene-solr YETUS_HOME=/path/to/yetus $0 LUCENE-12345"
+  echo "Usage 3: PROJECT_DIR=/path/to/lucene-solr YETUS_HOME=/path/to/yetus $0 ../local.patch"
+  echo "Usage 4: PROJECT_DIR=/path/to/lucene-solr YETUS_HOME=/path/to/yetus $0 http://example.com/remote.patch"
+}
+
+if [[ -z "${PROJECT_DIR}" || -z "${YETUS_HOME}" || -z "${1}" || "${1}" =~ ^-(-?h(elp)?|\?)$ ]] ; then
+  help
+  exit 1
+fi
+
+PATCH_REF=${1}
+TEST_PATCH_BIN="${YETUS_HOME}/precommit/test-patch.sh"
+SCRIPT_DIR="$( cd "$( dirname "${0}" )" && pwd )"
+declare -a YETUS_ARGS
+
+if [[ ${PATCH_REF} =~ ^(LUCENE|SOLR)- ]]; then
+  JIRA_PROJECT=${BASH_REMATCH[0]}
+  YETUS_ARGS+=("--project=${JIRA_PROJECT}")
+  
+  if [[ -n "${JIRA_USER}" ]] && [[ -n "${JIRA_PASSWORD}" ]] ; then 
+    YETUS_ARGS+=("--jira-user=${JIRA_USER}")
+    YETUS_ARGS+=("--jira-password=${JIRA_PASSWORD}")
+    YETUS_ARGS+=("--bugcomments=jira")
+  fi
+fi
+
+YETUS_ARGS+=("--basedir=${PROJECT_DIR}")
+YETUS_ARGS+=("--personality=${SCRIPT_DIR}/lucene-solr-yetus-personality.sh")
+YETUS_ARGS+=("--skip-dirs=dev-tools")
+YETUS_ARGS+=("--resetrepo")
+YETUS_ARGS+=("--run-tests")
+YETUS_ARGS+=("--debug")
+YETUS_ARGS+=("--robot")
+YETUS_ARGS+=("${PATCH_REF}")
+
+/bin/bash ${TEST_PATCH_BIN} "${YETUS_ARGS[@]}"

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/12372530/lucene/CHANGES.txt
----------------------------------------------------------------------
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 6be005d..b54b1ae 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -103,6 +103,10 @@ New Features
   deleted documents around for later reuse. See "IW.softUpdateDocument(...)"
   for reference. (Simon Willnauer)
 
+Other
+
+* SOLR-10912: Add automatic patch validation. (Mano Kovacs, Steve Rowe)
+
 ======================= Lucene 7.3.0 =======================
 
 API Changes