Browse Source

Update BWC version logic to adapt to removal of M.x branch (#79521)

Going forward we will no longer maintain an `M.x` branch which is
intended to be the next minor version where `master` is always `M+1`.
This simplifies a lot of our logic to no longer care much about major
vs minor version.
Mark Vieira 4 years ago
parent
commit
8f1204c507
18 changed files with 413 additions and 1122 deletions
  1. 1 2
      .backportrc.json
  2. 4 2
      .ci/jobs.t/elastic+elasticsearch+pull-request+packaging-tests-windows-sample.yml
  3. 4 2
      .ci/jobs.t/elastic+elasticsearch+pull-request+packaging-tests-windows.yml
  4. 4 2
      .ci/jobs.t/elastic+elasticsearch+pull-request+rest-compatibility.yml
  5. 2 2
      build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/InternalBwcGitPluginFuncTest.groovy
  6. 13 76
      build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/InternalDistributionBwcSetupPluginFuncTest.groovy
  7. 2 2
      build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/InternalDistributionDownloadPluginFuncTest.groovy
  8. 8 8
      build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/test/rest/YamlRestCompatTestPluginFuncTest.groovy
  9. 135 208
      build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/BwcVersions.java
  10. 3 1
      build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPlugin.java
  11. 1 1
      build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/rest/compat/YamlRestCompatTestPlugin.java
  12. 187 0
      build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/BwcVersionsSpec.groovy
  13. 14 14
      build-tools-internal/src/test/java/org/elasticsearch/gradle/AbstractDistributionDownloadPluginTests.java
  14. 4 4
      build-tools-internal/src/test/java/org/elasticsearch/gradle/DistributionDownloadPluginTests.java
  15. 0 774
      build-tools-internal/src/test/java/org/elasticsearch/gradle/internal/BwcVersionsTests.java
  16. 4 4
      build-tools-internal/src/test/java/org/elasticsearch/gradle/internal/InternalDistributionDownloadPluginTests.java
  17. 27 14
      build-tools/src/testFixtures/groovy/org/elasticsearch/gradle/fixtures/AbstractGradleFuncTest.groovy
  18. 0 6
      build.gradle

+ 1 - 2
.backportrc.json

@@ -2,7 +2,7 @@
   "upstream": "elastic/elasticsearch",
   "targetBranchChoices": [
     { "name": "master", "checked": true },
-    { "name": "7.x", "checked": true },
+    "7.16",
     "7.15",
     "7.14",
     "6.8"
@@ -10,7 +10,6 @@
   "targetPRLabels": ["backport"],
   "branchLabelMapping": {
     "^v8.0.0$": "master",
-    "^v7.16.0$": "7.x",
     "^v(\\d+).(\\d+).\\d+$": "$1.$2"
   }
 }

+ 4 - 2
.ci/jobs.t/elastic+elasticsearch+pull-request+packaging-tests-windows-sample.yml

@@ -21,8 +21,10 @@
           github-hooks: true
           status-context: elasticsearch-ci/packaging-tests-windows-sample
           cancel-builds-on-update: true
-          white-list-target-branches:
-            - master
+          black-list-target-branches:
+            - 7.16
+            - 7.15
+            - 6.8
           excluded-regions:
             - ^docs/.*
           black-list-labels:

+ 4 - 2
.ci/jobs.t/elastic+elasticsearch+pull-request+packaging-tests-windows.yml

@@ -21,8 +21,10 @@
           github-hooks: true
           status-context: elasticsearch-ci/packaging-tests-windows
           cancel-builds-on-update: true
-          white-list-target-branches:
-            - master
+          black-list-target-branches:
+            - 7.16
+            - 7.15
+            - 6.8
           excluded-regions:
             - ^docs/.*
           white-list-labels:

+ 4 - 2
.ci/jobs.t/elastic+elasticsearch+pull-request+rest-compatibility.yml

@@ -18,8 +18,10 @@
           github-hooks: true
           status-context: elasticsearch-ci/rest-compatibility
           cancel-builds-on-update: true
-          white-list-target-branches:
-            - master
+          black-list-target-branches:
+            - 7.16
+            - 7.15
+            - 6.8
           excluded-regions:
             - ^docs/.*
           black-list-labels:

+ 2 - 2
build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/InternalBwcGitPluginFuncTest.groovy

@@ -21,11 +21,11 @@ class InternalBwcGitPluginFuncTest extends AbstractGitAwareGradleFuncTest {
 
             bwcGitConfig {
                  bwcVersion = project.provider { Version.fromString("7.9.1") }
-                 bwcBranch = project.provider { "7.x" }
+                 bwcBranch = project.provider { "7.9" }
                  checkoutDir = project.provider{file("build/checkout")}
             }
         """
-        execute("git branch origin/7.x", file("cloned"))
+        execute("git branch origin/7.9", file("cloned"))
     }
 
     def "current repository can be cloned"() {

+ 13 - 76
build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/InternalDistributionBwcSetupPluginFuncTest.groovy

@@ -26,41 +26,15 @@ class InternalDistributionBwcSetupPluginFuncTest extends AbstractGitAwareGradleF
         buildFile << """
             apply plugin: 'elasticsearch.internal-distribution-bwc-setup'
         """
-        execute("git branch origin/7.x", file("cloned"))
-        execute("git branch origin/7.10", file("cloned"))
-    }
-
-    def "builds distribution from branches via archives assemble"() {
-        given:
-        buildFile.text = ""
-        internalBuild(buildFile, "7.10.1", "7.11.0", "7.12.0")
-        buildFile << """
-            apply plugin: 'elasticsearch.internal-distribution-bwc-setup'
-        """
-        when:
-        def result = gradleRunner(":distribution:bwc:${bwcProject}:buildBwcDarwinTar",
-                ":distribution:bwc:${bwcProject}:buildBwcOssDarwinTar",
-                "-DtestRemoteRepo=" + remoteGitRepo,
-                "-Dbwc.remote=origin",
-                "-Dbwc.dist.version=${bwcDistVersion}-SNAPSHOT")
-                .build()
-        then:
-        result.task(":distribution:bwc:${bwcProject}:buildBwcDarwinTar").outcome == TaskOutcome.SUCCESS
-        result.task(":distribution:bwc:${bwcProject}:buildBwcOssDarwinTar").outcome == TaskOutcome.SUCCESS
-
-        and: "assemble task triggered"
-        assertOutputContains(result.output, "[$bwcDistVersion] > Task :distribution:archives:darwin-tar:${expectedAssembleTaskName}")
-        assertOutputContains(result.output, "[$bwcDistVersion] > Task :distribution:archives:oss-darwin-tar:${expectedAssembleTaskName}")
-
-        where:
-        bwcDistVersion | bwcProject | expectedAssembleTaskName
-        "7.10.1"       | "bugfix"   | "assemble"
+        execute("git branch origin/8.0", file("cloned"))
+        execute("git branch origin/7.16", file("cloned"))
+        execute("git branch origin/7.15", file("cloned"))
     }
 
     def "builds distribution from branches via archives extractedAssemble"() {
         given:
         buildFile.text = ""
-        internalBuild(buildFile, "7.12.1", "7.13.0", "7.14.0")
+        internalBuild()
         buildFile << """
             apply plugin: 'elasticsearch.internal-distribution-bwc-setup'
         """
@@ -79,66 +53,29 @@ class InternalDistributionBwcSetupPluginFuncTest extends AbstractGitAwareGradleF
 
         where:
         bwcDistVersion | bwcProject | expectedAssembleTaskName
-        "7.14.0"       | "minor"    | "extractedAssemble"
+        "8.0.0"        | "minor"    | "extractedAssemble"
+        "7.16.0"       | "staged"   | "extractedAssemble"
+        "7.15.2"       | "bugfix"   | "extractedAssemble"
     }
 
     @Unroll
     def "supports #platform aarch distributions"() {
         when:
         def result = gradleRunner(":distribution:bwc:minor:buildBwc${platform.capitalize()}Aarch64Tar",
-                ":distribution:bwc:minor:buildBwcOss${platform.capitalize()}Aarch64Tar",
                 "-DtestRemoteRepo=" + remoteGitRepo,
                 "-Dbwc.remote=origin",
                 "-Dbwc.dist.version=${bwcDistVersion}-SNAPSHOT")
                 .build()
         then:
         result.task(":distribution:bwc:minor:buildBwc${platform.capitalize()}Aarch64Tar").outcome == TaskOutcome.SUCCESS
-        result.task(":distribution:bwc:minor:buildBwcOss${platform.capitalize()}Aarch64Tar").outcome == TaskOutcome.SUCCESS
 
         and: "assemble tasks triggered"
         assertOutputContains(result.output, "[$bwcDistVersion] > Task :distribution:archives:${platform}-aarch64-tar:extractedAssemble")
-        assertOutputContains(result.output, "[$bwcDistVersion] > Task :distribution:archives:oss-${platform}-aarch64-tar:extractedAssemble")
 
         where:
         bwcDistVersion | platform
-        "7.12.0"       | "darwin"
-        "7.12.0"       | "linux"
-    }
-
-    def "bwc distribution archives can be resolved as bwc project artifact"() {
-        setup:
-        buildFile << """
-
-        configurations {
-            dists
-        }
-
-        dependencies {
-            dists project(path: ":distribution:bwc:bugfix", configuration:"darwin-tar")
-        }
-
-        tasks.register("resolveDistributionArchive") {
-            inputs.files(configurations.dists)
-            doLast {
-                configurations.dists.files.each {
-                    println "distfile " + (it.absolutePath - project.rootDir.absolutePath)
-                }
-            }
-        }
-        """
-        when:
-        def result = gradleRunner(":resolveDistributionArchive",
-                "-DtestRemoteRepo=" + remoteGitRepo,
-                "-Dbwc.remote=origin")
-                .build()
-        then:
-        result.task(":resolveDistributionArchive").outcome == TaskOutcome.SUCCESS
-        result.task(":distribution:bwc:bugfix:buildBwcDarwinTar").outcome == TaskOutcome.SUCCESS
-
-        and: "assemble task triggered"
-        result.output.contains("[7.10.1] > Task :distribution:archives:darwin-tar:assemble")
-        result.output.contains("distfile /distribution/bwc/bugfix/build/bwc/checkout-7.10/distribution/archives/darwin-tar/" +
-                "build/distributions/elasticsearch-7.10.1-SNAPSHOT-darwin-x86_64.tar.gz")
+        "8.0.0"       | "darwin"
+        "8.0.0"       | "linux"
     }
 
     def "bwc expanded distribution folder can be resolved as bwc project artifact"() {
@@ -174,10 +111,10 @@ class InternalDistributionBwcSetupPluginFuncTest extends AbstractGitAwareGradleF
         result.task(":resolveExpandedDistribution").outcome == TaskOutcome.SUCCESS
         result.task(":distribution:bwc:minor:buildBwcDarwinTar").outcome == TaskOutcome.SUCCESS
         and: "assemble task triggered"
-        result.output.contains("[7.12.0] > Task :distribution:archives:darwin-tar:extractedAssemble")
-        result.output.contains("expandedRootPath /distribution/bwc/minor/build/bwc/checkout-7.x/" +
+        result.output.contains("[8.0.0] > Task :distribution:archives:darwin-tar:extractedAssemble")
+        result.output.contains("expandedRootPath /distribution/bwc/minor/build/bwc/checkout-8.0/" +
                         "distribution/archives/darwin-tar/build/install")
-        result.output.contains("nested folder /distribution/bwc/minor/build/bwc/checkout-7.x/" +
-                        "distribution/archives/darwin-tar/build/install/elasticsearch-7.12.0-SNAPSHOT")
+        result.output.contains("nested folder /distribution/bwc/minor/build/bwc/checkout-8.0/" +
+                        "distribution/archives/darwin-tar/build/install/elasticsearch-8.0.0-SNAPSHOT")
     }
 }

+ 2 - 2
build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/InternalDistributionDownloadPluginFuncTest.groovy

@@ -56,7 +56,7 @@ class InternalDistributionDownloadPluginFuncTest extends AbstractGradleFuncTest
 
             elasticsearch_distributions {
               test_distro {
-                  version = "7.12.0"
+                  version = "8.0.0"
                   type = "archive"
                   platform = "linux"
                   architecture = Architecture.current();
@@ -86,7 +86,7 @@ class InternalDistributionDownloadPluginFuncTest extends AbstractGradleFuncTest
 
             elasticsearch_distributions {
               test_distro {
-                  version = "7.12.0"
+                  version = "8.0.0"
                   type = "archive"
                   platform = "linux"
                   architecture = Architecture.current();

+ 8 - 8
build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/test/rest/YamlRestCompatTestPluginFuncTest.groovy

@@ -30,7 +30,7 @@ class YamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTest {
 
     def "yamlRestTestVxCompatTest does nothing when there are no tests"() {
         given:
-        addSubProject(":distribution:bwc:minor") << """
+        addSubProject(":distribution:bwc:staged") << """
         configurations { checkout }
         artifacts {
             checkout(new File(projectDir, "checkoutDir"))
@@ -53,11 +53,11 @@ class YamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTest {
         result.task(transformTask).outcome == TaskOutcome.NO_SOURCE
     }
 
-    def "yamlRestTestVxCompatTest executes and copies api and transforms tests from :bwc:minor"() {
+    def "yamlRestTestVxCompatTest executes and copies api and transforms tests from :bwc:staged"() {
         given:
         internalBuild()
 
-        addSubProject(":distribution:bwc:minor") << """
+        addSubProject(":distribution:bwc:staged") << """
         configurations { checkout }
         artifacts {
             checkout(new File(projectDir, "checkoutDir"))
@@ -90,8 +90,8 @@ class YamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTest {
         String api = "foo.json"
         String test = "10_basic.yml"
         //add the compatible test and api files, these are the prior version's normal yaml rest tests
-        file("distribution/bwc/minor/checkoutDir/rest-api-spec/src/main/resources/rest-api-spec/api/" + api) << ""
-        file("distribution/bwc/minor/checkoutDir/src/yamlRestTest/resources/rest-api-spec/test/" + test) << ""
+        file("distribution/bwc/staged/checkoutDir/rest-api-spec/src/main/resources/rest-api-spec/api/" + api) << ""
+        file("distribution/bwc/staged/checkoutDir/src/yamlRestTest/resources/rest-api-spec/test/" + test) << ""
 
         when:
         def result = gradleRunner("yamlRestTestV${compatibleVersion}CompatTest").build()
@@ -136,7 +136,7 @@ class YamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTest {
     def "yamlRestTestVxCompatTest is wired into check and checkRestCompat"() {
         given:
 
-        addSubProject(":distribution:bwc:minor") << """
+        addSubProject(":distribution:bwc:staged") << """
         configurations { checkout }
         artifacts {
             checkout(new File(projectDir, "checkoutDir"))
@@ -180,7 +180,7 @@ class YamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTest {
         given:
         internalBuild()
 
-        addSubProject(":distribution:bwc:minor") << """
+        addSubProject(":distribution:bwc:staged") << """
         configurations { checkout }
         artifacts {
             checkout(new File(projectDir, "checkoutDir"))
@@ -224,7 +224,7 @@ class YamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTest {
 
         setupRestResources([], [])
 
-        file("distribution/bwc/minor/checkoutDir/src/yamlRestTest/resources/rest-api-spec/test/test.yml" ) << """
+        file("distribution/bwc/staged/checkoutDir/src/yamlRestTest/resources/rest-api-spec/test/test.yml" ) << """
         "one":
           - do:
               do_.some.key_to_replace:

+ 135 - 208
build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/BwcVersions.java

@@ -10,16 +10,17 @@ package org.elasticsearch.gradle.internal;
 import org.elasticsearch.gradle.Architecture;
 import org.elasticsearch.gradle.Version;
 import org.elasticsearch.gradle.VersionProperties;
+import org.jetbrains.annotations.NotNull;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
+import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
-import java.util.SortedSet;
+import java.util.TreeMap;
 import java.util.TreeSet;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
@@ -27,9 +28,7 @@ import java.util.function.Predicate;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
-import static java.util.Collections.emptyList;
 import static java.util.Collections.unmodifiableList;
 
 /**
@@ -40,28 +39,14 @@ import static java.util.Collections.unmodifiableList;
  * On top of this, figure out which of these are unreleased and provide the branch they can be built from.
  * <p>
  * Note that in this context, currentVersion is the unreleased version this build operates on.
- * At any point in time there will surely be four such unreleased versions being worked on,
- * thus currentVersion will be one of these.
+ * At any point in time there will be at least three such versions and potentially four in the case of a staged release.
  * <p>
- * Considering:
- * <dl>
- *     <dt>M, M &gt; 0</dt>
- *     <dd>last released major</dd>
- *     <dt>N, N &gt; 0</dt>
- *     <dd>last released minor</dd>
- * </dl>
- *
  * <ul>
- * <li>the unreleased <b>major</b>, M+1.0.0 on the `master` branch</li>
- * <li>the unreleased <b>minor</b>,  M.N.0 on the `M.x` (x is literal) branch</li>
- * <li>the unreleased <b>bugfix</b>, M.N.c (c &gt; 0) on the `M.N` branch</li>
+ * <li>the current version on the `master` branch</li>
+ * <li>the staged next <b>minor</b> on the `M.N` branch</li>
+ * <li>the unreleased <b>bugfix</b>, `M.N-1` branch</li>
  * <li>the unreleased <b>maintenance</b>, M-1.d.e ( d &gt; 0, e &gt; 0) on the `(M-1).d` branch</li>
  * </ul>
- * In addition to these, there will be a fifth one when a minor reaches feature freeze, we call this the <i>staged</i>
- * version:
- * <ul>
- * <li>the unreleased <b>staged</b>, M.N-2.0 (N &gt; 2) on the `M.(N-2)` branch</li>
- * </ul>
  * <p>
  * Each build is only concerned with versions before it, as those are the ones that need to be tested
  * for backwards compatibility. We never look forward, and don't add forward facing version number to branches of previous
@@ -71,7 +56,7 @@ import static java.util.Collections.unmodifiableList;
  * We can reliably figure out which the unreleased versions are due to the convention of always adding the next unreleased
  * version number to server in all branches when a version is released.
  * E.x when M.N.c is released M.N.c+1 is added to the Version class mentioned above in all the following branches:
- *  `M.N`, `M.x` and `master` so we can reliably assume that the leafs of the version tree are unreleased.
+ *  `M.N`, and `master` so we can reliably assume that the leafs of the version tree are unreleased.
  * This convention is enforced by checking the versions we consider to be unreleased against an
  * authoritative source (maven central).
  * We are then able to map the unreleased version to branches in git and Gradle projects that are capable of checking
@@ -80,87 +65,55 @@ import static java.util.Collections.unmodifiableList;
 public class BwcVersions {
 
     private static final Pattern LINE_PATTERN = Pattern.compile(
-        "\\W+public static final Version V_(\\d+)_(\\d+)_(\\d+)(_alpha\\d+|_beta\\d+|_rc\\d+)? .*"
+        "\\W+public static final Version V_(\\d+)_(\\d+)_(\\d+)(_alpha\\d+|_beta\\d+|_rc\\d+)? .*?LUCENE_(\\d+)_(\\d+)_(\\d+)\\);"
     );
+    private static final Version MINIMUM_WIRE_COMPATIBLE_VERSION = Version.fromString("7.16.0");
 
-    private final Version currentVersion;
-    private final Map<Integer, List<Version>> groupByMajor;
+    private final VersionPair currentVersion;
+    private final List<VersionPair> versions;
     private final Map<Version, UnreleasedVersionInfo> unreleased;
 
-    public class UnreleasedVersionInfo {
-        public final Version version;
-        public final String branch;
-        public final String gradleProjectPath;
-
-        UnreleasedVersionInfo(Version version, String branch, String gradleProjectPath) {
-            this.version = version;
-            this.branch = branch;
-            this.gradleProjectPath = gradleProjectPath;
-        }
-    }
-
     public BwcVersions(List<String> versionLines) {
         this(versionLines, Version.fromString(VersionProperties.getElasticsearch()));
     }
 
-    protected BwcVersions(List<String> versionLines, Version currentVersionProperty) {
-        this(
-            versionLines.stream()
-                .map(LINE_PATTERN::matcher)
-                .filter(Matcher::matches)
-                .map(
-                    match -> new Version(
-                        Integer.parseInt(match.group(1)),
-                        Integer.parseInt(match.group(2)),
-                        Integer.parseInt(match.group(3))
-                    )
-                )
-                .collect(Collectors.toCollection(TreeSet::new)),
-            currentVersionProperty
-        );
-    }
-
-    // for testkit tests, until BwcVersions is extracted into an extension
-    public BwcVersions(SortedSet<Version> allVersions, Version currentVersionProperty) {
+    public BwcVersions(Version currentVersionProperty, List<VersionPair> allVersions) {
         if (allVersions.isEmpty()) {
             throw new IllegalArgumentException("Could not parse any versions");
         }
 
-        currentVersion = allVersions.last();
-
-        groupByMajor = allVersions.stream()
-            // We only care about the last 2 majors when it comes to BWC.
-            // It might take us time to remove the older ones from versionLines, so we allow them to exist.
-            .filter(version -> version.getMajor() > currentVersion.getMajor() - 2)
-            .collect(Collectors.groupingBy(Version::getMajor, Collectors.toList()));
-
+        this.versions = allVersions;
+        this.currentVersion = allVersions.get(allVersions.size() - 1);
         assertCurrentVersionMatchesParsed(currentVersionProperty);
 
-        assertNoOlderThanTwoMajors();
+        this.unreleased = computeUnreleased();
+    }
 
-        Map<Version, UnreleasedVersionInfo> unreleased = new HashMap<>();
-        for (Version unreleasedVersion : getUnreleased()) {
-            unreleased.put(
-                unreleasedVersion,
-                new UnreleasedVersionInfo(unreleasedVersion, getBranchFor(unreleasedVersion), getGradleProjectPathFor(unreleasedVersion))
-            );
-        }
-        this.unreleased = Collections.unmodifiableMap(unreleased);
+    // Visible for testing
+    BwcVersions(List<String> versionLines, Version currentVersionProperty) {
+        this(currentVersionProperty, parseVersionLines(versionLines));
     }
 
-    private void assertNoOlderThanTwoMajors() {
-        Set<Integer> majors = groupByMajor.keySet();
-        if (majors.size() != 2 && currentVersion.getMinor() != 0 && currentVersion.getRevision() != 0) {
-            throw new IllegalStateException("Expected exactly 2 majors in parsed versions but found: " + majors);
-        }
+    private static List<VersionPair> parseVersionLines(List<String> versionLines) {
+        return versionLines.stream()
+            .map(LINE_PATTERN::matcher)
+            .filter(Matcher::matches)
+            .map(
+                match -> new VersionPair(
+                    new Version(Integer.parseInt(match.group(1)), Integer.parseInt(match.group(2)), Integer.parseInt(match.group(3))),
+                    new Version(Integer.parseInt(match.group(5)), Integer.parseInt(match.group(6)), Integer.parseInt(match.group(7)))
+                )
+            )
+            .sorted()
+            .toList();
     }
 
     private void assertCurrentVersionMatchesParsed(Version currentVersionProperty) {
-        if (currentVersionProperty.equals(currentVersion) == false) {
+        if (currentVersionProperty.equals(currentVersion.elasticsearch) == false) {
             throw new IllegalStateException(
                 "Parsed versions latest version does not match the one configured in build properties. "
                     + "Parsed latest version is "
-                    + currentVersion
+                    + currentVersion.elasticsearch
                     + " but the build has "
                     + currentVersionProperty
             );
@@ -175,130 +128,78 @@ public class BwcVersions {
     }
 
     public void forPreviousUnreleased(Consumer<UnreleasedVersionInfo> consumer) {
-        List<UnreleasedVersionInfo> collect = filterSupportedVersions(
-            getUnreleased().stream().filter(version -> version.equals(currentVersion) == false).collect(Collectors.toList())
-        ).stream()
-            .map(version -> new UnreleasedVersionInfo(version, getBranchFor(version), getGradleProjectPathFor(version)))
-            .collect(Collectors.toList());
-
-        collect.forEach(consumer::accept);
-    }
-
-    private String getGradleProjectPathFor(Version version) {
-        // We have Gradle projects set up to check out and build unreleased versions based on the our branching
-        // conventions described in this classes javadoc
-        if (version.equals(currentVersion)) {
-            return ":distribution";
-        }
-
-        Map<Integer, List<Version>> releasedMajorGroupedByMinor = getReleasedMajorGroupedByMinor();
-
-        if (version.getRevision() == 0) {
-            List<Version> unreleasedStagedOrMinor = getUnreleased().stream().filter(v -> v.getRevision() == 0).collect(Collectors.toList());
-            if (unreleasedStagedOrMinor.size() > 2) {
-                if (unreleasedStagedOrMinor.get(unreleasedStagedOrMinor.size() - 2).equals(version)) {
-                    return ":distribution:bwc:minor";
-                } else {
-                    return ":distribution:bwc:staged";
-                }
-            } else {
-                return ":distribution:bwc:minor";
-            }
-        } else {
-            if (releasedMajorGroupedByMinor.getOrDefault(version.getMinor(), emptyList()).contains(version)) {
-                return ":distribution:bwc:bugfix";
-            } else {
-                return ":distribution:bwc:maintenance";
-            }
-        }
+        filterSupportedVersions(
+            getUnreleased().stream().filter(version -> version.equals(currentVersion.elasticsearch) == false).collect(Collectors.toList())
+        ).stream().map(unreleased::get).forEach(consumer);
     }
 
     private String getBranchFor(Version version) {
-        // based on the rules described in this classes javadoc, figure out the branch on which an unreleased version
-        // lives.
-        // We do this based on the Gradle project path because there's a direct correlation, so we dont have to duplicate
-        // the logic from there
-        switch (getGradleProjectPathFor(version)) {
-            case ":distribution":
-                return "master";
-            case ":distribution:bwc:minor":
-                // The .x branch will always point to the latest minor (for that major), so a "minor" project will be on the .x branch
-                // unless there is more recent (higher) minor.
-                final Version latestInMajor = getLatestVersionByKey(groupByMajor, version.getMajor());
-                if (latestInMajor.getMinor() == version.getMinor()) {
-                    return version.getMajor() + ".x";
-                } else {
-                    return version.getMajor() + "." + version.getMinor();
-                }
-            case ":distribution:bwc:staged":
-            case ":distribution:bwc:maintenance":
-            case ":distribution:bwc:bugfix":
-                return version.getMajor() + "." + version.getMinor();
-            default:
-                throw new IllegalStateException("Unexpected Gradle project name");
+        if (version.equals(currentVersion.elasticsearch)) {
+            // Just assume the current branch is 'master'. It's actually not important, we never check out the current branch.
+            return "master";
+        } else {
+            return version.getMajor() + "." + version.getMinor();
         }
     }
 
-    public List<Version> getUnreleased() {
-        List<Version> unreleased = new ArrayList<>();
+    private Map<Version, UnreleasedVersionInfo> computeUnreleased() {
+        Set<VersionPair> unreleased = new TreeSet<>();
         // The current version is being worked, is always unreleased
         unreleased.add(currentVersion);
-
-        // the tip of the previous major is unreleased for sure, be it a minor or a bugfix
-        final Version latestOfPreviousMajor = getLatestVersionByKey(this.groupByMajor, currentVersion.getMajor() - 1);
-        unreleased.add(latestOfPreviousMajor);
-        if (latestOfPreviousMajor.getRevision() == 0) {
-            // if the previous major is a x.y.0 release, then the tip of the minor before that (y-1) is also unreleased
-            final Version previousMinor = getLatestInMinor(latestOfPreviousMajor.getMajor(), latestOfPreviousMajor.getMinor() - 1);
-            if (previousMinor != null) {
-                unreleased.add(previousMinor);
-            }
-        }
-
-        final Map<Integer, List<Version>> groupByMinor = getReleasedMajorGroupedByMinor();
-        int greatestMinor = groupByMinor.keySet().stream().max(Integer::compareTo).orElse(0);
-
-        // the last bugfix for this minor series is always unreleased
-        unreleased.add(getLatestVersionByKey(groupByMinor, greatestMinor));
-
-        if (groupByMinor.get(greatestMinor).size() == 1) {
-            // we found an unreleased minor
-            unreleased.add(getLatestVersionByKey(groupByMinor, greatestMinor - 1));
-            if (groupByMinor.getOrDefault(greatestMinor - 1, emptyList()).size() == 1) {
-                // we found that the previous minor is staged but not yet released
-                // in this case, the minor before that has a bugfix, should there be such a minor
-                if (greatestMinor >= 2) {
-                    unreleased.add(getLatestVersionByKey(groupByMinor, greatestMinor - 2));
+        // Recurse for all unreleased versions starting from the current version
+        addUnreleased(unreleased, currentVersion, 0);
+
+        // Grab the latest version from the previous major if necessary as well, this is going to be a maintenance release
+        VersionPair maintenance = versions.stream()
+            .filter(v -> v.elasticsearch.getMajor() == currentVersion.elasticsearch.getMajor() - 1)
+            .sorted(Comparator.reverseOrder())
+            .findFirst()
+            .orElseThrow();
+        // This is considered the maintenance release only if we haven't yet encountered it
+        boolean hasMaintenanceRelease = unreleased.add(maintenance);
+
+        List<VersionPair> unreleasedList = unreleased.stream().sorted(Comparator.reverseOrder()).toList();
+        Map<Version, UnreleasedVersionInfo> result = new TreeMap<>();
+        for (int i = 0; i < unreleasedList.size(); i++) {
+            Version esVersion = unreleasedList.get(i).elasticsearch;
+            // This is either a new minor or staged release
+            if (currentVersion.elasticsearch.equals(esVersion)) {
+                result.put(esVersion, new UnreleasedVersionInfo(esVersion, getBranchFor(esVersion), ":distribution"));
+            } else if (esVersion.getRevision() == 0) {
+                // If there are two upcoming unreleased minors then this one is the new minor
+                if (unreleasedList.get(i + 1).elasticsearch.getRevision() == 0) {
+                    result.put(esVersion, new UnreleasedVersionInfo(esVersion, getBranchFor(esVersion), ":distribution:bwc:minor"));
+                } else {
+                    result.put(esVersion, new UnreleasedVersionInfo(esVersion, getBranchFor(esVersion), ":distribution:bwc:staged"));
+                }
+            } else {
+                // If this is the oldest unreleased version and we have a maintenance release
+                if (i == unreleasedList.size() - 1 && hasMaintenanceRelease) {
+                    result.put(esVersion, new UnreleasedVersionInfo(esVersion, getBranchFor(esVersion), ":distribution:bwc:maintenance"));
+                } else {
+                    result.put(esVersion, new UnreleasedVersionInfo(esVersion, getBranchFor(esVersion), ":distribution:bwc:bugfix"));
                 }
             }
         }
 
-        return unmodifiableList(unreleased.stream().sorted().distinct().collect(Collectors.toList()));
+        return Collections.unmodifiableMap(result);
     }
 
-    private Version getLatestInMinor(int major, int minor) {
-        return groupByMajor.get(major).stream().filter(v -> v.getMinor() == minor).max(Version::compareTo).orElse(null);
-    }
-
-    private Version getLatestVersionByKey(Map<Integer, List<Version>> groupByMajor, int key) {
-        return groupByMajor.getOrDefault(key, emptyList())
-            .stream()
-            .max(Version::compareTo)
-            .orElseThrow(() -> new IllegalStateException("Unexpected number of versions in collection"));
+    public List<Version> getUnreleased() {
+        return unreleased.keySet().stream().sorted().toList();
     }
 
-    private Map<Integer, List<Version>> getReleasedMajorGroupedByMinor() {
-        List<Version> currentMajorVersions = groupByMajor.get(currentVersion.getMajor());
-        List<Version> previousMajorVersions = groupByMajor.get(currentVersion.getMajor() - 1);
+    private void addUnreleased(Set<VersionPair> unreleased, VersionPair current, int index) {
+        if (current.elasticsearch.getRevision() == 0) {
+            // If the current version is a new minor, the next version is also unreleased
+            VersionPair next = versions.get(versions.size() - (index + 2));
+            unreleased.add(next);
 
-        final Map<Integer, List<Version>> groupByMinor;
-        if (currentMajorVersions.size() == 1) {
-            // Current is an unreleased major: x.0.0 so we have to look for other unreleased versions in the previous major
-            groupByMinor = previousMajorVersions.stream().collect(Collectors.groupingBy(Version::getMinor, Collectors.toList()));
+            // Keep looking through versions until we find the end of unreleased versions
+            addUnreleased(unreleased, next, index + 1);
         } else {
-            groupByMinor = currentMajorVersions.stream().collect(Collectors.groupingBy(Version::getMinor, Collectors.toList()));
+            unreleased.add(current);
         }
-        return groupByMinor;
     }
 
     public void compareToAuthoritative(List<Version> authoritativeReleasedVersions) {
@@ -326,20 +227,13 @@ public class BwcVersions {
     }
 
     private List<Version> getReleased() {
-        List<Version> unreleased = getUnreleased();
-        return groupByMajor.values()
-            .stream()
-            .flatMap(Collection::stream)
-            .filter(each -> unreleased.contains(each) == false)
-            .collect(Collectors.toList());
+        return versions.stream().map(v -> v.elasticsearch).filter(v -> unreleased.containsKey(v) == false).toList();
     }
 
     public List<Version> getIndexCompatible() {
-        var indexCompatibles = Stream.concat(
-            groupByMajor.get(currentVersion.getMajor() - 1).stream(),
-            groupByMajor.get(currentVersion.getMajor()).stream()
-        ).collect(Collectors.toList());
-        return unmodifiableList(filterSupportedVersions(indexCompatibles));
+        return filterSupportedVersions(
+            versions.stream().filter(v -> v.lucene.getMajor() >= (currentVersion.lucene.getMajor() - 1)).map(v -> v.elasticsearch).toList()
+        );
     }
 
     public void withIndexCompatiple(BiConsumer<Version, String> versionAction) {
@@ -351,16 +245,9 @@ public class BwcVersions {
     }
 
     public List<Version> getWireCompatible() {
-        List<Version> wireCompat = new ArrayList<>();
-        List<Version> prevMajors = groupByMajor.get(currentVersion.getMajor() - 1);
-        int minor = prevMajors.get(prevMajors.size() - 1).getMinor();
-        for (int i = prevMajors.size() - 1; i > 0 && prevMajors.get(i).getMinor() == minor; i--) {
-            wireCompat.add(prevMajors.get(i));
-        }
-        wireCompat.addAll(groupByMajor.get(currentVersion.getMajor()));
-        wireCompat.sort(Version::compareTo);
-
-        return unmodifiableList(filterSupportedVersions(wireCompat));
+        return filterSupportedVersions(
+            versions.stream().map(v -> v.elasticsearch).filter(v -> v.compareTo(MINIMUM_WIRE_COMPATIBLE_VERSION) >= 0).toList()
+        );
     }
 
     public void withWireCompatiple(BiConsumer<Version, String> versionAction) {
@@ -389,4 +276,44 @@ public class BwcVersions {
         return unmodifiableList(unreleasedWireCompatible);
     }
 
+    public static class UnreleasedVersionInfo {
+        public final Version version;
+        public final String branch;
+        public final String gradleProjectPath;
+
+        public UnreleasedVersionInfo(Version version, String branch, String gradleProjectPath) {
+            this.version = version;
+            this.branch = branch;
+            this.gradleProjectPath = gradleProjectPath;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            UnreleasedVersionInfo that = (UnreleasedVersionInfo) o;
+            return version.equals(that.version) && branch.equals(that.branch) && gradleProjectPath.equals(that.gradleProjectPath);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(version, branch, gradleProjectPath);
+        }
+    }
+
+    public static class VersionPair implements Comparable<VersionPair> {
+        public final Version elasticsearch;
+        public final Version lucene;
+
+        public VersionPair(Version elasticsearch, Version lucene) {
+            this.elasticsearch = elasticsearch;
+            this.lucene = lucene;
+        }
+
+        @Override
+        public int compareTo(@NotNull VersionPair o) {
+            // For ordering purposes, sort by Elasticsearch version
+            return this.elasticsearch.compareTo(o.elasticsearch);
+        }
+    }
 }

+ 3 - 1
build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPlugin.java

@@ -46,6 +46,7 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Random;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -108,7 +109,8 @@ public class GlobalBuildInfoPlugin implements Plugin<Project> {
             params.setDefaultParallel(ParallelDetector.findDefaultParallel(project));
             params.setInFipsJvm(Util.getBooleanProperty("tests.fips.enabled", false));
             params.setIsSnapshotBuild(Util.getBooleanProperty("build.snapshot", true));
-            params.setBwcVersions(providers.provider(() -> resolveBwcVersions(rootDir)));
+            AtomicReference<BwcVersions> cache = new AtomicReference<>();
+            params.setBwcVersions(providers.provider(() -> cache.updateAndGet(val -> val == null ? resolveBwcVersions(rootDir) : val)));
         });
 
         // Enforce the minimum compiler version

+ 1 - 1
build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/rest/compat/YamlRestCompatTestPlugin.java

@@ -81,7 +81,7 @@ public class YamlRestCompatTestPlugin implements Plugin<Project> {
 
         // copy compatible rest specs
         Configuration bwcMinorConfig = project.getConfigurations().create(BWC_MINOR_CONFIG_NAME);
-        Dependency bwcMinor = project.getDependencies().project(Map.of("path", ":distribution:bwc:minor", "configuration", "checkout"));
+        Dependency bwcMinor = project.getDependencies().project(Map.of("path", ":distribution:bwc:staged", "configuration", "checkout"));
         project.getDependencies().add(bwcMinorConfig.getName(), bwcMinor);
 
         Provider<CopyRestApiTask> copyCompatYamlSpecTask = project.getTasks()

+ 187 - 0
build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/BwcVersionsSpec.groovy

@@ -0,0 +1,187 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+package org.elasticsearch.gradle.internal
+
+import spock.lang.Specification
+
+import org.elasticsearch.gradle.Version
+import org.elasticsearch.gradle.internal.BwcVersions.UnreleasedVersionInfo
+
+
+class BwcVersionsSpec extends Specification {
+    List<String> versionLines = []
+
+    def "current version is next major with last minor staged"() {
+        given:
+        addVersion('7.14.0', '8.9.0')
+        addVersion('7.14.1', '8.9.0')
+        addVersion('7.14.2', '8.9.0')
+        addVersion('7.15.0', '8.9.0')
+        addVersion('7.15.1', '8.9.0')
+        addVersion('7.15.2', '8.9.0')
+        addVersion('7.16.0', '8.10.0')
+        addVersion('8.0.0', '9.0.0')
+
+        when:
+        def bwc = new BwcVersions(versionLines, v('8.0.0'))
+        def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] }
+
+        then:
+        unreleased == [
+            (v('7.15.2')): new UnreleasedVersionInfo(v('7.15.2'), '7.15', ':distribution:bwc:bugfix'),
+            (v('7.16.0')): new UnreleasedVersionInfo(v('7.16.0'), '7.16', ':distribution:bwc:staged'),
+            (v('8.0.0')): new UnreleasedVersionInfo(v('8.0.0'), 'master', ':distribution')
+        ]
+        bwc.wireCompatible == [v('7.16.0'), v('8.0.0')]
+        bwc.indexCompatible == [v('7.14.0'), v('7.14.1'), v('7.14.2'), v('7.15.0'), v('7.15.1'), v('7.15.2'), v('7.16.0'), v('8.0.0')]
+    }
+
+    def "current version is next minor with next major and last minor both staged"() {
+        given:
+        addVersion('7.14.0', '8.9.0')
+        addVersion('7.14.1', '8.9.0')
+        addVersion('7.14.2', '8.9.0')
+        addVersion('7.15.0', '8.9.0')
+        addVersion('7.15.1', '8.9.0')
+        addVersion('7.15.2', '8.9.0')
+        addVersion('7.16.0', '8.10.0')
+        addVersion('8.0.0', '9.0.0')
+        addVersion('8.1.0', '9.1.0')
+
+        when:
+        def bwc = new BwcVersions(versionLines, v('8.1.0'))
+        def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] }
+
+        then:
+        unreleased == [
+            (v('7.15.2')): new UnreleasedVersionInfo(v('7.15.2'), '7.15', ':distribution:bwc:bugfix'),
+            (v('7.16.0')): new UnreleasedVersionInfo(v('7.16.0'), '7.16', ':distribution:bwc:staged'),
+            (v('8.0.0')): new UnreleasedVersionInfo(v('8.0.0'), '8.0', ':distribution:bwc:minor'),
+            (v('8.1.0')): new UnreleasedVersionInfo(v('8.1.0'), 'master', ':distribution')
+        ]
+        bwc.wireCompatible == [v('7.16.0'), v('8.0.0'), v('8.1.0')]
+        bwc.indexCompatible == [v('7.14.0'), v('7.14.1'), v('7.14.2'), v('7.15.0'), v('7.15.1'), v('7.15.2'), v('7.16.0'), v('8.0.0'), v('8.1.0')]
+    }
+
+    def "current is next minor with upcoming minor staged"() {
+        given:
+        addVersion('7.14.0', '8.9.0')
+        addVersion('7.14.1', '8.9.0')
+        addVersion('7.14.2', '8.9.0')
+        addVersion('7.15.0', '8.9.0')
+        addVersion('7.15.1', '8.9.0')
+        addVersion('7.15.2', '8.9.0')
+        addVersion('7.16.0', '8.10.0')
+        addVersion('7.16.1', '8.10.0')
+        addVersion('8.0.0', '9.0.0')
+        addVersion('8.1.0', '9.1.0')
+
+        when:
+        def bwc = new BwcVersions(versionLines, v('8.1.0'))
+        def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] }
+
+        then:
+        unreleased == [
+            (v('7.16.1')): new UnreleasedVersionInfo(v('7.16.1'), '7.16', ':distribution:bwc:bugfix'),
+            (v('8.0.0')): new UnreleasedVersionInfo(v('8.0.0'), '8.0', ':distribution:bwc:staged'),
+            (v('8.1.0')): new UnreleasedVersionInfo(v('8.1.0'), 'master', ':distribution')
+        ]
+        bwc.wireCompatible == [v('7.16.0'), v('7.16.1'), v('8.0.0'), v('8.1.0')]
+        bwc.indexCompatible == [v('7.14.0'), v('7.14.1'), v('7.14.2'), v('7.15.0'), v('7.15.1'), v('7.15.2'), v('7.16.0'), v('7.16.1'), v('8.0.0'), v('8.1.0')]
+    }
+
+    def "current version is staged major"() {
+        given:
+        addVersion('7.14.0', '8.9.0')
+        addVersion('7.14.1', '8.9.0')
+        addVersion('7.14.2', '8.9.0')
+        addVersion('7.15.0', '8.9.0')
+        addVersion('7.15.1', '8.9.0')
+        addVersion('7.15.2', '8.9.0')
+        addVersion('7.16.0', '8.10.0')
+        addVersion('7.16.1', '8.10.0')
+        addVersion('8.0.0', '9.0.0')
+
+        when:
+        def bwc = new BwcVersions(versionLines, v('8.0.0'))
+        def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] }
+
+        then:
+        unreleased == [
+            (v('7.16.1')): new UnreleasedVersionInfo(v('7.16.1'), '7.16', ':distribution:bwc:bugfix'),
+            (v('8.0.0')): new UnreleasedVersionInfo(v('8.0.0'), 'master', ':distribution'),
+        ]
+        bwc.wireCompatible == [v('7.16.0'), v('7.16.1'), v('8.0.0')]
+        bwc.indexCompatible == [v('7.14.0'), v('7.14.1'), v('7.14.2'), v('7.15.0'), v('7.15.1'), v('7.15.2'), v('7.16.0'), v('7.16.1'), v('8.0.0')]
+    }
+
+    def "current version is next bugfix"() {
+        given:
+        addVersion('7.14.0', '8.9.0')
+        addVersion('7.14.1', '8.9.0')
+        addVersion('7.14.2', '8.9.0')
+        addVersion('7.15.0', '8.9.0')
+        addVersion('7.15.1', '8.9.0')
+        addVersion('7.15.2', '8.9.0')
+        addVersion('7.16.0', '8.10.0')
+        addVersion('7.16.1', '8.10.0')
+        addVersion('8.0.0', '9.0.0')
+        addVersion('8.0.1', '9.0.0')
+
+        when:
+        def bwc = new BwcVersions(versionLines, v('8.0.1'))
+        def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] }
+
+        then:
+        unreleased == [
+            (v('7.16.1')): new UnreleasedVersionInfo(v('7.16.1'), '7.16', ':distribution:bwc:maintenance'),
+            (v('8.0.1')): new UnreleasedVersionInfo(v('8.0.1'), 'master', ':distribution'),
+        ]
+        bwc.wireCompatible == [v('7.16.0'), v('7.16.1'), v('8.0.0'), v('8.0.1')]
+        bwc.indexCompatible == [v('7.14.0'), v('7.14.1'), v('7.14.2'), v('7.15.0'), v('7.15.1'), v('7.15.2'), v('7.16.0'), v('7.16.1'), v('8.0.0'), v('8.0.1')]
+    }
+
+    def "current version is next minor with no staged releases"() {
+        given:
+        addVersion('7.14.0', '8.9.0')
+        addVersion('7.14.1', '8.9.0')
+        addVersion('7.14.2', '8.9.0')
+        addVersion('7.15.0', '8.9.0')
+        addVersion('7.15.1', '8.9.0')
+        addVersion('7.15.2', '8.9.0')
+        addVersion('7.16.0', '8.10.0')
+        addVersion('7.16.1', '8.10.0')
+        addVersion('8.0.0', '9.0.0')
+        addVersion('8.0.1', '9.0.0')
+        addVersion('8.1.0', '9.1.0')
+
+        when:
+        def bwc = new BwcVersions(versionLines, v('8.1.0'))
+        def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] }
+
+        then:
+        unreleased == [
+            (v('7.16.1')): new UnreleasedVersionInfo(v('7.16.1'), '7.16', ':distribution:bwc:maintenance'),
+            (v('8.0.1')): new UnreleasedVersionInfo(v('8.0.1'), '8.0', ':distribution:bwc:bugfix'),
+            (v('8.1.0')): new UnreleasedVersionInfo(v('8.1.0'), 'master', ':distribution')
+        ]
+        bwc.wireCompatible == [v('7.16.0'), v('7.16.1'), v('8.0.0'), v('8.0.1'), v('8.1.0')]
+        bwc.indexCompatible == [v('7.14.0'), v('7.14.1'), v('7.14.2'), v('7.15.0'), v('7.15.1'), v('7.15.2'), v('7.16.0'), v('7.16.1'), v('8.0.0'), v('8.0.1'), v('8.1.0')]
+    }
+
+    private void addVersion(String elasticsearch, String lucene) {
+        def es = Version.fromString(elasticsearch)
+        def l = Version.fromString(lucene)
+        versionLines << "    public static final Version V_${es.major}_${es.minor}_${es.revision} = new Version(0000000, org.apache.lucene.util.Version.LUCENE_${l.major}_${l.minor}_${l.revision});".toString()
+    }
+
+    private Version v(String version) {
+        return Version.fromString(version)
+    }
+}

+ 14 - 14
build-tools-internal/src/test/java/org/elasticsearch/gradle/AbstractDistributionDownloadPluginTests.java

@@ -9,6 +9,7 @@
 package org.elasticsearch.gradle;
 
 import org.elasticsearch.gradle.internal.BwcVersions;
+import org.elasticsearch.gradle.internal.BwcVersions.VersionPair;
 import org.elasticsearch.gradle.internal.test.GradleUnitTestCase;
 import org.gradle.api.NamedDomainObjectContainer;
 import org.gradle.api.Project;
@@ -16,7 +17,6 @@ import org.gradle.testfixtures.ProjectBuilder;
 
 import java.io.File;
 import java.util.Arrays;
-import java.util.TreeSet;
 
 public class AbstractDistributionDownloadPluginTests extends GradleUnitTestCase {
     protected static Project rootProject;
@@ -24,27 +24,27 @@ public class AbstractDistributionDownloadPluginTests extends GradleUnitTestCase
     protected static Project packagesProject;
     protected static Project bwcProject;
 
-    protected static final Version BWC_MAJOR_VERSION = Version.fromString("2.0.0");
-    protected static final Version BWC_MINOR_VERSION = Version.fromString("1.1.0");
-    protected static final Version BWC_STAGED_VERSION = Version.fromString("1.0.0");
-    protected static final Version BWC_BUGFIX_VERSION = Version.fromString("1.0.1");
-    protected static final Version BWC_MAINTENANCE_VERSION = Version.fromString("0.90.1");
+    protected static final VersionPair BWC_MAJOR_VERSION = new VersionPair(Version.fromString("2.0.0"), Version.fromString("3.0.0"));
+    protected static final VersionPair BWC_MINOR_VERSION = new VersionPair(Version.fromString("1.1.0"), Version.fromString("2.1.0"));
+    protected static final VersionPair BWC_STAGED_VERSION = new VersionPair(Version.fromString("1.0.0"), Version.fromString("2.0.0"));
+    protected static final VersionPair BWC_BUGFIX_VERSION = new VersionPair(Version.fromString("1.0.1"), Version.fromString("2.0.0"));
+    protected static final VersionPair BWC_MAINTENANCE_VERSION = new VersionPair(Version.fromString("0.90.1"), Version.fromString("1.1.3"));
 
     protected static final BwcVersions BWC_MINOR = new BwcVersions(
-        new TreeSet<>(Arrays.asList(BWC_BUGFIX_VERSION, BWC_MINOR_VERSION, BWC_MAJOR_VERSION)),
-        BWC_MAJOR_VERSION
+        BWC_MAJOR_VERSION.elasticsearch,
+        Arrays.asList(BWC_BUGFIX_VERSION, BWC_MINOR_VERSION, BWC_MAJOR_VERSION)
     );
     protected static final BwcVersions BWC_STAGED = new BwcVersions(
-        new TreeSet<>(Arrays.asList(BWC_STAGED_VERSION, BWC_MINOR_VERSION, BWC_MAJOR_VERSION)),
-        BWC_MAJOR_VERSION
+        BWC_MAJOR_VERSION.elasticsearch,
+        Arrays.asList(BWC_MAINTENANCE_VERSION, BWC_STAGED_VERSION, BWC_MINOR_VERSION, BWC_MAJOR_VERSION)
     );
     protected static final BwcVersions BWC_BUGFIX = new BwcVersions(
-        new TreeSet<>(Arrays.asList(BWC_BUGFIX_VERSION, BWC_MINOR_VERSION, BWC_MAJOR_VERSION)),
-        BWC_MAJOR_VERSION
+        BWC_MAJOR_VERSION.elasticsearch,
+        Arrays.asList(BWC_BUGFIX_VERSION, BWC_MINOR_VERSION, BWC_MAJOR_VERSION)
     );
     protected static final BwcVersions BWC_MAINTENANCE = new BwcVersions(
-        new TreeSet<>(Arrays.asList(BWC_MAINTENANCE_VERSION, BWC_STAGED_VERSION, BWC_MINOR_VERSION)),
-        BWC_MINOR_VERSION
+        BWC_MINOR_VERSION.elasticsearch,
+        Arrays.asList(BWC_MAINTENANCE_VERSION, BWC_BUGFIX_VERSION, BWC_MINOR_VERSION)
     );
 
     protected static String projectName(String base, boolean bundledJdk) {

+ 4 - 4
build-tools-internal/src/test/java/org/elasticsearch/gradle/DistributionDownloadPluginTests.java

@@ -132,10 +132,10 @@ public class DistributionDownloadPluginTests extends AbstractDistributionDownloa
             String configName = projectName(platform.toString(), true);
             configName += (platform == Platform.WINDOWS ? "-zip" : "-tar");
             ElasticsearchDistributionType archiveType = ElasticsearchDistributionTypes.ARCHIVE;
-            checkBwc("minor", configName, BWC_MINOR_VERSION, archiveType, platform, BWC_MINOR);
-            checkBwc("staged", configName, BWC_STAGED_VERSION, archiveType, platform, BWC_STAGED);
-            checkBwc("bugfix", configName, BWC_BUGFIX_VERSION, archiveType, platform, BWC_BUGFIX);
-            checkBwc("maintenance", configName, BWC_MAINTENANCE_VERSION, archiveType, platform, BWC_MAINTENANCE);
+            checkBwc("minor", configName, BWC_MINOR_VERSION.elasticsearch, archiveType, platform, BWC_MINOR);
+            checkBwc("staged", configName, BWC_STAGED_VERSION.elasticsearch, archiveType, platform, BWC_STAGED);
+            checkBwc("bugfix", configName, BWC_BUGFIX_VERSION.elasticsearch, archiveType, platform, BWC_BUGFIX);
+            checkBwc("maintenance", configName, BWC_MAINTENANCE_VERSION.elasticsearch, archiveType, platform, BWC_MAINTENANCE);
         }
     }
 

+ 0 - 774
build-tools-internal/src/test/java/org/elasticsearch/gradle/internal/BwcVersionsTests.java

@@ -1,774 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0 and the Server Side Public License, v 1; you may not use this file except
- * in compliance with, at your election, the Elastic License 2.0 or the Server
- * Side Public License, v 1.
- */
-
-package org.elasticsearch.gradle.internal;
-
-import org.elasticsearch.gradle.Architecture;
-import org.elasticsearch.gradle.Version;
-import org.elasticsearch.gradle.internal.test.GradleUnitTestCase;
-import org.junit.Assume;
-import org.junit.BeforeClass;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import static java.util.Arrays.asList;
-import static java.util.Collections.singletonList;
-
-public class BwcVersionsTests extends GradleUnitTestCase {
-
-    private static final Map<String, List<String>> sampleVersions = new HashMap<>();
-
-    @Rule
-    public ExpectedException expectedEx = ExpectedException.none();
-
-    static {
-        // unreleased major and two unreleased minors ( minor in feature freeze )
-        sampleVersions.put("8.0.0", asList("7_0_0", "7_0_1", "7_1_0", "7_1_1", "7_2_0", "7_3_0", "8.0.0"));
-        sampleVersions.put(
-            "7.0.0-alpha1",
-            asList(
-                "6_0_0_alpha1",
-                "6_0_0_alpha2",
-                "6_0_0_beta1",
-                "6_0_0_beta2",
-                "6_0_0_rc1",
-                "6_0_0_rc2",
-                "6_0_0",
-                "6_0_1",
-                "6_1_0",
-                "6_1_1",
-                "6_1_2",
-                "6_1_3",
-                "6_1_4",
-                "6_2_0",
-                "6_2_1",
-                "6_2_2",
-                "6_2_3",
-                "6_2_4",
-                "6_3_0",
-                "6_3_1",
-                "6_3_2",
-                "6_4_0",
-                "6_4_1",
-                "6_4_2",
-                "6_5_0",
-                "7_0_0_alpha1"
-            )
-        );
-        sampleVersions.put(
-            "6.5.0",
-            asList(
-                "5_0_0_alpha1",
-                "5_0_0_alpha2",
-                "5_0_0_alpha3",
-                "5_0_0_alpha4",
-                "5_0_0_alpha5",
-                "5_0_0_beta1",
-                "5_0_0_rc1",
-                "5_0_0",
-                "5_0_1",
-                "5_0_2",
-                "5_1_1",
-                "5_1_2",
-                "5_2_0",
-                "5_2_1",
-                "5_2_2",
-                "5_3_0",
-                "5_3_1",
-                "5_3_2",
-                "5_3_3",
-                "5_4_0",
-                "5_4_1",
-                "5_4_2",
-                "5_4_3",
-                "5_5_0",
-                "5_5_1",
-                "5_5_2",
-                "5_5_3",
-                "5_6_0",
-                "5_6_1",
-                "5_6_2",
-                "5_6_3",
-                "5_6_4",
-                "5_6_5",
-                "5_6_6",
-                "5_6_7",
-                "5_6_8",
-                "5_6_9",
-                "5_6_10",
-                "5_6_11",
-                "5_6_12",
-                "5_6_13",
-                "6_0_0_alpha1",
-                "6_0_0_alpha2",
-                "6_0_0_beta1",
-                "6_0_0_beta2",
-                "6_0_0_rc1",
-                "6_0_0_rc2",
-                "6_0_0",
-                "6_0_1",
-                "6_1_0",
-                "6_1_1",
-                "6_1_2",
-                "6_1_3",
-                "6_1_4",
-                "6_2_0",
-                "6_2_1",
-                "6_2_2",
-                "6_2_3",
-                "6_2_4",
-                "6_3_0",
-                "6_3_1",
-                "6_3_2",
-                "6_4_0",
-                "6_4_1",
-                "6_4_2",
-                "6_5_0"
-            )
-        );
-        sampleVersions.put(
-            "6.6.0",
-            asList(
-                "5_0_0_alpha1",
-                "5_0_0_alpha2",
-                "5_0_0_alpha3",
-                "5_0_0_alpha4",
-                "5_0_0_alpha5",
-                "5_0_0_beta1",
-                "5_0_0_rc1",
-                "5_0_0",
-                "5_0_1",
-                "5_0_2",
-                "5_1_1",
-                "5_1_2",
-                "5_2_0",
-                "5_2_1",
-                "5_2_2",
-                "5_3_0",
-                "5_3_1",
-                "5_3_2",
-                "5_3_3",
-                "5_4_0",
-                "5_4_1",
-                "5_4_2",
-                "5_4_3",
-                "5_5_0",
-                "5_5_1",
-                "5_5_2",
-                "5_5_3",
-                "5_6_0",
-                "5_6_1",
-                "5_6_2",
-                "5_6_3",
-                "5_6_4",
-                "5_6_5",
-                "5_6_6",
-                "5_6_7",
-                "5_6_8",
-                "5_6_9",
-                "5_6_10",
-                "5_6_11",
-                "5_6_12",
-                "5_6_13",
-                "6_0_0_alpha1",
-                "6_0_0_alpha2",
-                "6_0_0_beta1",
-                "6_0_0_beta2",
-                "6_0_0_rc1",
-                "6_0_0_rc2",
-                "6_0_0",
-                "6_0_1",
-                "6_1_0",
-                "6_1_1",
-                "6_1_2",
-                "6_1_3",
-                "6_1_4",
-                "6_2_0",
-                "6_2_1",
-                "6_2_2",
-                "6_2_3",
-                "6_2_4",
-                "6_3_0",
-                "6_3_1",
-                "6_3_2",
-                "6_4_0",
-                "6_4_1",
-                "6_4_2",
-                "6_5_0",
-                "6_6_0"
-            )
-        );
-        sampleVersions.put(
-            "6.4.2",
-            asList(
-                "5_0_0_alpha1",
-                "5_0_0_alpha2",
-                "5_0_0_alpha3",
-                "5_0_0_alpha4",
-                "5_0_0_alpha5",
-                "5_0_0_beta1",
-                "5_0_0_rc1",
-                "5_0_0",
-                "5_0_1",
-                "5_0_2",
-                "5_1_1",
-                "5_1_2",
-                "5_2_0",
-                "5_2_1",
-                "5_2_2",
-                "5_3_0",
-                "5_3_1",
-                "5_3_2",
-                "5_3_3",
-                "5_4_0",
-                "5_4_1",
-                "5_4_2",
-                "5_4_3",
-                "5_5_0",
-                "5_5_1",
-                "5_5_2",
-                "5_5_3",
-                "5_6_0",
-                "5_6_1",
-                "5_6_2",
-                "5_6_3",
-                "5_6_4",
-                "5_6_5",
-                "5_6_6",
-                "5_6_7",
-                "5_6_8",
-                "5_6_9",
-                "5_6_10",
-                "5_6_11",
-                "5_6_12",
-                "5_6_13",
-                "6_0_0_alpha1",
-                "6_0_0_alpha2",
-                "6_0_0_beta1",
-                "6_0_0_beta2",
-                "6_0_0_rc1",
-                "6_0_0_rc2",
-                "6_0_0",
-                "6_0_1",
-                "6_1_0",
-                "6_1_1",
-                "6_1_2",
-                "6_1_3",
-                "6_1_4",
-                "6_2_0",
-                "6_2_1",
-                "6_2_2",
-                "6_2_3",
-                "6_2_4",
-                "6_3_0",
-                "6_3_1",
-                "6_3_2",
-                "6_4_0",
-                "6_4_1",
-                "6_4_2"
-            )
-        );
-        sampleVersions.put("7.1.0", asList("7_1_0", "7_0_0", "6_7_0", "6_6_1", "6_6_0"));
-    }
-
-    @BeforeClass
-    public static void setupAll() {
-        Assume.assumeFalse(Architecture.current() == Architecture.AARCH64);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testExceptionOnEmpty() {
-        new BwcVersions(asList("foo", "bar"), Version.fromString("7.0.0"));
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void testExceptionOnNonCurrent() {
-        new BwcVersions(singletonList(formatVersionToLine("6.5.0")), Version.fromString("7.0.0"));
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void testExceptionOnTooManyMajors() {
-        new BwcVersions(
-            asList(formatVersionToLine("5.6.12"), formatVersionToLine("6.5.0"), formatVersionToLine("7.0.0")),
-            Version.fromString("6.5.0")
-        );
-    }
-
-    public void testWireCompatible() {
-        assertVersionsEquals(asList("6.5.0", "7.0.0"), getVersionCollection("7.0.0-alpha1").getWireCompatible());
-        assertVersionsEquals(
-            asList(
-                "5.6.0",
-                "5.6.1",
-                "5.6.2",
-                "5.6.3",
-                "5.6.4",
-                "5.6.5",
-                "5.6.6",
-                "5.6.7",
-                "5.6.8",
-                "5.6.9",
-                "5.6.10",
-                "5.6.11",
-                "5.6.12",
-                "5.6.13",
-                "6.0.0",
-                "6.0.1",
-                "6.1.0",
-                "6.1.1",
-                "6.1.2",
-                "6.1.3",
-                "6.1.4",
-                "6.2.0",
-                "6.2.1",
-                "6.2.2",
-                "6.2.3",
-                "6.2.4",
-                "6.3.0",
-                "6.3.1",
-                "6.3.2",
-                "6.4.0",
-                "6.4.1",
-                "6.4.2",
-                "6.5.0"
-            ),
-            getVersionCollection("6.5.0").getWireCompatible()
-        );
-
-        assertVersionsEquals(
-            asList(
-                "5.6.0",
-                "5.6.1",
-                "5.6.2",
-                "5.6.3",
-                "5.6.4",
-                "5.6.5",
-                "5.6.6",
-                "5.6.7",
-                "5.6.8",
-                "5.6.9",
-                "5.6.10",
-                "5.6.11",
-                "5.6.12",
-                "5.6.13",
-                "6.0.0",
-                "6.0.1",
-                "6.1.0",
-                "6.1.1",
-                "6.1.2",
-                "6.1.3",
-                "6.1.4",
-                "6.2.0",
-                "6.2.1",
-                "6.2.2",
-                "6.2.3",
-                "6.2.4",
-                "6.3.0",
-                "6.3.1",
-                "6.3.2",
-                "6.4.0",
-                "6.4.1",
-                "6.4.2"
-            ),
-            getVersionCollection("6.4.2").getWireCompatible()
-        );
-
-        assertVersionsEquals(
-            asList(
-                "5.6.0",
-                "5.6.1",
-                "5.6.2",
-                "5.6.3",
-                "5.6.4",
-                "5.6.5",
-                "5.6.6",
-                "5.6.7",
-                "5.6.8",
-                "5.6.9",
-                "5.6.10",
-                "5.6.11",
-                "5.6.12",
-                "5.6.13",
-                "6.0.0",
-                "6.0.1",
-                "6.1.0",
-                "6.1.1",
-                "6.1.2",
-                "6.1.3",
-                "6.1.4",
-                "6.2.0",
-                "6.2.1",
-                "6.2.2",
-                "6.2.3",
-                "6.2.4",
-                "6.3.0",
-                "6.3.1",
-                "6.3.2",
-                "6.4.0",
-                "6.4.1",
-                "6.4.2",
-                "6.5.0",
-                "6.6.0"
-            ),
-            getVersionCollection("6.6.0").getWireCompatible()
-        );
-
-        assertVersionsEquals(asList("7.3.0", "8.0.0"), getVersionCollection("8.0.0").getWireCompatible());
-        assertVersionsEquals(asList("6.7.0", "7.0.0", "7.1.0"), getVersionCollection("7.1.0").getWireCompatible());
-
-    }
-
-    public void testWireCompatibleUnreleased() {
-        assertVersionsEquals(asList("6.5.0", "7.0.0"), getVersionCollection("7.0.0-alpha1").getUnreleasedWireCompatible());
-        assertVersionsEquals(asList("5.6.13", "6.4.2", "6.5.0"), getVersionCollection("6.5.0").getUnreleasedWireCompatible());
-
-        assertVersionsEquals(asList("5.6.13", "6.4.2"), getVersionCollection("6.4.2").getUnreleasedWireCompatible());
-
-        assertVersionsEquals(asList("5.6.13", "6.4.2", "6.5.0", "6.6.0"), getVersionCollection("6.6.0").getUnreleasedWireCompatible());
-
-        assertVersionsEquals(asList("7.3.0", "8.0.0"), getVersionCollection("8.0.0").getUnreleasedWireCompatible());
-        assertVersionsEquals(asList("6.7.0", "7.0.0", "7.1.0"), getVersionCollection("7.1.0").getWireCompatible());
-    }
-
-    public void testIndexCompatible() {
-        assertVersionsEquals(
-            asList(
-                "6.0.0",
-                "6.0.1",
-                "6.1.0",
-                "6.1.1",
-                "6.1.2",
-                "6.1.3",
-                "6.1.4",
-                "6.2.0",
-                "6.2.1",
-                "6.2.2",
-                "6.2.3",
-                "6.2.4",
-                "6.3.0",
-                "6.3.1",
-                "6.3.2",
-                "6.4.0",
-                "6.4.1",
-                "6.4.2",
-                "6.5.0",
-                "7.0.0"
-            ),
-            getVersionCollection("7.0.0-alpha1").getIndexCompatible()
-        );
-
-        assertVersionsEquals(
-            asList(
-                "5.0.0",
-                "5.0.1",
-                "5.0.2",
-                "5.1.1",
-                "5.1.2",
-                "5.2.0",
-                "5.2.1",
-                "5.2.2",
-                "5.3.0",
-                "5.3.1",
-                "5.3.2",
-                "5.3.3",
-                "5.4.0",
-                "5.4.1",
-                "5.4.2",
-                "5.4.3",
-                "5.5.0",
-                "5.5.1",
-                "5.5.2",
-                "5.5.3",
-                "5.6.0",
-                "5.6.1",
-                "5.6.2",
-                "5.6.3",
-                "5.6.4",
-                "5.6.5",
-                "5.6.6",
-                "5.6.7",
-                "5.6.8",
-                "5.6.9",
-                "5.6.10",
-                "5.6.11",
-                "5.6.12",
-                "5.6.13",
-                "6.0.0",
-                "6.0.1",
-                "6.1.0",
-                "6.1.1",
-                "6.1.2",
-                "6.1.3",
-                "6.1.4",
-                "6.2.0",
-                "6.2.1",
-                "6.2.2",
-                "6.2.3",
-                "6.2.4",
-                "6.3.0",
-                "6.3.1",
-                "6.3.2",
-                "6.4.0",
-                "6.4.1",
-                "6.4.2",
-                "6.5.0"
-            ),
-            getVersionCollection("6.5.0").getIndexCompatible()
-        );
-
-        assertVersionsEquals(
-            asList(
-                "5.0.0",
-                "5.0.1",
-                "5.0.2",
-                "5.1.1",
-                "5.1.2",
-                "5.2.0",
-                "5.2.1",
-                "5.2.2",
-                "5.3.0",
-                "5.3.1",
-                "5.3.2",
-                "5.3.3",
-                "5.4.0",
-                "5.4.1",
-                "5.4.2",
-                "5.4.3",
-                "5.5.0",
-                "5.5.1",
-                "5.5.2",
-                "5.5.3",
-                "5.6.0",
-                "5.6.1",
-                "5.6.2",
-                "5.6.3",
-                "5.6.4",
-                "5.6.5",
-                "5.6.6",
-                "5.6.7",
-                "5.6.8",
-                "5.6.9",
-                "5.6.10",
-                "5.6.11",
-                "5.6.12",
-                "5.6.13",
-                "6.0.0",
-                "6.0.1",
-                "6.1.0",
-                "6.1.1",
-                "6.1.2",
-                "6.1.3",
-                "6.1.4",
-                "6.2.0",
-                "6.2.1",
-                "6.2.2",
-                "6.2.3",
-                "6.2.4",
-                "6.3.0",
-                "6.3.1",
-                "6.3.2",
-                "6.4.0",
-                "6.4.1",
-                "6.4.2"
-            ),
-            getVersionCollection("6.4.2").getIndexCompatible()
-        );
-
-        assertVersionsEquals(
-            asList(
-                "5.0.0",
-                "5.0.1",
-                "5.0.2",
-                "5.1.1",
-                "5.1.2",
-                "5.2.0",
-                "5.2.1",
-                "5.2.2",
-                "5.3.0",
-                "5.3.1",
-                "5.3.2",
-                "5.3.3",
-                "5.4.0",
-                "5.4.1",
-                "5.4.2",
-                "5.4.3",
-                "5.5.0",
-                "5.5.1",
-                "5.5.2",
-                "5.5.3",
-                "5.6.0",
-                "5.6.1",
-                "5.6.2",
-                "5.6.3",
-                "5.6.4",
-                "5.6.5",
-                "5.6.6",
-                "5.6.7",
-                "5.6.8",
-                "5.6.9",
-                "5.6.10",
-                "5.6.11",
-                "5.6.12",
-                "5.6.13",
-                "6.0.0",
-                "6.0.1",
-                "6.1.0",
-                "6.1.1",
-                "6.1.2",
-                "6.1.3",
-                "6.1.4",
-                "6.2.0",
-                "6.2.1",
-                "6.2.2",
-                "6.2.3",
-                "6.2.4",
-                "6.3.0",
-                "6.3.1",
-                "6.3.2",
-                "6.4.0",
-                "6.4.1",
-                "6.4.2",
-                "6.5.0",
-                "6.6.0"
-            ),
-            getVersionCollection("6.6.0").getIndexCompatible()
-        );
-
-        assertVersionsEquals(
-            asList("7.0.0", "7.0.1", "7.1.0", "7.1.1", "7.2.0", "7.3.0", "8.0.0"),
-            getVersionCollection("8.0.0").getIndexCompatible()
-        );
-    }
-
-    public void testIndexCompatibleUnreleased() {
-        assertVersionsEquals(asList("6.4.2", "6.5.0", "7.0.0"), getVersionCollection("7.0.0-alpha1").getUnreleasedIndexCompatible());
-
-        assertVersionsEquals(asList("5.6.13", "6.4.2", "6.5.0"), getVersionCollection("6.5.0").getUnreleasedIndexCompatible());
-
-        assertVersionsEquals(asList("5.6.13", "6.4.2"), getVersionCollection("6.4.2").getUnreleasedIndexCompatible());
-
-        assertVersionsEquals(asList("5.6.13", "6.4.2", "6.5.0", "6.6.0"), getVersionCollection("6.6.0").getUnreleasedIndexCompatible());
-
-        assertVersionsEquals(asList("7.1.1", "7.2.0", "7.3.0", "8.0.0"), getVersionCollection("8.0.0").getUnreleasedIndexCompatible());
-    }
-
-    public void testGetUnreleased() {
-        assertVersionsEquals(asList("6.4.2", "6.5.0", "7.0.0-alpha1"), getVersionCollection("7.0.0-alpha1").getUnreleased());
-        assertVersionsEquals(asList("5.6.13", "6.4.2", "6.5.0"), getVersionCollection("6.5.0").getUnreleased());
-        assertVersionsEquals(asList("5.6.13", "6.4.2"), getVersionCollection("6.4.2").getUnreleased());
-        assertVersionsEquals(asList("5.6.13", "6.4.2", "6.5.0", "6.6.0"), getVersionCollection("6.6.0").getUnreleased());
-        assertVersionsEquals(asList("7.1.1", "7.2.0", "7.3.0", "8.0.0"), getVersionCollection("8.0.0").getUnreleased());
-    }
-
-    public void testGetBranch() {
-        assertUnreleasedBranchNames(asList("6.4", "6.x"), getVersionCollection("7.0.0-alpha1"));
-        assertUnreleasedBranchNames(asList("5.6", "6.4"), getVersionCollection("6.5.0"));
-        assertUnreleasedBranchNames(singletonList("5.6"), getVersionCollection("6.4.2"));
-        assertUnreleasedBranchNames(asList("5.6", "6.4", "6.5"), getVersionCollection("6.6.0"));
-        assertUnreleasedBranchNames(asList("7.1", "7.2", "7.x"), getVersionCollection("8.0.0"));
-    }
-
-    public void testGetGradleProjectPath() {
-        assertUnreleasedGradleProjectPaths(
-            asList(":distribution:bwc:bugfix", ":distribution:bwc:minor"),
-            getVersionCollection("7.0.0-alpha1")
-        );
-        assertUnreleasedGradleProjectPaths(
-            asList(":distribution:bwc:maintenance", ":distribution:bwc:bugfix"),
-            getVersionCollection("6.5.0")
-        );
-        assertUnreleasedGradleProjectPaths(singletonList(":distribution:bwc:maintenance"), getVersionCollection("6.4.2"));
-        assertUnreleasedGradleProjectPaths(
-            asList(":distribution:bwc:maintenance", ":distribution:bwc:bugfix", ":distribution:bwc:minor"),
-            getVersionCollection("6.6.0")
-        );
-        assertUnreleasedGradleProjectPaths(
-            asList(":distribution:bwc:bugfix", ":distribution:bwc:staged", ":distribution:bwc:minor"),
-            getVersionCollection("8.0.0")
-        );
-        assertUnreleasedGradleProjectPaths(
-            asList(":distribution:bwc:maintenance", ":distribution:bwc:staged", ":distribution:bwc:minor"),
-            getVersionCollection("7.1.0")
-        );
-    }
-
-    public void testCompareToAuthoritative() {
-        List<String> listOfVersions = asList("7.0.0", "7.0.1", "7.1.0", "7.1.1", "7.2.0", "7.3.0", "8.0.0");
-        List<Version> authoritativeReleasedVersions = Stream.of("7.0.0", "7.0.1", "7.1.0")
-            .map(Version::fromString)
-            .collect(Collectors.toList());
-
-        BwcVersions vc = new BwcVersions(
-            listOfVersions.stream().map(this::formatVersionToLine).collect(Collectors.toList()),
-            Version.fromString("8.0.0")
-        );
-        vc.compareToAuthoritative(authoritativeReleasedVersions);
-    }
-
-    public void testCompareToAuthoritativeUnreleasedActuallyReleased() {
-        List<String> listOfVersions = asList("7.0.0", "7.0.1", "7.1.0", "7.1.1", "7.2.0", "7.3.0", "8.0.0");
-        List<Version> authoritativeReleasedVersions = Stream.of("7.0.0", "7.0.1", "7.1.0", "7.1.1", "8.0.0")
-            .map(Version::fromString)
-            .collect(Collectors.toList());
-
-        BwcVersions vc = new BwcVersions(
-            listOfVersions.stream().map(this::formatVersionToLine).collect(Collectors.toList()),
-            Version.fromString("8.0.0")
-        );
-        expectedEx.expect(IllegalStateException.class);
-        expectedEx.expectMessage("but they are released");
-        vc.compareToAuthoritative(authoritativeReleasedVersions);
-    }
-
-    public void testCompareToAuthoritativeNotReallyRelesed() {
-        List<String> listOfVersions = asList("7.0.0", "7.0.1", "7.1.0", "7.1.1", "7.2.0", "7.3.0", "8.0.0");
-        List<Version> authoritativeReleasedVersions = Stream.of("7.0.0", "7.0.1").map(Version::fromString).collect(Collectors.toList());
-        BwcVersions vc = new BwcVersions(
-            listOfVersions.stream().map(this::formatVersionToLine).collect(Collectors.toList()),
-            Version.fromString("8.0.0")
-        );
-        expectedEx.expect(IllegalStateException.class);
-        expectedEx.expectMessage("not really released");
-        vc.compareToAuthoritative(authoritativeReleasedVersions);
-    }
-
-    private void assertUnreleasedGradleProjectPaths(List<String> expectedNAmes, BwcVersions bwcVersions) {
-        List<String> actualNames = new ArrayList<>();
-        bwcVersions.forPreviousUnreleased(unreleasedVersion -> actualNames.add(unreleasedVersion.gradleProjectPath));
-        assertEquals(expectedNAmes, actualNames);
-    }
-
-    private void assertUnreleasedBranchNames(List<String> expectedBranches, BwcVersions bwcVersions) {
-        List<String> actualBranches = new ArrayList<>();
-        bwcVersions.forPreviousUnreleased(unreleasedVersionInfo -> actualBranches.add(unreleasedVersionInfo.branch));
-        assertEquals(expectedBranches, actualBranches);
-    }
-
-    private String formatVersionToLine(final String version) {
-        return " public static final Version V_" + version.replaceAll("\\.", "_") + " ";
-    }
-
-    private void assertVersionsEquals(List<String> expected, List<Version> actual) {
-        assertEquals(expected.stream().map(Version::fromString).collect(Collectors.toList()), actual);
-    }
-
-    private BwcVersions getVersionCollection(String currentVersion) {
-        return new BwcVersions(
-            sampleVersions.get(currentVersion).stream().map(this::formatVersionToLine).collect(Collectors.toList()),
-            Version.fromString(currentVersion)
-        );
-    }
-}

+ 4 - 4
build-tools-internal/src/test/java/org/elasticsearch/gradle/internal/InternalDistributionDownloadPluginTests.java

@@ -38,10 +38,10 @@ public class InternalDistributionDownloadPluginTests extends AbstractDistributio
         for (ElasticsearchDistributionType packageType : types) {
             // note: no non bundled jdk for bwc
             String configName = projectName(packageType.toString(), true);
-            checkBwc("minor", configName, BWC_MINOR_VERSION, packageType, null, BWC_MINOR);
-            checkBwc("staged", configName, BWC_STAGED_VERSION, packageType, null, BWC_STAGED);
-            checkBwc("bugfix", configName, BWC_BUGFIX_VERSION, packageType, null, BWC_BUGFIX);
-            checkBwc("maintenance", configName, BWC_MAINTENANCE_VERSION, packageType, null, BWC_MAINTENANCE);
+            checkBwc("minor", configName, BWC_MINOR_VERSION.elasticsearch, packageType, null, BWC_MINOR);
+            checkBwc("staged", configName, BWC_STAGED_VERSION.elasticsearch, packageType, null, BWC_STAGED);
+            checkBwc("bugfix", configName, BWC_BUGFIX_VERSION.elasticsearch, packageType, null, BWC_BUGFIX);
+            checkBwc("maintenance", configName, BWC_MAINTENANCE_VERSION.elasticsearch, packageType, null, BWC_MAINTENANCE);
         }
     }
 }

+ 27 - 14
build-tools/src/testFixtures/groovy/org/elasticsearch/gradle/fixtures/AbstractGradleFuncTest.groovy

@@ -35,10 +35,11 @@ abstract class AbstractGradleFuncTest extends Specification {
         settingsFile << "rootProject.name = 'hello-world'\n"
         buildFile = testProjectDir.newFile('build.gradle')
         propertiesFile = testProjectDir.newFile('gradle.properties')
-        propertiesFile << "org.gradle.java.installations.fromEnv=JAVA_HOME,RUNTIME_JAVA_HOME,JAVA15_HOME,JAVA14_HOME,JAVA13_HOME,JAVA12_HOME,JAVA11_HOME,JAVA8_HOME"
+        propertiesFile <<
+            "org.gradle.java.installations.fromEnv=JAVA_HOME,RUNTIME_JAVA_HOME,JAVA15_HOME,JAVA14_HOME,JAVA13_HOME,JAVA12_HOME,JAVA11_HOME,JAVA8_HOME"
     }
 
-    File addSubProject(String subProjectPath){
+    File addSubProject(String subProjectPath) {
         def subProjectBuild = file(subProjectPath.replace(":", "/") + "/build.gradle")
         settingsFile << "include \"${subProjectPath}\"\n"
         subProjectBuild
@@ -50,13 +51,14 @@ abstract class AbstractGradleFuncTest extends Specification {
 
     GradleRunner gradleRunner(File projectDir, String... arguments) {
         return new NormalizeOutputGradleRunner(
-                new InternalAwareGradleRunner(GradleRunner.create()
+            new InternalAwareGradleRunner(
+                GradleRunner.create()
                     .withDebug(ManagementFactory.getRuntimeMXBean().getInputArguments().toString().indexOf("-agentlib:jdwp") > 0)
                     .withProjectDir(projectDir)
                     .withPluginClasspath()
                     .forwardOutput()
-                ),
-                projectDir
+            ),
+            projectDir
         ).withArguments(arguments)
     }
 
@@ -96,23 +98,34 @@ abstract class AbstractGradleFuncTest extends Specification {
         return jarFile;
     }
 
-    File internalBuild(File buildScript = buildFile, String bugfix = "7.10.1", String staged = "7.11.0", String minor = "7.12.0") {
+    File internalBuild(
+        File buildScript = buildFile,
+        String bugfix = "7.15.2",
+        String bugfixLucene = "8.9.0",
+        String staged = "7.16.0",
+        String stagedLucene = "8.10.0",
+        String minor = "8.0.0",
+        String minorLucene = "9.0.0"
+    ) {
         buildScript << """plugins {
           id 'elasticsearch.global-build-info'
         }
         import org.elasticsearch.gradle.Architecture
         import org.elasticsearch.gradle.internal.info.BuildParams
 
+        import org.elasticsearch.gradle.internal.BwcVersions.VersionPair
         import org.elasticsearch.gradle.internal.BwcVersions
         import org.elasticsearch.gradle.Version
 
-        Version currentVersion = Version.fromString("8.0.0")
-         def versionList = []
-               versionList.addAll(
-            Arrays.asList(Version.fromString("$bugfix"), Version.fromString("$staged"), Version.fromString("$minor"), currentVersion)
-        )
+        Version currentVersion = Version.fromString("8.1.0")
+        def versionList = [
+          new VersionPair(Version.fromString("$bugfix"), Version.fromString("$bugfixLucene")),
+          new VersionPair(Version.fromString("$staged"), Version.fromString("$stagedLucene")),
+          new VersionPair(Version.fromString("$minor"), Version.fromString("$minorLucene")),
+          new VersionPair(currentVersion, Version.fromString("9.0.0"))
+        ]
 
-        BwcVersions versions = new BwcVersions(new TreeSet<>(versionList), currentVersion)
+        BwcVersions versions = new BwcVersions(currentVersion, versionList)
         BuildParams.init { it.setBwcVersions(provider(() -> versions)) }
         """
     }
@@ -128,9 +141,9 @@ abstract class AbstractGradleFuncTest extends Specification {
     void execute(String command, File workingDir = testProjectDir.root) {
         def proc = command.execute(Collections.emptyList(), workingDir)
         proc.waitFor()
-        if(proc.exitValue()) {
+        if (proc.exitValue()) {
             System.err.println("Error running command ${command}:")
             System.err.println("Syserr: " + proc.errorStream.text)
         }
     }
-}
+}

+ 0 - 6
build.gradle

@@ -116,12 +116,6 @@ tasks.register("verifyVersions") {
         throw new GradleException("No branch choice exists for development branch ${unreleasedVersion.branch} in .backportrc.json.")
       }
     }
-    BwcVersions.UnreleasedVersionInfo nextMinor = unreleased.find { it.branch.endsWith("x") }
-    String versionMapping = backportConfig.get("branchLabelMapping").fields().find { it.value.textValue() == nextMinor.branch }.key
-    if (versionMapping != "^v${nextMinor.version}\$") {
-      throw new GradleException("Backport label mapping for branch ${nextMinor.branch} is '${versionMapping}' but should be " +
-        "'^v${nextMinor.version}\$'. Update .backportrc.json.")
-    }
   }
 }