Skip to content

Add explicit versions in the branches.json and use them for calculating unreleased versions for BwC tests #129637

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: main
Choose a base branch
from

Conversation

jozala
Copy link
Contributor

@jozala jozala commented Jun 18, 2025

Work in progress

Gradle task added to modify branches.json. Possibility to add, remove and update branches in the file with the version field added next to the branch.
After the change the file looks like this:

{
    "branches": [
        {
            "branch": "main",
            "version": "9.1.0"
        },
        {
            "branch": "9.0",
            "version": "9.0.3"
        },
        {
            "branch": "8.19",
            "version": "8.19.1"
        }
    ]
}

BwC test configuration has been changed to use changed branches.json file to calculate unreleased ES versions, instead of combining data from Version.java and branches.json.

@elasticsearchmachine elasticsearchmachine added the serverless-linked Added by automation, don't add manually label Jun 18, 2025
@jozala jozala force-pushed the bwc-explicit-branches-version branch from 1e1a591 to d84fef9 Compare June 20, 2025 10:24
@jozala jozala self-assigned this Jun 20, 2025

@TaskAction
public void executeTask() throws IOException {
File branchesFile = new File(Util.locateElasticsearchWorkspace(project.getGradle()), "branches.json");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

referencing project here while executing task is deprecated. In this case I think we should just inject ProjectLayout instead of project in the ctor and then use layout.getSettingsDirectory().file('branches.json').asFile.

The Util.locateElasticsearchWorkspace is just a utility method only used to resolve files relateive from internal builds within this repository, which we don't need here as we never run this task anywhere else but in the :elasticsearch root build

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also we probably want to make this file an outputFile property of the task

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've now changed the way to reference branches.json file location using ProjectLayout and also made the branches file an @OutputFile property of the task.

private final ObjectMapper objectMapper;
private final BranchesFileParser branchesFileParser;

@Nullable
Copy link
Contributor

@breskeby breskeby Jun 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually we do not use that annotation. In Gradle tasks, you usually mark properties that are optional as @optional and as @input

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made all three parameters @Input and @Optional.

@jozala jozala force-pushed the bwc-explicit-branches-version branch from 4d569dc to 1e2fb07 Compare June 26, 2025 12:04
@jozala jozala force-pushed the bwc-explicit-branches-version branch 2 times, most recently from 022297f to 4d3aa2f Compare July 3, 2025 09:33
@jozala
Copy link
Contributor Author

jozala commented Jul 7, 2025

There is still a problem with GlobalBuildInfoPluginSpec failing because BwcVersions directly use static version from VersionProperties. It needs to be solved before merging to main.

@jozala jozala force-pushed the bwc-explicit-branches-version branch 2 times, most recently from e0f1357 to 2bf9688 Compare July 24, 2025 08:18
Comment on lines +117 to +124
project.getPlugins().apply(VersionPropertiesPlugin.class);
Properties versionProperties = (Properties) project.getExtensions().getByName(VERSIONS_EXT);
JavaVersion minimumCompilerVersion = JavaVersion.toVersion(versionProperties.get("minimumCompilerJava"));
JavaVersion minimumRuntimeVersion = JavaVersion.toVersion(versionProperties.get("minimumRuntimeJava"));

String bundledJdkVersion = versionProperties.getProperty("bundled_jdk");
String bundledJdkMajorVersion = bundledJdkVersion.split("[.+]")[0];
Version elasticsearchVersionProperty = Version.fromString(versionProperties.getProperty("elasticsearch"));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not using static VersionProperties any more here as this make the class untestable.
This solves the problem described here: #129637 (comment)

When VersionProperties is static it is loaded only once per JVM. It means it cannot be changed in the tests. This is a problem, because we want to test that GlobalBuildInfoPlugin can calculate unreleased versions with BwcVersions for some fixed configuration. This was impossible when VersionProperties reads the real version.properties file which changes with every release.

Comment on lines +12 to +19
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"
Copy link
Contributor Author

@jozala jozala Jul 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is simplification of the naming convention.
Name minorN means previous minor on the same major where number N is a distance from the current.
Name majorN means previous major, still actively developed versions, where N is a distance from the current, omitting all the previous minor versions.

Related changes in other files across this PR.

Comment on lines +110 to +147
Map<String, List<DevelopmentBranch>> 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<DevelopmentBranch> 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<String> 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<DevelopmentBranch> 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))
);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the main change. Now we use branches.json to get information about the unreleased versions instead of calculating it from Version.java.

Comment on lines +227 to +247
private List<DevelopmentBranch> getDevelopmentBranches() {
String branchesFileLocation = project.getProviders()
.gradleProperty(BRANCHES_FILE_LOCATION_PROPERTY)
.getOrElse(DEFAULT_BRANCHES_FILE_URL);
LOGGER.info("Reading branches.json from {}", branchesFileLocation);
byte[] branchesBytes;
if (branchesFileLocation.startsWith("http")) {
try (InputStream in = URI.create(branchesFileLocation).toURL().openStream()) {
branchesBytes = in.readAllBytes();
} catch (IOException e) {
throw new UncheckedIOException("Failed to download branches.json from: " + branchesFileLocation, e);
}
} else {
try {
branchesBytes = Files.readAllBytes(new File(branchesFileLocation).toPath());
} catch (IOException e) {
throw new UncheckedIOException("Failed to read branches.json from: " + branchesFileLocation, e);
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}

return branches;
return branchesFileParser.parse(branchesBytes);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It reads development branches (with current versions of ES on these branches) from main branch using raw GitHub link. This URL can be overridden with org.elasticsearch.build.branches-file-location Gradle property if needed.

jozala added 6 commits July 24, 2025 16:06
This change is needed to make  GlobalBuildInfoPlugin testable.
Previously it was using static VersionProperties which loaded values
once for JVM so it was impossible to test with different values.
This file is needed now during the tests as it is no longer fully static
 during full JVM execution.
@jozala jozala force-pushed the bwc-explicit-branches-version branch from 8be969c to 9dc8160 Compare July 24, 2025 14:06
@jozala jozala marked this pull request as ready for review July 24, 2025 16:07
@jozala jozala requested a review from a team as a code owner July 24, 2025 16:07
@elasticsearchmachine elasticsearchmachine added the needs:triage Requires assignment of a team area label label Jul 24, 2025
@jozala jozala added the :Delivery/Build Build or test infrastructure label Jul 24, 2025
@elasticsearchmachine elasticsearchmachine added the Team:Delivery Meta label for Delivery team label Jul 24, 2025
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-delivery (Team:Delivery)

@elasticsearchmachine elasticsearchmachine removed the needs:triage Requires assignment of a team area label label Jul 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
:Delivery/Build Build or test infrastructure >non-issue serverless-linked Added by automation, don't add manually Team:Delivery Meta label for Delivery team v9.2.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants