diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/bugfix/build.gradle b/.github/git-commit-instructions.md similarity index 100% rename from build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/bugfix/build.gradle rename to .github/git-commit-instructions.md diff --git a/branches.json b/branches.json deleted file mode 100644 index 5c942999591c8..0000000000000 --- a/branches.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "notice": "This file is not maintained outside of the main branch and should only be used for tooling.", - "branches": [ - { - "branch": "main" - }, - { - "branch": "9.0" - }, - { - "branch": "8.18" - }, - { - "branch": "7.17" - } - ] -} diff --git a/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/VersionPropertiesPlugin.java b/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/VersionPropertiesPlugin.java index ab29204062318..1b93c6b43a395 100644 --- a/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/VersionPropertiesPlugin.java +++ b/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/VersionPropertiesPlugin.java @@ -18,6 +18,8 @@ public class VersionPropertiesPlugin implements Plugin { + public static final String VERSIONS_EXT = "versions"; + @Override public void apply(Project project) { File workspaceDir = Util.locateElasticsearchWorkspace(project.getGradle()); @@ -28,6 +30,6 @@ public void apply(Project project) { .registerIfAbsent("versions", VersionPropertiesBuildService.class, spec -> { spec.getParameters().getInfoPath().set(infoPath); }); - project.getExtensions().add("versions", serviceProvider.get().getProperties()); + project.getExtensions().add(VERSIONS_EXT, serviceProvider.get().getProperties()); } } diff --git a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/fixtures/AbstractGitAwareGradleFuncTest.groovy b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/fixtures/AbstractGitAwareGradleFuncTest.groovy index d8cb55fc0373f..754431907ba60 100644 --- a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/fixtures/AbstractGitAwareGradleFuncTest.groovy +++ b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/fixtures/AbstractGitAwareGradleFuncTest.groovy @@ -26,6 +26,17 @@ abstract class AbstractGitAwareGradleFuncTest extends AbstractGradleFuncTest { execute("git clone ${remoteGitRepo.absolutePath} cloned", testProjectDir.root) buildFile = new File(testProjectDir.root, 'cloned/build.gradle') settingsFile = new File(testProjectDir.root, 'cloned/settings.gradle') + versionPropertiesFile = new File(testProjectDir.root, 'cloned/build-tools-internal/version.properties') + versionPropertiesFile.text = """ + elasticsearch = 9.1.0 + lucene = 10.2.2 + + bundled_jdk_vendor = openjdk + bundled_jdk = 24+36@1f9ff9062db4449d8ca828c504ffae90 + minimumJdkVersion = 21 + minimumRuntimeJava = 21 + minimumCompilerJava = 21 + """ } File setupGitRemote() { diff --git a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/InternalDistributionBwcSetupPluginFuncTest.groovy b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/InternalDistributionBwcSetupPluginFuncTest.groovy index effcb8a54afb6..9a235675918c6 100644 --- a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/InternalDistributionBwcSetupPluginFuncTest.groovy +++ b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/InternalDistributionBwcSetupPluginFuncTest.groovy @@ -51,23 +51,23 @@ class InternalDistributionBwcSetupPluginFuncTest extends AbstractGitAwareGradleF assertOutputContains(result.output, "[$bwcDistVersion] > Task :distribution:archives:darwin-tar:${expectedAssembleTaskName}") where: - bwcDistVersion | bwcProject | expectedAssembleTaskName - "8.4.0" | "minor" | "extractedAssemble" - "8.3.0" | "staged" | "extractedAssemble" - "8.2.1" | "bugfix" | "extractedAssemble" - "8.1.3" | "bugfix2" | "extractedAssemble" + bwcDistVersion | bwcProject | expectedAssembleTaskName + "8.4.0" | "major1" | "extractedAssemble" + "8.3.0" | "major2" | "extractedAssemble" + "8.2.1" | "major3" | "extractedAssemble" + "8.1.3" | "major4" | "extractedAssemble" } @Unroll def "supports #platform aarch distributions"() { when: - def result = gradleRunner(":distribution:bwc:minor:buildBwc${platform.capitalize()}Aarch64Tar", + def result = gradleRunner(":distribution:bwc:major1:buildBwc${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:major1:buildBwc${platform.capitalize()}Aarch64Tar").outcome == TaskOutcome.SUCCESS and: "assemble tasks triggered" assertOutputContains(result.output, "[$bwcDistVersion] > Task :distribution:archives:${platform}-aarch64-tar:extractedAssemble") @@ -87,7 +87,7 @@ class InternalDistributionBwcSetupPluginFuncTest extends AbstractGitAwareGradleF } dependencies { - expandedDist project(path: ":distribution:bwc:minor", configuration:"expanded-darwin-tar") + expandedDist project(path: ":distribution:bwc:major1", configuration:"expanded-darwin-tar") } tasks.register("resolveExpandedDistribution") { @@ -109,12 +109,12 @@ class InternalDistributionBwcSetupPluginFuncTest extends AbstractGitAwareGradleF .build() then: result.task(":resolveExpandedDistribution").outcome == TaskOutcome.SUCCESS - result.task(":distribution:bwc:minor:buildBwcDarwinTar").outcome == TaskOutcome.SUCCESS + result.task(":distribution:bwc:major1:buildBwcDarwinTar").outcome == TaskOutcome.SUCCESS and: "assemble task triggered" result.output.contains("[8.4.0] > Task :distribution:archives:darwin-tar:extractedAssemble") - result.output.contains("expandedRootPath /distribution/bwc/minor/build/bwc/checkout-8.x/" + + result.output.contains("expandedRootPath /distribution/bwc/major1/build/bwc/checkout-8.x/" + "distribution/archives/darwin-tar/build/install") - result.output.contains("nested folder /distribution/bwc/minor/build/bwc/checkout-8.x/" + + result.output.contains("nested folder /distribution/bwc/major1/build/bwc/checkout-8.x/" + "distribution/archives/darwin-tar/build/install/elasticsearch-8.4.0-SNAPSHOT") } } diff --git a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/InternalDistributionDownloadPluginFuncTest.groovy b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/InternalDistributionDownloadPluginFuncTest.groovy index d27c895aea53b..c7643da1c73b9 100644 --- a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/InternalDistributionDownloadPluginFuncTest.groovy +++ b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/InternalDistributionDownloadPluginFuncTest.groovy @@ -51,7 +51,7 @@ class InternalDistributionDownloadPluginFuncTest extends AbstractGradleFuncTest def "resolves expanded bwc versions from source"() { given: internalBuild() - bwcMinorProjectSetup() + bwcMajor1ProjectSetup() buildFile << """ apply plugin: 'elasticsearch.internal-distribution-download' @@ -72,16 +72,16 @@ class InternalDistributionDownloadPluginFuncTest extends AbstractGradleFuncTest def result = gradleRunner("setupDistro").build() then: - result.task(":distribution:bwc:minor:buildBwcExpandedTask").outcome == TaskOutcome.SUCCESS + result.task(":distribution:bwc:major1:buildBwcExpandedTask").outcome == TaskOutcome.SUCCESS result.task(":setupDistro").outcome == TaskOutcome.SUCCESS - assertExtractedDistroIsCreated("distribution/bwc/minor/build/install/elastic-distro", + assertExtractedDistroIsCreated("distribution/bwc/major1/build/install/elastic-distro", 'bwc-marker.txt') } def "fails on resolving bwc versions with no bundled jdk"() { given: internalBuild() - bwcMinorProjectSetup() + bwcMajor1ProjectSetup() buildFile << """ apply plugin: 'elasticsearch.internal-distribution-download' @@ -105,12 +105,12 @@ class InternalDistributionDownloadPluginFuncTest extends AbstractGradleFuncTest "without a bundled JDK is not supported.") } - private void bwcMinorProjectSetup() { + private void bwcMajor1ProjectSetup() { settingsFile << """ - include ':distribution:bwc:minor' + include ':distribution:bwc:major1' """ - def bwcSubProjectFolder = testProjectDir.newFolder("distribution", "bwc", "minor") - new File(bwcSubProjectFolder, 'bwc-marker.txt') << "bwc=minor" + def bwcSubProjectFolder = testProjectDir.newFolder("distribution", "bwc", "major1") + new File(bwcSubProjectFolder, 'bwc-marker.txt') << "bwc=major1" new File(bwcSubProjectFolder, 'build.gradle') << """ apply plugin:'base' diff --git a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/test/rest/LegacyYamlRestCompatTestPluginFuncTest.groovy b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/test/rest/LegacyYamlRestCompatTestPluginFuncTest.groovy index 15b057a05e039..2d08f34e16f03 100644 --- a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/test/rest/LegacyYamlRestCompatTestPluginFuncTest.groovy +++ b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/test/rest/LegacyYamlRestCompatTestPluginFuncTest.groovy @@ -40,7 +40,7 @@ class LegacyYamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTe given: internalBuild() - subProject(":distribution:bwc:minor") << """ + subProject(":distribution:bwc:major1") << """ configurations { checkout } artifacts { checkout(new File(projectDir, "checkoutDir")) @@ -61,11 +61,11 @@ class LegacyYamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTe result.task(transformTask).outcome == TaskOutcome.NO_SOURCE } - def "yamlRestCompatTest executes and copies api and transforms tests from :bwc:minor"() { + def "yamlRestCompatTest executes and copies api and transforms tests from :bwc:major1"() { given: internalBuild() - subProject(":distribution:bwc:minor") << """ + subProject(":distribution:bwc:major1") << """ configurations { checkout } artifacts { checkout(new File(projectDir, "checkoutDir")) @@ -98,8 +98,8 @@ class LegacyYamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTe 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/major1/checkoutDir/rest-api-spec/src/main/resources/rest-api-spec/api/" + api) << "" + file("distribution/bwc/major1/checkoutDir/src/yamlRestTest/resources/rest-api-spec/test/" + test) << "" when: def result = gradleRunner("yamlRestCompatTest").build() @@ -145,7 +145,7 @@ class LegacyYamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTe given: internalBuild() withVersionCatalogue() - subProject(":distribution:bwc:minor") << """ + subProject(":distribution:bwc:major1") << """ configurations { checkout } artifacts { checkout(new File(projectDir, "checkoutDir")) @@ -186,7 +186,7 @@ class LegacyYamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTe given: internalBuild() - subProject(":distribution:bwc:minor") << """ + subProject(":distribution:bwc:major1") << """ configurations { checkout } artifacts { checkout(new File(projectDir, "checkoutDir")) @@ -230,7 +230,7 @@ class LegacyYamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTe setupRestResources([], []) - file("distribution/bwc/minor/checkoutDir/src/yamlRestTest/resources/rest-api-spec/test/test.yml" ) << """ + file("distribution/bwc/major1/checkoutDir/src/yamlRestTest/resources/rest-api-spec/test/test.yml" ) << """ "one": - do: do_.some.key_to_replace: diff --git a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/transport/AbstractTransportVersionFuncTest.groovy b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/transport/AbstractTransportVersionFuncTest.groovy index 051a4f809fd25..5c63de1701c4f 100644 --- a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/transport/AbstractTransportVersionFuncTest.groovy +++ b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/transport/AbstractTransportVersionFuncTest.groovy @@ -116,4 +116,12 @@ class AbstractTransportVersionFuncTest extends AbstractGradleFuncTest { execute("git checkout -b main") execute("git checkout -b test") } + + void setupLocalGitRepo() { + execute("git init") + execute('git config user.email "build-tool@elastic.co"') + execute('git config user.name "Build tool"') + execute("git add .") + execute('git commit -m "Initial"') + } } diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/bugfix2/build.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major1/build.gradle similarity index 100% rename from build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/bugfix2/build.gradle rename to build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major1/build.gradle diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/bugfix3/build.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major2/build.gradle similarity index 100% rename from build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/bugfix3/build.gradle rename to build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major2/build.gradle diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/bugfix4/build.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major3/build.gradle similarity index 100% rename from build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/bugfix4/build.gradle rename to build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major3/build.gradle diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/bugfix5/build.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major4/build.gradle similarity index 100% rename from build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/bugfix5/build.gradle rename to build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major4/build.gradle diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/maintenance/build.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor1/build.gradle similarity index 100% rename from build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/maintenance/build.gradle rename to build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor1/build.gradle diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major/build.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor2/build.gradle similarity index 100% rename from build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major/build.gradle rename to build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor2/build.gradle diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor/build.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor3/build.gradle similarity index 100% rename from build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor/build.gradle rename to build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor3/build.gradle diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/staged/build.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor4/build.gradle similarity index 100% rename from build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/staged/build.gradle rename to build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor4/build.gradle diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/settings.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/settings.gradle index e931537fcd6e9..00691191e0fd5 100644 --- a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/settings.gradle +++ b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/settings.gradle @@ -9,12 +9,14 @@ rootProject.name = "root" -include ":distribution:bwc:bugfix" -include ":distribution:bwc:bugfix2" -include ":distribution:bwc:minor" -include ":distribution:bwc:major" -include ":distribution:bwc:staged" -include ":distribution:bwc:maintenance" +include ":distribution:bwc:major1" +include ":distribution:bwc:major2" +include ":distribution:bwc:major3" +include ":distribution:bwc:major4" +include ":distribution:bwc:minor1" +include ":distribution:bwc:minor2" +include ":distribution:bwc:minor3" +include ":distribution:bwc:minor4" include ":distribution:archives:darwin-tar" include ":distribution:archives:oss-darwin-tar" include ":distribution:archives:darwin-aarch64-tar" diff --git a/build-tools-internal/src/main/groovy/elasticsearch.fwc-test.gradle b/build-tools-internal/src/main/groovy/elasticsearch.fwc-test.gradle index 51301b405e514..e08118397450a 100644 --- a/build-tools-internal/src/main/groovy/elasticsearch.fwc-test.gradle +++ b/build-tools-internal/src/main/groovy/elasticsearch.fwc-test.gradle @@ -10,16 +10,19 @@ import org.elasticsearch.gradle.VersionProperties import org.elasticsearch.gradle.testclusters.StandaloneRestIntegTestTask -def fwcVersions = buildParams.bwcVersions.released.findAll { it.major == VersionProperties.elasticsearchVersion.major && it.minor == VersionProperties.elasticsearchVersion.minor } +def fwcVersions = buildParams.bwcVersions.released.findAll { it.major == VersionProperties.elasticsearchVersion.major && it.minor == VersionProperties.elasticsearchVersion.minor } def previousMinorSnapshot = buildParams.bwcVersions.unreleased.find { it.major == VersionProperties.elasticsearchVersion.major && it.minor == VersionProperties.elasticsearchVersion.minor - 1 } fwcVersions.each { fwcVersion -> - tasks.register("v${fwcVersion}#fwcTest", StandaloneRestIntegTestTask) { - usesBwcDistribution(previousMinorSnapshot) - usesBwcDistribution(fwcVersion) - systemProperty("tests.old_cluster_version", previousMinorSnapshot) - systemProperty("tests.new_cluster_version", fwcVersion) - nonInputProperties.systemProperty 'tests.fwc', 'true' + // There might be no previous minor snapshot if development of the previous minor version has been completed + if (previousMinorSnapshot != null) { + tasks.register("v${fwcVersion}#fwcTest", StandaloneRestIntegTestTask) { + usesBwcDistribution(previousMinorSnapshot) + usesBwcDistribution(fwcVersion) + systemProperty("tests.old_cluster_version", previousMinorSnapshot) + systemProperty("tests.new_cluster_version", fwcVersion) + nonInputProperties.systemProperty 'tests.fwc', 'true' + } } } diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/BwcVersions.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/BwcVersions.java index 9f7645349e852..6b7925e622ddb 100644 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/BwcVersions.java +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/BwcVersions.java @@ -9,7 +9,7 @@ package org.elasticsearch.gradle.internal; import org.elasticsearch.gradle.Version; -import org.elasticsearch.gradle.VersionProperties; +import org.elasticsearch.gradle.internal.info.DevelopmentBranch; import java.io.Serializable; import java.util.ArrayList; @@ -24,8 +24,7 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.stream.Collectors; import static java.util.Collections.reverseOrder; import static java.util.Collections.unmodifiableList; @@ -65,20 +64,13 @@ public class BwcVersions implements Serializable { - private static final Pattern LINE_PATTERN = Pattern.compile( - "\\W+public static final Version V_(\\d+)_(\\d+)_(\\d+)(_alpha\\d+|_beta\\d+|_rc\\d+)?.*\\);" - ); private static final String GLIBC_VERSION_ENV_VAR = "GLIBC_VERSION"; private final Version currentVersion; private final transient List versions; private final Map unreleased; - public BwcVersions(List versionLines, List developmentBranches) { - this(versionLines, Version.fromString(VersionProperties.getElasticsearch()), developmentBranches); - } - - public BwcVersions(Version currentVersionProperty, List allVersions, List developmentBranches) { + public BwcVersions(Version currentVersionProperty, List allVersions, List developmentBranches) { if (allVersions.isEmpty()) { throw new IllegalArgumentException("Could not parse any versions"); } @@ -87,21 +79,7 @@ public BwcVersions(Version currentVersionProperty, List allVersions, Li this.currentVersion = allVersions.get(allVersions.size() - 1); assertCurrentVersionMatchesParsed(currentVersionProperty); - this.unreleased = computeUnreleased(developmentBranches); - } - - // Visible for testing - BwcVersions(List versionLines, Version currentVersionProperty, List developmentBranches) { - this(currentVersionProperty, parseVersionLines(versionLines), developmentBranches); - } - - private static List parseVersionLines(List versionLines) { - return 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)))) - .sorted() - .toList(); + this.unreleased = filterOutIncompatibleVersions(computeUnreleased(developmentBranches)); } private void assertCurrentVersionMatchesParsed(Version currentVersionProperty) { @@ -116,6 +94,14 @@ private void assertCurrentVersionMatchesParsed(Version currentVersionProperty) { } } + private Map filterOutIncompatibleVersions(Map unreleasedVersions) { + return unreleasedVersions.entrySet().stream().filter(entry -> { + var version = entry.getKey(); + // Exclude version 8.19 which is not backward compatible with 9.0 + return (version.getMajor() == 8 && version.getMinor() == 19) == false; + }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + /** * Returns info about the unreleased version, or {@code null} if the version is released. */ @@ -127,78 +113,46 @@ public void forPreviousUnreleased(Consumer consumer) { getUnreleased().stream().filter(version -> version.equals(currentVersion) == false).map(unreleased::get).forEach(consumer); } - private String getBranchFor(Version version, List developmentBranches) { - // If the current version matches a specific feature freeze branch, use that - if (developmentBranches.contains(version.getMajor() + "." + version.getMinor())) { - return version.getMajor() + "." + version.getMinor(); - } else if (developmentBranches.contains(version.getMajor() + ".x")) { // Otherwise if an n.x branch exists and we are that major - return version.getMajor() + ".x"; - } else { // otherwise we're the main branch - return "main"; - } - } - - private Map computeUnreleased(List developmentBranches) { + private Map computeUnreleased(List developmentBranches) { Map result = new TreeMap<>(); + Map> bwcBranches = developmentBranches.stream() + .filter(developmentBranch -> developmentBranch.version().before(currentVersion)) + .sorted(reverseOrder(comparing(DevelopmentBranch::version))) + .collect(Collectors.groupingBy(branch -> { + if (branch.version().getMajor() == currentVersion.getMajor()) { + return "minor"; + } else if (branch.version().getMajor() == currentVersion.getMajor() - 1) { + return "major"; + } + return "older"; + })); + + developmentBranches.stream() + .filter(branch -> branch.version().equals(currentVersion)) + .findFirst() + .ifPresent( + developmentBranch -> result.put( + currentVersion, + new UnreleasedVersionInfo(currentVersion, developmentBranch.name(), ":distribution") + ) + ); - // The current version is always in development - String currentBranch = getBranchFor(currentVersion, developmentBranches); - result.put(currentVersion, new UnreleasedVersionInfo(currentVersion, currentBranch, ":distribution")); - - // Check for an n.x branch as well - if (currentBranch.equals("main") && developmentBranches.stream().anyMatch(s -> s.endsWith(".x"))) { - // This should correspond to the latest new minor - Version version = versions.stream() - .sorted(Comparator.reverseOrder()) - .filter(v -> v.getMajor() == (currentVersion.getMajor() - 1) && v.getRevision() == 0) - .findFirst() - .orElseThrow(() -> new IllegalStateException("Unable to determine development version for branch")); - String branch = getBranchFor(version, developmentBranches); - assert branch.equals(currentVersion.getMajor() - 1 + ".x") : "Expected branch does not match development branch"; - - result.put(version, new UnreleasedVersionInfo(version, branch, ":distribution:bwc:minor")); + List previousMinorBranches = bwcBranches.getOrDefault("minor", Collections.emptyList()); + for (int i = 0; i < previousMinorBranches.size(); i++) { + DevelopmentBranch previousMinorBranch = previousMinorBranches.get(i); + result.put( + previousMinorBranch.version(), + new UnreleasedVersionInfo(previousMinorBranch.version(), previousMinorBranch.name(), ":distribution:bwc:minor" + (i + 1)) + ); } - // Now handle all the feature freeze branches - List featureFreezeBranches = developmentBranches.stream() - .filter(b -> Pattern.matches("[0-9]+\\.[0-9]+", b)) - .sorted(reverseOrder(comparing(s -> Version.fromString(s, Version.Mode.RELAXED)))) - .toList(); - - boolean existingBugfix = false; - for (int i = 0; i < featureFreezeBranches.size(); i++) { - String branch = featureFreezeBranches.get(i); - Version version = versions.stream() - .sorted(Comparator.reverseOrder()) - .filter(v -> v.toString().startsWith(branch)) - .findFirst() - .orElse(null); - - // If we don't know about this version we can ignore it - if (version == null) { - continue; - } - - // If this is the current version we can ignore as we've already handled it - if (version.equals(currentVersion)) { - continue; - } - - // We only maintain compatibility back one major so ignore anything older - if (currentVersion.getMajor() - version.getMajor() > 1) { - continue; - } - - // This is the maintenance version - if (i == featureFreezeBranches.size() - 1) { - result.put(version, new UnreleasedVersionInfo(version, branch, ":distribution:bwc:maintenance")); - } else if (version.getRevision() == 0) { // This is the next staged minor - result.put(version, new UnreleasedVersionInfo(version, branch, ":distribution:bwc:staged")); - } else { // This is a bugfix - String project = existingBugfix ? "bugfix2" : "bugfix"; - result.put(version, new UnreleasedVersionInfo(version, branch, ":distribution:bwc:" + project)); - existingBugfix = true; - } + List previousMajorBranches = bwcBranches.getOrDefault("major", Collections.emptyList()); + for (int i = 0; i < previousMajorBranches.size(); i++) { + DevelopmentBranch previousMajorBranch = previousMajorBranches.get(i); + result.put( + previousMajorBranch.version(), + new UnreleasedVersionInfo(previousMajorBranch.version(), previousMajorBranch.name(), ":distribution:bwc:major" + (i + 1)) + ); } return Collections.unmodifiableMap(result); @@ -208,19 +162,6 @@ public List getUnreleased() { return unreleased.keySet().stream().sorted().toList(); } - private void addUnreleased(Set unreleased, Version current, int index) { - if (current.getRevision() == 0) { - // If the current version is a new minor, the next version is also unreleased - Version next = versions.get(versions.size() - (index + 2)); - unreleased.add(next); - - // Keep looking through versions until we find the end of unreleased versions - addUnreleased(unreleased, next, index + 1); - } else { - unreleased.add(current); - } - } - public void compareToAuthoritative(List authoritativeReleasedVersions) { Set notReallyReleased = new HashSet<>(getReleased()); notReallyReleased.removeAll(authoritativeReleasedVersions); diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/BranchesFileParser.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/BranchesFileParser.java new file mode 100644 index 0000000000000..1c502a633e1ec --- /dev/null +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/BranchesFileParser.java @@ -0,0 +1,46 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +package org.elasticsearch.gradle.internal.info; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.elasticsearch.gradle.Version; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.ArrayList; +import java.util.List; + +/** + * A parser for the branches.json file + */ +public class BranchesFileParser { + + private final ObjectMapper objectMapper; + + public BranchesFileParser(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + public List parse(byte[] bytes) { + List branches = new ArrayList<>(); + try { + JsonNode json = objectMapper.readTree(bytes); + for (JsonNode node : json.get("branches")) { + branches.add(new DevelopmentBranch(node.get("branch").asText(), Version.fromString(node.get("version").asText()))); + } + } catch (IOException e) { + throw new UncheckedIOException("Failed to parse content of branches.json", e); + } + + return branches; + } +} diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/DevelopmentBranch.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/DevelopmentBranch.java new file mode 100644 index 0000000000000..964db5eabd4b7 --- /dev/null +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/DevelopmentBranch.java @@ -0,0 +1,28 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +package org.elasticsearch.gradle.internal.info; + +import org.elasticsearch.gradle.Version; + +import java.io.Serializable; +import java.util.Objects; + +/** + * Information about a development branch used in branches.json file + * + * @param name Name of the development branch + * @param version Elasticsearch version on the development branch + */ +public record DevelopmentBranch(String name, Version version) implements Serializable { + public DevelopmentBranch { + Objects.requireNonNull(name); + Objects.requireNonNull(version); + } +} diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPlugin.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPlugin.java index 2c0c5ebb67248..801a3897e94a9 100644 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPlugin.java +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPlugin.java @@ -8,17 +8,17 @@ */ package org.elasticsearch.gradle.internal.info; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.io.IOUtils; import org.elasticsearch.gradle.Architecture; import org.elasticsearch.gradle.OS; -import org.elasticsearch.gradle.VersionProperties; +import org.elasticsearch.gradle.Version; import org.elasticsearch.gradle.internal.BwcVersions; import org.elasticsearch.gradle.internal.Jdk; import org.elasticsearch.gradle.internal.JdkDownloadPlugin; import org.elasticsearch.gradle.internal.conventions.GitInfoPlugin; +import org.elasticsearch.gradle.internal.conventions.VersionPropertiesPlugin; import org.elasticsearch.gradle.internal.conventions.info.GitInfo; import org.elasticsearch.gradle.internal.conventions.info.ParallelDetector; import org.elasticsearch.gradle.internal.conventions.util.Util; @@ -56,29 +56,38 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.UncheckedIOException; +import java.net.URI; import java.nio.file.Files; -import java.util.ArrayList; import java.util.List; import java.util.Locale; +import java.util.Properties; import java.util.Random; import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.inject.Inject; import static org.elasticsearch.gradle.internal.conventions.GUtils.elvis; +import static org.elasticsearch.gradle.internal.conventions.VersionPropertiesPlugin.VERSIONS_EXT; import static org.elasticsearch.gradle.internal.toolchain.EarlyAccessCatalogJdkToolchainResolver.findLatestEABuildNumber; public class GlobalBuildInfoPlugin implements Plugin { private static final Logger LOGGER = Logging.getLogger(GlobalBuildInfoPlugin.class); private static final String DEFAULT_VERSION_JAVA_FILE_PATH = "server/src/main/java/org/elasticsearch/Version.java"; + private static final String DEFAULT_BRANCHES_FILE_URL = "https://raw.githubusercontent.com/elastic/elasticsearch/main/branches.json"; + private static final String BRANCHES_FILE_LOCATION_PROPERTY = "org.elasticsearch.build.branches-file-location"; + private static final Pattern LINE_PATTERN = Pattern.compile( + "\\W+public static final Version V_(\\d+)_(\\d+)_(\\d+)(_alpha\\d+|_beta\\d+|_rc\\d+)?.*\\);" + ); private ObjectFactory objectFactory; private final JavaInstallationRegistry javaInstallationRegistry; private final JvmMetadataDetector metadataDetector; private final ProviderFactory providers; - private final ObjectMapper objectMapper; + private final BranchesFileParser branchesFileParser; private JavaToolchainService toolChainService; private Project project; @@ -93,7 +102,7 @@ public GlobalBuildInfoPlugin( this.javaInstallationRegistry = javaInstallationRegistry; this.metadataDetector = new ErrorTraceMetadataDetector(metadataDetector); this.providers = providers; - this.objectMapper = new ObjectMapper(); + this.branchesFileParser = new BranchesFileParser(new ObjectMapper()); } @Override @@ -113,21 +122,27 @@ public void apply(Project project) { throw new GradleException("Gradle " + minimumGradleVersion.getVersion() + "+ is required"); } - JavaVersion minimumCompilerVersion = JavaVersion.toVersion(getResourceContents("/minimumCompilerVersion")); - JavaVersion minimumRuntimeVersion = JavaVersion.toVersion(getResourceContents("/minimumRuntimeVersion")); + project.getPlugins().apply(VersionPropertiesPlugin.class); + Properties versionProperties = (Properties) project.getExtensions().getByName(VERSIONS_EXT); + JavaVersion minimumCompilerVersion = JavaVersion.toVersion(versionProperties.get("minimumCompilerJava")); + JavaVersion minimumRuntimeVersion = JavaVersion.toVersion(versionProperties.get("minimumRuntimeJava")); + + String bundledJdkVersion = versionProperties.getProperty("bundled_jdk"); + String bundledJdkMajorVersion = bundledJdkVersion.split("[.+]")[0]; + Version elasticsearchVersionProperty = Version.fromString(versionProperties.getProperty("elasticsearch")); Provider explicitRuntimeJavaHome = findRuntimeJavaHome(); boolean isRuntimeJavaHomeExplicitlySet = explicitRuntimeJavaHome.isPresent(); Provider actualRuntimeJavaHome = isRuntimeJavaHomeExplicitlySet ? explicitRuntimeJavaHome - : resolveJavaHomeFromToolChainService(VersionProperties.getBundledJdkMajorVersion()); + : resolveJavaHomeFromToolChainService(bundledJdkMajorVersion); Provider runtimeJdkMetaData = actualRuntimeJavaHome.map( runtimeJavaHome -> metadataDetector.getMetadata(getJavaInstallation(runtimeJavaHome)) ); AtomicReference cache = new AtomicReference<>(); Provider bwcVersionsProvider = providers.provider( - () -> cache.updateAndGet(val -> val == null ? resolveBwcVersions() : val) + () -> cache.updateAndGet(val -> val == null ? resolveBwcVersions(elasticsearchVersionProperty) : val) ); BuildParameterExtension buildParams = project.getExtensions() .create( @@ -141,9 +156,7 @@ public void apply(Project project) { javaHome -> determineJavaVersion( "runtime java.home", javaHome, - isRuntimeJavaHomeExplicitlySet - ? minimumRuntimeVersion - : JavaVersion.toVersion(VersionProperties.getBundledJdkMajorVersion()) + isRuntimeJavaHomeExplicitlySet ? minimumRuntimeVersion : JavaVersion.toVersion(bundledJdkMajorVersion) ) ), isRuntimeJavaHomeExplicitlySet, @@ -197,32 +210,49 @@ private String formatJavaVendorDetails(JvmInstallationMetadata runtimeJdkMetaDat /* Introspect all versions of ES that may be tested against for backwards * compatibility. It is *super* important that this logic is the same as the * logic in VersionUtils.java. */ - private BwcVersions resolveBwcVersions() { + private BwcVersions resolveBwcVersions(Version currentElasticsearchVersion) { String versionsFilePath = elvis( System.getProperty("BWC_VERSION_SOURCE"), new File(Util.locateElasticsearchWorkspace(project.getGradle()), DEFAULT_VERSION_JAVA_FILE_PATH).getPath() ); try (var is = new FileInputStream(versionsFilePath)) { List versionLines = IOUtils.readLines(is, "UTF-8"); - return new BwcVersions(versionLines, getDevelopmentBranches()); + return new BwcVersions(currentElasticsearchVersion, parseVersionLines(versionLines), getDevelopmentBranches()); } catch (IOException e) { throw new IllegalStateException("Unable to resolve to resolve bwc versions from versionsFile.", e); } } - private List getDevelopmentBranches() { - List branches = new ArrayList<>(); - File branchesFile = new File(Util.locateElasticsearchWorkspace(project.getGradle()), "branches.json"); - try (InputStream is = new FileInputStream(branchesFile)) { - JsonNode json = objectMapper.readTree(is); - for (JsonNode node : json.get("branches")) { - branches.add(node.get("branch").asText()); + private List parseVersionLines(List versionLines) { + return 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)))) + .sorted() + .toList(); + } + + private List getDevelopmentBranches() { + String branchesFileLocation = project.getProviders() + .gradleProperty(BRANCHES_FILE_LOCATION_PROPERTY) + .getOrElse(DEFAULT_BRANCHES_FILE_URL); + LOGGER.info("Reading branches.json from {}", branchesFileLocation); + byte[] branchesBytes; + if (branchesFileLocation.startsWith("http")) { + try (InputStream in = URI.create(branchesFileLocation).toURL().openStream()) { + branchesBytes = in.readAllBytes(); + } catch (IOException e) { + throw new UncheckedIOException("Failed to download branches.json from: " + branchesFileLocation, e); + } + } else { + try { + branchesBytes = Files.readAllBytes(new File(branchesFileLocation).toPath()); + } catch (IOException e) { + throw new UncheckedIOException("Failed to read branches.json from: " + branchesFileLocation, e); } - } catch (IOException e) { - throw new UncheckedIOException(e); } - return branches; + return branchesFileParser.parse(branchesBytes); } private void logGlobalBuildInfo(BuildParameterExtension buildParams) { diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/ReleaseToolsPlugin.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/ReleaseToolsPlugin.java index 22af46a4e5ccd..ed7a0ba65bf72 100644 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/ReleaseToolsPlugin.java +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/ReleaseToolsPlugin.java @@ -54,6 +54,7 @@ public void apply(Project project) { project.getTasks().register("extractCurrentVersions", ExtractCurrentVersionsTask.class); project.getTasks().register("tagVersions", TagVersionsTask.class); project.getTasks().register("setCompatibleVersions", SetCompatibleVersionsTask.class, t -> t.setThisVersion(version)); + project.getTasks().register("updateBranchesJson", UpdateBranchesJsonTask.class); final FileTree yamlFiles = projectDirectory.dir("docs/changelog") .getAsFileTree() diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTask.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTask.java new file mode 100644 index 0000000000000..584bfc07693e2 --- /dev/null +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTask.java @@ -0,0 +1,176 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +package org.elasticsearch.gradle.internal.release; + +import com.fasterxml.jackson.core.util.DefaultIndenter; +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import org.elasticsearch.gradle.Version; +import org.elasticsearch.gradle.internal.info.BranchesFileParser; +import org.elasticsearch.gradle.internal.info.DevelopmentBranch; +import org.gradle.api.DefaultTask; +import org.gradle.api.InvalidUserDataException; +import org.gradle.api.file.ProjectLayout; +import org.gradle.api.logging.Logger; +import org.gradle.api.logging.Logging; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.TaskAction; +import org.gradle.api.tasks.options.Option; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.util.Comparator; +import java.util.List; + +import javax.inject.Inject; + +/** + * Updates the branches.json file in the root of the repository + */ +public class UpdateBranchesJsonTask extends DefaultTask { + + private static final Logger LOGGER = Logging.getLogger(UpdateBranchesJsonTask.class); + + private final ObjectMapper objectMapper; + private final BranchesFileParser branchesFileParser; + + @OutputFile + private File branchesFile; + + @Input + @Optional + private DevelopmentBranch addBranch; + @Input + @Optional + private String removeBranch; + @Input + @Optional + private DevelopmentBranch updateBranch; + + @Inject + public UpdateBranchesJsonTask(ProjectLayout projectLayout) { + this.objectMapper = new ObjectMapper(); + this.branchesFileParser = new BranchesFileParser(objectMapper); + this.branchesFile = projectLayout.getSettingsDirectory().file("branches.json").getAsFile(); + } + + public File getBranchesFile() { + return branchesFile; + } + + public void setBranchesFile(File branchesFile) { + this.branchesFile = branchesFile; + } + + public DevelopmentBranch getAddBranch() { + return addBranch; + } + + public String getRemoveBranch() { + return removeBranch; + } + + public DevelopmentBranch getUpdateBranch() { + return updateBranch; + } + + @Option(option = "add-branch", description = "Specifies the branch and corresponding version to add in format :") + public void addBranch(String branchAndVersion) { + this.addBranch = toDevelopmentBranch(branchAndVersion); + } + + @Option(option = "remove-branch", description = "Specifies the branch to remove") + public void removeBranch(String branch) { + this.removeBranch = branch; + } + + @Option(option = "update-branch", description = "Specifies the branch and corresponding version to update in format :") + public void updateBranch(String branchAndVersion) { + this.updateBranch = toDevelopmentBranch(branchAndVersion); + } + + private DevelopmentBranch toDevelopmentBranch(String branchAndVersion) { + String[] parts = branchAndVersion.split(":"); + if (parts.length != 2) { + throw new InvalidUserDataException("Expected branch and version in format :"); + } + return new DevelopmentBranch(parts[0], Version.fromString(parts[1])); + } + + @TaskAction + public void executeTask() throws IOException { + List developmentBranches = readBranches(branchesFile); + + if (addBranch == null && removeBranch == null && updateBranch == null) { + throw new InvalidUserDataException("At least one of add-branch, remove-branch or update-branch must be specified"); + } + + if (addBranch != null) { + LOGGER.info("Adding branch {} with version {}", addBranch.name(), addBranch.version()); + if (developmentBranches.stream().anyMatch(developmentBranch -> developmentBranch.name().equals(addBranch.name()))) { + throw new InvalidUserDataException("Branch " + addBranch.name() + " already exists"); + } + developmentBranches.add(addBranch); + } + if (removeBranch != null) { + LOGGER.info("Removing branch {}", removeBranch); + if (developmentBranches.stream().noneMatch(developmentBranch -> developmentBranch.name().equals(removeBranch))) { + throw new InvalidUserDataException("Branch " + removeBranch + " does not exist"); + } + developmentBranches.removeIf(developmentBranch -> developmentBranch.name().equals(removeBranch)); + } + if (updateBranch != null) { + LOGGER.info("Updating branch {} with version {}", updateBranch.name(), updateBranch.version()); + if (developmentBranches.stream().noneMatch(developmentBranch -> developmentBranch.name().equals(updateBranch.name()))) { + throw new InvalidUserDataException("Branch " + updateBranch.name() + " does not exist"); + } + developmentBranches.removeIf(developmentBranch -> developmentBranch.name().equals(updateBranch.name())); + developmentBranches.add(updateBranch); + } + + developmentBranches.sort(Comparator.comparing(DevelopmentBranch::version).reversed()); + + JsonNode jsonNode = objectMapper.readTree(new FileInputStream(branchesFile)); + ArrayNode updatedBranches = objectMapper.createArrayNode(); + for (DevelopmentBranch branch : developmentBranches) { + ObjectNode objectNode = objectMapper.createObjectNode(); + objectNode.put("branch", branch.name()); + objectNode.put("version", branch.version().toString()); + updatedBranches.add(objectNode); + } + ((ObjectNode) jsonNode).replace("branches", updatedBranches); + + DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter(); + prettyPrinter.indentArraysWith(new DefaultIndenter(" ", DefaultIndenter.SYS_LF)); + objectMapper.writer(prettyPrinter).writeValue(branchesFile, jsonNode); + } + + private List readBranches(File branchesFile) { + if (branchesFile.isFile() == false) { + throw new InvalidUserDataException("File branches.json has not been found in " + branchesFile.getAbsolutePath()); + } + + try { + byte[] branchesBytes = Files.readAllBytes(branchesFile.toPath()); + return branchesFileParser.parse(branchesBytes); + } catch (IOException e) { + throw new UncheckedIOException("Failed to read branches.json from " + branchesFile.getPath(), e); + } + } +} diff --git a/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/BwcVersionsSpec.groovy b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/BwcVersionsSpec.groovy index 4d033564a42b4..0b7d71b49176b 100644 --- a/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/BwcVersionsSpec.groovy +++ b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/BwcVersionsSpec.groovy @@ -13,33 +13,40 @@ import spock.lang.Specification import org.elasticsearch.gradle.Version import org.elasticsearch.gradle.internal.BwcVersions.UnreleasedVersionInfo +import org.elasticsearch.gradle.internal.info.DevelopmentBranch class BwcVersionsSpec extends Specification { - List versionLines = [] + List versions = [] def "current version is next major"() { given: - addVersion('7.17.10', '8.9.0') - addVersion('8.14.0', '9.9.0') - addVersion('8.14.1', '9.9.0') - addVersion('8.14.2', '9.9.0') - addVersion('8.15.0', '9.9.0') - addVersion('8.15.1', '9.9.0') - addVersion('8.15.2', '9.9.0') - addVersion('8.16.0', '9.10.0') - addVersion('8.16.1', '9.10.0') - addVersion('8.17.0', '9.10.0') - addVersion('9.0.0', '10.0.0') + addVersion('7.17.10') + addVersion('8.14.0') + addVersion('8.14.1') + addVersion('8.14.2') + addVersion('8.15.0') + addVersion('8.15.1') + addVersion('8.15.2') + addVersion('8.16.0') + addVersion('8.16.1') + addVersion('8.17.0') + addVersion('9.0.0') when: - def bwc = new BwcVersions(versionLines, v('9.0.0'), ['main', '8.x', '8.16', '8.15', '7.17']) + def bwc = new BwcVersions(v('9.0.0'), versions, [ + branch('main', '9.0.0'), + branch('8.x', '8.17.0'), + branch('8.16', '8.16.1'), + branch('8.15', '8.15.2'), + branch('7.17', '7.17.10') + ]) def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] } then: unreleased == [ - (v('8.15.2')): new UnreleasedVersionInfo(v('8.15.2'), '8.15', ':distribution:bwc:bugfix2'), - (v('8.16.1')): new UnreleasedVersionInfo(v('8.16.1'), '8.16', ':distribution:bwc:bugfix'), - (v('8.17.0')): new UnreleasedVersionInfo(v('8.17.0'), '8.x', ':distribution:bwc:minor'), + (v('8.15.2')): new UnreleasedVersionInfo(v('8.15.2'), '8.15', ':distribution:bwc:major3'), + (v('8.16.1')): new UnreleasedVersionInfo(v('8.16.1'), '8.16', ':distribution:bwc:major2'), + (v('8.17.0')): new UnreleasedVersionInfo(v('8.17.0'), '8.x', ':distribution:bwc:major1'), (v('9.0.0')): new UnreleasedVersionInfo(v('9.0.0'), 'main', ':distribution'), ] bwc.wireCompatible == [v('8.17.0'), v('9.0.0')] @@ -48,53 +55,104 @@ class BwcVersionsSpec extends Specification { def "current version is next major with staged minor"() { given: - addVersion('7.17.10', '8.9.0') - addVersion('8.14.0', '9.9.0') - addVersion('8.14.1', '9.9.0') - addVersion('8.14.2', '9.9.0') - addVersion('8.15.0', '9.9.0') - addVersion('8.15.1', '9.9.0') - addVersion('8.15.2', '9.9.0') - addVersion('8.16.0', '9.10.0') - addVersion('8.16.1', '9.10.0') - addVersion('8.17.0', '9.10.0') - addVersion('8.18.0', '9.10.0') - addVersion('9.0.0', '10.0.0') + addVersion('7.17.10') + addVersion('8.14.0') + addVersion('8.14.1') + addVersion('8.14.2') + addVersion('8.15.0') + addVersion('8.15.1') + addVersion('8.15.2') + addVersion('8.16.0') + addVersion('8.16.1') + addVersion('8.17.0') + addVersion('8.18.0') + addVersion('9.0.0') when: - def bwc = new BwcVersions(versionLines, v('9.0.0'), ['main', '8.x', '8.17', '8.16', '8.15', '7.17']) + def bwc = new BwcVersions(v('9.0.0'), versions, [ + branch('main', '9.0.0'), + branch('8.x', '8.18.0'), + branch('8.17', '8.17.0'), + branch('8.16', '8.16.1'), + branch('8.15', '8.15.2'), + branch('7.17', '7.17.10'), + ]) def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] } then: unreleased == [ - (v('8.15.2')): new UnreleasedVersionInfo(v('8.15.2'), '8.15', ':distribution:bwc:bugfix2'), - (v('8.16.1')): new UnreleasedVersionInfo(v('8.16.1'), '8.16', ':distribution:bwc:bugfix'), - (v('8.17.0')): new UnreleasedVersionInfo(v('8.17.0'), '8.17', ':distribution:bwc:staged'), - (v('8.18.0')): new UnreleasedVersionInfo(v('8.18.0'), '8.x', ':distribution:bwc:minor'), + (v('8.15.2')): new UnreleasedVersionInfo(v('8.15.2'), '8.15', ':distribution:bwc:major4'), + (v('8.16.1')): new UnreleasedVersionInfo(v('8.16.1'), '8.16', ':distribution:bwc:major3'), + (v('8.17.0')): new UnreleasedVersionInfo(v('8.17.0'), '8.17', ':distribution:bwc:major2'), + (v('8.18.0')): new UnreleasedVersionInfo(v('8.18.0'), '8.x', ':distribution:bwc:major1'), (v('9.0.0')): new UnreleasedVersionInfo(v('9.0.0'), 'main', ':distribution'), ] bwc.wireCompatible == [v('8.18.0'), v('9.0.0')] bwc.indexCompatible == [v('8.14.0'), v('8.14.1'), v('8.14.2'), v('8.15.0'), v('8.15.1'), v('8.15.2'), v('8.16.0'), v('8.16.1'), v('8.17.0'), v('8.18.0'), v('9.0.0')] } + def "current version is next major with two staged minors"() { + given: + addVersion('7.17.10') + addVersion('8.15.0') + addVersion('8.15.1') + addVersion('8.15.2') + addVersion('8.16.0') + addVersion('8.16.1') + addVersion('8.16.2') + addVersion('8.17.0') + addVersion('8.17.1') + addVersion('8.18.0') + addVersion('8.19.0') + addVersion('9.0.0') + addVersion('9.1.0') + + when: + def bwc = new BwcVersions(v('9.1.0'), versions, [ + branch('main', '9.1.0'), + branch('9.0', '9.0.0'), + branch('8.x', '8.19.0'), // 8.19 is incompatible with 9.0 so it is not listed in unreleased for BwC + branch('8.18', '8.18.0'), + branch('8.17', '8.17.1'), + branch('8.16', '8.16.2'), + branch('7.17', '7.17.10') + ]) + def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] } + + then: + unreleased == [ + (v('8.16.2')): new UnreleasedVersionInfo(v('8.16.2'), '8.16', ':distribution:bwc:major4'), + (v('8.17.1')): new UnreleasedVersionInfo(v('8.17.1'), '8.17', ':distribution:bwc:major3'), + (v('8.18.0')): new UnreleasedVersionInfo(v('8.18.0'), '8.18', ':distribution:bwc:major2'), + (v('9.0.0')): new UnreleasedVersionInfo(v('9.0.0'), '9.0', ':distribution:bwc:minor1'), + (v('9.1.0')): new UnreleasedVersionInfo(v('9.1.0'), 'main', ':distribution'), + ] + bwc.wireCompatible == [v('8.19.0'), v('9.0.0'), v('9.1.0')] + bwc.indexCompatible == [v('8.15.0'), v('8.15.1'), v('8.15.2'), v('8.16.0'), v('8.16.1'), v('8.16.2'), v('8.17.0'), v('8.17.1'), v('8.18.0'), v('8.19.0'), v('9.0.0'), v('9.1.0')] + } + def "current version is first new minor in major series"() { given: - addVersion('7.17.10', '8.9.0') - addVersion('8.16.0', '9.10.0') - addVersion('8.16.1', '9.10.0') - addVersion('8.17.0', '9.10.0') - addVersion('8.18.0', '9.10.0') - addVersion('9.0.0', '10.0.0') - addVersion('9.1.0', '10.0.0') + addVersion('7.17.10') + addVersion('8.16.0') + addVersion('8.16.1') + addVersion('8.17.0') + addVersion('8.18.0') + addVersion('9.0.0') + addVersion('9.1.0') when: - def bwc = new BwcVersions(versionLines, v('9.1.0'), ['main', '9.0', '8.18']) + def bwc = new BwcVersions(v('9.1.0'), versions, [ + branch('main','9.1.0'), + branch('9.0', '9.0.0'), + branch('8.18', '8.18.0') + ]) def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] } then: unreleased == [ - (v('8.18.0')): new UnreleasedVersionInfo(v('8.18.0'), '8.18', ':distribution:bwc:maintenance'), - (v('9.0.0')): new UnreleasedVersionInfo(v('9.0.0'), '9.0', ':distribution:bwc:staged'), + (v('8.18.0')): new UnreleasedVersionInfo(v('8.18.0'), '8.18', ':distribution:bwc:major1'), + (v('9.0.0')): new UnreleasedVersionInfo(v('9.0.0'), '9.0', ':distribution:bwc:minor1'), (v('9.1.0')): new UnreleasedVersionInfo(v('9.1.0'), 'main', ':distribution'), ] bwc.wireCompatible == [v('8.18.0'), v('9.0.0'), v('9.1.0')] @@ -103,23 +161,27 @@ class BwcVersionsSpec extends Specification { def "current version is new minor with single bugfix"() { given: - addVersion('7.17.10', '8.9.0') - addVersion('8.16.0', '9.10.0') - addVersion('8.16.1', '9.10.0') - addVersion('8.17.0', '9.10.0') - addVersion('8.18.0', '9.10.0') - addVersion('9.0.0', '10.0.0') - addVersion('9.0.1', '10.0.0') - addVersion('9.1.0', '10.0.0') + addVersion('7.17.10') + addVersion('8.16.0') + addVersion('8.16.1') + addVersion('8.17.0') + addVersion('8.18.0') + addVersion('9.0.0') + addVersion('9.0.1') + addVersion('9.1.0') when: - def bwc = new BwcVersions(versionLines, v('9.1.0'), ['main', '9.0', '8.18']) + def bwc = new BwcVersions(v('9.1.0'), versions, [ + branch('main','9.1.0'), + branch('9.0','9.0.1'), + branch('8.18','8.18.0') + ]) def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] } then: unreleased == [ - (v('8.18.0')): new UnreleasedVersionInfo(v('8.18.0'), '8.18', ':distribution:bwc:maintenance'), - (v('9.0.1')): new UnreleasedVersionInfo(v('9.0.1'), '9.0', ':distribution:bwc:bugfix'), + (v('8.18.0')): new UnreleasedVersionInfo(v('8.18.0'), '8.18', ':distribution:bwc:major1'), + (v('9.0.1')): new UnreleasedVersionInfo(v('9.0.1'), '9.0', ':distribution:bwc:minor1'), (v('9.1.0')): new UnreleasedVersionInfo(v('9.1.0'), 'main', ':distribution'), ] bwc.wireCompatible == [v('8.18.0'), v('9.0.0'), v('9.0.1'), v('9.1.0')] @@ -128,25 +190,30 @@ class BwcVersionsSpec extends Specification { def "current version is new minor with single bugfix and staged minor"() { given: - addVersion('7.17.10', '8.9.0') - addVersion('8.16.0', '9.10.0') - addVersion('8.16.1', '9.10.0') - addVersion('8.17.0', '9.10.0') - addVersion('8.18.0', '9.10.0') - addVersion('9.0.0', '10.0.0') - addVersion('9.0.1', '10.0.0') - addVersion('9.1.0', '10.0.0') - addVersion('9.2.0', '10.0.0') + addVersion('7.17.10') + addVersion('8.16.0') + addVersion('8.16.1') + addVersion('8.17.0') + addVersion('8.18.0') + addVersion('9.0.0') + addVersion('9.0.1') + addVersion('9.1.0') + addVersion('9.2.0') when: - def bwc = new BwcVersions(versionLines, v('9.2.0'), ['main', '9.1', '9.0', '8.18']) + def bwc = new BwcVersions(v('9.2.0'), versions, [ + branch('main','9.2.0'), + branch('9.1','9.1.0'), + branch('9.0','9.0.1'), + branch('8.18', '8.18.0') + ]) def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] } then: unreleased == [ - (v('8.18.0')): new UnreleasedVersionInfo(v('8.18.0'), '8.18', ':distribution:bwc:maintenance'), - (v('9.0.1')): new UnreleasedVersionInfo(v('9.0.1'), '9.0', ':distribution:bwc:bugfix'), - (v('9.1.0')): new UnreleasedVersionInfo(v('9.1.0'), '9.1', ':distribution:bwc:staged'), + (v('8.18.0')): new UnreleasedVersionInfo(v('8.18.0'), '8.18', ':distribution:bwc:major1'), + (v('9.0.1')): new UnreleasedVersionInfo(v('9.0.1'), '9.0', ':distribution:bwc:minor2'), + (v('9.1.0')): new UnreleasedVersionInfo(v('9.1.0'), '9.1', ':distribution:bwc:minor1'), (v('9.2.0')): new UnreleasedVersionInfo(v('9.2.0'), 'main', ':distribution'), ] bwc.wireCompatible == [v('8.18.0'), v('9.0.0'), v('9.0.1'), v('9.1.0'), v('9.2.0')] @@ -155,30 +222,37 @@ class BwcVersionsSpec extends Specification { def "current version is next minor"() { given: - addVersion('7.16.3', '8.9.0') - addVersion('7.17.0', '8.9.0') - addVersion('7.17.1', '8.9.0') - addVersion('8.14.0', '9.9.0') - addVersion('8.14.1', '9.9.0') - addVersion('8.14.2', '9.9.0') - addVersion('8.15.0', '9.9.0') - addVersion('8.15.1', '9.9.0') - addVersion('8.15.2', '9.9.0') - addVersion('8.16.0', '9.10.0') - addVersion('8.16.1', '9.10.0') - addVersion('8.17.0', '9.10.0') - addVersion('8.17.1', '9.10.0') - addVersion('8.18.0', '9.10.0') + addVersion('7.16.3') + addVersion('7.17.0') + addVersion('7.17.1') + addVersion('8.14.0') + addVersion('8.14.1') + addVersion('8.14.2') + addVersion('8.15.0') + addVersion('8.15.1') + addVersion('8.15.2') + addVersion('8.16.0') + addVersion('8.16.1') + addVersion('8.17.0') + addVersion('8.17.1') + addVersion('8.18.0') when: - def bwc = new BwcVersions(versionLines, v('8.18.0'), ['main', '8.x', '8.17', '8.16', '7.17']) + def bwc = new BwcVersions(v('8.18.0'), versions, [ + branch('main', '9.1.0'), + branch('9.0', '9.0.1'), + branch('8.x', '8.18.0'), + branch('8.17', '8.17.1'), + branch('8.16', '8.16.1'), + branch('7.17', '7.17.1'), + ]) def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] } then: unreleased == [ - (v('7.17.1')): new UnreleasedVersionInfo(v('7.17.1'), '7.17', ':distribution:bwc:maintenance'), - (v('8.16.1')): new UnreleasedVersionInfo(v('8.16.1'), '8.16', ':distribution:bwc:bugfix2'), - (v('8.17.1')): new UnreleasedVersionInfo(v('8.17.1'), '8.17', ':distribution:bwc:bugfix'), + (v('7.17.1')): new UnreleasedVersionInfo(v('7.17.1'), '7.17', ':distribution:bwc:major1'), + (v('8.16.1')): new UnreleasedVersionInfo(v('8.16.1'), '8.16', ':distribution:bwc:minor2'), + (v('8.17.1')): new UnreleasedVersionInfo(v('8.17.1'), '8.17', ':distribution:bwc:minor1'), (v('8.18.0')): new UnreleasedVersionInfo(v('8.18.0'), '8.x', ':distribution'), ] bwc.wireCompatible == [v('7.17.0'), v('7.17.1'), v('8.14.0'), v('8.14.1'), v('8.14.2'), v('8.15.0'), v('8.15.1'), v('8.15.2'), v('8.16.0'), v('8.16.1'), v('8.17.0'), v('8.17.1'), v('8.18.0')] @@ -187,30 +261,37 @@ class BwcVersionsSpec extends Specification { def "current version is new minor with staged minor"() { given: - addVersion('7.16.3', '8.9.0') - addVersion('7.17.0', '8.9.0') - addVersion('7.17.1', '8.9.0') - addVersion('8.14.0', '9.9.0') - addVersion('8.14.1', '9.9.0') - addVersion('8.14.2', '9.9.0') - addVersion('8.15.0', '9.9.0') - addVersion('8.15.1', '9.9.0') - addVersion('8.15.2', '9.9.0') - addVersion('8.16.0', '9.10.0') - addVersion('8.16.1', '9.10.0') - addVersion('8.17.0', '9.10.0') - addVersion('8.18.0', '9.10.0') + addVersion('7.16.3') + addVersion('7.17.0') + addVersion('7.17.1') + addVersion('8.14.0') + addVersion('8.14.1') + addVersion('8.14.2') + addVersion('8.15.0') + addVersion('8.15.1') + addVersion('8.15.2') + addVersion('8.16.0') + addVersion('8.16.1') + addVersion('8.17.0') + addVersion('8.18.0') when: - def bwc = new BwcVersions(versionLines, v('8.18.0'), ['main', '8.x', '8.17', '8.16', '8.15', '7.17']) + def bwc = new BwcVersions(v('8.18.0'), versions, [ + branch('main', '9.0.0'), + branch('8.x', '8.18.0'), + branch('8.17', '8.17.0'), + branch('8.16', '8.16.1'), + branch('8.15', '8.15.2'), + branch('7.17', '7.17.1'), + ]) def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] } then: unreleased == [ - (v('7.17.1')): new UnreleasedVersionInfo(v('7.17.1'), '7.17', ':distribution:bwc:maintenance'), - (v('8.15.2')): new UnreleasedVersionInfo(v('8.15.2'), '8.15', ':distribution:bwc:bugfix2'), - (v('8.16.1')): new UnreleasedVersionInfo(v('8.16.1'), '8.16', ':distribution:bwc:bugfix'), - (v('8.17.0')): new UnreleasedVersionInfo(v('8.17.0'), '8.17', ':distribution:bwc:staged'), + (v('7.17.1')): new UnreleasedVersionInfo(v('7.17.1'), '7.17', ':distribution:bwc:major1'), + (v('8.15.2')): new UnreleasedVersionInfo(v('8.15.2'), '8.15', ':distribution:bwc:minor3'), + (v('8.16.1')): new UnreleasedVersionInfo(v('8.16.1'), '8.16', ':distribution:bwc:minor2'), + (v('8.17.0')): new UnreleasedVersionInfo(v('8.17.0'), '8.17', ':distribution:bwc:minor1'), (v('8.18.0')): new UnreleasedVersionInfo(v('8.18.0'), '8.x', ':distribution'), ] bwc.wireCompatible == [v('7.17.0'), v('7.17.1'), v('8.14.0'), v('8.14.1'), v('8.14.2'), v('8.15.0'), v('8.15.1'), v('8.15.2'), v('8.16.0'), v('8.16.1'), v('8.17.0'), v('8.18.0')] @@ -219,26 +300,33 @@ class BwcVersionsSpec extends Specification { def "current version is first bugfix"() { given: - addVersion('7.16.3', '8.9.0') - addVersion('7.17.0', '8.9.0') - addVersion('7.17.1', '8.9.0') - addVersion('8.14.0', '9.9.0') - addVersion('8.14.1', '9.9.0') - addVersion('8.14.2', '9.9.0') - addVersion('8.15.0', '9.9.0') - addVersion('8.15.1', '9.9.0') - addVersion('8.15.2', '9.9.0') - addVersion('8.16.0', '9.10.0') - addVersion('8.16.1', '9.10.0') + addVersion('7.16.3') + addVersion('7.17.0') + addVersion('7.17.1') + addVersion('8.14.0') + addVersion('8.14.1') + addVersion('8.14.2') + addVersion('8.15.0') + addVersion('8.15.1') + addVersion('8.15.2') + addVersion('8.16.0') + addVersion('8.16.1') when: - def bwc = new BwcVersions(versionLines, v('8.16.1'), ['main', '8.x', '8.17', '8.16', '8.15', '7.17']) + def bwc = new BwcVersions(v('8.16.1'), versions, [ + branch('main','9.0.1'), + branch('8.x','8.18.0'), + branch('8.17','8.17.0'), + branch('8.16','8.16.1'), + branch('8.15','8.15.2'), + branch('7.17','7.17.1'), + ]) def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] } then: unreleased == [ - (v('7.17.1')): new UnreleasedVersionInfo(v('7.17.1'), '7.17', ':distribution:bwc:maintenance'), - (v('8.15.2')): new UnreleasedVersionInfo(v('8.15.2'), '8.15', ':distribution:bwc:bugfix'), + (v('7.17.1')): new UnreleasedVersionInfo(v('7.17.1'), '7.17', ':distribution:bwc:major1'), + (v('8.15.2')): new UnreleasedVersionInfo(v('8.15.2'), '8.15', ':distribution:bwc:minor1'), (v('8.16.1')): new UnreleasedVersionInfo(v('8.16.1'), '8.16', ':distribution'), ] bwc.wireCompatible == [v('7.17.0'), v('7.17.1'), v('8.14.0'), v('8.14.1'), v('8.14.2'), v('8.15.0'), v('8.15.1'), v('8.15.2'), v('8.16.0'), v('8.16.1')] @@ -247,37 +335,46 @@ class BwcVersionsSpec extends Specification { def "current version is second bugfix"() { given: - addVersion('7.16.3', '8.9.0') - addVersion('7.17.0', '8.9.0') - addVersion('7.17.1', '8.9.0') - addVersion('8.14.0', '9.9.0') - addVersion('8.14.1', '9.9.0') - addVersion('8.14.2', '9.9.0') - addVersion('8.15.0', '9.9.0') - addVersion('8.15.1', '9.9.0') - addVersion('8.15.2', '9.9.0') + addVersion('7.16.3') + addVersion('7.17.0') + addVersion('7.17.1') + addVersion('8.14.0') + addVersion('8.14.1') + addVersion('8.14.2') + addVersion('8.15.0') + addVersion('8.15.1') + addVersion('8.15.2') when: - def bwc = new BwcVersions(versionLines, v('8.15.2'), ['main', '8.x', '8.17', '8.16', '8.15', '7.17']) + def bwc = new BwcVersions(v('8.15.2'), versions, [ + branch('main', '9.0.1'), + branch('8.x', '8.18.1'), + branch('8.17', '8.17.2'), + branch('8.16', '8.16.10'), + branch('8.15', '8.15.2'), + branch('7.17', '7.17.1'), + ]) def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] } then: unreleased == [ - (v('7.17.1')): new UnreleasedVersionInfo(v('7.17.1'), '7.17', ':distribution:bwc:maintenance'), + (v('7.17.1')): new UnreleasedVersionInfo(v('7.17.1'), '7.17', ':distribution:bwc:major1'), (v('8.15.2')): new UnreleasedVersionInfo(v('8.15.2'), '8.15', ':distribution'), ] bwc.wireCompatible == [v('7.17.0'), v('7.17.1'), v('8.14.0'), v('8.14.1'), v('8.14.2'), v('8.15.0'), v('8.15.1'), v('8.15.2')] bwc.indexCompatible == [v('7.16.3'), v('7.17.0'), v('7.17.1'), v('8.14.0'), v('8.14.1'), v('8.14.2'), v('8.15.0'), v('8.15.1'), v('8.15.2')] } - 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 void addVersion(String elasticsearch) { + versions.add(Version.fromString(elasticsearch)) } private Version v(String version) { return Version.fromString(version) } + private DevelopmentBranch branch(String name, String version) { + return new DevelopmentBranch(name, v(version)) + } + } diff --git a/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPluginSpec.groovy b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPluginSpec.groovy new file mode 100644 index 0000000000000..4bcbdbfbb587e --- /dev/null +++ b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPluginSpec.groovy @@ -0,0 +1,136 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +package org.elasticsearch.gradle.internal.info + +import groovy.json.JsonOutput +import spock.lang.Specification +import spock.lang.TempDir + +import org.elasticsearch.gradle.Version +import org.elasticsearch.gradle.internal.BwcVersions +import org.gradle.api.Project +import org.gradle.api.provider.Provider +import org.gradle.api.provider.ProviderFactory +import org.gradle.testfixtures.ProjectBuilder + +import java.nio.file.Path + +class GlobalBuildInfoPluginSpec extends Specification { + + @TempDir + File projectRoot + + Project project + + def setup() { + project = ProjectBuilder.builder() + .withProjectDir(projectRoot) + .withName("bwcTestProject") + .build() + project = Spy(project) + project.getRootProject() >> project + + File buildToolsInternalDir = new File(projectRoot, "build-tools-internal") + buildToolsInternalDir.mkdirs() + new File(buildToolsInternalDir, "version.properties").text = """ + elasticsearch = 9.1.0 + lucene = 10.2.2 + + bundled_jdk_vendor = openjdk + bundled_jdk = 24+36@1f9ff9062db4449d8ca828c504ffae90 + minimumJdkVersion = 21 + minimumRuntimeJava = 21 + minimumCompilerJava = 21 + """ + File versionFileDir = new File(projectRoot, "server/src/main/java/org/elasticsearch") + versionFileDir.mkdirs() + new File(versionFileDir, "Version.java").text = """ + package org.elasticsearch; + public class Version { + public static final Version V_8_17_8 = new Version(8_17_08_99); + public static final Version V_8_18_0 = new Version(8_18_00_99); + public static final Version V_8_18_1 = new Version(8_18_01_99); + public static final Version V_8_18_2 = new Version(8_18_02_99); + public static final Version V_8_18_3 = new Version(8_18_03_99); + public static final Version V_8_19_0 = new Version(8_19_00_99); + public static final Version V_9_0_0 = new Version(9_00_00_99); + public static final Version V_9_0_1 = new Version(9_00_01_99); + public static final Version V_9_0_2 = new Version(9_00_02_99); + public static final Version V_9_0_3 = new Version(9_00_03_99); + public static final Version V_9_1_0 = new Version(9_01_00_99); + public static final Version CURRENT = V_9_1_0; + + } + """ + } + + def "resolve unreleased versions from branches file set by Gradle property"() { + given: + ProviderFactory providerFactorySpy = Spy(project.getProviders()) + Path branchesJsonPath = projectRoot.toPath().resolve("myBranches.json") + Provider gradleBranchesLocationProvider = project.providers.provider { return branchesJsonPath.toString() } + providerFactorySpy.gradleProperty("org.elasticsearch.build.branches-file-location") >> gradleBranchesLocationProvider + project.getProviders() >> providerFactorySpy + branchesJsonPath.text = branchesJson( + [ + new DevelopmentBranch("main", Version.fromString("9.1.0")), + new DevelopmentBranch("9.0", Version.fromString("9.0.3")), + new DevelopmentBranch("8.18", Version.fromString("8.18.2")), + ] + ) + + when: + project.objects.newInstance(GlobalBuildInfoPlugin).apply(project) + BuildParameterExtension ext = project.extensions.getByType(BuildParameterExtension) + BwcVersions bwcVersions = ext.bwcVersions + + then: + bwcVersions != null + bwcVersions.unreleased.toSet() == ["9.1.0", "9.0.3", "8.18.2"].collect { Version.fromString(it) }.toSet() + } + + def "should filter 8.19 version as it is incompatible with 9.0 branch - ONLY ON 9.0 BRANCH"() { + given: + ProviderFactory providerFactorySpy = Spy(project.getProviders()) + Path branchesJsonPath = projectRoot.toPath().resolve("myBranches.json") + Provider gradleBranchesLocationProvider = project.providers.provider { return branchesJsonPath.toString() } + providerFactorySpy.gradleProperty("org.elasticsearch.build.branches-file-location") >> gradleBranchesLocationProvider + project.getProviders() >> providerFactorySpy + branchesJsonPath.text = branchesJson( + [ + new DevelopmentBranch("main", Version.fromString("9.1.0")), + new DevelopmentBranch("9.0", Version.fromString("9.0.3")), + new DevelopmentBranch("8.19", Version.fromString("8.19.3")), + new DevelopmentBranch("8.18", Version.fromString("8.18.2")), + ] + ) + + when: + project.objects.newInstance(GlobalBuildInfoPlugin).apply(project) + BuildParameterExtension ext = project.extensions.getByType(BuildParameterExtension) + BwcVersions bwcVersions = ext.bwcVersions + + then: + bwcVersions != null + bwcVersions.unreleased.toSet() == ["9.1.0", "9.0.3", "8.18.2"].collect { Version.fromString(it) }.toSet() + } + + String branchesJson(List branches) { + Map branchesFileContent = [ + branches: branches.collect { branch -> + [ + branch : branch.name(), + version: branch.version().toString(), + ] + } + ] + return JsonOutput.prettyPrint(JsonOutput.toJson(branchesFileContent)) + } +} diff --git a/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTaskSpec.groovy b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTaskSpec.groovy new file mode 100644 index 0000000000000..705c3a8335dc1 --- /dev/null +++ b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTaskSpec.groovy @@ -0,0 +1,267 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +package org.elasticsearch.gradle.internal.release + +import groovy.json.JsonOutput +import groovy.json.JsonSlurper +import spock.lang.Specification +import spock.lang.Subject + +import org.elasticsearch.gradle.Version +import org.elasticsearch.gradle.internal.info.DevelopmentBranch +import org.gradle.api.InvalidUserDataException +import org.gradle.api.Project +import org.gradle.testfixtures.ProjectBuilder +import spock.lang.TempDir + +class UpdateBranchesJsonTaskSpec extends Specification { + + @Subject + UpdateBranchesJsonTask task + + @TempDir + File projectRoot + JsonSlurper jsonSlurper = new JsonSlurper() + + def setup() { + Project project = ProjectBuilder.builder().withProjectDir(projectRoot).build() + task = project.tasks.register("updateBranchesJson", UpdateBranchesJsonTask).get() + task.branchesFile = new File(projectRoot, "branches.json") + } + + def "add new branch to branches.json (sorted by version) when --add-branch is specified"() { + given: + branchesJson([ + new DevelopmentBranch("main", Version.fromString("9.1.0")), + new DevelopmentBranch("8.18", Version.fromString("8.18.2")), + new DevelopmentBranch("8.17", Version.fromString("8.17.7")), + ]) + task.addBranch('8.19:8.19.0') + + when: + task.executeTask() + + then: + def branchesFile = new File(projectRoot, "branches.json") + def json = jsonSlurper.parse(branchesFile) + json.branches == [ + [branch: 'main', version: '9.1.0'], + [branch: '8.19', version: '8.19.0'], + [branch: '8.18', version: '8.18.2'], + [branch: '8.17', version: '8.17.7'], + ] + } + + def "remove branch from branches.json when --remove-branch is specified"() { + given: + branchesJson([ + new DevelopmentBranch("main", Version.fromString("9.1.0")), + new DevelopmentBranch("8.18", Version.fromString("8.18.2")), + new DevelopmentBranch("8.17", Version.fromString("8.17.7")), + ]) + task.removeBranch('8.18') + + when: + task.executeTask() + + then: + def branchesFile = new File(projectRoot, "branches.json") + def json = jsonSlurper.parse(branchesFile) + json.branches == [ + [branch: 'main', version: '9.1.0'], + [branch: '8.17', version: '8.17.7'], + ] + } + + def "update branch version when --update-branch is specified"() { + given: + branchesJson([ + new DevelopmentBranch("main", Version.fromString("9.1.0")), + new DevelopmentBranch("8.18", Version.fromString("8.18.2")), + new DevelopmentBranch("8.17", Version.fromString("8.17.7")), + ]) + task.updateBranch('8.18:8.18.3') + + when: + task.executeTask() + + then: + def branchesFile = new File(projectRoot, "branches.json") + def json = jsonSlurper.parse(branchesFile) + json.branches == [ + [branch: 'main', version: '9.1.0'], + [branch: '8.18', version: '8.18.3'], + [branch: '8.17', version: '8.17.7'], + ] + } + + def "change branches.json when multiple options are specified"() { + given: + branchesJson([ + new DevelopmentBranch("main", Version.fromString("9.1.0")), + new DevelopmentBranch("8.18", Version.fromString("8.18.2")), + new DevelopmentBranch("8.17", Version.fromString("8.17.7")), + ]) + task.addBranch('8.19:8.19.0') + task.removeBranch('8.18') + task.updateBranch('8.17:8.17.8') + + when: + task.executeTask() + + then: + def branchesFile = new File(projectRoot, "branches.json") + def json = jsonSlurper.parse(branchesFile) + json.branches == [ + [branch: 'main', version: '9.1.0'], + [branch: '8.19', version: '8.19.0'], + [branch: '8.17', version: '8.17.8'], + ] + } + + def "fail when no --add-branch, --remove-branch or --update-branch is specified"() { + given: + branchesJson([ + new DevelopmentBranch("main", Version.fromString("9.1.0")), + new DevelopmentBranch("8.18", Version.fromString("8.18.2")), + new DevelopmentBranch("8.17", Version.fromString("8.17.7")), + ]) + + when: + task.executeTask() + + then: + def branchesFile = new File(projectRoot, "branches.json") + def json = jsonSlurper.parse(branchesFile) + json.branches == [ + [branch: 'main', version: '9.1.0'], + [branch: '8.18', version: '8.18.2'], + [branch: '8.17', version: '8.17.7'], + ] + thrown(InvalidUserDataException) + } + + def "fail when adding a branch that already exists"() { + given: + branchesJson([ + new DevelopmentBranch("main", Version.fromString("9.1.0")), + new DevelopmentBranch("8.18", Version.fromString("8.18.2")), + new DevelopmentBranch("8.17", Version.fromString("8.17.7")), + ]) + task.addBranch('8.18:8.18.3') + + when: + task.executeTask() + + then: + def branchesFile = new File(projectRoot, "branches.json") + def json = jsonSlurper.parse(branchesFile) + json.branches == [ + [branch: 'main', version: '9.1.0'], + [branch: '8.18', version: '8.18.2'], + [branch: '8.17', version: '8.17.7'], + ] + thrown(InvalidUserDataException) + } + + def "fail when removing a branch that does not exist"() { + given: + branchesJson([ + new DevelopmentBranch("main", Version.fromString("9.1.0")), + new DevelopmentBranch("8.18", Version.fromString("8.18.2")), + new DevelopmentBranch("8.17", Version.fromString("8.17.7")), + ]) + task.removeBranch('8.19') + + when: + task.executeTask() + + then: + def branchesFile = new File(projectRoot, "branches.json") + def json = jsonSlurper.parse(branchesFile) + json.branches == [ + [branch: 'main', version: '9.1.0'], + [branch: '8.18', version: '8.18.2'], + [branch: '8.17', version: '8.17.7'], + ] + thrown(InvalidUserDataException) + } + + def "fail when updating a branch that does not exist"() { + given: + branchesJson([ + new DevelopmentBranch("main", Version.fromString("9.1.0")), + new DevelopmentBranch("8.18", Version.fromString("8.18.2")), + new DevelopmentBranch("8.17", Version.fromString("8.17.7")), + ]) + task.updateBranch('8.19:8.19.0') + + when: + task.executeTask() + + then: + def branchesFile = new File(projectRoot, "branches.json") + def json = jsonSlurper.parse(branchesFile) + json.branches == [ + [branch: 'main', version: '9.1.0'], + [branch: '8.18', version: '8.18.2'], + [branch: '8.17', version: '8.17.7'], + ] + thrown(InvalidUserDataException) + } + + def "fail when adding a branch with an invalid version"() { + given: + branchesJson([ + new DevelopmentBranch("main", Version.fromString("9.1.0")), + new DevelopmentBranch("8.18", Version.fromString("8.18.2")), + new DevelopmentBranch("8.17", Version.fromString("8.17.7")), + ]) + + when: + task.addBranch('8.19:invalid') + task.executeTask() + + then: + def branchesFile = new File(projectRoot, "branches.json") + def json = jsonSlurper.parse(branchesFile) + json.branches == [ + [branch: 'main', version: '9.1.0'], + [branch: '8.18', version: '8.18.2'], + [branch: '8.17', version: '8.17.7'], + ] + thrown(IllegalArgumentException) + } + + def "fail when branches.json is missing"() { + given: + task.updateBranch('8.19:8.19.0') + + when: + task.executeTask() + + then: + def exception = thrown(InvalidUserDataException) + exception.message.contains("branches.json has not been found") + } + + void branchesJson(List branches) { + File branchesFile = new File(projectRoot, "branches.json") + Map branchesFileContent = [ + branches: branches.collect { branch -> + [ + branch: branch.name(), + version: branch.version().toString(), + ] + } + ] + branchesFile.text = JsonOutput.prettyPrint(JsonOutput.toJson(branchesFileContent)) + } +} diff --git a/build-tools-internal/src/test/java/org/elasticsearch/gradle/AbstractDistributionDownloadPluginTests.java b/build-tools-internal/src/test/java/org/elasticsearch/gradle/AbstractDistributionDownloadPluginTests.java index 7512fa20814c6..d2c9bb2c88b5e 100644 --- a/build-tools-internal/src/test/java/org/elasticsearch/gradle/AbstractDistributionDownloadPluginTests.java +++ b/build-tools-internal/src/test/java/org/elasticsearch/gradle/AbstractDistributionDownloadPluginTests.java @@ -10,6 +10,7 @@ package org.elasticsearch.gradle; import org.elasticsearch.gradle.internal.BwcVersions; +import org.elasticsearch.gradle.internal.info.DevelopmentBranch; import org.gradle.api.NamedDomainObjectContainer; import org.gradle.api.Project; import org.gradle.testfixtures.ProjectBuilder; @@ -29,7 +30,12 @@ public class AbstractDistributionDownloadPluginTests { 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 List DEVELOPMENT_BRANCHES = Arrays.asList("main", "1.1", "1.0", "0.90"); + protected static final List DEVELOPMENT_BRANCHES = Arrays.asList( + new DevelopmentBranch("main", Version.fromString("2.0.0")), + new DevelopmentBranch("1.1", Version.fromString("1.1.0")), + new DevelopmentBranch("1.0", Version.fromString("1.0.1")), + new DevelopmentBranch("0.90", Version.fromString("0.90.1")) + ); protected static final BwcVersions BWC_MINOR = new BwcVersions( BWC_MAJOR_VERSION, diff --git a/build-tools/src/main/java/org/elasticsearch/gradle/VersionProperties.java b/build-tools/src/main/java/org/elasticsearch/gradle/VersionProperties.java index 88c7ea3684226..34d3554beaddf 100644 --- a/build-tools/src/main/java/org/elasticsearch/gradle/VersionProperties.java +++ b/build-tools/src/main/java/org/elasticsearch/gradle/VersionProperties.java @@ -16,7 +16,12 @@ /** * Accessor for shared dependency versions used by elasticsearch, namely the elasticsearch and lucene versions. + * + * @deprecated use ext values set by {@link org.elasticsearch.gradle.internal.conventions.VersionPropertiesPlugin} or + * {@link org.elasticsearch.gradle.internal.conventions.VersionPropertiesBuildService} + * */ +@Deprecated public class VersionProperties { public static String getElasticsearch() { diff --git a/build-tools/src/testFixtures/groovy/org/elasticsearch/gradle/fixtures/AbstractGradleFuncTest.groovy b/build-tools/src/testFixtures/groovy/org/elasticsearch/gradle/fixtures/AbstractGradleFuncTest.groovy index aa9b9d07c4461..447f0b8af496f 100644 --- a/build-tools/src/testFixtures/groovy/org/elasticsearch/gradle/fixtures/AbstractGradleFuncTest.groovy +++ b/build-tools/src/testFixtures/groovy/org/elasticsearch/gradle/fixtures/AbstractGradleFuncTest.groovy @@ -9,25 +9,22 @@ package org.elasticsearch.gradle.fixtures +import spock.lang.Specification +import spock.lang.TempDir + import org.apache.commons.io.FileUtils import org.apache.commons.io.IOUtils import org.elasticsearch.gradle.internal.test.BuildConfigurationAwareGradleRunner import org.elasticsearch.gradle.internal.test.InternalAwareGradleRunner import org.elasticsearch.gradle.internal.test.NormalizeOutputGradleRunner import org.elasticsearch.gradle.internal.test.TestResultExtension -import org.gradle.internal.component.external.model.ComponentVariant import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.GradleRunner import org.junit.Rule import org.junit.rules.TemporaryFolder -import spock.lang.Specification -import spock.lang.TempDir import java.lang.management.ManagementFactory import java.nio.charset.StandardCharsets -import java.nio.file.Files -import java.io.File -import java.nio.file.Path import java.util.jar.JarEntry import java.util.jar.JarOutputStream import java.util.zip.ZipEntry @@ -47,6 +44,7 @@ abstract class AbstractGradleFuncTest extends Specification { File buildFile File propertiesFile File projectDir + File versionPropertiesFile protected boolean configurationCacheCompatible = true protected boolean buildApiRestrictionsDisabled = false @@ -57,6 +55,18 @@ abstract class AbstractGradleFuncTest extends Specification { settingsFile << "rootProject.name = 'hello-world'\n" buildFile = testProjectDir.newFile('build.gradle') propertiesFile = testProjectDir.newFile('gradle.properties') + File buildToolsDir = testProjectDir.newFolder("build-tools-internal") + versionPropertiesFile = new File(buildToolsDir, 'version.properties') + versionPropertiesFile.text = """ + elasticsearch = 9.1.0 + lucene = 10.2.2 + + bundled_jdk_vendor = openjdk + bundled_jdk = 24+36@1f9ff9062db4449d8ca828c504ffae90 + minimumJdkVersion = 21 + minimumRuntimeJava = 21 + minimumCompilerJava = 21 + """ propertiesFile << "org.gradle.java.installations.fromEnv=JAVA_HOME,RUNTIME_JAVA_HOME,JAVA15_HOME,JAVA14_HOME,JAVA13_HOME,JAVA12_HOME,JAVA11_HOME,JAVA8_HOME" @@ -162,10 +172,10 @@ abstract class AbstractGradleFuncTest extends Specification { File internalBuild( List extraPlugins = [], String maintenance = "7.16.10", - String bugfix2 = "8.1.3", - String bugfix = "8.2.1", - String staged = "8.3.0", - String minor = "8.4.0", + String major4 = "8.1.3", + String major3 = "8.2.1", + String major2 = "8.3.0", + String major1 = "8.4.0", String current = "9.0.0" ) { buildFile << """plugins { @@ -175,31 +185,31 @@ abstract class AbstractGradleFuncTest extends Specification { import org.elasticsearch.gradle.Architecture import org.elasticsearch.gradle.internal.BwcVersions + import org.elasticsearch.gradle.internal.info.DevelopmentBranch import org.elasticsearch.gradle.Version Version currentVersion = Version.fromString("${current}") def versionList = [ Version.fromString("$maintenance"), - Version.fromString("$bugfix2"), - Version.fromString("$bugfix"), - Version.fromString("$staged"), - Version.fromString("$minor"), + Version.fromString("$major4"), + Version.fromString("$major3"), + Version.fromString("$major2"), + Version.fromString("$major1"), currentVersion ] - BwcVersions versions = new BwcVersions(currentVersion, versionList, ['main', '8.x', '8.3', '8.2', '8.1', '7.16']) - buildParams.setBwcVersions(project.provider { versions} ) + BwcVersions versions = new BwcVersions(currentVersion, versionList, [ + new DevelopmentBranch('main', Version.fromString("$current")), + new DevelopmentBranch('8.x', Version.fromString("$major1")), + new DevelopmentBranch('8.3', Version.fromString("$major2")), + new DevelopmentBranch('8.2', Version.fromString("$major3")), + new DevelopmentBranch('8.1', Version.fromString("$major4")), + new DevelopmentBranch('7.16', Version.fromString("$maintenance")), + ]) + buildParams.setBwcVersions(project.provider { versions } ) """ } - void setupLocalGitRepo() { - execute("git init") - execute('git config user.email "build-tool@elastic.co"') - execute('git config user.name "Build tool"') - execute("git add .") - execute('git commit -m "Initial"') - } - void execute(String command, File workingDir = testProjectDir.root) { def proc = command.execute(Collections.emptyList(), workingDir) proc.waitFor() diff --git a/changes.patch b/changes.patch new file mode 100644 index 0000000000000..126dda9ffb457 --- /dev/null +++ b/changes.patch @@ -0,0 +1,2157 @@ +diff --git a/branches.json b/branches.json +index a2feaf07155..7ac5464e5cd 100644 +--- a/branches.json ++++ b/branches.json +@@ -1,26 +1,33 @@ + { +- "notice": "This file is not maintained outside of the main branch and should only be used for tooling.", +- "branches": [ ++ "notice" : "This file is not maintained outside of the main branch and should only be used for tooling.", ++ "branches" : [ + { +- "branch": "main" ++ "branch" : "main", ++ "version" : "9.2.0" + }, + { +- "branch": "9.1" ++ "branch" : "9.1", ++ "version" : "9.1.3" + }, + { +- "branch": "9.0" ++ "branch" : "9.0", ++ "version" : "9.0.6" + }, + { +- "branch": "8.19" ++ "branch" : "8.19", ++ "version" : "8.19.3" + }, + { +- "branch": "8.18" ++ "branch" : "8.18", ++ "version" : "8.18.6" + }, + { +- "branch": "8.17" ++ "branch" : "8.17", ++ "version" : "8.17.11" + }, + { +- "branch": "7.17" ++ "branch" : "7.17", ++ "version" : "7.17.30" + } + ] + } +diff --git a/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/VersionPropertiesPlugin.java b/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/VersionPropertiesPlugin.java +index ab292040623..1b93c6b43a3 100644 +--- a/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/VersionPropertiesPlugin.java ++++ b/build-conventions/src/main/java/org/elasticsearch/gradle/internal/conventions/VersionPropertiesPlugin.java +@@ -18,6 +18,8 @@ import java.io.File; + + public class VersionPropertiesPlugin implements Plugin { + ++ public static final String VERSIONS_EXT = "versions"; ++ + @Override + public void apply(Project project) { + File workspaceDir = Util.locateElasticsearchWorkspace(project.getGradle()); +@@ -28,6 +30,6 @@ public class VersionPropertiesPlugin implements Plugin { + .registerIfAbsent("versions", VersionPropertiesBuildService.class, spec -> { + spec.getParameters().getInfoPath().set(infoPath); + }); +- project.getExtensions().add("versions", serviceProvider.get().getProperties()); ++ project.getExtensions().add(VERSIONS_EXT, serviceProvider.get().getProperties()); + } + } +diff --git a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/fixtures/AbstractGitAwareGradleFuncTest.groovy b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/fixtures/AbstractGitAwareGradleFuncTest.groovy +index d8cb55fc037..754431907ba 100644 +--- a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/fixtures/AbstractGitAwareGradleFuncTest.groovy ++++ b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/fixtures/AbstractGitAwareGradleFuncTest.groovy +@@ -26,6 +26,17 @@ abstract class AbstractGitAwareGradleFuncTest extends AbstractGradleFuncTest { + execute("git clone ${remoteGitRepo.absolutePath} cloned", testProjectDir.root) + buildFile = new File(testProjectDir.root, 'cloned/build.gradle') + settingsFile = new File(testProjectDir.root, 'cloned/settings.gradle') ++ versionPropertiesFile = new File(testProjectDir.root, 'cloned/build-tools-internal/version.properties') ++ versionPropertiesFile.text = """ ++ elasticsearch = 9.1.0 ++ lucene = 10.2.2 ++ ++ bundled_jdk_vendor = openjdk ++ bundled_jdk = 24+36@1f9ff9062db4449d8ca828c504ffae90 ++ minimumJdkVersion = 21 ++ minimumRuntimeJava = 21 ++ minimumCompilerJava = 21 ++ """ + } + + File setupGitRemote() { +diff --git a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/InternalDistributionBwcSetupPluginFuncTest.groovy b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/InternalDistributionBwcSetupPluginFuncTest.groovy +index effcb8a54af..9a235675918 100644 +--- a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/InternalDistributionBwcSetupPluginFuncTest.groovy ++++ b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/InternalDistributionBwcSetupPluginFuncTest.groovy +@@ -51,23 +51,23 @@ class InternalDistributionBwcSetupPluginFuncTest extends AbstractGitAwareGradleF + assertOutputContains(result.output, "[$bwcDistVersion] > Task :distribution:archives:darwin-tar:${expectedAssembleTaskName}") + + where: +- bwcDistVersion | bwcProject | expectedAssembleTaskName +- "8.4.0" | "minor" | "extractedAssemble" +- "8.3.0" | "staged" | "extractedAssemble" +- "8.2.1" | "bugfix" | "extractedAssemble" +- "8.1.3" | "bugfix2" | "extractedAssemble" ++ bwcDistVersion | bwcProject | expectedAssembleTaskName ++ "8.4.0" | "major1" | "extractedAssemble" ++ "8.3.0" | "major2" | "extractedAssemble" ++ "8.2.1" | "major3" | "extractedAssemble" ++ "8.1.3" | "major4" | "extractedAssemble" + } + + @Unroll + def "supports #platform aarch distributions"() { + when: +- def result = gradleRunner(":distribution:bwc:minor:buildBwc${platform.capitalize()}Aarch64Tar", ++ def result = gradleRunner(":distribution:bwc:major1:buildBwc${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:major1:buildBwc${platform.capitalize()}Aarch64Tar").outcome == TaskOutcome.SUCCESS + + and: "assemble tasks triggered" + assertOutputContains(result.output, "[$bwcDistVersion] > Task :distribution:archives:${platform}-aarch64-tar:extractedAssemble") +@@ -87,7 +87,7 @@ class InternalDistributionBwcSetupPluginFuncTest extends AbstractGitAwareGradleF + } + + dependencies { +- expandedDist project(path: ":distribution:bwc:minor", configuration:"expanded-darwin-tar") ++ expandedDist project(path: ":distribution:bwc:major1", configuration:"expanded-darwin-tar") + } + + tasks.register("resolveExpandedDistribution") { +@@ -109,12 +109,12 @@ class InternalDistributionBwcSetupPluginFuncTest extends AbstractGitAwareGradleF + .build() + then: + result.task(":resolveExpandedDistribution").outcome == TaskOutcome.SUCCESS +- result.task(":distribution:bwc:minor:buildBwcDarwinTar").outcome == TaskOutcome.SUCCESS ++ result.task(":distribution:bwc:major1:buildBwcDarwinTar").outcome == TaskOutcome.SUCCESS + and: "assemble task triggered" + result.output.contains("[8.4.0] > Task :distribution:archives:darwin-tar:extractedAssemble") +- result.output.contains("expandedRootPath /distribution/bwc/minor/build/bwc/checkout-8.x/" + ++ result.output.contains("expandedRootPath /distribution/bwc/major1/build/bwc/checkout-8.x/" + + "distribution/archives/darwin-tar/build/install") +- result.output.contains("nested folder /distribution/bwc/minor/build/bwc/checkout-8.x/" + ++ result.output.contains("nested folder /distribution/bwc/major1/build/bwc/checkout-8.x/" + + "distribution/archives/darwin-tar/build/install/elasticsearch-8.4.0-SNAPSHOT") + } + } +diff --git a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/InternalDistributionDownloadPluginFuncTest.groovy b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/InternalDistributionDownloadPluginFuncTest.groovy +index fc5d432a9ef..3d0c6cc390e 100644 +--- a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/InternalDistributionDownloadPluginFuncTest.groovy ++++ b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/InternalDistributionDownloadPluginFuncTest.groovy +@@ -51,7 +51,7 @@ class InternalDistributionDownloadPluginFuncTest extends AbstractGradleFuncTest + def "resolves expanded bwc versions from source"() { + given: + internalBuild() +- bwcMinorProjectSetup() ++ bwcMajor1ProjectSetup() + buildFile << """ + apply plugin: 'elasticsearch.internal-distribution-download' + +@@ -72,16 +72,16 @@ class InternalDistributionDownloadPluginFuncTest extends AbstractGradleFuncTest + + def result = gradleRunner("setupDistro").build() + then: +- result.task(":distribution:bwc:minor:buildBwcExpandedTask").outcome == TaskOutcome.SUCCESS ++ result.task(":distribution:bwc:major1:buildBwcExpandedTask").outcome == TaskOutcome.SUCCESS + result.task(":setupDistro").outcome == TaskOutcome.SUCCESS +- assertExtractedDistroIsCreated("distribution/bwc/minor/build/install/elastic-distro", ++ assertExtractedDistroIsCreated("distribution/bwc/major1/build/install/elastic-distro", + 'bwc-marker.txt') + } + + def "fails on resolving bwc versions with no bundled jdk"() { + given: + internalBuild() +- bwcMinorProjectSetup() ++ bwcMajor1ProjectSetup() + buildFile << """ + apply plugin: 'elasticsearch.internal-distribution-download' + +@@ -105,12 +105,12 @@ class InternalDistributionDownloadPluginFuncTest extends AbstractGradleFuncTest + "without a bundled JDK is not supported.") + } + +- private void bwcMinorProjectSetup() { ++ private void bwcMajor1ProjectSetup() { + settingsFile << """ +- include ':distribution:bwc:minor' ++ include ':distribution:bwc:major1' + """ +- def bwcSubProjectFolder = testProjectDir.newFolder("distribution", "bwc", "minor") +- new File(bwcSubProjectFolder, 'bwc-marker.txt') << "bwc=minor" ++ def bwcSubProjectFolder = testProjectDir.newFolder("distribution", "bwc", "major1") ++ new File(bwcSubProjectFolder, 'bwc-marker.txt') << "bwc=major1" + new File(bwcSubProjectFolder, 'build.gradle') << """ + apply plugin:'base' + +diff --git a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/test/rest/LegacyYamlRestCompatTestPluginFuncTest.groovy b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/test/rest/LegacyYamlRestCompatTestPluginFuncTest.groovy +index 15b057a05e0..2d08f34e16f 100644 +--- a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/test/rest/LegacyYamlRestCompatTestPluginFuncTest.groovy ++++ b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/test/rest/LegacyYamlRestCompatTestPluginFuncTest.groovy +@@ -40,7 +40,7 @@ class LegacyYamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTe + given: + internalBuild() + +- subProject(":distribution:bwc:minor") << """ ++ subProject(":distribution:bwc:major1") << """ + configurations { checkout } + artifacts { + checkout(new File(projectDir, "checkoutDir")) +@@ -61,11 +61,11 @@ class LegacyYamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTe + result.task(transformTask).outcome == TaskOutcome.NO_SOURCE + } + +- def "yamlRestCompatTest executes and copies api and transforms tests from :bwc:minor"() { ++ def "yamlRestCompatTest executes and copies api and transforms tests from :bwc:major1"() { + given: + internalBuild() + +- subProject(":distribution:bwc:minor") << """ ++ subProject(":distribution:bwc:major1") << """ + configurations { checkout } + artifacts { + checkout(new File(projectDir, "checkoutDir")) +@@ -98,8 +98,8 @@ class LegacyYamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTe + 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/major1/checkoutDir/rest-api-spec/src/main/resources/rest-api-spec/api/" + api) << "" ++ file("distribution/bwc/major1/checkoutDir/src/yamlRestTest/resources/rest-api-spec/test/" + test) << "" + + when: + def result = gradleRunner("yamlRestCompatTest").build() +@@ -145,7 +145,7 @@ class LegacyYamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTe + given: + internalBuild() + withVersionCatalogue() +- subProject(":distribution:bwc:minor") << """ ++ subProject(":distribution:bwc:major1") << """ + configurations { checkout } + artifacts { + checkout(new File(projectDir, "checkoutDir")) +@@ -186,7 +186,7 @@ class LegacyYamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTe + given: + internalBuild() + +- subProject(":distribution:bwc:minor") << """ ++ subProject(":distribution:bwc:major1") << """ + configurations { checkout } + artifacts { + checkout(new File(projectDir, "checkoutDir")) +@@ -230,7 +230,7 @@ class LegacyYamlRestCompatTestPluginFuncTest extends AbstractRestResourcesFuncTe + + setupRestResources([], []) + +- file("distribution/bwc/minor/checkoutDir/src/yamlRestTest/resources/rest-api-spec/test/test.yml" ) << """ ++ file("distribution/bwc/major1/checkoutDir/src/yamlRestTest/resources/rest-api-spec/test/test.yml" ) << """ + "one": + - do: + do_.some.key_to_replace: +diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/bugfix/build.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major1/build.gradle +similarity index 100% +rename from build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/bugfix/build.gradle +rename to build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major1/build.gradle +diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/bugfix2/build.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major2/build.gradle +similarity index 100% +rename from build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/bugfix2/build.gradle +rename to build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major2/build.gradle +diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/maintenance/build.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major3/build.gradle +similarity index 100% +rename from build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/maintenance/build.gradle +rename to build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major3/build.gradle +diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major/build.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major4/build.gradle +similarity index 100% +rename from build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major/build.gradle +rename to build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major4/build.gradle +diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor/build.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor1/build.gradle +similarity index 100% +rename from build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor/build.gradle +rename to build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor1/build.gradle +diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/staged/build.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor2/build.gradle +similarity index 100% +rename from build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/staged/build.gradle +rename to build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor2/build.gradle +diff --git a/distribution/bwc/bugfix/build.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor3/build.gradle +similarity index 100% +rename from distribution/bwc/bugfix/build.gradle +rename to build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor3/build.gradle +diff --git a/distribution/bwc/bugfix2/build.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor4/build.gradle +similarity index 100% +rename from distribution/bwc/bugfix2/build.gradle +rename to build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor4/build.gradle +diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/settings.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/settings.gradle +index 02be9628c8a..00691191e0f 100644 +--- a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/settings.gradle ++++ b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/settings.gradle +@@ -9,16 +9,14 @@ + + rootProject.name = "root" + +-include ":distribution:bwc:bugfix" +-include ":distribution:bwc:bugfix2" +-include ":distribution:bwc:bugfix3" +-include ":distribution:bwc:bugfix4" +-include ":distribution:bwc:bugfix5" +-include ":distribution:bwc:minor" +-include ":distribution:bwc:major" +-include ":distribution:bwc:staged" +-include ":distribution:bwc:staged2" +-include ":distribution:bwc:maintenance" ++include ":distribution:bwc:major1" ++include ":distribution:bwc:major2" ++include ":distribution:bwc:major3" ++include ":distribution:bwc:major4" ++include ":distribution:bwc:minor1" ++include ":distribution:bwc:minor2" ++include ":distribution:bwc:minor3" ++include ":distribution:bwc:minor4" + include ":distribution:archives:darwin-tar" + include ":distribution:archives:oss-darwin-tar" + include ":distribution:archives:darwin-aarch64-tar" +diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/BwcVersions.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/BwcVersions.java +index 15d13e567ca..fc43cf6f7f0 100644 +--- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/BwcVersions.java ++++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/BwcVersions.java +@@ -9,7 +9,7 @@ + package org.elasticsearch.gradle.internal; + + import org.elasticsearch.gradle.Version; +-import org.elasticsearch.gradle.VersionProperties; ++import org.elasticsearch.gradle.internal.info.DevelopmentBranch; + + import java.io.Serializable; + import java.util.ArrayList; +@@ -24,8 +24,7 @@ import java.util.TreeMap; + import java.util.function.BiConsumer; + import java.util.function.Consumer; + import java.util.function.Predicate; +-import java.util.regex.Matcher; +-import java.util.regex.Pattern; ++import java.util.stream.Collectors; + + import static java.util.Collections.reverseOrder; + import static java.util.Collections.unmodifiableList; +@@ -65,20 +64,13 @@ import static java.util.Comparator.comparing; + + public class BwcVersions implements Serializable { + +- private static final Pattern LINE_PATTERN = Pattern.compile( +- "\\W+public static final Version V_(\\d+)_(\\d+)_(\\d+)(_alpha\\d+|_beta\\d+|_rc\\d+)?.*\\);" +- ); + private static final String GLIBC_VERSION_ENV_VAR = "GLIBC_VERSION"; + + private final Version currentVersion; + private final transient List versions; + private final Map unreleased; + +- public BwcVersions(List versionLines, List developmentBranches) { +- this(versionLines, Version.fromString(VersionProperties.getElasticsearch()), developmentBranches); +- } +- +- public BwcVersions(Version currentVersionProperty, List allVersions, List developmentBranches) { ++ public BwcVersions(Version currentVersionProperty, List allVersions, List developmentBranches) { + if (allVersions.isEmpty()) { + throw new IllegalArgumentException("Could not parse any versions"); + } +@@ -90,20 +82,6 @@ public class BwcVersions implements Serializable { + this.unreleased = computeUnreleased(developmentBranches); + } + +- // Visible for testing +- BwcVersions(List versionLines, Version currentVersionProperty, List developmentBranches) { +- this(currentVersionProperty, parseVersionLines(versionLines), developmentBranches); +- } +- +- private static List parseVersionLines(List versionLines) { +- return 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)))) +- .sorted() +- .toList(); +- } +- + private void assertCurrentVersionMatchesParsed(Version currentVersionProperty) { + if (currentVersionProperty.equals(currentVersion) == false) { + throw new IllegalStateException( +@@ -127,81 +105,46 @@ public class BwcVersions implements Serializable { + getUnreleased().stream().filter(version -> version.equals(currentVersion) == false).map(unreleased::get).forEach(consumer); + } + +- private String getBranchFor(Version version, List developmentBranches) { +- // If the current version matches a specific feature freeze branch, use that +- if (developmentBranches.contains(version.getMajor() + "." + version.getMinor())) { +- return version.getMajor() + "." + version.getMinor(); +- } else if (developmentBranches.contains(version.getMajor() + ".x")) { // Otherwise if an n.x branch exists and we are that major +- return version.getMajor() + ".x"; +- } else { // otherwise we're the main branch +- return "main"; +- } +- } +- +- private Map computeUnreleased(List developmentBranches) { ++ private Map computeUnreleased(List developmentBranches) { + Map result = new TreeMap<>(); ++ Map> bwcBranches = developmentBranches.stream() ++ .filter(developmentBranch -> developmentBranch.version().before(currentVersion)) ++ .sorted(reverseOrder(comparing(DevelopmentBranch::version))) ++ .collect(Collectors.groupingBy(branch -> { ++ if (branch.version().getMajor() == currentVersion.getMajor()) { ++ return "minor"; ++ } else if (branch.version().getMajor() == currentVersion.getMajor() - 1) { ++ return "major"; ++ } ++ return "older"; ++ })); ++ ++ developmentBranches.stream() ++ .filter(branch -> branch.version().equals(currentVersion)) ++ .findFirst() ++ .ifPresent( ++ developmentBranch -> result.put( ++ currentVersion, ++ new UnreleasedVersionInfo(currentVersion, developmentBranch.name(), ":distribution") ++ ) ++ ); + +- // The current version is always in development +- String currentBranch = getBranchFor(currentVersion, developmentBranches); +- result.put(currentVersion, new UnreleasedVersionInfo(currentVersion, currentBranch, ":distribution")); +- +- // Check for an n.x branch as well +- if (currentBranch.equals("main") && developmentBranches.stream().anyMatch(s -> s.endsWith(".x"))) { +- // This should correspond to the latest new minor +- Version version = versions.stream() +- .sorted(Comparator.reverseOrder()) +- .filter(v -> v.getMajor() == (currentVersion.getMajor() - 1) && v.getRevision() == 0) +- .findFirst() +- .orElseThrow(() -> new IllegalStateException("Unable to determine development version for branch")); +- String branch = getBranchFor(version, developmentBranches); +- assert branch.equals(currentVersion.getMajor() - 1 + ".x") : "Expected branch does not match development branch"; +- +- result.put(version, new UnreleasedVersionInfo(version, branch, ":distribution:bwc:minor")); ++ List previousMinorBranches = bwcBranches.getOrDefault("minor", Collections.emptyList()); ++ for (int i = 0; i < previousMinorBranches.size(); i++) { ++ DevelopmentBranch previousMinorBranch = previousMinorBranches.get(i); ++ result.put( ++ previousMinorBranch.version(), ++ new UnreleasedVersionInfo(previousMinorBranch.version(), previousMinorBranch.name(), ":distribution:bwc:minor" + (i + 1)) ++ ); + } + +- // Now handle all the feature freeze branches +- List featureFreezeBranches = developmentBranches.stream() +- .filter(b -> Pattern.matches("[0-9]+\\.[0-9]+", b)) +- .sorted(reverseOrder(comparing(s -> Version.fromString(s, Version.Mode.RELAXED)))) +- .toList(); +- +- int bugfixCount = 0; +- boolean existingStaged = false; +- for (int i = 0; i < featureFreezeBranches.size(); i++) { +- String branch = featureFreezeBranches.get(i); +- Version version = versions.stream() +- .sorted(Comparator.reverseOrder()) +- .filter(v -> v.toString().startsWith(branch)) +- .findFirst() +- .orElse(null); +- +- // If we don't know about this version we can ignore it +- if (version == null) { +- continue; +- } +- +- // If this is the current version we can ignore as we've already handled it +- if (version.equals(currentVersion)) { +- continue; +- } +- +- // We only maintain compatibility back one major so ignore anything older +- if (currentVersion.getMajor() - version.getMajor() > 1) { +- continue; +- } +- +- // This is the maintenance version +- if (i == featureFreezeBranches.size() - 1) { +- result.put(version, new UnreleasedVersionInfo(version, branch, ":distribution:bwc:maintenance")); +- } else if (version.getRevision() == 0) { // This is the next staged minor +- String project = existingStaged ? "staged2" : "staged"; +- result.put(version, new UnreleasedVersionInfo(version, branch, ":distribution:bwc:" + project)); +- existingStaged = true; +- } else { // This is a bugfix +- bugfixCount++; +- String project = "bugfix" + (bugfixCount > 1 ? bugfixCount : ""); +- result.put(version, new UnreleasedVersionInfo(version, branch, ":distribution:bwc:" + project)); +- } ++ List previousMajorBranches = bwcBranches.getOrDefault("major", Collections.emptyList()); ++ for (int i = 0; i < previousMajorBranches.size(); i++) { ++ DevelopmentBranch previousMajorBranch = previousMajorBranches.get(i); ++ result.put( ++ previousMajorBranch.version(), ++ new UnreleasedVersionInfo(previousMajorBranch.version(), previousMajorBranch.name(), ":distribution:bwc:major" + (i + 1)) ++ ); + } + + return Collections.unmodifiableMap(result); +@@ -211,19 +154,6 @@ public class BwcVersions implements Serializable { + return unreleased.keySet().stream().sorted().toList(); + } + +- private void addUnreleased(Set unreleased, Version current, int index) { +- if (current.getRevision() == 0) { +- // If the current version is a new minor, the next version is also unreleased +- Version next = versions.get(versions.size() - (index + 2)); +- unreleased.add(next); +- +- // Keep looking through versions until we find the end of unreleased versions +- addUnreleased(unreleased, next, index + 1); +- } else { +- unreleased.add(current); +- } +- } +- + public void compareToAuthoritative(List authoritativeReleasedVersions) { + Set notReallyReleased = new HashSet<>(getReleased()); + notReallyReleased.removeAll(authoritativeReleasedVersions); +diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/BranchesFileParser.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/BranchesFileParser.java +new file mode 100644 +index 00000000000..1c502a633e1 +--- /dev/null ++++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/BranchesFileParser.java +@@ -0,0 +1,46 @@ ++/* ++ * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public ++ * License v3.0 only", or the "Server Side Public License, v 1". ++ */ ++ ++package org.elasticsearch.gradle.internal.info; ++ ++import com.fasterxml.jackson.databind.JsonNode; ++import com.fasterxml.jackson.databind.ObjectMapper; ++ ++import org.elasticsearch.gradle.Version; ++ ++import java.io.IOException; ++import java.io.UncheckedIOException; ++import java.util.ArrayList; ++import java.util.List; ++ ++/** ++ * A parser for the branches.json file ++ */ ++public class BranchesFileParser { ++ ++ private final ObjectMapper objectMapper; ++ ++ public BranchesFileParser(ObjectMapper objectMapper) { ++ this.objectMapper = objectMapper; ++ } ++ ++ public List parse(byte[] bytes) { ++ List branches = new ArrayList<>(); ++ try { ++ JsonNode json = objectMapper.readTree(bytes); ++ for (JsonNode node : json.get("branches")) { ++ branches.add(new DevelopmentBranch(node.get("branch").asText(), Version.fromString(node.get("version").asText()))); ++ } ++ } catch (IOException e) { ++ throw new UncheckedIOException("Failed to parse content of branches.json", e); ++ } ++ ++ return branches; ++ } ++} +diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/DevelopmentBranch.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/DevelopmentBranch.java +new file mode 100644 +index 00000000000..964db5eabd4 +--- /dev/null ++++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/DevelopmentBranch.java +@@ -0,0 +1,28 @@ ++/* ++ * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public ++ * License v3.0 only", or the "Server Side Public License, v 1". ++ */ ++ ++package org.elasticsearch.gradle.internal.info; ++ ++import org.elasticsearch.gradle.Version; ++ ++import java.io.Serializable; ++import java.util.Objects; ++ ++/** ++ * Information about a development branch used in branches.json file ++ * ++ * @param name Name of the development branch ++ * @param version Elasticsearch version on the development branch ++ */ ++public record DevelopmentBranch(String name, Version version) implements Serializable { ++ public DevelopmentBranch { ++ Objects.requireNonNull(name); ++ Objects.requireNonNull(version); ++ } ++} +diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPlugin.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPlugin.java +index 2c0c5ebb672..31fa6080865 100644 +--- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPlugin.java ++++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPlugin.java +@@ -8,17 +8,17 @@ + */ + package org.elasticsearch.gradle.internal.info; + +-import com.fasterxml.jackson.databind.JsonNode; + import com.fasterxml.jackson.databind.ObjectMapper; + + import org.apache.commons.io.IOUtils; ++import org.elasticsearch.gradle.Version; + import org.elasticsearch.gradle.Architecture; + import org.elasticsearch.gradle.OS; +-import org.elasticsearch.gradle.VersionProperties; + import org.elasticsearch.gradle.internal.BwcVersions; + import org.elasticsearch.gradle.internal.Jdk; + import org.elasticsearch.gradle.internal.JdkDownloadPlugin; + import org.elasticsearch.gradle.internal.conventions.GitInfoPlugin; ++import org.elasticsearch.gradle.internal.conventions.VersionPropertiesPlugin; + import org.elasticsearch.gradle.internal.conventions.info.GitInfo; + import org.elasticsearch.gradle.internal.conventions.info.ParallelDetector; + import org.elasticsearch.gradle.internal.conventions.util.Util; +@@ -56,12 +56,15 @@ import java.io.IOException; + import java.io.InputStream; + import java.io.InputStreamReader; + import java.io.UncheckedIOException; ++import java.net.URI; + import java.nio.file.Files; +-import java.util.ArrayList; + import java.util.List; + import java.util.Locale; ++import java.util.Properties; + import java.util.Random; + import java.util.concurrent.atomic.AtomicReference; ++import java.util.regex.Matcher; ++import java.util.regex.Pattern; + import java.util.stream.Collectors; + import java.util.stream.Stream; + +@@ -69,16 +72,22 @@ import javax.inject.Inject; + + import static org.elasticsearch.gradle.internal.conventions.GUtils.elvis; + import static org.elasticsearch.gradle.internal.toolchain.EarlyAccessCatalogJdkToolchainResolver.findLatestEABuildNumber; ++import static org.elasticsearch.gradle.internal.conventions.VersionPropertiesPlugin.VERSIONS_EXT; + + public class GlobalBuildInfoPlugin implements Plugin { + private static final Logger LOGGER = Logging.getLogger(GlobalBuildInfoPlugin.class); + private static final String DEFAULT_VERSION_JAVA_FILE_PATH = "server/src/main/java/org/elasticsearch/Version.java"; ++ private static final String DEFAULT_BRANCHES_FILE_URL = "https://raw.githubusercontent.com/elastic/elasticsearch/main/branches.json"; ++ private static final String BRANCHES_FILE_LOCATION_PROPERTY = "org.elasticsearch.build.branches-file-location"; ++ private static final Pattern LINE_PATTERN = Pattern.compile( ++ "\\W+public static final Version V_(\\d+)_(\\d+)_(\\d+)(_alpha\\d+|_beta\\d+|_rc\\d+)?.*\\);" ++ ); + + private ObjectFactory objectFactory; + private final JavaInstallationRegistry javaInstallationRegistry; + private final JvmMetadataDetector metadataDetector; + private final ProviderFactory providers; +- private final ObjectMapper objectMapper; ++ private final BranchesFileParser branchesFileParser; + private JavaToolchainService toolChainService; + private Project project; + +@@ -93,7 +102,7 @@ public class GlobalBuildInfoPlugin implements Plugin { + this.javaInstallationRegistry = javaInstallationRegistry; + this.metadataDetector = new ErrorTraceMetadataDetector(metadataDetector); + this.providers = providers; +- this.objectMapper = new ObjectMapper(); ++ this.branchesFileParser = new BranchesFileParser(new ObjectMapper()); + } + + @Override +@@ -113,21 +122,27 @@ public class GlobalBuildInfoPlugin implements Plugin { + throw new GradleException("Gradle " + minimumGradleVersion.getVersion() + "+ is required"); + } + +- JavaVersion minimumCompilerVersion = JavaVersion.toVersion(getResourceContents("/minimumCompilerVersion")); +- JavaVersion minimumRuntimeVersion = JavaVersion.toVersion(getResourceContents("/minimumRuntimeVersion")); ++ project.getPlugins().apply(VersionPropertiesPlugin.class); ++ Properties versionProperties = (Properties) project.getExtensions().getByName(VERSIONS_EXT); ++ JavaVersion minimumCompilerVersion = JavaVersion.toVersion(versionProperties.get("minimumCompilerJava")); ++ JavaVersion minimumRuntimeVersion = JavaVersion.toVersion(versionProperties.get("minimumRuntimeJava")); ++ ++ String bundledJdkVersion = versionProperties.getProperty("bundled_jdk"); ++ String bundledJdkMajorVersion = bundledJdkVersion.split("[.+]")[0]; ++ Version elasticsearchVersionProperty = Version.fromString(versionProperties.getProperty("elasticsearch")); + + Provider explicitRuntimeJavaHome = findRuntimeJavaHome(); + boolean isRuntimeJavaHomeExplicitlySet = explicitRuntimeJavaHome.isPresent(); + Provider actualRuntimeJavaHome = isRuntimeJavaHomeExplicitlySet + ? explicitRuntimeJavaHome +- : resolveJavaHomeFromToolChainService(VersionProperties.getBundledJdkMajorVersion()); ++ : resolveJavaHomeFromToolChainService(bundledJdkMajorVersion); + + Provider runtimeJdkMetaData = actualRuntimeJavaHome.map( + runtimeJavaHome -> metadataDetector.getMetadata(getJavaInstallation(runtimeJavaHome)) + ); + AtomicReference cache = new AtomicReference<>(); + Provider bwcVersionsProvider = providers.provider( +- () -> cache.updateAndGet(val -> val == null ? resolveBwcVersions() : val) ++ () -> cache.updateAndGet(val -> val == null ? resolveBwcVersions(elasticsearchVersionProperty) : val) + ); + BuildParameterExtension buildParams = project.getExtensions() + .create( +@@ -141,9 +156,7 @@ public class GlobalBuildInfoPlugin implements Plugin { + javaHome -> determineJavaVersion( + "runtime java.home", + javaHome, +- isRuntimeJavaHomeExplicitlySet +- ? minimumRuntimeVersion +- : JavaVersion.toVersion(VersionProperties.getBundledJdkMajorVersion()) ++ isRuntimeJavaHomeExplicitlySet ? minimumRuntimeVersion : JavaVersion.toVersion(bundledJdkMajorVersion) + ) + ), + isRuntimeJavaHomeExplicitlySet, +@@ -197,32 +210,49 @@ public class GlobalBuildInfoPlugin implements Plugin { + /* Introspect all versions of ES that may be tested against for backwards + * compatibility. It is *super* important that this logic is the same as the + * logic in VersionUtils.java. */ +- private BwcVersions resolveBwcVersions() { ++ private BwcVersions resolveBwcVersions(Version currentElasticsearchVersion) { + String versionsFilePath = elvis( + System.getProperty("BWC_VERSION_SOURCE"), + new File(Util.locateElasticsearchWorkspace(project.getGradle()), DEFAULT_VERSION_JAVA_FILE_PATH).getPath() + ); + try (var is = new FileInputStream(versionsFilePath)) { + List versionLines = IOUtils.readLines(is, "UTF-8"); +- return new BwcVersions(versionLines, getDevelopmentBranches()); ++ return new BwcVersions(currentElasticsearchVersion, parseVersionLines(versionLines), getDevelopmentBranches()); + } catch (IOException e) { + throw new IllegalStateException("Unable to resolve to resolve bwc versions from versionsFile.", e); + } + } + +- private List getDevelopmentBranches() { +- List branches = new ArrayList<>(); +- File branchesFile = new File(Util.locateElasticsearchWorkspace(project.getGradle()), "branches.json"); +- try (InputStream is = new FileInputStream(branchesFile)) { +- JsonNode json = objectMapper.readTree(is); +- for (JsonNode node : json.get("branches")) { +- branches.add(node.get("branch").asText()); ++ private List parseVersionLines(List versionLines) { ++ return 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)))) ++ .sorted() ++ .toList(); ++ } ++ ++ private List getDevelopmentBranches() { ++ String branchesFileLocation = project.getProviders() ++ .gradleProperty(BRANCHES_FILE_LOCATION_PROPERTY) ++ .getOrElse(DEFAULT_BRANCHES_FILE_URL); ++ LOGGER.info("Reading branches.json from {}", branchesFileLocation); ++ byte[] branchesBytes; ++ if (branchesFileLocation.startsWith("http")) { ++ try (InputStream in = URI.create(branchesFileLocation).toURL().openStream()) { ++ branchesBytes = in.readAllBytes(); ++ } catch (IOException e) { ++ throw new UncheckedIOException("Failed to download branches.json from: " + branchesFileLocation, e); ++ } ++ } else { ++ try { ++ branchesBytes = Files.readAllBytes(new File(branchesFileLocation).toPath()); ++ } catch (IOException e) { ++ throw new UncheckedIOException("Failed to read branches.json from: " + branchesFileLocation, e); + } +- } catch (IOException e) { +- throw new UncheckedIOException(e); + } + +- return branches; ++ return branchesFileParser.parse(branchesBytes); + } + + private void logGlobalBuildInfo(BuildParameterExtension buildParams) { +diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/ReleaseToolsPlugin.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/ReleaseToolsPlugin.java +index fce8b0c545d..f0fb0073d43 100644 +--- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/ReleaseToolsPlugin.java ++++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/ReleaseToolsPlugin.java +@@ -53,6 +53,7 @@ public class ReleaseToolsPlugin implements Plugin { + project.getTasks().register("extractCurrentVersions", ExtractCurrentVersionsTask.class); + project.getTasks().register("tagVersions", TagVersionsTask.class); + project.getTasks().register("setCompatibleVersions", SetCompatibleVersionsTask.class, t -> t.setThisVersion(version)); ++ project.getTasks().register("updateBranchesJson", UpdateBranchesJsonTask.class); + + final Directory changeLogDirectory = projectDirectory.dir("docs/changelog"); + final Directory changeLogBundlesDirectory = projectDirectory.dir("docs/release-notes/changelog-bundles"); +diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTask.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTask.java +new file mode 100644 +index 00000000000..584bfc07693 +--- /dev/null ++++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTask.java +@@ -0,0 +1,176 @@ ++/* ++ * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public ++ * License v3.0 only", or the "Server Side Public License, v 1". ++ */ ++ ++package org.elasticsearch.gradle.internal.release; ++ ++import com.fasterxml.jackson.core.util.DefaultIndenter; ++import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; ++import com.fasterxml.jackson.databind.JsonNode; ++import com.fasterxml.jackson.databind.ObjectMapper; ++import com.fasterxml.jackson.databind.node.ArrayNode; ++import com.fasterxml.jackson.databind.node.ObjectNode; ++ ++import org.elasticsearch.gradle.Version; ++import org.elasticsearch.gradle.internal.info.BranchesFileParser; ++import org.elasticsearch.gradle.internal.info.DevelopmentBranch; ++import org.gradle.api.DefaultTask; ++import org.gradle.api.InvalidUserDataException; ++import org.gradle.api.file.ProjectLayout; ++import org.gradle.api.logging.Logger; ++import org.gradle.api.logging.Logging; ++import org.gradle.api.tasks.Input; ++import org.gradle.api.tasks.Optional; ++import org.gradle.api.tasks.OutputFile; ++import org.gradle.api.tasks.TaskAction; ++import org.gradle.api.tasks.options.Option; ++ ++import java.io.File; ++import java.io.FileInputStream; ++import java.io.IOException; ++import java.io.UncheckedIOException; ++import java.nio.file.Files; ++import java.util.Comparator; ++import java.util.List; ++ ++import javax.inject.Inject; ++ ++/** ++ * Updates the branches.json file in the root of the repository ++ */ ++public class UpdateBranchesJsonTask extends DefaultTask { ++ ++ private static final Logger LOGGER = Logging.getLogger(UpdateBranchesJsonTask.class); ++ ++ private final ObjectMapper objectMapper; ++ private final BranchesFileParser branchesFileParser; ++ ++ @OutputFile ++ private File branchesFile; ++ ++ @Input ++ @Optional ++ private DevelopmentBranch addBranch; ++ @Input ++ @Optional ++ private String removeBranch; ++ @Input ++ @Optional ++ private DevelopmentBranch updateBranch; ++ ++ @Inject ++ public UpdateBranchesJsonTask(ProjectLayout projectLayout) { ++ this.objectMapper = new ObjectMapper(); ++ this.branchesFileParser = new BranchesFileParser(objectMapper); ++ this.branchesFile = projectLayout.getSettingsDirectory().file("branches.json").getAsFile(); ++ } ++ ++ public File getBranchesFile() { ++ return branchesFile; ++ } ++ ++ public void setBranchesFile(File branchesFile) { ++ this.branchesFile = branchesFile; ++ } ++ ++ public DevelopmentBranch getAddBranch() { ++ return addBranch; ++ } ++ ++ public String getRemoveBranch() { ++ return removeBranch; ++ } ++ ++ public DevelopmentBranch getUpdateBranch() { ++ return updateBranch; ++ } ++ ++ @Option(option = "add-branch", description = "Specifies the branch and corresponding version to add in format :") ++ public void addBranch(String branchAndVersion) { ++ this.addBranch = toDevelopmentBranch(branchAndVersion); ++ } ++ ++ @Option(option = "remove-branch", description = "Specifies the branch to remove") ++ public void removeBranch(String branch) { ++ this.removeBranch = branch; ++ } ++ ++ @Option(option = "update-branch", description = "Specifies the branch and corresponding version to update in format :") ++ public void updateBranch(String branchAndVersion) { ++ this.updateBranch = toDevelopmentBranch(branchAndVersion); ++ } ++ ++ private DevelopmentBranch toDevelopmentBranch(String branchAndVersion) { ++ String[] parts = branchAndVersion.split(":"); ++ if (parts.length != 2) { ++ throw new InvalidUserDataException("Expected branch and version in format :"); ++ } ++ return new DevelopmentBranch(parts[0], Version.fromString(parts[1])); ++ } ++ ++ @TaskAction ++ public void executeTask() throws IOException { ++ List developmentBranches = readBranches(branchesFile); ++ ++ if (addBranch == null && removeBranch == null && updateBranch == null) { ++ throw new InvalidUserDataException("At least one of add-branch, remove-branch or update-branch must be specified"); ++ } ++ ++ if (addBranch != null) { ++ LOGGER.info("Adding branch {} with version {}", addBranch.name(), addBranch.version()); ++ if (developmentBranches.stream().anyMatch(developmentBranch -> developmentBranch.name().equals(addBranch.name()))) { ++ throw new InvalidUserDataException("Branch " + addBranch.name() + " already exists"); ++ } ++ developmentBranches.add(addBranch); ++ } ++ if (removeBranch != null) { ++ LOGGER.info("Removing branch {}", removeBranch); ++ if (developmentBranches.stream().noneMatch(developmentBranch -> developmentBranch.name().equals(removeBranch))) { ++ throw new InvalidUserDataException("Branch " + removeBranch + " does not exist"); ++ } ++ developmentBranches.removeIf(developmentBranch -> developmentBranch.name().equals(removeBranch)); ++ } ++ if (updateBranch != null) { ++ LOGGER.info("Updating branch {} with version {}", updateBranch.name(), updateBranch.version()); ++ if (developmentBranches.stream().noneMatch(developmentBranch -> developmentBranch.name().equals(updateBranch.name()))) { ++ throw new InvalidUserDataException("Branch " + updateBranch.name() + " does not exist"); ++ } ++ developmentBranches.removeIf(developmentBranch -> developmentBranch.name().equals(updateBranch.name())); ++ developmentBranches.add(updateBranch); ++ } ++ ++ developmentBranches.sort(Comparator.comparing(DevelopmentBranch::version).reversed()); ++ ++ JsonNode jsonNode = objectMapper.readTree(new FileInputStream(branchesFile)); ++ ArrayNode updatedBranches = objectMapper.createArrayNode(); ++ for (DevelopmentBranch branch : developmentBranches) { ++ ObjectNode objectNode = objectMapper.createObjectNode(); ++ objectNode.put("branch", branch.name()); ++ objectNode.put("version", branch.version().toString()); ++ updatedBranches.add(objectNode); ++ } ++ ((ObjectNode) jsonNode).replace("branches", updatedBranches); ++ ++ DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter(); ++ prettyPrinter.indentArraysWith(new DefaultIndenter(" ", DefaultIndenter.SYS_LF)); ++ objectMapper.writer(prettyPrinter).writeValue(branchesFile, jsonNode); ++ } ++ ++ private List readBranches(File branchesFile) { ++ if (branchesFile.isFile() == false) { ++ throw new InvalidUserDataException("File branches.json has not been found in " + branchesFile.getAbsolutePath()); ++ } ++ ++ try { ++ byte[] branchesBytes = Files.readAllBytes(branchesFile.toPath()); ++ return branchesFileParser.parse(branchesBytes); ++ } catch (IOException e) { ++ throw new UncheckedIOException("Failed to read branches.json from " + branchesFile.getPath(), e); ++ } ++ } ++} +diff --git a/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/BwcVersionsSpec.groovy b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/BwcVersionsSpec.groovy +index a662a76db4d..09f1f12dada 100644 +--- a/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/BwcVersionsSpec.groovy ++++ b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/BwcVersionsSpec.groovy +@@ -13,33 +13,40 @@ import spock.lang.Specification + + import org.elasticsearch.gradle.Version + import org.elasticsearch.gradle.internal.BwcVersions.UnreleasedVersionInfo ++import org.elasticsearch.gradle.internal.info.DevelopmentBranch + + class BwcVersionsSpec extends Specification { +- List versionLines = [] ++ List versions = [] + + def "current version is next major"() { + given: +- addVersion('7.17.10', '8.9.0') +- addVersion('8.14.0', '9.9.0') +- addVersion('8.14.1', '9.9.0') +- addVersion('8.14.2', '9.9.0') +- addVersion('8.15.0', '9.9.0') +- addVersion('8.15.1', '9.9.0') +- addVersion('8.15.2', '9.9.0') +- addVersion('8.16.0', '9.10.0') +- addVersion('8.16.1', '9.10.0') +- addVersion('8.17.0', '9.10.0') +- addVersion('9.0.0', '10.0.0') ++ addVersion('7.17.10') ++ addVersion('8.14.0') ++ addVersion('8.14.1') ++ addVersion('8.14.2') ++ addVersion('8.15.0') ++ addVersion('8.15.1') ++ addVersion('8.15.2') ++ addVersion('8.16.0') ++ addVersion('8.16.1') ++ addVersion('8.17.0') ++ addVersion('9.0.0') + + when: +- def bwc = new BwcVersions(versionLines, v('9.0.0'), ['main', '8.x', '8.16', '8.15', '7.17']) ++ def bwc = new BwcVersions(v('9.0.0'), versions, [ ++ branch('main', '9.0.0'), ++ branch('8.x', '8.17.0'), ++ branch('8.16', '8.16.1'), ++ branch('8.15', '8.15.2'), ++ branch('7.17', '7.17.10') ++ ]) + def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] } + + then: + unreleased == [ +- (v('8.15.2')): new UnreleasedVersionInfo(v('8.15.2'), '8.15', ':distribution:bwc:bugfix2'), +- (v('8.16.1')): new UnreleasedVersionInfo(v('8.16.1'), '8.16', ':distribution:bwc:bugfix'), +- (v('8.17.0')): new UnreleasedVersionInfo(v('8.17.0'), '8.x', ':distribution:bwc:minor'), ++ (v('8.15.2')): new UnreleasedVersionInfo(v('8.15.2'), '8.15', ':distribution:bwc:major3'), ++ (v('8.16.1')): new UnreleasedVersionInfo(v('8.16.1'), '8.16', ':distribution:bwc:major2'), ++ (v('8.17.0')): new UnreleasedVersionInfo(v('8.17.0'), '8.x', ':distribution:bwc:major1'), + (v('9.0.0')): new UnreleasedVersionInfo(v('9.0.0'), 'main', ':distribution'), + ] + bwc.wireCompatible == [v('8.17.0'), v('9.0.0')] +@@ -48,29 +55,36 @@ class BwcVersionsSpec extends Specification { + + def "current version is next major with staged minor"() { + given: +- addVersion('7.17.10', '8.9.0') +- addVersion('8.14.0', '9.9.0') +- addVersion('8.14.1', '9.9.0') +- addVersion('8.14.2', '9.9.0') +- addVersion('8.15.0', '9.9.0') +- addVersion('8.15.1', '9.9.0') +- addVersion('8.15.2', '9.9.0') +- addVersion('8.16.0', '9.10.0') +- addVersion('8.16.1', '9.10.0') +- addVersion('8.17.0', '9.10.0') +- addVersion('8.18.0', '9.10.0') +- addVersion('9.0.0', '10.0.0') ++ addVersion('7.17.10') ++ addVersion('8.14.0') ++ addVersion('8.14.1') ++ addVersion('8.14.2') ++ addVersion('8.15.0') ++ addVersion('8.15.1') ++ addVersion('8.15.2') ++ addVersion('8.16.0') ++ addVersion('8.16.1') ++ addVersion('8.17.0') ++ addVersion('8.18.0') ++ addVersion('9.0.0') + + when: +- def bwc = new BwcVersions(versionLines, v('9.0.0'), ['main', '8.x', '8.17', '8.16', '8.15', '7.17']) ++ def bwc = new BwcVersions(v('9.0.0'), versions, [ ++ branch('main', '9.0.0'), ++ branch('8.x', '8.18.0'), ++ branch('8.17', '8.17.0'), ++ branch('8.16', '8.16.1'), ++ branch('8.15', '8.15.2'), ++ branch('7.17', '7.17.10'), ++ ]) + def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] } + + then: + unreleased == [ +- (v('8.15.2')): new UnreleasedVersionInfo(v('8.15.2'), '8.15', ':distribution:bwc:bugfix2'), +- (v('8.16.1')): new UnreleasedVersionInfo(v('8.16.1'), '8.16', ':distribution:bwc:bugfix'), +- (v('8.17.0')): new UnreleasedVersionInfo(v('8.17.0'), '8.17', ':distribution:bwc:staged'), +- (v('8.18.0')): new UnreleasedVersionInfo(v('8.18.0'), '8.x', ':distribution:bwc:minor'), ++ (v('8.15.2')): new UnreleasedVersionInfo(v('8.15.2'), '8.15', ':distribution:bwc:major4'), ++ (v('8.16.1')): new UnreleasedVersionInfo(v('8.16.1'), '8.16', ':distribution:bwc:major3'), ++ (v('8.17.0')): new UnreleasedVersionInfo(v('8.17.0'), '8.17', ':distribution:bwc:major2'), ++ (v('8.18.0')): new UnreleasedVersionInfo(v('8.18.0'), '8.x', ':distribution:bwc:major1'), + (v('9.0.0')): new UnreleasedVersionInfo(v('9.0.0'), 'main', ':distribution'), + ] + bwc.wireCompatible == [v('8.18.0'), v('9.0.0')] +@@ -79,31 +93,39 @@ class BwcVersionsSpec extends Specification { + + def "current version is next major with two staged minors"() { + given: +- addVersion('7.17.10', '8.9.0') +- addVersion('8.15.0', '9.9.0') +- addVersion('8.15.1', '9.9.0') +- addVersion('8.15.2', '9.9.0') +- addVersion('8.16.0', '9.10.0') +- addVersion('8.16.1', '9.10.0') +- addVersion('8.16.2', '9.10.0') +- addVersion('8.17.0', '9.10.0') +- addVersion('8.17.1', '9.10.0') +- addVersion('8.18.0', '9.10.0') +- addVersion('8.19.0', '9.10.0') +- addVersion('9.0.0', '10.0.0') +- addVersion('9.1.0', '10.1.0') ++ addVersion('7.17.10') ++ addVersion('8.15.0') ++ addVersion('8.15.1') ++ addVersion('8.15.2') ++ addVersion('8.16.0') ++ addVersion('8.16.1') ++ addVersion('8.16.2') ++ addVersion('8.17.0') ++ addVersion('8.17.1') ++ addVersion('8.18.0') ++ addVersion('8.19.0') ++ addVersion('9.0.0') ++ addVersion('9.1.0') + + when: +- def bwc = new BwcVersions(versionLines, v('9.1.0'), ['main', '9.0', '8.x', '8.18', '8.17', '8.16', '7.17']) ++ def bwc = new BwcVersions(v('9.1.0'), versions, [ ++ branch('main', '9.1.0'), ++ branch('9.0', '9.0.0'), ++ branch('8.x', '8.19.0'), ++ branch('8.18', '8.18.0'), ++ branch('8.17', '8.17.1'), ++ branch('8.16', '8.16.2'), ++ branch('7.17', '7.17.10') ++ ]) + def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] } + + then: + unreleased == [ +- (v('8.16.2')): new UnreleasedVersionInfo(v('8.16.2'), '8.16', ':distribution:bwc:bugfix2'), +- (v('8.17.1')): new UnreleasedVersionInfo(v('8.17.1'), '8.17', ':distribution:bwc:bugfix'), +- (v('8.18.0')): new UnreleasedVersionInfo(v('8.18.0'), '8.18', ':distribution:bwc:staged2'), +- (v('8.19.0')): new UnreleasedVersionInfo(v('8.19.0'), '8.x', ':distribution:bwc:minor'), +- (v('9.0.0')): new UnreleasedVersionInfo(v('9.0.0'), '9.0', ':distribution:bwc:staged'), ++ (v('8.16.2')): new UnreleasedVersionInfo(v('8.16.2'), '8.16', ':distribution:bwc:major4'), ++ (v('8.17.1')): new UnreleasedVersionInfo(v('8.17.1'), '8.17', ':distribution:bwc:major3'), ++ (v('8.18.0')): new UnreleasedVersionInfo(v('8.18.0'), '8.18', ':distribution:bwc:major2'), ++ (v('8.19.0')): new UnreleasedVersionInfo(v('8.19.0'), '8.x', ':distribution:bwc:major1'), ++ (v('9.0.0')): new UnreleasedVersionInfo(v('9.0.0'), '9.0', ':distribution:bwc:minor1'), + (v('9.1.0')): new UnreleasedVersionInfo(v('9.1.0'), 'main', ':distribution'), + ] + bwc.wireCompatible == [v('8.19.0'), v('9.0.0'), v('9.1.0')] +@@ -112,22 +134,26 @@ class BwcVersionsSpec extends Specification { + + def "current version is first new minor in major series"() { + given: +- addVersion('7.17.10', '8.9.0') +- addVersion('8.16.0', '9.10.0') +- addVersion('8.16.1', '9.10.0') +- addVersion('8.17.0', '9.10.0') +- addVersion('8.18.0', '9.10.0') +- addVersion('9.0.0', '10.0.0') +- addVersion('9.1.0', '10.0.0') ++ addVersion('7.17.10') ++ addVersion('8.16.0') ++ addVersion('8.16.1') ++ addVersion('8.17.0') ++ addVersion('8.18.0') ++ addVersion('9.0.0') ++ addVersion('9.1.0') + + when: +- def bwc = new BwcVersions(versionLines, v('9.1.0'), ['main', '9.0', '8.18']) ++ def bwc = new BwcVersions(v('9.1.0'), versions, [ ++ branch('main','9.1.0'), ++ branch('9.0', '9.0.0'), ++ branch('8.18', '8.18.0') ++ ]) + def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] } + + then: + unreleased == [ +- (v('8.18.0')): new UnreleasedVersionInfo(v('8.18.0'), '8.18', ':distribution:bwc:maintenance'), +- (v('9.0.0')): new UnreleasedVersionInfo(v('9.0.0'), '9.0', ':distribution:bwc:staged'), ++ (v('8.18.0')): new UnreleasedVersionInfo(v('8.18.0'), '8.18', ':distribution:bwc:major1'), ++ (v('9.0.0')): new UnreleasedVersionInfo(v('9.0.0'), '9.0', ':distribution:bwc:minor1'), + (v('9.1.0')): new UnreleasedVersionInfo(v('9.1.0'), 'main', ':distribution'), + ] + bwc.wireCompatible == [v('8.18.0'), v('9.0.0'), v('9.1.0')] +@@ -136,23 +162,27 @@ class BwcVersionsSpec extends Specification { + + def "current version is new minor with single bugfix"() { + given: +- addVersion('7.17.10', '8.9.0') +- addVersion('8.16.0', '9.10.0') +- addVersion('8.16.1', '9.10.0') +- addVersion('8.17.0', '9.10.0') +- addVersion('8.18.0', '9.10.0') +- addVersion('9.0.0', '10.0.0') +- addVersion('9.0.1', '10.0.0') +- addVersion('9.1.0', '10.0.0') ++ addVersion('7.17.10') ++ addVersion('8.16.0') ++ addVersion('8.16.1') ++ addVersion('8.17.0') ++ addVersion('8.18.0') ++ addVersion('9.0.0') ++ addVersion('9.0.1') ++ addVersion('9.1.0') + + when: +- def bwc = new BwcVersions(versionLines, v('9.1.0'), ['main', '9.0', '8.18']) ++ def bwc = new BwcVersions(v('9.1.0'), versions, [ ++ branch('main','9.1.0'), ++ branch('9.0','9.0.1'), ++ branch('8.18','8.18.0') ++ ]) + def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] } + + then: + unreleased == [ +- (v('8.18.0')): new UnreleasedVersionInfo(v('8.18.0'), '8.18', ':distribution:bwc:maintenance'), +- (v('9.0.1')): new UnreleasedVersionInfo(v('9.0.1'), '9.0', ':distribution:bwc:bugfix'), ++ (v('8.18.0')): new UnreleasedVersionInfo(v('8.18.0'), '8.18', ':distribution:bwc:major1'), ++ (v('9.0.1')): new UnreleasedVersionInfo(v('9.0.1'), '9.0', ':distribution:bwc:minor1'), + (v('9.1.0')): new UnreleasedVersionInfo(v('9.1.0'), 'main', ':distribution'), + ] + bwc.wireCompatible == [v('8.18.0'), v('9.0.0'), v('9.0.1'), v('9.1.0')] +@@ -161,25 +191,30 @@ class BwcVersionsSpec extends Specification { + + def "current version is new minor with single bugfix and staged minor"() { + given: +- addVersion('7.17.10', '8.9.0') +- addVersion('8.16.0', '9.10.0') +- addVersion('8.16.1', '9.10.0') +- addVersion('8.17.0', '9.10.0') +- addVersion('8.18.0', '9.10.0') +- addVersion('9.0.0', '10.0.0') +- addVersion('9.0.1', '10.0.0') +- addVersion('9.1.0', '10.0.0') +- addVersion('9.2.0', '10.0.0') ++ addVersion('7.17.10') ++ addVersion('8.16.0') ++ addVersion('8.16.1') ++ addVersion('8.17.0') ++ addVersion('8.18.0') ++ addVersion('9.0.0') ++ addVersion('9.0.1') ++ addVersion('9.1.0') ++ addVersion('9.2.0') + + when: +- def bwc = new BwcVersions(versionLines, v('9.2.0'), ['main', '9.1', '9.0', '8.18']) ++ def bwc = new BwcVersions(v('9.2.0'), versions, [ ++ branch('main','9.2.0'), ++ branch('9.1','9.1.0'), ++ branch('9.0','9.0.1'), ++ branch('8.18', '8.18.0') ++ ]) + def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] } + + then: + unreleased == [ +- (v('8.18.0')): new UnreleasedVersionInfo(v('8.18.0'), '8.18', ':distribution:bwc:maintenance'), +- (v('9.0.1')): new UnreleasedVersionInfo(v('9.0.1'), '9.0', ':distribution:bwc:bugfix'), +- (v('9.1.0')): new UnreleasedVersionInfo(v('9.1.0'), '9.1', ':distribution:bwc:staged'), ++ (v('8.18.0')): new UnreleasedVersionInfo(v('8.18.0'), '8.18', ':distribution:bwc:major1'), ++ (v('9.0.1')): new UnreleasedVersionInfo(v('9.0.1'), '9.0', ':distribution:bwc:minor2'), ++ (v('9.1.0')): new UnreleasedVersionInfo(v('9.1.0'), '9.1', ':distribution:bwc:minor1'), + (v('9.2.0')): new UnreleasedVersionInfo(v('9.2.0'), 'main', ':distribution'), + ] + bwc.wireCompatible == [v('8.18.0'), v('9.0.0'), v('9.0.1'), v('9.1.0'), v('9.2.0')] +@@ -188,30 +223,37 @@ class BwcVersionsSpec extends Specification { + + def "current version is next minor"() { + given: +- addVersion('7.16.3', '8.9.0') +- addVersion('7.17.0', '8.9.0') +- addVersion('7.17.1', '8.9.0') +- addVersion('8.14.0', '9.9.0') +- addVersion('8.14.1', '9.9.0') +- addVersion('8.14.2', '9.9.0') +- addVersion('8.15.0', '9.9.0') +- addVersion('8.15.1', '9.9.0') +- addVersion('8.15.2', '9.9.0') +- addVersion('8.16.0', '9.10.0') +- addVersion('8.16.1', '9.10.0') +- addVersion('8.17.0', '9.10.0') +- addVersion('8.17.1', '9.10.0') +- addVersion('8.18.0', '9.10.0') ++ addVersion('7.16.3') ++ addVersion('7.17.0') ++ addVersion('7.17.1') ++ addVersion('8.14.0') ++ addVersion('8.14.1') ++ addVersion('8.14.2') ++ addVersion('8.15.0') ++ addVersion('8.15.1') ++ addVersion('8.15.2') ++ addVersion('8.16.0') ++ addVersion('8.16.1') ++ addVersion('8.17.0') ++ addVersion('8.17.1') ++ addVersion('8.18.0') + + when: +- def bwc = new BwcVersions(versionLines, v('8.18.0'), ['main', '8.x', '8.17', '8.16', '7.17']) ++ def bwc = new BwcVersions(v('8.18.0'), versions, [ ++ branch('main', '9.1.0'), ++ branch('9.0', '9.0.1'), ++ branch('8.x', '8.18.0'), ++ branch('8.17', '8.17.1'), ++ branch('8.16', '8.16.1'), ++ branch('7.17', '7.17.1'), ++ ]) + def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] } + + then: + unreleased == [ +- (v('7.17.1')): new UnreleasedVersionInfo(v('7.17.1'), '7.17', ':distribution:bwc:maintenance'), +- (v('8.16.1')): new UnreleasedVersionInfo(v('8.16.1'), '8.16', ':distribution:bwc:bugfix2'), +- (v('8.17.1')): new UnreleasedVersionInfo(v('8.17.1'), '8.17', ':distribution:bwc:bugfix'), ++ (v('7.17.1')): new UnreleasedVersionInfo(v('7.17.1'), '7.17', ':distribution:bwc:major1'), ++ (v('8.16.1')): new UnreleasedVersionInfo(v('8.16.1'), '8.16', ':distribution:bwc:minor2'), ++ (v('8.17.1')): new UnreleasedVersionInfo(v('8.17.1'), '8.17', ':distribution:bwc:minor1'), + (v('8.18.0')): new UnreleasedVersionInfo(v('8.18.0'), '8.x', ':distribution'), + ] + bwc.wireCompatible == [v('7.17.0'), v('7.17.1'), v('8.14.0'), v('8.14.1'), v('8.14.2'), v('8.15.0'), v('8.15.1'), v('8.15.2'), v('8.16.0'), v('8.16.1'), v('8.17.0'), v('8.17.1'), v('8.18.0')] +@@ -220,30 +262,37 @@ class BwcVersionsSpec extends Specification { + + def "current version is new minor with staged minor"() { + given: +- addVersion('7.16.3', '8.9.0') +- addVersion('7.17.0', '8.9.0') +- addVersion('7.17.1', '8.9.0') +- addVersion('8.14.0', '9.9.0') +- addVersion('8.14.1', '9.9.0') +- addVersion('8.14.2', '9.9.0') +- addVersion('8.15.0', '9.9.0') +- addVersion('8.15.1', '9.9.0') +- addVersion('8.15.2', '9.9.0') +- addVersion('8.16.0', '9.10.0') +- addVersion('8.16.1', '9.10.0') +- addVersion('8.17.0', '9.10.0') +- addVersion('8.18.0', '9.10.0') ++ addVersion('7.16.3') ++ addVersion('7.17.0') ++ addVersion('7.17.1') ++ addVersion('8.14.0') ++ addVersion('8.14.1') ++ addVersion('8.14.2') ++ addVersion('8.15.0') ++ addVersion('8.15.1') ++ addVersion('8.15.2') ++ addVersion('8.16.0') ++ addVersion('8.16.1') ++ addVersion('8.17.0') ++ addVersion('8.18.0') + + when: +- def bwc = new BwcVersions(versionLines, v('8.18.0'), ['main', '8.x', '8.17', '8.16', '8.15', '7.17']) ++ def bwc = new BwcVersions(v('8.18.0'), versions, [ ++ branch('main', '9.0.0'), ++ branch('8.x', '8.18.0'), ++ branch('8.17', '8.17.0'), ++ branch('8.16', '8.16.1'), ++ branch('8.15', '8.15.2'), ++ branch('7.17', '7.17.1'), ++ ]) + def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] } + + then: + unreleased == [ +- (v('7.17.1')): new UnreleasedVersionInfo(v('7.17.1'), '7.17', ':distribution:bwc:maintenance'), +- (v('8.15.2')): new UnreleasedVersionInfo(v('8.15.2'), '8.15', ':distribution:bwc:bugfix2'), +- (v('8.16.1')): new UnreleasedVersionInfo(v('8.16.1'), '8.16', ':distribution:bwc:bugfix'), +- (v('8.17.0')): new UnreleasedVersionInfo(v('8.17.0'), '8.17', ':distribution:bwc:staged'), ++ (v('7.17.1')): new UnreleasedVersionInfo(v('7.17.1'), '7.17', ':distribution:bwc:major1'), ++ (v('8.15.2')): new UnreleasedVersionInfo(v('8.15.2'), '8.15', ':distribution:bwc:minor3'), ++ (v('8.16.1')): new UnreleasedVersionInfo(v('8.16.1'), '8.16', ':distribution:bwc:minor2'), ++ (v('8.17.0')): new UnreleasedVersionInfo(v('8.17.0'), '8.17', ':distribution:bwc:minor1'), + (v('8.18.0')): new UnreleasedVersionInfo(v('8.18.0'), '8.x', ':distribution'), + ] + bwc.wireCompatible == [v('7.17.0'), v('7.17.1'), v('8.14.0'), v('8.14.1'), v('8.14.2'), v('8.15.0'), v('8.15.1'), v('8.15.2'), v('8.16.0'), v('8.16.1'), v('8.17.0'), v('8.18.0')] +@@ -252,26 +301,33 @@ class BwcVersionsSpec extends Specification { + + def "current version is first bugfix"() { + given: +- addVersion('7.16.3', '8.9.0') +- addVersion('7.17.0', '8.9.0') +- addVersion('7.17.1', '8.9.0') +- addVersion('8.14.0', '9.9.0') +- addVersion('8.14.1', '9.9.0') +- addVersion('8.14.2', '9.9.0') +- addVersion('8.15.0', '9.9.0') +- addVersion('8.15.1', '9.9.0') +- addVersion('8.15.2', '9.9.0') +- addVersion('8.16.0', '9.10.0') +- addVersion('8.16.1', '9.10.0') ++ addVersion('7.16.3') ++ addVersion('7.17.0') ++ addVersion('7.17.1') ++ addVersion('8.14.0') ++ addVersion('8.14.1') ++ addVersion('8.14.2') ++ addVersion('8.15.0') ++ addVersion('8.15.1') ++ addVersion('8.15.2') ++ addVersion('8.16.0') ++ addVersion('8.16.1') + + when: +- def bwc = new BwcVersions(versionLines, v('8.16.1'), ['main', '8.x', '8.17', '8.16', '8.15', '7.17']) ++ def bwc = new BwcVersions(v('8.16.1'), versions, [ ++ branch('main','9.0.1'), ++ branch('8.x','8.18.0'), ++ branch('8.17','8.17.0'), ++ branch('8.16','8.16.1'), ++ branch('8.15','8.15.2'), ++ branch('7.17','7.17.1'), ++ ]) + def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] } + + then: + unreleased == [ +- (v('7.17.1')): new UnreleasedVersionInfo(v('7.17.1'), '7.17', ':distribution:bwc:maintenance'), +- (v('8.15.2')): new UnreleasedVersionInfo(v('8.15.2'), '8.15', ':distribution:bwc:bugfix'), ++ (v('7.17.1')): new UnreleasedVersionInfo(v('7.17.1'), '7.17', ':distribution:bwc:major1'), ++ (v('8.15.2')): new UnreleasedVersionInfo(v('8.15.2'), '8.15', ':distribution:bwc:minor1'), + (v('8.16.1')): new UnreleasedVersionInfo(v('8.16.1'), '8.16', ':distribution'), + ] + bwc.wireCompatible == [v('7.17.0'), v('7.17.1'), v('8.14.0'), v('8.14.1'), v('8.14.2'), v('8.15.0'), v('8.15.1'), v('8.15.2'), v('8.16.0'), v('8.16.1')] +@@ -280,37 +336,46 @@ class BwcVersionsSpec extends Specification { + + def "current version is second bugfix"() { + given: +- addVersion('7.16.3', '8.9.0') +- addVersion('7.17.0', '8.9.0') +- addVersion('7.17.1', '8.9.0') +- addVersion('8.14.0', '9.9.0') +- addVersion('8.14.1', '9.9.0') +- addVersion('8.14.2', '9.9.0') +- addVersion('8.15.0', '9.9.0') +- addVersion('8.15.1', '9.9.0') +- addVersion('8.15.2', '9.9.0') ++ addVersion('7.16.3') ++ addVersion('7.17.0') ++ addVersion('7.17.1') ++ addVersion('8.14.0') ++ addVersion('8.14.1') ++ addVersion('8.14.2') ++ addVersion('8.15.0') ++ addVersion('8.15.1') ++ addVersion('8.15.2') + + when: +- def bwc = new BwcVersions(versionLines, v('8.15.2'), ['main', '8.x', '8.17', '8.16', '8.15', '7.17']) ++ def bwc = new BwcVersions(v('8.15.2'), versions, [ ++ branch('main', '9.0.1'), ++ branch('8.x', '8.18.1'), ++ branch('8.17', '8.17.2'), ++ branch('8.16', '8.16.10'), ++ branch('8.15', '8.15.2'), ++ branch('7.17', '7.17.1'), ++ ]) + def unreleased = bwc.unreleased.collectEntries { [it, bwc.unreleasedInfo(it)] } + + then: + unreleased == [ +- (v('7.17.1')): new UnreleasedVersionInfo(v('7.17.1'), '7.17', ':distribution:bwc:maintenance'), ++ (v('7.17.1')): new UnreleasedVersionInfo(v('7.17.1'), '7.17', ':distribution:bwc:major1'), + (v('8.15.2')): new UnreleasedVersionInfo(v('8.15.2'), '8.15', ':distribution'), + ] + bwc.wireCompatible == [v('7.17.0'), v('7.17.1'), v('8.14.0'), v('8.14.1'), v('8.14.2'), v('8.15.0'), v('8.15.1'), v('8.15.2')] + bwc.indexCompatible == [v('7.16.3'), v('7.17.0'), v('7.17.1'), v('8.14.0'), v('8.14.1'), v('8.14.2'), v('8.15.0'), v('8.15.1'), v('8.15.2')] + } + +- 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 void addVersion(String elasticsearch) { ++ versions.add(Version.fromString(elasticsearch)) + } + + private Version v(String version) { + return Version.fromString(version) + } + ++ private DevelopmentBranch branch(String name, String version) { ++ return new DevelopmentBranch(name, v(version)) ++ } ++ + } +diff --git a/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPluginSpec.groovy b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPluginSpec.groovy +new file mode 100644 +index 00000000000..bbb58041eee +--- /dev/null ++++ b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPluginSpec.groovy +@@ -0,0 +1,111 @@ ++/* ++ * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public ++ * License v3.0 only", or the "Server Side Public License, v 1". ++ */ ++ ++package org.elasticsearch.gradle.internal.info ++ ++import groovy.json.JsonOutput ++import spock.lang.Specification ++import spock.lang.TempDir ++ ++import org.elasticsearch.gradle.Version ++import org.elasticsearch.gradle.internal.BwcVersions ++import org.gradle.api.Project ++import org.gradle.api.provider.Provider ++import org.gradle.api.provider.ProviderFactory ++import org.gradle.testfixtures.ProjectBuilder ++ ++import java.nio.file.Path ++ ++class GlobalBuildInfoPluginSpec extends Specification { ++ ++ @TempDir ++ File projectRoot ++ ++ Project project ++ ++ def setup() { ++ project = ProjectBuilder.builder() ++ .withProjectDir(projectRoot) ++ .withName("bwcTestProject") ++ .build() ++ project = Spy(project) ++ project.getRootProject() >> project ++ ++ File buildToolsInternalDir = new File(projectRoot, "build-tools-internal") ++ buildToolsInternalDir.mkdirs() ++ new File(buildToolsInternalDir, "version.properties").text = """ ++ elasticsearch = 9.1.0 ++ lucene = 10.2.2 ++ ++ bundled_jdk_vendor = openjdk ++ bundled_jdk = 24+36@1f9ff9062db4449d8ca828c504ffae90 ++ minimumJdkVersion = 21 ++ minimumRuntimeJava = 21 ++ minimumCompilerJava = 21 ++ """ ++ File versionFileDir = new File(projectRoot, "server/src/main/java/org/elasticsearch") ++ versionFileDir.mkdirs() ++ new File(versionFileDir, "Version.java").text = """ ++ package org.elasticsearch; ++ public class Version { ++ public static final Version V_8_17_8 = new Version(8_17_08_99); ++ public static final Version V_8_18_0 = new Version(8_18_00_99); ++ public static final Version V_8_18_1 = new Version(8_18_01_99); ++ public static final Version V_8_18_2 = new Version(8_18_02_99); ++ public static final Version V_8_18_3 = new Version(8_18_03_99); ++ public static final Version V_8_19_0 = new Version(8_19_00_99); ++ public static final Version V_9_0_0 = new Version(9_00_00_99); ++ public static final Version V_9_0_1 = new Version(9_00_01_99); ++ public static final Version V_9_0_2 = new Version(9_00_02_99); ++ public static final Version V_9_0_3 = new Version(9_00_03_99); ++ public static final Version V_9_1_0 = new Version(9_01_00_99); ++ public static final Version CURRENT = V_9_1_0; ++ ++ } ++ """ ++ } ++ ++ def "resolve unreleased versions from branches file set by Gradle property"() { ++ given: ++ ProviderFactory providerFactorySpy = Spy(project.getProviders()) ++ Path branchesJsonPath = projectRoot.toPath().resolve("myBranches.json") ++ Provider gradleBranchesLocationProvider = project.providers.provider { return branchesJsonPath.toString() } ++ providerFactorySpy.gradleProperty("org.elasticsearch.build.branches-file-location") >> gradleBranchesLocationProvider ++ project.getProviders() >> providerFactorySpy ++ branchesJsonPath.text = branchesJson( ++ [ ++ new DevelopmentBranch("main", Version.fromString("9.1.0")), ++ new DevelopmentBranch("9.0", Version.fromString("9.0.3")), ++ new DevelopmentBranch("8.19", Version.fromString("8.19.1")), ++ new DevelopmentBranch("8.18", Version.fromString("8.18.2")), ++ ] ++ ) ++ ++ when: ++ project.objects.newInstance(GlobalBuildInfoPlugin).apply(project) ++ BuildParameterExtension ext = project.extensions.getByType(BuildParameterExtension) ++ BwcVersions bwcVersions = ext.bwcVersions ++ ++ then: ++ bwcVersions != null ++ bwcVersions.unreleased.toSet() == ["9.1.0", "9.0.3", "8.19.1", "8.18.2"].collect { Version.fromString(it) }.toSet() ++ } ++ ++ String branchesJson(List branches) { ++ Map branchesFileContent = [ ++ branches: branches.collect { branch -> ++ [ ++ branch : branch.name(), ++ version: branch.version().toString(), ++ ] ++ } ++ ] ++ return JsonOutput.prettyPrint(JsonOutput.toJson(branchesFileContent)) ++ } ++} +diff --git a/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTaskSpec.groovy b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTaskSpec.groovy +new file mode 100644 +index 00000000000..705c3a8335d +--- /dev/null ++++ b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTaskSpec.groovy +@@ -0,0 +1,267 @@ ++/* ++ * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public ++ * License v3.0 only", or the "Server Side Public License, v 1". ++ */ ++ ++package org.elasticsearch.gradle.internal.release ++ ++import groovy.json.JsonOutput ++import groovy.json.JsonSlurper ++import spock.lang.Specification ++import spock.lang.Subject ++ ++import org.elasticsearch.gradle.Version ++import org.elasticsearch.gradle.internal.info.DevelopmentBranch ++import org.gradle.api.InvalidUserDataException ++import org.gradle.api.Project ++import org.gradle.testfixtures.ProjectBuilder ++import spock.lang.TempDir ++ ++class UpdateBranchesJsonTaskSpec extends Specification { ++ ++ @Subject ++ UpdateBranchesJsonTask task ++ ++ @TempDir ++ File projectRoot ++ JsonSlurper jsonSlurper = new JsonSlurper() ++ ++ def setup() { ++ Project project = ProjectBuilder.builder().withProjectDir(projectRoot).build() ++ task = project.tasks.register("updateBranchesJson", UpdateBranchesJsonTask).get() ++ task.branchesFile = new File(projectRoot, "branches.json") ++ } ++ ++ def "add new branch to branches.json (sorted by version) when --add-branch is specified"() { ++ given: ++ branchesJson([ ++ new DevelopmentBranch("main", Version.fromString("9.1.0")), ++ new DevelopmentBranch("8.18", Version.fromString("8.18.2")), ++ new DevelopmentBranch("8.17", Version.fromString("8.17.7")), ++ ]) ++ task.addBranch('8.19:8.19.0') ++ ++ when: ++ task.executeTask() ++ ++ then: ++ def branchesFile = new File(projectRoot, "branches.json") ++ def json = jsonSlurper.parse(branchesFile) ++ json.branches == [ ++ [branch: 'main', version: '9.1.0'], ++ [branch: '8.19', version: '8.19.0'], ++ [branch: '8.18', version: '8.18.2'], ++ [branch: '8.17', version: '8.17.7'], ++ ] ++ } ++ ++ def "remove branch from branches.json when --remove-branch is specified"() { ++ given: ++ branchesJson([ ++ new DevelopmentBranch("main", Version.fromString("9.1.0")), ++ new DevelopmentBranch("8.18", Version.fromString("8.18.2")), ++ new DevelopmentBranch("8.17", Version.fromString("8.17.7")), ++ ]) ++ task.removeBranch('8.18') ++ ++ when: ++ task.executeTask() ++ ++ then: ++ def branchesFile = new File(projectRoot, "branches.json") ++ def json = jsonSlurper.parse(branchesFile) ++ json.branches == [ ++ [branch: 'main', version: '9.1.0'], ++ [branch: '8.17', version: '8.17.7'], ++ ] ++ } ++ ++ def "update branch version when --update-branch is specified"() { ++ given: ++ branchesJson([ ++ new DevelopmentBranch("main", Version.fromString("9.1.0")), ++ new DevelopmentBranch("8.18", Version.fromString("8.18.2")), ++ new DevelopmentBranch("8.17", Version.fromString("8.17.7")), ++ ]) ++ task.updateBranch('8.18:8.18.3') ++ ++ when: ++ task.executeTask() ++ ++ then: ++ def branchesFile = new File(projectRoot, "branches.json") ++ def json = jsonSlurper.parse(branchesFile) ++ json.branches == [ ++ [branch: 'main', version: '9.1.0'], ++ [branch: '8.18', version: '8.18.3'], ++ [branch: '8.17', version: '8.17.7'], ++ ] ++ } ++ ++ def "change branches.json when multiple options are specified"() { ++ given: ++ branchesJson([ ++ new DevelopmentBranch("main", Version.fromString("9.1.0")), ++ new DevelopmentBranch("8.18", Version.fromString("8.18.2")), ++ new DevelopmentBranch("8.17", Version.fromString("8.17.7")), ++ ]) ++ task.addBranch('8.19:8.19.0') ++ task.removeBranch('8.18') ++ task.updateBranch('8.17:8.17.8') ++ ++ when: ++ task.executeTask() ++ ++ then: ++ def branchesFile = new File(projectRoot, "branches.json") ++ def json = jsonSlurper.parse(branchesFile) ++ json.branches == [ ++ [branch: 'main', version: '9.1.0'], ++ [branch: '8.19', version: '8.19.0'], ++ [branch: '8.17', version: '8.17.8'], ++ ] ++ } ++ ++ def "fail when no --add-branch, --remove-branch or --update-branch is specified"() { ++ given: ++ branchesJson([ ++ new DevelopmentBranch("main", Version.fromString("9.1.0")), ++ new DevelopmentBranch("8.18", Version.fromString("8.18.2")), ++ new DevelopmentBranch("8.17", Version.fromString("8.17.7")), ++ ]) ++ ++ when: ++ task.executeTask() ++ ++ then: ++ def branchesFile = new File(projectRoot, "branches.json") ++ def json = jsonSlurper.parse(branchesFile) ++ json.branches == [ ++ [branch: 'main', version: '9.1.0'], ++ [branch: '8.18', version: '8.18.2'], ++ [branch: '8.17', version: '8.17.7'], ++ ] ++ thrown(InvalidUserDataException) ++ } ++ ++ def "fail when adding a branch that already exists"() { ++ given: ++ branchesJson([ ++ new DevelopmentBranch("main", Version.fromString("9.1.0")), ++ new DevelopmentBranch("8.18", Version.fromString("8.18.2")), ++ new DevelopmentBranch("8.17", Version.fromString("8.17.7")), ++ ]) ++ task.addBranch('8.18:8.18.3') ++ ++ when: ++ task.executeTask() ++ ++ then: ++ def branchesFile = new File(projectRoot, "branches.json") ++ def json = jsonSlurper.parse(branchesFile) ++ json.branches == [ ++ [branch: 'main', version: '9.1.0'], ++ [branch: '8.18', version: '8.18.2'], ++ [branch: '8.17', version: '8.17.7'], ++ ] ++ thrown(InvalidUserDataException) ++ } ++ ++ def "fail when removing a branch that does not exist"() { ++ given: ++ branchesJson([ ++ new DevelopmentBranch("main", Version.fromString("9.1.0")), ++ new DevelopmentBranch("8.18", Version.fromString("8.18.2")), ++ new DevelopmentBranch("8.17", Version.fromString("8.17.7")), ++ ]) ++ task.removeBranch('8.19') ++ ++ when: ++ task.executeTask() ++ ++ then: ++ def branchesFile = new File(projectRoot, "branches.json") ++ def json = jsonSlurper.parse(branchesFile) ++ json.branches == [ ++ [branch: 'main', version: '9.1.0'], ++ [branch: '8.18', version: '8.18.2'], ++ [branch: '8.17', version: '8.17.7'], ++ ] ++ thrown(InvalidUserDataException) ++ } ++ ++ def "fail when updating a branch that does not exist"() { ++ given: ++ branchesJson([ ++ new DevelopmentBranch("main", Version.fromString("9.1.0")), ++ new DevelopmentBranch("8.18", Version.fromString("8.18.2")), ++ new DevelopmentBranch("8.17", Version.fromString("8.17.7")), ++ ]) ++ task.updateBranch('8.19:8.19.0') ++ ++ when: ++ task.executeTask() ++ ++ then: ++ def branchesFile = new File(projectRoot, "branches.json") ++ def json = jsonSlurper.parse(branchesFile) ++ json.branches == [ ++ [branch: 'main', version: '9.1.0'], ++ [branch: '8.18', version: '8.18.2'], ++ [branch: '8.17', version: '8.17.7'], ++ ] ++ thrown(InvalidUserDataException) ++ } ++ ++ def "fail when adding a branch with an invalid version"() { ++ given: ++ branchesJson([ ++ new DevelopmentBranch("main", Version.fromString("9.1.0")), ++ new DevelopmentBranch("8.18", Version.fromString("8.18.2")), ++ new DevelopmentBranch("8.17", Version.fromString("8.17.7")), ++ ]) ++ ++ when: ++ task.addBranch('8.19:invalid') ++ task.executeTask() ++ ++ then: ++ def branchesFile = new File(projectRoot, "branches.json") ++ def json = jsonSlurper.parse(branchesFile) ++ json.branches == [ ++ [branch: 'main', version: '9.1.0'], ++ [branch: '8.18', version: '8.18.2'], ++ [branch: '8.17', version: '8.17.7'], ++ ] ++ thrown(IllegalArgumentException) ++ } ++ ++ def "fail when branches.json is missing"() { ++ given: ++ task.updateBranch('8.19:8.19.0') ++ ++ when: ++ task.executeTask() ++ ++ then: ++ def exception = thrown(InvalidUserDataException) ++ exception.message.contains("branches.json has not been found") ++ } ++ ++ void branchesJson(List branches) { ++ File branchesFile = new File(projectRoot, "branches.json") ++ Map branchesFileContent = [ ++ branches: branches.collect { branch -> ++ [ ++ branch: branch.name(), ++ version: branch.version().toString(), ++ ] ++ } ++ ] ++ branchesFile.text = JsonOutput.prettyPrint(JsonOutput.toJson(branchesFileContent)) ++ } ++} +diff --git a/build-tools-internal/src/test/java/org/elasticsearch/gradle/AbstractDistributionDownloadPluginTests.java b/build-tools-internal/src/test/java/org/elasticsearch/gradle/AbstractDistributionDownloadPluginTests.java +index 7512fa20814..d2c9bb2c88b 100644 +--- a/build-tools-internal/src/test/java/org/elasticsearch/gradle/AbstractDistributionDownloadPluginTests.java ++++ b/build-tools-internal/src/test/java/org/elasticsearch/gradle/AbstractDistributionDownloadPluginTests.java +@@ -10,6 +10,7 @@ + package org.elasticsearch.gradle; + + import org.elasticsearch.gradle.internal.BwcVersions; ++import org.elasticsearch.gradle.internal.info.DevelopmentBranch; + import org.gradle.api.NamedDomainObjectContainer; + import org.gradle.api.Project; + import org.gradle.testfixtures.ProjectBuilder; +@@ -29,7 +30,12 @@ public class AbstractDistributionDownloadPluginTests { + 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 List DEVELOPMENT_BRANCHES = Arrays.asList("main", "1.1", "1.0", "0.90"); ++ protected static final List DEVELOPMENT_BRANCHES = Arrays.asList( ++ new DevelopmentBranch("main", Version.fromString("2.0.0")), ++ new DevelopmentBranch("1.1", Version.fromString("1.1.0")), ++ new DevelopmentBranch("1.0", Version.fromString("1.0.1")), ++ new DevelopmentBranch("0.90", Version.fromString("0.90.1")) ++ ); + + protected static final BwcVersions BWC_MINOR = new BwcVersions( + BWC_MAJOR_VERSION, +diff --git a/build-tools/src/main/java/org/elasticsearch/gradle/VersionProperties.java b/build-tools/src/main/java/org/elasticsearch/gradle/VersionProperties.java +index 88c7ea36842..34d3554bead 100644 +--- a/build-tools/src/main/java/org/elasticsearch/gradle/VersionProperties.java ++++ b/build-tools/src/main/java/org/elasticsearch/gradle/VersionProperties.java +@@ -16,7 +16,12 @@ import java.util.Properties; + + /** + * Accessor for shared dependency versions used by elasticsearch, namely the elasticsearch and lucene versions. ++ * ++ * @deprecated use ext values set by {@link org.elasticsearch.gradle.internal.conventions.VersionPropertiesPlugin} or ++ * {@link org.elasticsearch.gradle.internal.conventions.VersionPropertiesBuildService} ++ * + */ ++@Deprecated + public class VersionProperties { + + public static String getElasticsearch() { +diff --git a/build-tools/src/testFixtures/groovy/org/elasticsearch/gradle/fixtures/AbstractGradleFuncTest.groovy b/build-tools/src/testFixtures/groovy/org/elasticsearch/gradle/fixtures/AbstractGradleFuncTest.groovy +index aa9b9d07c44..447f0b8af49 100644 +--- a/build-tools/src/testFixtures/groovy/org/elasticsearch/gradle/fixtures/AbstractGradleFuncTest.groovy ++++ b/build-tools/src/testFixtures/groovy/org/elasticsearch/gradle/fixtures/AbstractGradleFuncTest.groovy +@@ -9,25 +9,22 @@ + + package org.elasticsearch.gradle.fixtures + ++import spock.lang.Specification ++import spock.lang.TempDir ++ + import org.apache.commons.io.FileUtils + import org.apache.commons.io.IOUtils + import org.elasticsearch.gradle.internal.test.BuildConfigurationAwareGradleRunner + import org.elasticsearch.gradle.internal.test.InternalAwareGradleRunner + import org.elasticsearch.gradle.internal.test.NormalizeOutputGradleRunner + import org.elasticsearch.gradle.internal.test.TestResultExtension +-import org.gradle.internal.component.external.model.ComponentVariant + import org.gradle.testkit.runner.BuildResult + import org.gradle.testkit.runner.GradleRunner + import org.junit.Rule + import org.junit.rules.TemporaryFolder +-import spock.lang.Specification +-import spock.lang.TempDir + + import java.lang.management.ManagementFactory + import java.nio.charset.StandardCharsets +-import java.nio.file.Files +-import java.io.File +-import java.nio.file.Path + import java.util.jar.JarEntry + import java.util.jar.JarOutputStream + import java.util.zip.ZipEntry +@@ -47,6 +44,7 @@ abstract class AbstractGradleFuncTest extends Specification { + File buildFile + File propertiesFile + File projectDir ++ File versionPropertiesFile + + protected boolean configurationCacheCompatible = true + protected boolean buildApiRestrictionsDisabled = false +@@ -57,6 +55,18 @@ abstract class AbstractGradleFuncTest extends Specification { + settingsFile << "rootProject.name = 'hello-world'\n" + buildFile = testProjectDir.newFile('build.gradle') + propertiesFile = testProjectDir.newFile('gradle.properties') ++ File buildToolsDir = testProjectDir.newFolder("build-tools-internal") ++ versionPropertiesFile = new File(buildToolsDir, 'version.properties') ++ versionPropertiesFile.text = """ ++ elasticsearch = 9.1.0 ++ lucene = 10.2.2 ++ ++ bundled_jdk_vendor = openjdk ++ bundled_jdk = 24+36@1f9ff9062db4449d8ca828c504ffae90 ++ minimumJdkVersion = 21 ++ minimumRuntimeJava = 21 ++ minimumCompilerJava = 21 ++ """ + propertiesFile << + "org.gradle.java.installations.fromEnv=JAVA_HOME,RUNTIME_JAVA_HOME,JAVA15_HOME,JAVA14_HOME,JAVA13_HOME,JAVA12_HOME,JAVA11_HOME,JAVA8_HOME" + +@@ -162,10 +172,10 @@ abstract class AbstractGradleFuncTest extends Specification { + File internalBuild( + List extraPlugins = [], + String maintenance = "7.16.10", +- String bugfix2 = "8.1.3", +- String bugfix = "8.2.1", +- String staged = "8.3.0", +- String minor = "8.4.0", ++ String major4 = "8.1.3", ++ String major3 = "8.2.1", ++ String major2 = "8.3.0", ++ String major1 = "8.4.0", + String current = "9.0.0" + ) { + buildFile << """plugins { +@@ -175,31 +185,31 @@ abstract class AbstractGradleFuncTest extends Specification { + import org.elasticsearch.gradle.Architecture + + import org.elasticsearch.gradle.internal.BwcVersions ++ import org.elasticsearch.gradle.internal.info.DevelopmentBranch + import org.elasticsearch.gradle.Version + + Version currentVersion = Version.fromString("${current}") + def versionList = [ + Version.fromString("$maintenance"), +- Version.fromString("$bugfix2"), +- Version.fromString("$bugfix"), +- Version.fromString("$staged"), +- Version.fromString("$minor"), ++ Version.fromString("$major4"), ++ Version.fromString("$major3"), ++ Version.fromString("$major2"), ++ Version.fromString("$major1"), + currentVersion + ] + +- BwcVersions versions = new BwcVersions(currentVersion, versionList, ['main', '8.x', '8.3', '8.2', '8.1', '7.16']) +- buildParams.setBwcVersions(project.provider { versions} ) ++ BwcVersions versions = new BwcVersions(currentVersion, versionList, [ ++ new DevelopmentBranch('main', Version.fromString("$current")), ++ new DevelopmentBranch('8.x', Version.fromString("$major1")), ++ new DevelopmentBranch('8.3', Version.fromString("$major2")), ++ new DevelopmentBranch('8.2', Version.fromString("$major3")), ++ new DevelopmentBranch('8.1', Version.fromString("$major4")), ++ new DevelopmentBranch('7.16', Version.fromString("$maintenance")), ++ ]) ++ buildParams.setBwcVersions(project.provider { versions } ) + """ + } + +- void setupLocalGitRepo() { +- execute("git init") +- execute('git config user.email "build-tool@elastic.co"') +- execute('git config user.name "Build tool"') +- execute("git add .") +- execute('git commit -m "Initial"') +- } +- + void execute(String command, File workingDir = testProjectDir.root) { + def proc = command.execute(Collections.emptyList(), workingDir) + proc.waitFor() +diff --git a/build.gradle b/build.gradle +index d437d004649..810c7fd7ec0 100644 +--- a/build.gradle ++++ b/build.gradle +@@ -327,14 +327,14 @@ allprojects { + resolveJavaToolChain = true + + // ensure we have best possible caching of bwc builds +- dependsOn ":distribution:bwc:bugfix:buildBwcLinuxTar" +- dependsOn ":distribution:bwc:bugfix2:buildBwcLinuxTar" +- dependsOn ":distribution:bwc:bugfix3:buildBwcLinuxTar" +- dependsOn ":distribution:bwc:bugfix4:buildBwcLinuxTar" +- dependsOn ":distribution:bwc:bugfix5:buildBwcLinuxTar" +- dependsOn ":distribution:bwc:minor:buildBwcLinuxTar" +- dependsOn ":distribution:bwc:staged:buildBwcLinuxTar" +- dependsOn ":distribution:bwc:staged2:buildBwcLinuxTar" ++ dependsOn ":distribution:bwc:major1:buildBwcLinuxTar" ++ dependsOn ":distribution:bwc:major2:buildBwcLinuxTar" ++ dependsOn ":distribution:bwc:major3:buildBwcLinuxTar" ++ dependsOn ":distribution:bwc:major4:buildBwcLinuxTar" ++ dependsOn ":distribution:bwc:minor1:buildBwcLinuxTar" ++ dependsOn ":distribution:bwc:minor2:buildBwcLinuxTar" ++ dependsOn ":distribution:bwc:minor3:buildBwcLinuxTar" ++ dependsOn ":distribution:bwc:minor4:buildBwcLinuxTar" + } + if (project.path.contains("fixture")) { + dependsOn tasks.withType(ComposePull) +diff --git a/distribution/bwc/bugfix3/build.gradle b/distribution/bwc/major1/build.gradle +similarity index 100% +rename from distribution/bwc/bugfix3/build.gradle +rename to distribution/bwc/major1/build.gradle +diff --git a/distribution/bwc/bugfix4/build.gradle b/distribution/bwc/major2/build.gradle +similarity index 100% +rename from distribution/bwc/bugfix4/build.gradle +rename to distribution/bwc/major2/build.gradle +diff --git a/distribution/bwc/bugfix5/build.gradle b/distribution/bwc/major3/build.gradle +similarity index 100% +rename from distribution/bwc/bugfix5/build.gradle +rename to distribution/bwc/major3/build.gradle +diff --git a/distribution/bwc/maintenance/build.gradle b/distribution/bwc/major4/build.gradle +similarity index 100% +rename from distribution/bwc/maintenance/build.gradle +rename to distribution/bwc/major4/build.gradle +diff --git a/distribution/bwc/minor/build.gradle b/distribution/bwc/minor1/build.gradle +similarity index 100% +rename from distribution/bwc/minor/build.gradle +rename to distribution/bwc/minor1/build.gradle +diff --git a/distribution/bwc/staged/build.gradle b/distribution/bwc/minor2/build.gradle +similarity index 100% +rename from distribution/bwc/staged/build.gradle +rename to distribution/bwc/minor2/build.gradle +diff --git a/distribution/bwc/staged2/build.gradle b/distribution/bwc/minor3/build.gradle +similarity index 100% +rename from distribution/bwc/staged2/build.gradle +rename to distribution/bwc/minor3/build.gradle +diff --git a/distribution/bwc/minor4/build.gradle b/distribution/bwc/minor4/build.gradle +new file mode 100644 +index 00000000000..e69de29bb2d +diff --git a/gradle.properties b/gradle.properties +index 7c781d859ce..02ff63abc34 100644 +--- a/gradle.properties ++++ b/gradle.properties +@@ -22,3 +22,7 @@ org.gradle.java.installations.fromEnv=RUNTIME_JAVA_HOME + + # if configuration cache enabled then enable parallel support too + org.gradle.configuration-cache.parallel=true ++ ++# This is needed for testing changes in BwC before version field is introduced in branches.json on the main branch ++# TODO remove before merging to main ++org.elasticsearch.build.branches-file-location=https://raw.githubusercontent.com/jozala/elasticsearch/bwc-explicit-branches-version/branches.json +diff --git a/settings.gradle b/settings.gradle +index 379c24ce6e1..ee67bffe7a9 100644 +--- a/settings.gradle ++++ b/settings.gradle +@@ -77,15 +77,14 @@ List projects = [ + 'distribution:packages:deb', + 'distribution:packages:aarch64-rpm', + 'distribution:packages:rpm', +- 'distribution:bwc:bugfix', +- 'distribution:bwc:bugfix2', +- 'distribution:bwc:bugfix3', +- 'distribution:bwc:bugfix4', +- 'distribution:bwc:bugfix5', +- 'distribution:bwc:maintenance', +- 'distribution:bwc:minor', +- 'distribution:bwc:staged', +- 'distribution:bwc:staged2', ++ 'distribution:bwc:major1', ++ 'distribution:bwc:major2', ++ 'distribution:bwc:major3', ++ 'distribution:bwc:major4', ++ 'distribution:bwc:minor1', ++ 'distribution:bwc:minor2', ++ 'distribution:bwc:minor3', ++ 'distribution:bwc:minor4', + 'distribution:bwc:main', + 'distribution:tools:java-version-checker', + 'distribution:tools:cli-launcher', diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/staged2/build.gradle b/distribution/bwc/major1/build.gradle similarity index 100% rename from build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/staged2/build.gradle rename to distribution/bwc/major1/build.gradle diff --git a/distribution/bwc/bugfix/build.gradle b/distribution/bwc/major2/build.gradle similarity index 100% rename from distribution/bwc/bugfix/build.gradle rename to distribution/bwc/major2/build.gradle diff --git a/distribution/bwc/bugfix2/build.gradle b/distribution/bwc/major3/build.gradle similarity index 100% rename from distribution/bwc/bugfix2/build.gradle rename to distribution/bwc/major3/build.gradle diff --git a/distribution/bwc/maintenance/build.gradle b/distribution/bwc/major4/build.gradle similarity index 100% rename from distribution/bwc/maintenance/build.gradle rename to distribution/bwc/major4/build.gradle diff --git a/distribution/bwc/minor/build.gradle b/distribution/bwc/minor1/build.gradle similarity index 100% rename from distribution/bwc/minor/build.gradle rename to distribution/bwc/minor1/build.gradle diff --git a/distribution/bwc/staged/build.gradle b/distribution/bwc/minor2/build.gradle similarity index 100% rename from distribution/bwc/staged/build.gradle rename to distribution/bwc/minor2/build.gradle diff --git a/distribution/bwc/minor3/build.gradle b/distribution/bwc/minor3/build.gradle new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/distribution/bwc/minor4/build.gradle b/distribution/bwc/minor4/build.gradle new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/settings.gradle b/settings.gradle index 22d8cfecdce7e..bca83a9bfa944 100644 --- a/settings.gradle +++ b/settings.gradle @@ -77,11 +77,14 @@ List projects = [ 'distribution:packages:deb', 'distribution:packages:aarch64-rpm', 'distribution:packages:rpm', - 'distribution:bwc:bugfix', - 'distribution:bwc:bugfix2', - 'distribution:bwc:maintenance', - 'distribution:bwc:minor', - 'distribution:bwc:staged', + 'distribution:bwc:major1', + 'distribution:bwc:major2', + 'distribution:bwc:major3', + 'distribution:bwc:major4', + 'distribution:bwc:minor1', + 'distribution:bwc:minor2', + 'distribution:bwc:minor3', + 'distribution:bwc:minor4', 'distribution:bwc:main', 'distribution:tools:java-version-checker', 'distribution:tools:cli-launcher',