From c0840cc8126a9f61559f58cc2fd987b2966ea6e5 Mon Sep 17 00:00:00 2001 From: David Smiley Date: Sat, 4 Jul 2026 12:59:35 -0400 Subject: [PATCH 1/4] smokeTestRelease.py: Remove --test-alt-java support This option (PR-#2685, ported from a similar Lucene feature) added complexity: parallel lists of alt-JDK homes/runners/versions threaded through the Java config, a closure to dedupe per-JDK test runs, and per-JDK copies of the unpacked binary distribution. It was never well exercised -- it merged via lazy consensus with the sole tester noting they hadn't actually tried it with a different JDK version -- and it isn't invoked anywhere in the documented release process (releaseWizard.yaml never passes it). It has caused at least two real bugs, including a TypeError crash when no alt JDK is given at all. Testing with another JDK is just as easy by setting JAVA_HOME and re-running the script; the modest cost of redoing some work is a fair trade for a much simpler script. --- dev-tools/scripts/README.md | 4 +- dev-tools/scripts/smokeTestRelease.py | 114 ++++++++++---------------- 2 files changed, 46 insertions(+), 72 deletions(-) diff --git a/dev-tools/scripts/README.md b/dev-tools/scripts/README.md index 0618a1aae14b..b32b63c4a565 100644 --- a/dev-tools/scripts/README.md +++ b/dev-tools/scripts/README.md @@ -15,7 +15,7 @@ Used to validate new release candidates (RC). The script downloads an RC from a or local folder, then runs a number of sanity checks on the artifacts, and then runs the full tests. - usage: smokeTestRelease.py [-h] [--tmp-dir PATH] [--not-signed] [--local-keys PATH] [--revision REVISION] [--version X.Y.Z(-ALPHA|-BETA)?] [--test-alt-java TEST_ALT_JAVA] [--download-only] [--dev-mode] url ... + usage: smokeTestRelease.py [-h] [--tmp-dir PATH] [--not-signed] [--local-keys PATH] [--revision REVISION] [--version X.Y.Z(-ALPHA|-BETA)?] [--download-only] [--dev-mode] url ... Utility to test a release. @@ -31,8 +31,6 @@ the full tests. --revision REVISION GIT revision number that release was built with, defaults to that in URL --version X.Y.Z(-ALPHA|-BETA)? Version of the release, defaults to that in URL - --test-alt-java TEST_ALT_JAVA - Path to Java alternative home directory, to run tests with if specified --download-only Only perform download and sha hash check steps --dev-mode Enable dev mode, will not check branch compatibility diff --git a/dev-tools/scripts/smokeTestRelease.py b/dev-tools/scripts/smokeTestRelease.py index 21f7b8a25290..b7e1d78b325d 100755 --- a/dev-tools/scripts/smokeTestRelease.py +++ b/dev-tools/scripts/smokeTestRelease.py @@ -15,6 +15,23 @@ # See the License for the specific language governing permissions and # limitations under the License. +# -------------------------------------------------------------------------- +# Quick local smoke test against a release built from this checkout (no +# download, no signing). Copy-paste from a shell at the repo root: +# +# VERSION=$(sed -n "s/.*baseVersion = '\(.*\)'/\1/p" build.gradle)-SNAPSHOT +# ./gradlew -p solr/distribution assembleRelease -Dversion.release=$VERSION +# python3 -u dev-tools/scripts/smokeTestRelease.py \ +# --tmp-dir /tmp/smoke_solr \ +# --not-signed \ +# --revision skip \ +# --version $VERSION \ +# file://$(pwd)/solr/distribution/build/release +# +# --revision skip bypasses the JAR-manifest git-revision check, which is +# otherwise brittle against a Gradle build cache holding a stale commit hash. +# -------------------------------------------------------------------------- + import argparse import codecs import datetime @@ -627,19 +644,13 @@ def verifySrcUnpacked(java, artifact, unpackPath, version, testArgs): print(' run "%s"' % validateCmd) java.run_java(validateCmd, '%s/validate.log' % unpackPath) - def _run_for_java(run_java, java_home, java_version, clean_first=False): - suffix = '-java%s' % java_version - print(" run tests w/ Java %s and testArgs='%s'..." % (java_version, testArgs)) - run_java('./gradlew --no-daemon %stest %s' % ('clean ' if clean_first else '', testArgs), '%s/test%s.log' % (unpackPath, suffix)) - print(" run integration tests w/ Java %s" % java_version) - run_java('./gradlew --no-daemon integrationTest -Dversion.release=%s' % version, '%s/itest%s.log' % (unpackPath, suffix)) - print(" build binary release w/ Java %s" % java_version) - run_java('./gradlew --no-daemon dev -Dversion.release=%s' % version, '%s/assemble%s.log' % (unpackPath, suffix)) - testSolrExample("%s/solr/packaging/build/dev" % unpackPath, java_home, False) - - _run_for_java(java.run_java, java.java_home, BASE_JAVA_VERSION) - for run_alt_java, alt_java_home, alt_java_version in zip(java.run_alt_javas, java.alt_java_homes, java.alt_java_versions): - _run_for_java(run_alt_java, alt_java_home, alt_java_version, clean_first=True) + print(" run tests w/ Java %s and testArgs='%s'..." % (BASE_JAVA_VERSION, testArgs)) + java.run_java('./gradlew --no-daemon test %s' % testArgs, '%s/test.log' % unpackPath) + print(" run integration tests w/ Java %s" % BASE_JAVA_VERSION) + java.run_java('./gradlew --no-daemon integrationTest -Dversion.release=%s' % version, '%s/itest.log' % unpackPath) + print(" build binary release w/ Java %s" % BASE_JAVA_VERSION) + java.run_java('./gradlew --no-daemon dev -Dversion.release=%s' % version, '%s/assemble.log' % unpackPath) + testSolrExample("%s/solr/packaging/build/dev" % unpackPath, java.java_home, False) testChangelogMd('.', version) @@ -670,27 +681,9 @@ def verifyBinaryUnpacked(java, artifact, unpackPath, version, gitRevision): checkAllJARs(os.getcwd(), gitRevision, version) - print(' copying unpacked distribution for Java %s ...' % BASE_JAVA_VERSION) - javaBaseVersionUnpackPath = '%s-java%s' % (unpackPath, BASE_JAVA_VERSION) - if os.path.exists(javaBaseVersionUnpackPath): - shutil.rmtree(javaBaseVersionUnpackPath) - shutil.copytree(unpackPath, javaBaseVersionUnpackPath) - os.chdir(javaBaseVersionUnpackPath) print(' test solr example w/ Java %s...' % BASE_JAVA_VERSION) - testSolrExample(javaBaseVersionUnpackPath, java.java_home, isSlim) - - if java.run_alt_javas: - for run_alt_java, alt_java_home, alt_java_version in zip(java.run_alt_javas, java.alt_java_homes, java.alt_java_versions): - print(' copying unpacked distribution for Java %s ...' % alt_java_version) - javaAltVersionUnpackPath = '%s-java%s' % (unpackPath, alt_java_version) - if os.path.exists(javaAltVersionUnpackPath): - shutil.rmtree(javaAltVersionUnpackPath) - shutil.copytree(unpackPath, javaAltVersionUnpackPath) - os.chdir(javaAltVersionUnpackPath) - print(' test solr example w/ Java %s...' % alt_java_version) - testSolrExample(javaAltVersionUnpackPath, alt_java_home, isSlim) + testSolrExample(unpackPath, java.java_home, isSlim) - os.chdir(unpackPath) testChangelogMd('.', version) @@ -982,42 +975,27 @@ def crawl(downloadedFiles, urlString, targetDir, exclusions=set()): sys.stdout.write('.') -def make_java_config(parser, alt_java_homes): - def _make_runner(java_home, is_base_version=False): - if cygwin: - java_home = subprocess.check_output('cygpath -u "%s"' % java_home, shell=True).decode('utf-8').strip() - cmd_prefix = 'export JAVA_HOME="%s" PATH="%s/bin:$PATH" JAVACMD="%s/bin/java"' % \ - (java_home, java_home, java_home) - s = subprocess.check_output('%s; java -version' % cmd_prefix, - shell=True, stderr=subprocess.STDOUT).decode('utf-8') - actual_version = re.search(r'version "([1-9][0-9]*)', s).group(1) - print('Java %s JAVA_HOME=%s' % (actual_version, java_home)) - - # validate Java version - if is_base_version: - if BASE_JAVA_VERSION != actual_version: - parser.error('got wrong base version for java %s:\n%s' % (BASE_JAVA_VERSION, s)) - else: - if int(actual_version) < int(BASE_JAVA_VERSION): - parser.error('got wrong version for java %s, less than base version %s:\n%s' % (actual_version, BASE_JAVA_VERSION, s)) - def run_java(cmd, logfile): - run('%s; %s' % (cmd_prefix, cmd), logfile) - return run_java, actual_version - - java_home = os.environ.get('JAVA_HOME') +def make_java_config(parser): + java_home = os.environ.get('JAVA_HOME') if java_home is None: parser.error('JAVA_HOME must be set') - run_java, _ = _make_runner(java_home, True) - run_alt_javas = [] - alt_java_versions = [] - if alt_java_homes: - for alt_java_home in alt_java_homes: - run_alt_java, version = _make_runner(alt_java_home) - run_alt_javas.append(run_alt_java) - alt_java_versions.append(version) - print("alt java ", run_alt_javas, alt_java_versions) - jc = namedtuple('JavaConfig', 'run_java java_home run_alt_javas alt_java_homes alt_java_versions') - return jc(run_java, java_home, run_alt_javas, alt_java_homes, alt_java_versions) + if cygwin: + java_home = subprocess.check_output('cygpath -u "%s"' % java_home, shell=True).decode('utf-8').strip() + cmd_prefix = 'export JAVA_HOME="%s" PATH="%s/bin:$PATH" JAVACMD="%s/bin/java"' % \ + (java_home, java_home, java_home) + s = subprocess.check_output('%s; java -version' % cmd_prefix, + shell=True, stderr=subprocess.STDOUT).decode('utf-8') + actual_version = re.search(r'version "([1-9][0-9]*)', s).group(1) + print('Java %s JAVA_HOME=%s' % (actual_version, java_home)) + + if BASE_JAVA_VERSION != actual_version: + parser.error('got wrong base version for java %s:\n%s' % (BASE_JAVA_VERSION, s)) + + def run_java(cmd, logfile): + run('%s; %s' % (cmd_prefix, cmd), logfile) + + jc = namedtuple('JavaConfig', 'run_java java_home') + return jc(run_java, java_home) version_re = re.compile(r'(\d+\.\d+\.\d+(-ALPHA|-BETA)?)') @@ -1040,8 +1018,6 @@ def parse_config(): help='GIT revision number that release was built with, defaults to that in URL') parser.add_argument('--version', metavar='X.Y.Z(-ALPHA|-BETA)?', help='Version of the release, defaults to that in URL') - parser.add_argument('--test-alt-java', action='append', - help='Path to Java alternative home directory, to run tests with if specified') parser.add_argument('--download-only', action='store_true', default=False, help='Only perform download and sha hash check steps') parser.add_argument('--dev-mode', action='store_true', default=False, @@ -1070,7 +1046,7 @@ def parse_config(): if c.local_keys is not None and not os.path.exists(c.local_keys): parser.error('Local KEYS file "%s" not found' % c.local_keys) - c.java = make_java_config(parser, c.test_alt_java) + c.java = make_java_config(parser) if c.tmp_dir: c.tmp_dir = os.path.abspath(c.tmp_dir) From 1806d653ee464e993792db57ae7bf5d2f5512416 Mon Sep 17 00:00:00 2001 From: David Smiley Date: Sat, 4 Jul 2026 13:03:14 -0400 Subject: [PATCH 2/4] changelog --- .../unreleased/PR#4608-smokeTestRelease_removeAltJava.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 changelog/unreleased/PR#4608-smokeTestRelease_removeAltJava.yml diff --git a/changelog/unreleased/PR#4608-smokeTestRelease_removeAltJava.yml b/changelog/unreleased/PR#4608-smokeTestRelease_removeAltJava.yml new file mode 100644 index 000000000000..de3d651654ef --- /dev/null +++ b/changelog/unreleased/PR#4608-smokeTestRelease_removeAltJava.yml @@ -0,0 +1,8 @@ +title: > + smokeTestRelease.py: Remove --test-alt-java support +type: other +authors: + - name: David Smiley +links: + - name: PR#4608 + url: https://github.com/apache/solr/pull/4608 From d926096bb7015748f065e4936e0621e62c8e081e Mon Sep 17 00:00:00 2001 From: David Smiley Date: Sun, 5 Jul 2026 23:53:11 -0400 Subject: [PATCH 3/4] smokeTestRelease.py: add --reuse-tmp-dir to skip re-downloads Add --reuse-tmp-dir, letting --tmp-dir point at a prior run's directory and skip re-fetching artifacts, sigs, KEYS, and the maven tree that are already there. Print the exact re-run command (e.g. to retest with a different JAVA_HOME) both right after the tmp dir is set up and again on success, so it's visible even if the run fails. --- ...PR#4608-smokeTestRelease_removeAltJava.yml | 8 ------ dev-tools/scripts/smokeTestRelease.py | 26 +++++++++++++++++++ 2 files changed, 26 insertions(+), 8 deletions(-) delete mode 100644 changelog/unreleased/PR#4608-smokeTestRelease_removeAltJava.yml diff --git a/changelog/unreleased/PR#4608-smokeTestRelease_removeAltJava.yml b/changelog/unreleased/PR#4608-smokeTestRelease_removeAltJava.yml deleted file mode 100644 index de3d651654ef..000000000000 --- a/changelog/unreleased/PR#4608-smokeTestRelease_removeAltJava.yml +++ /dev/null @@ -1,8 +0,0 @@ -title: > - smokeTestRelease.py: Remove --test-alt-java support -type: other -authors: - - name: David Smiley -links: - - name: PR#4608 - url: https://github.com/apache/solr/pull/4608 diff --git a/dev-tools/scripts/smokeTestRelease.py b/dev-tools/scripts/smokeTestRelease.py index b7e1d78b325d..5bb7887ba479 100755 --- a/dev-tools/scripts/smokeTestRelease.py +++ b/dev-tools/scripts/smokeTestRelease.py @@ -1010,6 +1010,9 @@ def parse_config(): formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument('--tmp-dir', metavar='PATH', help='Temporary directory to test inside, defaults to /tmp/smoke_solr_$version_$revision') + parser.add_argument('--reuse-tmp-dir', action='store_true', default=False, + help='Allows --tmp-dir to already exist and reuses whatever it already downloaded, ' + 'instead of re-downloading everything from scratch') parser.add_argument('--not-signed', dest='is_signed', action='store_false', default=True, help='Indicates the release is not signed') parser.add_argument('--local-keys', metavar='PATH', @@ -1072,13 +1075,32 @@ def main(): if not c.version.startswith(scriptVersion + '.') and not c.dev_mode: raise RuntimeError('smokeTestRelease.py for %s.X is incompatible with a %s release.' % (scriptVersion, c.version)) + global FORCE_CLEAN + FORCE_CLEAN = not c.reuse_tmp_dir + print('NOTE: output encoding is %s' % sys.stdout.encoding) smokeTest(c.java, c.url, c.revision, c.version, c.tmp_dir, c.is_signed, c.local_keys, ' '.join(c.test_args), downloadOnly=c.download_only) +def printReuseHint(baseURL, gitRevision, version, tmpDir, isSigned, local_keys, testArgs): + cmd = ['./smokeTestRelease.py', '--tmp-dir', tmpDir, '--reuse-tmp-dir', + '--revision', gitRevision, '--version', version] + if not isSigned: + cmd.append('--not-signed') + if local_keys is not None: + cmd.extend(['--local-keys', local_keys]) + cmd.append(baseURL) + if testArgs: + cmd.append(testArgs) + print() + print('NOTE: to re-run (e.g. with a different Java version) reusing what is already downloaded to %s:' % tmpDir) + print(' JAVA_HOME=/path/to/java %s' % ' '.join(cmd)) + + def smokeTest(java, baseURL, gitRevision, version, tmpDir, isSigned, local_keys, testArgs, downloadOnly=False): startTime = datetime.datetime.now() + origTestArgs = testArgs # Avoid @Nightly and @Badapple tests as they are slow and buggy # Instead verify that the recent Jenkins tests pass @@ -1092,6 +1114,8 @@ def smokeTest(java, baseURL, gitRevision, version, tmpDir, isSigned, local_keys, if not os.path.exists(tmpDir): os.makedirs(tmpDir) + printReuseHint(baseURL, gitRevision, version, tmpDir, isSigned, local_keys, origTestArgs) + solrPath = None print() print('Load release URL "%s"...' % baseURL) @@ -1144,6 +1168,8 @@ def smokeTest(java, baseURL, gitRevision, version, tmpDir, isSigned, local_keys, else: print("Solr test done (--download-only specified)") + printReuseHint(baseURL, gitRevision, version, tmpDir, isSigned, local_keys, origTestArgs) + print('\nSUCCESS! [%s]\n' % (datetime.datetime.now() - startTime)) From 0d6148b3344d138dd9036d7498f4ffe1cffebb53 Mon Sep 17 00:00:00 2001 From: David Smiley Date: Mon, 6 Jul 2026 01:40:12 -0400 Subject: [PATCH 4/4] Support higher Java versions than 21 --- dev-tools/scripts/smokeTestRelease.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev-tools/scripts/smokeTestRelease.py b/dev-tools/scripts/smokeTestRelease.py index 5bb7887ba479..9701ec575b8a 100755 --- a/dev-tools/scripts/smokeTestRelease.py +++ b/dev-tools/scripts/smokeTestRelease.py @@ -988,8 +988,8 @@ def make_java_config(parser): actual_version = re.search(r'version "([1-9][0-9]*)', s).group(1) print('Java %s JAVA_HOME=%s' % (actual_version, java_home)) - if BASE_JAVA_VERSION != actual_version: - parser.error('got wrong base version for java %s:\n%s' % (BASE_JAVA_VERSION, s)) + if int(actual_version) < int(BASE_JAVA_VERSION): + parser.error('got wrong version for java %s, must be at least base version %s:\n%s' % (actual_version, BASE_JAVA_VERSION, s)) def run_java(cmd, logfile): run('%s; %s' % (cmd_prefix, cmd), logfile)