From 5941dcc65ca9327c37ad43ed12b3fd29892a6b8f Mon Sep 17 00:00:00 2001 From: Mariusz Jozala Date: Tue, 3 Jun 2025 10:14:10 +0200 Subject: [PATCH 01/10] Initial work for having explicit active branch with version connection # Conflicts: # branches.json --- branches.json | 21 ++- .../distribution/bwc/major1/build.gradle | 0 .../distribution/bwc/major2/build.gradle | 0 .../distribution/bwc/major3/build.gradle | 0 .../distribution/bwc/major4/build.gradle | 0 .../distribution/bwc/minor1/build.gradle | 0 .../distribution/bwc/minor2/build.gradle | 0 .../distribution/bwc/minor3/build.gradle | 0 .../distribution/bwc/minor4/build.gradle | 0 .../gradle/internal/BwcVersions.java | 128 +++++----------- .../internal/info/DevelopmentBranch.java | 14 ++ .../internal/info/GlobalBuildInfoPlugin.java | 12 +- .../gradle/internal/BwcVersionsSpec.groovy | 145 +++++++++++++----- ...stractDistributionDownloadPluginTests.java | 8 +- settings.gradle | 8 + 15 files changed, 199 insertions(+), 137 deletions(-) create mode 100644 build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major1/build.gradle create mode 100644 build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major2/build.gradle create mode 100644 build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major3/build.gradle create mode 100644 build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major4/build.gradle create mode 100644 build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor1/build.gradle create mode 100644 build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor2/build.gradle create mode 100644 build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor3/build.gradle create mode 100644 build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor4/build.gradle create mode 100644 build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/DevelopmentBranch.java diff --git a/branches.json b/branches.json index a2feaf0715575..11b42f85e505d 100644 --- a/branches.json +++ b/branches.json @@ -2,25 +2,32 @@ "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.0" }, { - "branch": "9.0" + "branch": "9.0", + "version": "9.0.3" }, { - "branch": "8.19" + "branch": "8.19", + "version": "8.19.0" }, { - "branch": "8.18" + "branch": "8.18", + "version": "8.18.3" }, { - "branch": "8.17" + "branch": "8.17", + "version": "8.17.8" }, { - "branch": "7.17" + "branch": "7.17", + "version": "7.17.29" } ] } diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major1/build.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major1/build.gradle new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major2/build.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major2/build.gradle new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major3/build.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major3/build.gradle new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major4/build.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major4/build.gradle new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor1/build.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor1/build.gradle new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor2/build.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor2/build.gradle new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor3/build.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor3/build.gradle new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor4/build.gradle b/build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor4/build.gradle new file mode 100644 index 0000000000000..e69de29bb2d1d 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 15d13e567ca4b..3e41103b112cd 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 @@ -10,6 +10,7 @@ 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; @@ -26,6 +27,7 @@ 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; @@ -74,11 +76,11 @@ public class BwcVersions implements Serializable { private final transient List versions; private final Map unreleased; - public BwcVersions(List versionLines, List developmentBranches) { + 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"); } @@ -91,7 +93,7 @@ public BwcVersions(Version currentVersionProperty, List allVersions, Li } // Visible for testing - BwcVersions(List versionLines, Version currentVersionProperty, List developmentBranches) { + BwcVersions(List versionLines, Version currentVersionProperty, List developmentBranches) { this(currentVersionProperty, parseVersionLines(versionLines), developmentBranches); } @@ -127,81 +129,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(); - - 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 +178,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/DevelopmentBranch.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/DevelopmentBranch.java new file mode 100644 index 0000000000000..8aeda4cdf79b1 --- /dev/null +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/DevelopmentBranch.java @@ -0,0 +1,14 @@ +/* + * 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; + +public record DevelopmentBranch(String name, Version 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 675f1198b2a7d..1990b56300676 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 @@ -12,6 +12,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.io.IOUtils; +import org.elasticsearch.gradle.Version; import org.elasticsearch.gradle.VersionProperties; import org.elasticsearch.gradle.internal.BwcVersions; import org.elasticsearch.gradle.internal.conventions.GitInfoPlugin; @@ -202,13 +203,18 @@ private BwcVersions resolveBwcVersions() { } } - private List getDevelopmentBranches() { - List branches = new ArrayList<>(); + 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()); + branches.add( + new DevelopmentBranch( + node.get("branch").asText(), + Version.fromString(node.get("version").asText()) + ) + ); } } catch (IOException e) { throw new UncheckedIOException(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 a662a76db4da7..9a94f8727385e 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,6 +13,7 @@ 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 = [] @@ -32,14 +33,20 @@ class BwcVersionsSpec extends Specification { addVersion('9.0.0', '10.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(versionLines, v('9.0.0'), [ + 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')] @@ -62,15 +69,22 @@ class BwcVersionsSpec extends Specification { addVersion('9.0.0', '10.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(versionLines, v('9.0.0'), [ + 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')] @@ -94,16 +108,24 @@ class BwcVersionsSpec extends Specification { addVersion('9.1.0', '10.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(versionLines, v('9.1.0'), [ + 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')] @@ -121,13 +143,17 @@ class BwcVersionsSpec extends Specification { addVersion('9.1.0', '10.0.0') when: - def bwc = new BwcVersions(versionLines, v('9.1.0'), ['main', '9.0', '8.18']) + def bwc = new BwcVersions(versionLines, v('9.1.0'), [ + 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')] @@ -146,13 +172,17 @@ class BwcVersionsSpec extends Specification { addVersion('9.1.0', '10.0.0') when: - def bwc = new BwcVersions(versionLines, v('9.1.0'), ['main', '9.0', '8.18']) + def bwc = new BwcVersions(versionLines, v('9.1.0'), [ + 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')] @@ -172,14 +202,19 @@ class BwcVersionsSpec extends Specification { addVersion('9.2.0', '10.0.0') when: - def bwc = new BwcVersions(versionLines, v('9.2.0'), ['main', '9.1', '9.0', '8.18']) + def bwc = new BwcVersions(versionLines, v('9.2.0'), [ + 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')] @@ -204,14 +239,21 @@ class BwcVersionsSpec extends Specification { addVersion('8.18.0', '9.10.0') when: - def bwc = new BwcVersions(versionLines, v('8.18.0'), ['main', '8.x', '8.17', '8.16', '7.17']) + def bwc = new BwcVersions(versionLines, v('8.18.0'),[ + 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')] @@ -235,15 +277,22 @@ class BwcVersionsSpec extends Specification { addVersion('8.18.0', '9.10.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(versionLines, v('8.18.0'), [ + 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')] @@ -265,13 +314,20 @@ class BwcVersionsSpec extends Specification { addVersion('8.16.1', '9.10.0') 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(versionLines, v('8.16.1'), [ + 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')] @@ -291,12 +347,19 @@ class BwcVersionsSpec extends Specification { addVersion('8.15.2', '9.9.0') 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(versionLines, v('8.15.2'), [ + 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')] @@ -313,4 +376,8 @@ class BwcVersionsSpec extends Specification { 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/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/settings.gradle b/settings.gradle index f3463efc5af38..4880ead13e9b7 100644 --- a/settings.gradle +++ b/settings.gradle @@ -80,7 +80,15 @@ List projects = [ 'distribution:bwc:bugfix2', 'distribution:bwc:bugfix3', 'distribution:bwc:maintenance', + 'distribution:bwc:major1', + 'distribution:bwc:major2', + 'distribution:bwc:major3', + 'distribution:bwc:major4', 'distribution:bwc:minor', + 'distribution:bwc:minor1', + 'distribution:bwc:minor2', + 'distribution:bwc:minor3', + 'distribution:bwc:minor4', 'distribution:bwc:staged', 'distribution:bwc:staged2', 'distribution:bwc:main', From 33819096b53414d4b24bc34cdbc853b4b76a12f9 Mon Sep 17 00:00:00 2001 From: Mariusz Jozala Date: Mon, 16 Jun 2025 14:04:44 +0200 Subject: [PATCH 02/10] Basic implementation with single test passing --- .../distribution/bwc/bugfix/build.gradle | 0 .../distribution/bwc/bugfix2/build.gradle | 0 .../distribution/bwc/maintenance/build.gradle | 0 .../distribution/bwc/major/build.gradle | 0 .../distribution/bwc/minor/build.gradle | 0 .../distribution/bwc/staged/build.gradle | 0 .../internal/info/GlobalBuildInfoPlugin.java | 1 + .../release/UpdateBranchesJsonTask.java | 135 ++++++++++++++++++ .../release/UpdateBranchesJsonTaskTest.groovy | 75 ++++++++++ settings.gradle | 7 - 10 files changed, 211 insertions(+), 7 deletions(-) delete mode 100644 build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/bugfix/build.gradle delete mode 100644 build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/bugfix2/build.gradle delete mode 100644 build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/maintenance/build.gradle delete mode 100644 build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/major/build.gradle delete mode 100644 build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/minor/build.gradle delete mode 100644 build-tools-internal/src/integTest/resources/org/elasticsearch/gradle/internal/fake_git/remote/distribution/bwc/staged/build.gradle create mode 100644 build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTask.java create mode 100644 build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTaskTest.groovy 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/bugfix/build.gradle deleted file mode 100644 index e69de29bb2d1d..0000000000000 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/bugfix2/build.gradle deleted file mode 100644 index e69de29bb2d1d..0000000000000 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/maintenance/build.gradle deleted file mode 100644 index e69de29bb2d1d..0000000000000 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/major/build.gradle deleted file mode 100644 index e69de29bb2d1d..0000000000000 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/minor/build.gradle deleted file mode 100644 index e69de29bb2d1d..0000000000000 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/staged/build.gradle deleted file mode 100644 index e69de29bb2d1d..0000000000000 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 1990b56300676..3c363cab19b5f 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 @@ -205,6 +205,7 @@ private BwcVersions resolveBwcVersions() { private List getDevelopmentBranches() { List branches = new ArrayList<>(); + // TODO jozala: change to get branches.json from raw GitHub link (where to keep the URL?) File branchesFile = new File(Util.locateElasticsearchWorkspace(project.getGradle()), "branches.json"); try (InputStream is = new FileInputStream(branchesFile)) { JsonNode json = objectMapper.readTree(is); 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..9582dc13f7934 --- /dev/null +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTask.java @@ -0,0 +1,135 @@ +/* + * 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.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.conventions.util.Util; +import org.elasticsearch.gradle.internal.info.DevelopmentBranch; +import org.gradle.api.DefaultTask; +import org.gradle.api.Project; +import org.gradle.api.logging.Logger; +import org.gradle.api.logging.Logging; +import org.gradle.api.tasks.TaskAction; +import org.gradle.api.tasks.options.Option; +import org.gradle.initialization.layout.BuildLayout; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import java.io.*; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +public class UpdateBranchesJsonTask extends DefaultTask { + + private static final Logger LOGGER = Logging.getLogger(UpdateBranchesJsonTask.class); + + private final Project project; + private final ObjectMapper objectMapper; + + @Nullable + private DevelopmentBranch addBranch; + @Nullable + private String removeBranch; + @Nullable + private DevelopmentBranch updateBranch; + + @Inject + public UpdateBranchesJsonTask(Project project) { + this.project = project; + // TODO jozala: is there a better way to pass ObjectMapper? + this.objectMapper = new ObjectMapper(); + } + + @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 setCurrent(String branchAndVersion) { + this.updateBranch = toDevelopmentBranch(branchAndVersion); + } + + private DevelopmentBranch toDevelopmentBranch(String branchAndVersion) { + String[] parts = branchAndVersion.split(":"); + if (parts.length != 2) { + throw new IllegalArgumentException("Expected branch and version in format :"); + } + return new DevelopmentBranch(parts[0], Version.fromString(parts[1])); + } + + @TaskAction + public void executeTask() throws IOException { + File branchesFile = new File(Util.locateElasticsearchWorkspace(project.getGradle()), "branches.json"); + List developmentBranches = readBranches(branchesFile); + + if (addBranch != null) { + LOGGER.info("Adding branch {} with version {}", addBranch.name(), addBranch.version()); + developmentBranches.add(addBranch); + } + if (removeBranch != null) { + LOGGER.info("Removing branch {}", removeBranch); + developmentBranches.removeIf(developmentBranch -> developmentBranch.name().equals(removeBranch)); + } + if (updateBranch != null) { + LOGGER.info("Updating branch {} with version {}", updateBranch.name(), updateBranch.version()); + 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); + objectMapper.writeValue(branchesFile, jsonNode); + + // TODO jozala: where to actually push this change to Git? + } + + private List readBranches(File branchesFile) throws IOException { + // TODO jozala: move to some common space and reuse with GlobalBuildInfoPlugin + List branches = new ArrayList<>(); + try (InputStream is = new FileInputStream(branchesFile)) { + JsonNode json = objectMapper.readTree(is); + 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(e); + } + + return branches; + } + + +} diff --git a/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTaskTest.groovy b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTaskTest.groovy new file mode 100644 index 0000000000000..9edb74e6df0a2 --- /dev/null +++ b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTaskTest.groovy @@ -0,0 +1,75 @@ +/* + * 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.Project +import org.gradle.testfixtures.ProjectBuilder + +class UpdateBranchesJsonTaskTest extends Specification { + + @Subject + UpdateBranchesJsonTask task + + File projectRoot + Project project + JsonSlurper jsonSlurper = new JsonSlurper() + + def setup() { + projectRoot = File.createTempDir("update-branches-json-test") + projectRoot.deleteOnExit() + + project = ProjectBuilder.builder().withProjectDir(projectRoot).build() + task = project.tasks.register("updateBranchesJson", UpdateBranchesJsonTask).get() + } + + def "should add new branch to branches.json"() { + 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'], + ] + } + + 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/settings.gradle b/settings.gradle index 4880ead13e9b7..d7c7e57b118ff 100644 --- a/settings.gradle +++ b/settings.gradle @@ -76,21 +76,14 @@ List projects = [ 'distribution:packages:deb', 'distribution:packages:aarch64-rpm', 'distribution:packages:rpm', - 'distribution:bwc:bugfix', - 'distribution:bwc:bugfix2', - 'distribution:bwc:bugfix3', - 'distribution:bwc:maintenance', 'distribution:bwc:major1', 'distribution:bwc:major2', 'distribution:bwc:major3', 'distribution:bwc:major4', - 'distribution:bwc:minor', 'distribution:bwc:minor1', 'distribution:bwc:minor2', 'distribution:bwc:minor3', 'distribution:bwc:minor4', - 'distribution:bwc:staged', - 'distribution:bwc:staged2', 'distribution:bwc:main', 'distribution:tools:java-version-checker', 'distribution:tools:cli-launcher', From f73c58967be00c39017efac273b0ddfc4d13d41f Mon Sep 17 00:00:00 2001 From: Mariusz Jozala Date: Wed, 18 Jun 2025 16:29:04 +0200 Subject: [PATCH 03/10] Tests for UpdateBranchesJsonTask --- .../internal/info/BranchesFileParser.java | 52 ++++ .../internal/info/DevelopmentBranch.java | 15 +- .../release/UpdateBranchesJsonTask.java | 68 +++-- .../release/UpdateBranchesJsonTaskSpec.groovy | 266 ++++++++++++++++++ .../release/UpdateBranchesJsonTaskTest.groovy | 75 ----- 5 files changed, 372 insertions(+), 104 deletions(-) create mode 100644 build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/BranchesFileParser.java create mode 100644 build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTaskSpec.groovy delete mode 100644 build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTaskTest.groovy 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..93002ac04aef1 --- /dev/null +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/BranchesFileParser.java @@ -0,0 +1,52 @@ +/* + * 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 index 8aeda4cdf79b1..c3f8529da21a4 100644 --- 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 @@ -11,4 +11,17 @@ import org.elasticsearch.gradle.Version; -public record DevelopmentBranch(String name, Version version) {} +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) { + public DevelopmentBranch { + Objects.requireNonNull(name); + Objects.requireNonNull(version); + } +} 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 index 9582dc13f7934..dc1cf9a7206e4 100644 --- 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 @@ -13,31 +13,40 @@ 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.conventions.util.Util; +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.Project; import org.gradle.api.logging.Logger; import org.gradle.api.logging.Logging; import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.options.Option; -import org.gradle.initialization.layout.BuildLayout; -import javax.annotation.Nullable; -import javax.inject.Inject; -import java.io.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.UncheckedIOException; import java.nio.file.Files; -import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import javax.annotation.Nullable; +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 Project project; private final ObjectMapper objectMapper; + private final BranchesFileParser branchesFileParser; @Nullable private DevelopmentBranch addBranch; @@ -49,8 +58,8 @@ public class UpdateBranchesJsonTask extends DefaultTask { @Inject public UpdateBranchesJsonTask(Project project) { this.project = project; - // TODO jozala: is there a better way to pass ObjectMapper? this.objectMapper = new ObjectMapper(); + this.branchesFileParser = new BranchesFileParser(objectMapper); } @Option(option = "add-branch", description = "Specifies the branch and corresponding version to add in format :") @@ -64,14 +73,14 @@ public void removeBranch(String branch) { } @Option(option = "update-branch", description = "Specifies the branch and corresponding version to update in format :") - public void setCurrent(String branchAndVersion) { + public void updateBranch(String branchAndVersion) { this.updateBranch = toDevelopmentBranch(branchAndVersion); } private DevelopmentBranch toDevelopmentBranch(String branchAndVersion) { String[] parts = branchAndVersion.split(":"); if (parts.length != 2) { - throw new IllegalArgumentException("Expected branch and version in format :"); + throw new InvalidUserDataException("Expected branch and version in format :"); } return new DevelopmentBranch(parts[0], Version.fromString(parts[1])); } @@ -81,16 +90,29 @@ public void executeTask() throws IOException { File branchesFile = new File(Util.locateElasticsearchWorkspace(project.getGradle()), "branches.json"); 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); } @@ -107,29 +129,19 @@ public void executeTask() throws IOException { } ((ObjectNode) jsonNode).replace("branches", updatedBranches); objectMapper.writeValue(branchesFile, jsonNode); - - // TODO jozala: where to actually push this change to Git? } - private List readBranches(File branchesFile) throws IOException { - // TODO jozala: move to some common space and reuse with GlobalBuildInfoPlugin - List branches = new ArrayList<>(); - try (InputStream is = new FileInputStream(branchesFile)) { - JsonNode json = objectMapper.readTree(is); - 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(e); + private List readBranches(File branchesFile) { + if (branchesFile.isFile() == false) { + throw new InvalidUserDataException("File branches.json has not been found in " + + branchesFile.getAbsolutePath()); } - return branches; + 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/release/UpdateBranchesJsonTaskSpec.groovy b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTaskSpec.groovy new file mode 100644 index 0000000000000..73d42e5c169d6 --- /dev/null +++ b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTaskSpec.groovy @@ -0,0 +1,266 @@ +/* + * 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() + } + + 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/groovy/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTaskTest.groovy b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTaskTest.groovy deleted file mode 100644 index 9edb74e6df0a2..0000000000000 --- a/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/release/UpdateBranchesJsonTaskTest.groovy +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", 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.Project -import org.gradle.testfixtures.ProjectBuilder - -class UpdateBranchesJsonTaskTest extends Specification { - - @Subject - UpdateBranchesJsonTask task - - File projectRoot - Project project - JsonSlurper jsonSlurper = new JsonSlurper() - - def setup() { - projectRoot = File.createTempDir("update-branches-json-test") - projectRoot.deleteOnExit() - - project = ProjectBuilder.builder().withProjectDir(projectRoot).build() - task = project.tasks.register("updateBranchesJson", UpdateBranchesJsonTask).get() - } - - def "should add new branch to branches.json"() { - 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'], - ] - } - - 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)) - - } -} From 359abfae43a6f69a378958d550d4c1f3bacb63e5 Mon Sep 17 00:00:00 2001 From: Mariusz Jozala Date: Wed, 18 Jun 2025 16:31:07 +0200 Subject: [PATCH 04/10] Allow changing branch.json location in GlobalBuildInfoPlugin By default, it now reads branches.json from main branch of Elasticsearch (through GitHub raw link) . This can be overridden for testing or when there is a problem with remote call. --- .../internal/info/GlobalBuildInfoPlugin.java | 41 ++++---- .../info/GlobalBuildInfoPluginSpec.groovy | 99 +++++++++++++++++++ gradle.properties | 4 + 3 files changed, 124 insertions(+), 20 deletions(-) create mode 100644 build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPluginSpec.groovy 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 3c363cab19b5f..42b115d477fb3 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,11 +8,9 @@ */ 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.VersionProperties; import org.elasticsearch.gradle.internal.BwcVersions; import org.elasticsearch.gradle.internal.conventions.GitInfoPlugin; @@ -52,8 +50,8 @@ 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.Random; @@ -68,12 +66,14 @@ 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/master/branches.json"; + private static final String BRANCHES_FILE_LOCATION_PROPERTY = "org.elasticsearch.build.branches-file-location"; 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; @@ -88,7 +88,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 @@ -204,24 +204,25 @@ private BwcVersions resolveBwcVersions() { } private List getDevelopmentBranches() { - List branches = new ArrayList<>(); - // TODO jozala: change to get branches.json from raw GitHub link (where to keep the URL?) - 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( - new DevelopmentBranch( - node.get("branch").asText(), - Version.fromString(node.get("version").asText()) - ) - ); + 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/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..c60efd6a694ae --- /dev/null +++ b/build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPluginSpec.groovy @@ -0,0 +1,99 @@ +/* + * 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 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/gradle.properties b/gradle.properties index 7c781d859cea6..11714892bee1b 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/elastic/elasticsearch/bwc-explicit-branches-version/branches.json From 68be53e0fae00ee028c0cfc5e1eb4b1ca19dbd96 Mon Sep 17 00:00:00 2001 From: Mariusz Jozala Date: Wed, 18 Jun 2025 22:25:30 +0200 Subject: [PATCH 05/10] Change branch.json location temporarily to point to the forked repo --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 11714892bee1b..02ff63abc34f0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,4 +25,4 @@ 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/elastic/elasticsearch/bwc-explicit-branches-version/branches.json +org.elasticsearch.build.branches-file-location=https://raw.githubusercontent.com/jozala/elasticsearch/bwc-explicit-branches-version/branches.json From 4a4a9127b0b43954e06ac2590f2a8a7586032fb4 Mon Sep 17 00:00:00 2001 From: elasticsearchmachine Date: Wed, 18 Jun 2025 20:33:44 +0000 Subject: [PATCH 06/10] [CI] Auto commit changes from spotless --- .../org/elasticsearch/gradle/internal/BwcVersions.java | 4 ++-- .../gradle/internal/info/BranchesFileParser.java | 8 +------- .../gradle/internal/info/GlobalBuildInfoPlugin.java | 3 ++- .../gradle/internal/release/UpdateBranchesJsonTask.java | 3 +-- 4 files changed, 6 insertions(+), 12 deletions(-) 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 3e41103b112cd..8e0c14f64d5ff 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 @@ -158,7 +158,7 @@ private Map computeUnreleased(List computeUnreleased(List parse(byte[] bytes) { 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()) - ) - ); + 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); 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 42b115d477fb3..6d9ca2d640951 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 @@ -204,7 +204,8 @@ private BwcVersions resolveBwcVersions() { } private List getDevelopmentBranches() { - String branchesFileLocation = project.getProviders().gradleProperty(BRANCHES_FILE_LOCATION_PROPERTY) + String branchesFileLocation = project.getProviders() + .gradleProperty(BRANCHES_FILE_LOCATION_PROPERTY) .getOrElse(DEFAULT_BRANCHES_FILE_URL); LOGGER.info("Reading branches.json from {}", branchesFileLocation); byte[] branchesBytes; 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 index dc1cf9a7206e4..ca8e3bfdf6088 100644 --- 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 @@ -133,8 +133,7 @@ public void executeTask() throws IOException { private List readBranches(File branchesFile) { if (branchesFile.isFile() == false) { - throw new InvalidUserDataException("File branches.json has not been found in " + - branchesFile.getAbsolutePath()); + throw new InvalidUserDataException("File branches.json has not been found in " + branchesFile.getAbsolutePath()); } try { From 34d20f1fc1c371deda4aa9ff4ac483d23aefa9f8 Mon Sep 17 00:00:00 2001 From: Mariusz Jozala Date: Thu, 19 Jun 2025 00:01:50 +0200 Subject: [PATCH 07/10] Artificial Gradle projects for BwC tests added --- distribution/bwc/{bugfix => major1}/build.gradle | 0 distribution/bwc/{bugfix2 => major2}/build.gradle | 0 distribution/bwc/{bugfix3 => major3}/build.gradle | 0 distribution/bwc/{maintenance => major4}/build.gradle | 0 distribution/bwc/{minor => minor1}/build.gradle | 0 distribution/bwc/{staged => minor2}/build.gradle | 0 distribution/bwc/{staged2 => minor3}/build.gradle | 0 distribution/bwc/minor4/build.gradle | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename distribution/bwc/{bugfix => major1}/build.gradle (100%) rename distribution/bwc/{bugfix2 => major2}/build.gradle (100%) rename distribution/bwc/{bugfix3 => major3}/build.gradle (100%) rename distribution/bwc/{maintenance => major4}/build.gradle (100%) rename distribution/bwc/{minor => minor1}/build.gradle (100%) rename distribution/bwc/{staged => minor2}/build.gradle (100%) rename distribution/bwc/{staged2 => minor3}/build.gradle (100%) create mode 100644 distribution/bwc/minor4/build.gradle diff --git a/distribution/bwc/bugfix/build.gradle b/distribution/bwc/major1/build.gradle similarity index 100% rename from distribution/bwc/bugfix/build.gradle rename to distribution/bwc/major1/build.gradle diff --git a/distribution/bwc/bugfix2/build.gradle b/distribution/bwc/major2/build.gradle similarity index 100% rename from distribution/bwc/bugfix2/build.gradle rename to distribution/bwc/major2/build.gradle diff --git a/distribution/bwc/bugfix3/build.gradle b/distribution/bwc/major3/build.gradle similarity index 100% rename from distribution/bwc/bugfix3/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 0000000000000..e69de29bb2d1d From ac753685777899937167927bb198e9891ff7bcf8 Mon Sep 17 00:00:00 2001 From: Mariusz Jozala Date: Fri, 20 Jun 2025 12:20:18 +0200 Subject: [PATCH 08/10] Tests corrected to be in line with new BwC test naming --- ...lDistributionBwcSetupPluginFuncTest.groovy | 22 ++++----- ...lDistributionDownloadPluginFuncTest.groovy | 16 +++---- ...acyYamlRestCompatTestPluginFuncTest.groovy | 16 +++---- .../internal/fake_git/remote/settings.gradle | 16 +++---- .../fixtures/AbstractGradleFuncTest.groovy | 45 +++++++++---------- build.gradle | 14 +++--- 6 files changed, 64 insertions(+), 65 deletions(-) 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 bb100b6b23882..d9009a276d23f 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 fc5d432a9ef9a..3d0c6cc390e9f 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/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 a07060c3be18f..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,14 +9,14 @@ rootProject.name = "root" -include ":distribution:bwc:bugfix" -include ":distribution:bwc:bugfix2" -include ":distribution:bwc:bugfix3" -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/src/testFixtures/groovy/org/elasticsearch/gradle/fixtures/AbstractGradleFuncTest.groovy b/build-tools/src/testFixtures/groovy/org/elasticsearch/gradle/fixtures/AbstractGradleFuncTest.groovy index d52b7d321c729..a398176aff46b 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 @@ -161,10 +158,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 { @@ -174,31 +171,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 5b534394b4e1c..c8737ef15a481 100644 --- a/build.gradle +++ b/build.gradle @@ -325,12 +325,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: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) From 69a2526f8a68c2dc4411de54cc6aab5b3acc2cb7 Mon Sep 17 00:00:00 2001 From: Mariusz Jozala Date: Fri, 20 Jun 2025 19:26:46 +0200 Subject: [PATCH 09/10] Register the task in the plugin --- .../gradle/internal/release/ReleaseToolsPlugin.java | 1 + 1 file changed, 1 insertion(+) 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 fce8b0c545dbb..f0fb0073d4350 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 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 Directory changeLogDirectory = projectDirectory.dir("docs/changelog"); final Directory changeLogBundlesDirectory = projectDirectory.dir("docs/release-notes/changelog-bundles"); From 1e2fb07f18eb68fe7eb418720f05a8f80a115165 Mon Sep 17 00:00:00 2001 From: Mariusz Jozala Date: Fri, 20 Jun 2025 19:27:10 +0200 Subject: [PATCH 10/10] Configure JSON formatting for branches.json --- .../gradle/internal/release/UpdateBranchesJsonTask.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 index ca8e3bfdf6088..32cf6e339bbb4 100644 --- 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 @@ -9,6 +9,8 @@ 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; @@ -128,7 +130,11 @@ public void executeTask() throws IOException { updatedBranches.add(objectNode); } ((ObjectNode) jsonNode).replace("branches", updatedBranches); - objectMapper.writeValue(branchesFile, jsonNode); + + DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter(); + prettyPrinter.indentArraysWith(new DefaultIndenter(" ", DefaultIndenter.SYS_LF)); + prettyPrinter.withoutSpacesInObjectEntries(); + objectMapper.writer(prettyPrinter).writeValue(branchesFile, jsonNode); } private List readBranches(File branchesFile) {