From d59169c9cc96dd60c976079d18ef4da56024bb26 Mon Sep 17 00:00:00 2001 From: David Smiley Date: Tue, 27 Oct 2026 20:48:34 -0400 Subject: [PATCH 1/3] SOLR-18432: SolrVersion tolerates prerelease qualifier - satisfies() ignores the prerelease qualifier; add withoutPrerelease() - ZkController version-compat check compares without prerelease - SystemInfoProvider solrImplVersion fallback uses LATEST_STRING Co-Authored-By: Claude Sonnet 5.5 --- .../SOLR-18432-solrversion-prerelease.yml | 7 +++++++ .../apache/solr/client/api/util/SolrVersion.java | 12 ++++++++++-- .../test/org/apache/solr/util/TestSolrVersion.java | 14 ++++++++++++++ .../java/org/apache/solr/cloud/ZkController.java | 2 +- .../solr/handler/admin/SystemInfoProvider.java | 3 +-- .../handler/admin/NodeSystemInfoProviderTest.java | 2 +- 6 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 changelog/unreleased/SOLR-18432-solrversion-prerelease.yml diff --git a/changelog/unreleased/SOLR-18432-solrversion-prerelease.yml b/changelog/unreleased/SOLR-18432-solrversion-prerelease.yml new file mode 100644 index 000000000000..ee6404c23cde --- /dev/null +++ b/changelog/unreleased/SOLR-18432-solrversion-prerelease.yml @@ -0,0 +1,7 @@ +title: SolrVersion now tolerates a prerelease qualifier such as -SNAPSHOT. Package version constraints are matched ignoring the qualifier, the cluster version-compatibility warning no longer treats a prerelease as older than its release, and /api/node/system reports the correct Solr implementation version. +type: fixed +authors: + - name: David Smiley +links: + - name: SOLR-18432 + url: https://issues.apache.org/jira/browse/SOLR-18432 diff --git a/solr/api/src/java/org/apache/solr/client/api/util/SolrVersion.java b/solr/api/src/java/org/apache/solr/client/api/util/SolrVersion.java index c0d5324e5aab..92b1c82e60cc 100644 --- a/solr/api/src/java/org/apache/solr/client/api/util/SolrVersion.java +++ b/solr/api/src/java/org/apache/solr/client/api/util/SolrVersion.java @@ -76,12 +76,20 @@ public boolean lessThanOrEqualTo(SolrVersion other) { /** * Returns true if this version satisfies the provided SemVer - * Expression + * Expression. Any prerelease qualifier (e.g. "-SNAPSHOT") is ignored, unlike plain SemVer + * range semantics where a prerelease satisfies no range that doesn't name one. * * @param semVerExpression the expression to test */ public boolean satisfies(String semVerExpression) { - return version.satisfies(semVerExpression); + return version.withClearedPreRelease().satisfies(semVerExpression); + } + + /** + * Returns this version with any prerelease qualifier removed, e.g. "10.1.0-SNAPSHOT" to "10.1.0" + */ + public SolrVersion withoutPrerelease() { + return new SolrVersion(version.withClearedPreRelease()); } public int getMajorVersion() { diff --git a/solr/api/src/test/org/apache/solr/util/TestSolrVersion.java b/solr/api/src/test/org/apache/solr/util/TestSolrVersion.java index ff55514e4ab2..2789ab57aa9c 100644 --- a/solr/api/src/test/org/apache/solr/util/TestSolrVersion.java +++ b/solr/api/src/test/org/apache/solr/util/TestSolrVersion.java @@ -59,4 +59,18 @@ public void testSatisfies() { assertFalse(SOLR_9_0_1.satisfies("8.x")); assertTrue(SOLR_9_0_1.satisfies("8 - 9")); } + + public void testPrereleaseIgnoredBySatisfies() { + SolrVersion snapshot = SolrVersion.valueOf("10.1.0-SNAPSHOT"); + assertTrue(snapshot.satisfies(">=9.0.0")); + assertTrue(snapshot.satisfies("10.x")); + assertTrue(snapshot.satisfies("~10.1")); + assertFalse(snapshot.satisfies("<10.1")); + } + + public void testWithoutPrerelease() { + SolrVersion snapshot = SolrVersion.valueOf("10.1.0-SNAPSHOT"); + assertTrue(snapshot.lessThan(SolrVersion.valueOf("10.1.0"))); + assertEquals(SolrVersion.valueOf("10.1.0"), snapshot.withoutPrerelease()); + } } diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkController.java b/solr/core/src/java/org/apache/solr/cloud/ZkController.java index 0abc95cbd9b8..3daf99712afb 100644 --- a/solr/core/src/java/org/apache/solr/cloud/ZkController.java +++ b/solr/core/src/java/org/apache/solr/cloud/ZkController.java @@ -642,7 +642,7 @@ private void checkClusterVersionCompatibility() throws InterruptedException, Kee SolrVersion ourVersion = SolrVersion.LATEST; SolrVersion clusterVersion = lowestVersion.get(); - if (ourVersion.lessThan(clusterVersion)) { + if (ourVersion.withoutPrerelease().lessThan(clusterVersion.withoutPrerelease())) { log.warn( "Our Solr version {} is older than cluster version {}", ourVersion, clusterVersion); diff --git a/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoProvider.java b/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoProvider.java index f9accfe106b0..52d279a3f6b4 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoProvider.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoProvider.java @@ -338,8 +338,7 @@ public NodeSystemResponse.Lucene getLuceneInfo() { String implVersion = p.getImplementationVersion(); // non-null mostly for testing info.solrSpecVersion = specVersion == null ? SolrVersion.LATEST_STRING : specVersion; - info.solrImplVersion = - implVersion == null ? SolrVersion.LATEST.getPrereleaseVersion() : implVersion; + info.solrImplVersion = implVersion == null ? SolrVersion.LATEST_STRING : implVersion; info.luceneSpecVersion = Version.LATEST.toString(); info.luceneImplVersion = Version.getPackageImplementationVersion(); diff --git a/solr/core/src/test/org/apache/solr/handler/admin/NodeSystemInfoProviderTest.java b/solr/core/src/test/org/apache/solr/handler/admin/NodeSystemInfoProviderTest.java index 13b3ee4ce3e4..5d2e29a51194 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/NodeSystemInfoProviderTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/NodeSystemInfoProviderTest.java @@ -67,7 +67,7 @@ public void testGetNodeSystemInfo() { Assert.assertEquals(h.getCoreContainer().getCoreRootDirectory().toString(), info.coreRoot); Assert.assertNotNull(info.lucene); Assert.assertNotNull(info.lucene.solrImplVersion); - Assert.assertEquals(info.lucene.solrImplVersion, SolrVersion.LATEST.getPrereleaseVersion()); + Assert.assertEquals(info.lucene.solrImplVersion, SolrVersion.LATEST_STRING); Assert.assertNotNull(info.lucene.solrSpecVersion); Assert.assertEquals(info.lucene.solrSpecVersion, SolrVersion.LATEST_STRING); Assert.assertNotNull(info.lucene.luceneImplVersion); From 72e189ad2bf97f488969e3f0a8dd98054111e001 Mon Sep 17 00:00:00 2001 From: Nick Shanin Date: Tue, 27 Oct 2026 23:58:48 -0400 Subject: [PATCH 2/3] SOLR-18432: Cover prerelease normalization boundaries --- .../org/apache/solr/util/TestSolrVersion.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/solr/api/src/test/org/apache/solr/util/TestSolrVersion.java b/solr/api/src/test/org/apache/solr/util/TestSolrVersion.java index 2789ab57aa9c..5778dcec4049 100644 --- a/solr/api/src/test/org/apache/solr/util/TestSolrVersion.java +++ b/solr/api/src/test/org/apache/solr/util/TestSolrVersion.java @@ -68,9 +68,56 @@ public void testPrereleaseIgnoredBySatisfies() { assertFalse(snapshot.satisfies("<10.1")); } + public void testPrereleaseRangeBoundaries() { + SolrVersion release = SolrVersion.valueOf("10.1.0"); + for (String versionString : new String[] {"10.1.0-SNAPSHOT", "10.1.0-rc.1+b123"}) { + SolrVersion version = SolrVersion.valueOf(versionString); + assertTrue(versionString, version.satisfies(">=10.1.0")); + assertTrue(versionString, version.satisfies("<=10.1.0")); + assertTrue(versionString, version.satisfies("10.1.0")); + assertFalse(versionString, version.satisfies("<10.1.0")); + assertFalse(versionString, version.satisfies(">10.1.0")); + assertTrue(versionString, version.compareTo(release) < 0); + assertNotEquals(release, version); + } + } + public void testWithoutPrerelease() { SolrVersion snapshot = SolrVersion.valueOf("10.1.0-SNAPSHOT"); assertTrue(snapshot.lessThan(SolrVersion.valueOf("10.1.0"))); assertEquals(SolrVersion.valueOf("10.1.0"), snapshot.withoutPrerelease()); } + + public void testWithoutPrereleasePreservesBuildMetadata() { + SolrVersion prerelease = SolrVersion.valueOf("10.1.0-rc.1+build.123"); + SolrVersion normalized = prerelease.withoutPrerelease(); + assertEquals("10.1.0+build.123", normalized.toString()); + assertEquals("10.1.0-rc.1+build.123", prerelease.toString()); + assertTrue(prerelease.lessThan(normalized)); + } + + public void testWithoutPrereleaseOnRelease() { + for (String versionString : new String[] {"10.1.2", "10.1.2+build.123"}) { + SolrVersion release = SolrVersion.valueOf(versionString); + SolrVersion normalized = release.withoutPrerelease(); + assertEquals(versionString, normalized.toString()); + assertEquals(versionString, normalized.withoutPrerelease().toString()); + } + } + + public void testWithoutPrereleaseOrdering() { + SolrVersion normalized = SolrVersion.valueOf("10.1.0-SNAPSHOT").withoutPrerelease(); + for (String versionString : + new String[] {"10.1.0", "10.1.0-alpha.1", "10.1.0-rc.1+b123"}) { + SolrVersion sameRelease = SolrVersion.valueOf(versionString).withoutPrerelease(); + assertFalse(versionString, normalized.lessThan(sameRelease)); + assertFalse(versionString, sameRelease.lessThan(normalized)); + } + for (String versionString : + new String[] {"10.1.1-SNAPSHOT", "10.2.0-SNAPSHOT", "11.0.0-SNAPSHOT"}) { + SolrVersion higherRelease = SolrVersion.valueOf(versionString).withoutPrerelease(); + assertTrue(versionString, normalized.lessThan(higherRelease)); + assertFalse(versionString, higherRelease.lessThan(normalized)); + } + } } From 2a0f00c152a2da53a0901eed701972812b913e39 Mon Sep 17 00:00:00 2001 From: Nick Shanin Date: Wed, 28 Oct 2026 06:14:22 +0000 Subject: [PATCH 3/3] SOLR-18432: wrap a test loop the way tidy formats it --- solr/api/src/test/org/apache/solr/util/TestSolrVersion.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/solr/api/src/test/org/apache/solr/util/TestSolrVersion.java b/solr/api/src/test/org/apache/solr/util/TestSolrVersion.java index 5778dcec4049..cb2394468fef 100644 --- a/solr/api/src/test/org/apache/solr/util/TestSolrVersion.java +++ b/solr/api/src/test/org/apache/solr/util/TestSolrVersion.java @@ -107,8 +107,7 @@ public void testWithoutPrereleaseOnRelease() { public void testWithoutPrereleaseOrdering() { SolrVersion normalized = SolrVersion.valueOf("10.1.0-SNAPSHOT").withoutPrerelease(); - for (String versionString : - new String[] {"10.1.0", "10.1.0-alpha.1", "10.1.0-rc.1+b123"}) { + for (String versionString : new String[] {"10.1.0", "10.1.0-alpha.1", "10.1.0-rc.1+b123"}) { SolrVersion sameRelease = SolrVersion.valueOf(versionString).withoutPrerelease(); assertFalse(versionString, normalized.lessThan(sameRelease)); assertFalse(versionString, sameRelease.lessThan(normalized));