Sfoglia il codice sorgente

[Gradle] Unify resolving pre release java versions like RCs and EAs (#133708)

* Update distro packaging when testing java ea versions

* Unify resolving pre release java versions like RCs and EAs

This reworks how we deal with pre-release java versions for testing.

Passing `-Druntime.java=25-pre` will pick the lastest build that could be either an EA
or and RC version.

Passing explicitly a build number works by running build via
`-Druntime.java=25-pre -Druntime.java.build=36` which as of now would pick a RC build.

This also tweaks the archive packaging in case of a defined pre release version. This is
used downstream when packaging serverless images including ea / rc versions

* Bring back getJavaDetails used when configuring fips

* Adopt ReproduceInfoPrinter
Rene Groeschke 1 mese fa
parent
commit
5e71f1aa28

+ 6 - 6
BUILDING.md

@@ -186,23 +186,23 @@ dependencies {
 
 To test an unreleased development version of a third party dependency you have several options.
 
-### How do I test against java early access (ea) versions?
+### How do I test against java pre-release versions like early access (ea) or release candidates (rc)?
 
 Currently only openjdk EA builds by oracle are supported.
 To test against an early access version java version you can pass the major
-java version appended with `-ea` as a system property (e.g. -Druntime.java=26-ea) to the Gradle build:
+java version appended with `-pre` as a system property (e.g. -Druntime.java=26-pre) to the Gradle build:
 
 ```
-./gradlew clean test -Druntime.java=26-ea
+./gradlew clean test -Druntime.java=26-pre
 ```
 
-This will run the tests using the JDK 26 EA version and pick the latest available build of the matching JDK EA version we expose
-in our custom jdk catalogue at `https://storage.googleapis.com/elasticsearch-jdk-archive/jdks/openjdk/latest.json`.
+This will run the tests using the JDK 26 pre-release version and pick the latest available build of the matching JDK EA version we expose
+in our custom jdk catalogue at `https://builds.es-jdk-archive.com/jdks/openjdk/recent.json`.
 
 To run against a specific build number of the EA build you can pass a second system property (e.g. `-Druntime.java.build=6`):
 
 ```
-./gradlew clean test -Druntime.java=26-ea -Druntime.java.build=6
+./gradlew clean test -Druntime.java=26-pre -Druntime.java.build=6
 ```
 
 #### How to use a Maven based third party dependency via `mavenlocal`?

+ 1 - 1
build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/Jdk.java

@@ -32,7 +32,7 @@ public class Jdk implements Buildable, Iterable<File> {
         "(\\d+)(\\.\\d+\\.\\d+(?:\\.\\d+)?)?\\+(\\d+(?:\\.\\d+)?)(@([a-f0-9]{32}))?"
     );
     private static final Pattern LEGACY_VERSION_PATTERN = Pattern.compile("(\\d)(u\\d+)\\+(b\\d+?)(@([a-f0-9]{32}))?");
-    private static final Pattern EA_VERSION_PATTERN = Pattern.compile("(\\d+)-ea\\+(\\d+)(@([a-f0-9]{32}))?");
+    private static final Pattern EA_VERSION_PATTERN = Pattern.compile("(\\d+)-(?:ea|rc)\\+(\\d+)(@([a-f0-9]{32}))?");
 
     private final String name;
     private final FileCollection configuration;

+ 31 - 24
build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/JdkDownloadPlugin.java

@@ -114,33 +114,40 @@ public class JdkDownloadPlugin implements Plugin<Project> {
                     + jdk.getBuild()
                     + "/[module]/[classifier]/jdk/hotspot/normal/adoptium";
             }
-        } else if (jdk.getVendor().equals(VENDOR_OPENJDK) && "ea".equals(jdk.getDistributionVersion())) {
-            repoUrl = "https://builds.es-jdk-archive.com/";
-            // current pattern since 12.0.1
-            artifactPattern = "jdks/openjdk/"
-                + jdk.getMajor()
-                + "/openjdk-"
-                + jdk.getBaseVersion()
-                + "/"
-                + "openjdk-[revision]_[module]-[classifier]_bin.[ext]";
         } else if (jdk.getVendor().equals(VENDOR_OPENJDK)) {
-            repoUrl = "https://download.oracle.com";
-            if (jdk.getHash() != null) {
+            if ("ea".equals(jdk.getDistributionVersion())) {
+                repoUrl = "https://builds.es-jdk-archive.com/";
                 // current pattern since 12.0.1
-                artifactPattern = "java/GA/jdk"
-                    + jdk.getBaseVersion()
-                    + "/"
-                    + jdk.getHash()
-                    + "/"
-                    + jdk.getBuild()
-                    + "/GPL/openjdk-[revision]_[module]-[classifier]_bin.[ext]";
-            } else {
-                // simpler legacy pattern from JDK 9 to JDK 12 that we are advocating to Oracle to bring back
-                artifactPattern = "java/GA/jdk"
+                artifactPattern = "jdks/openjdk/"
                     + jdk.getMajor()
-                    + "/"
-                    + jdk.getBuild()
-                    + "/GPL/openjdk-[revision]_[module]-[classifier]_bin.[ext]";
+                    + "/openjdk-[revision]/openjdk-[revision]_[module]-[classifier]_bin.[ext]";
+            } else if ("rc".equals(jdk.getDistributionVersion())) {
+                repoUrl = "https://builds.es-jdk-archive.com/";
+                // current pattern since 12.0.1
+                artifactPattern = "jdks/openjdk/"
+                    + jdk.getMajor()
+                    + "/openjdk-[revision]/openjdk-"
+                    + jdk.getMajor()
+                    + "_[module]-[classifier]_bin.[ext]";
+            } else {
+                repoUrl = "https://download.oracle.com";
+                if (jdk.getHash() != null) {
+                    // current pattern since 12.0.1
+                    artifactPattern = "java/GA/jdk"
+                        + jdk.getBaseVersion()
+                        + "/"
+                        + jdk.getHash()
+                        + "/"
+                        + jdk.getBuild()
+                        + "/GPL/openjdk-[revision]_[module]-[classifier]_bin.[ext]";
+                } else {
+                    // simpler legacy pattern from JDK 9 to JDK 12 that we are advocating to Oracle to bring back
+                    artifactPattern = "java/GA/jdk"
+                        + jdk.getMajor()
+                        + "/"
+                        + jdk.getBuild()
+                        + "/GPL/openjdk-[revision]_[module]-[classifier]_bin.[ext]";
+                }
             }
         } else if (jdk.getVendor().equals(VENDOR_ZULU)) {
             repoUrl = "https://cdn.azul.com";

+ 2 - 0
build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/BuildParameterExtension.java

@@ -32,6 +32,8 @@ public interface BuildParameterExtension {
 
     Boolean getIsRuntimeJavaHomeSet();
 
+    RuntimeJava getRuntimeJava();
+
     List<JavaHome> getJavaVersions();
 
     JavaVersion getMinimumCompilerVersion();

+ 12 - 10
build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/DefaultBuildParameterExtension.java

@@ -28,7 +28,7 @@ import java.util.function.Supplier;
 public abstract class DefaultBuildParameterExtension implements BuildParameterExtension {
     private final Provider<Boolean> inFipsJvm;
     private final Provider<File> runtimeJavaHome;
-    private final Boolean isRuntimeJavaHomeSet;
+    private final RuntimeJava runtimeJava;
     private final List<JavaHome> javaVersions;
     private final JavaVersion minimumCompilerVersion;
     private final JavaVersion minimumRuntimeVersion;
@@ -50,11 +50,8 @@ public abstract class DefaultBuildParameterExtension implements BuildParameterEx
 
     public DefaultBuildParameterExtension(
         ProviderFactory providers,
-        Provider<File> runtimeJavaHome,
+        RuntimeJava runtimeJava,
         Provider<? extends Action<JavaToolchainSpec>> javaToolChainSpec,
-        Provider<JavaVersion> runtimeJavaVersion,
-        boolean isRuntimeJavaHomeSet,
-        Provider<String> runtimeJavaDetails,
         List<JavaHome> javaVersions,
         JavaVersion minimumCompilerVersion,
         JavaVersion minimumRuntimeVersion,
@@ -68,11 +65,11 @@ public abstract class DefaultBuildParameterExtension implements BuildParameterEx
         Provider<BwcVersions> bwcVersions
     ) {
         this.inFipsJvm = providers.systemProperty("tests.fips.enabled").map(DefaultBuildParameterExtension::parseBoolean);
-        this.runtimeJavaHome = cache(providers, runtimeJavaHome);
+        this.runtimeJava = runtimeJava;
+        this.runtimeJavaHome = cache(providers, runtimeJava.getJavahome());
         this.javaToolChainSpec = cache(providers, javaToolChainSpec);
-        this.runtimeJavaVersion = cache(providers, runtimeJavaVersion);
-        this.isRuntimeJavaHomeSet = isRuntimeJavaHomeSet;
-        this.runtimeJavaDetails = cache(providers, runtimeJavaDetails);
+        this.runtimeJavaVersion = cache(providers, runtimeJava.getJavaVersion());
+        this.runtimeJavaDetails = cache(providers, runtimeJava.getVendorDetails());
         this.javaVersions = javaVersions;
         this.minimumCompilerVersion = minimumCompilerVersion;
         this.minimumRuntimeVersion = minimumRuntimeVersion;
@@ -116,7 +113,12 @@ public abstract class DefaultBuildParameterExtension implements BuildParameterEx
 
     @Override
     public Boolean getIsRuntimeJavaHomeSet() {
-        return isRuntimeJavaHomeSet;
+        return runtimeJava.isExplicitlySet();
+    }
+
+    @Override
+    public RuntimeJava getRuntimeJava() {
+        return runtimeJava;
     }
 
     @Override

+ 56 - 49
build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/GlobalBuildInfoPlugin.java

@@ -47,7 +47,6 @@ import org.gradle.jvm.toolchain.JavaToolchainSpec;
 import org.gradle.jvm.toolchain.JvmVendorSpec;
 import org.gradle.jvm.toolchain.internal.InstallationLocation;
 import org.gradle.util.GradleVersion;
-import org.jetbrains.annotations.NotNull;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -68,7 +67,8 @@ import java.util.stream.Stream;
 import javax.inject.Inject;
 
 import static org.elasticsearch.gradle.internal.conventions.GUtils.elvis;
-import static org.elasticsearch.gradle.internal.toolchain.EarlyAccessCatalogJdkToolchainResolver.findLatestEABuildNumber;
+import static org.elasticsearch.gradle.internal.toolchain.EarlyAccessCatalogJdkToolchainResolver.findLatestPreReleaseBuild;
+import static org.elasticsearch.gradle.internal.toolchain.EarlyAccessCatalogJdkToolchainResolver.findPreReleaseBuild;
 
 public class GlobalBuildInfoPlugin implements Plugin<Project> {
     private static final Logger LOGGER = Logging.getLogger(GlobalBuildInfoPlugin.class);
@@ -116,38 +116,20 @@ public class GlobalBuildInfoPlugin implements Plugin<Project> {
         JavaVersion minimumCompilerVersion = JavaVersion.toVersion(getResourceContents("/minimumCompilerVersion"));
         JavaVersion minimumRuntimeVersion = JavaVersion.toVersion(getResourceContents("/minimumRuntimeVersion"));
 
-        Provider<File> explicitRuntimeJavaHome = findRuntimeJavaHome();
-        boolean isRuntimeJavaHomeExplicitlySet = explicitRuntimeJavaHome.isPresent();
-        Provider<File> actualRuntimeJavaHome = isRuntimeJavaHomeExplicitlySet
-            ? explicitRuntimeJavaHome
-            : resolveJavaHomeFromToolChainService(VersionProperties.getBundledJdkMajorVersion());
-
-        Provider<JvmInstallationMetadata> runtimeJdkMetaData = actualRuntimeJavaHome.map(
-            runtimeJavaHome -> metadataDetector.getMetadata(getJavaInstallation(runtimeJavaHome))
-        );
+        RuntimeJava runtimeJavaHome = findRuntimeJavaHome();
         AtomicReference<BwcVersions> cache = new AtomicReference<>();
         Provider<BwcVersions> bwcVersionsProvider = providers.provider(
             () -> cache.updateAndGet(val -> val == null ? resolveBwcVersions() : val)
         );
+
         BuildParameterExtension buildParams = project.getExtensions()
             .create(
                 BuildParameterExtension.class,
                 BuildParameterExtension.EXTENSION_NAME,
                 DefaultBuildParameterExtension.class,
                 providers,
-                actualRuntimeJavaHome,
+                runtimeJavaHome,
                 resolveToolchainSpecFromEnv(),
-                actualRuntimeJavaHome.map(
-                    javaHome -> determineJavaVersion(
-                        "runtime java.home",
-                        javaHome,
-                        isRuntimeJavaHomeExplicitlySet
-                            ? minimumRuntimeVersion
-                            : JavaVersion.toVersion(VersionProperties.getBundledJdkMajorVersion())
-                    )
-                ),
-                isRuntimeJavaHomeExplicitlySet,
-                runtimeJdkMetaData.map(m -> formatJavaVendorDetails(m)),
                 getAvailableJavaVersions(),
                 minimumCompilerVersion,
                 minimumRuntimeVersion,
@@ -237,7 +219,7 @@ public class GlobalBuildInfoPlugin implements Plugin<Project> {
         LOGGER.quiet("Elasticsearch Build Hamster says Hello!");
         LOGGER.quiet("  Gradle Version        : " + GradleVersion.current().getVersion());
         LOGGER.quiet("  OS Info               : " + osName + " " + osVersion + " (" + osArch + ")");
-        if (buildParams.getIsRuntimeJavaHomeSet()) {
+        if (buildParams.getRuntimeJava().isExplicitlySet()) {
             JvmInstallationMetadata runtimeJvm = metadataDetector.getMetadata(getJavaInstallation(buildParams.getRuntimeJavaHome().get()));
             final String runtimeJvmVendorDetails = runtimeJvm.getVendor().getDisplayName();
             final String runtimeJvmImplementationVersion = runtimeJvm.getJvmVersion();
@@ -344,50 +326,74 @@ public class GlobalBuildInfoPlugin implements Plugin<Project> {
         }
     }
 
-    private Provider<File> findRuntimeJavaHome() {
+    private RuntimeJava findRuntimeJavaHome() {
         String runtimeJavaProperty = System.getProperty("runtime.java");
-
         if (runtimeJavaProperty != null) {
-            if (runtimeJavaProperty.toLowerCase().endsWith("-ea")) {
-                // handle EA builds differently due to lack of support in Gradle toolchain service
+            if (runtimeJavaProperty.toLowerCase().endsWith("-pre")) {
+                // handle pre-release builds differently due to lack of support in Gradle toolchain service
                 // we resolve them using JdkDownloadPlugin for now.
-                Integer major = Integer.parseInt(runtimeJavaProperty.substring(0, runtimeJavaProperty.length() - 3));
-                return resolveEarlyAccessJavaHome(major);
+                return resolvePreReleaseRuntimeJavaHome(runtimeJavaProperty);
             } else {
-                return resolveJavaHomeFromToolChainService(runtimeJavaProperty);
+                return runtimeJavaHome(resolveJavaHomeFromToolChainService(runtimeJavaProperty), true);
             }
         }
         if (System.getenv("RUNTIME_JAVA_HOME") != null) {
-            return providers.provider(() -> new File(System.getenv("RUNTIME_JAVA_HOME")));
+            return runtimeJavaHome(providers.provider(() -> new File(System.getenv("RUNTIME_JAVA_HOME"))), true);
         }
         // fall back to tool chain if set.
         String env = System.getenv("JAVA_TOOLCHAIN_HOME");
-        return providers.provider(() -> {
-            if (env == null) {
-                return null;
-            }
-            return new File(env);
-        });
+        boolean explicitlySet = env != null;
+        Provider<File> javaHome = explicitlySet
+            ? providers.provider(() -> new File(env))
+            : resolveJavaHomeFromToolChainService(VersionProperties.getBundledJdkMajorVersion());
+        return runtimeJavaHome(javaHome, explicitlySet);
     }
 
-    private Provider<File> resolveEarlyAccessJavaHome(Integer runtimeJavaProperty) {
-        NamedDomainObjectContainer<Jdk> container = (NamedDomainObjectContainer<Jdk>) project.getExtensions().getByName("jdks");
+    private RuntimeJava runtimeJavaHome(Provider<File> fileProvider, boolean explicitlySet) {
+        return runtimeJavaHome(fileProvider, explicitlySet, null);
+    }
+
+    private RuntimeJava runtimeJavaHome(Provider<File> fileProvider, boolean explicitlySet, String preReleasePostfix) {
+        Provider<JavaVersion> javaVersion = fileProvider.map(
+            javaHome -> determineJavaVersion(
+                "runtime java.home",
+                javaHome,
+                fileProvider.isPresent()
+                    ? JavaVersion.toVersion(getResourceContents("/minimumRuntimeVersion"))
+                    : JavaVersion.toVersion(VersionProperties.getBundledJdkMajorVersion())
+            )
+        );
+
+        Provider<String> vendorDetails = fileProvider.map(j -> metadataDetector.getMetadata(getJavaInstallation(j)))
+            .map(m -> formatJavaVendorDetails(m));
+
+        return new RuntimeJava(fileProvider, javaVersion, vendorDetails, explicitlySet, preReleasePostfix);
+    }
+
+    private RuntimeJava resolvePreReleaseRuntimeJavaHome(String runtimeJavaProperty) {
+        var major = JavaLanguageVersion.of(Integer.parseInt(runtimeJavaProperty.substring(0, runtimeJavaProperty.length() - 4)));
         Integer buildNumber = Integer.getInteger("runtime.java.build");
-        if (buildNumber == null) {
-            buildNumber = findLatestEABuildNumber(runtimeJavaProperty);
-        }
-        String eaVersionString = String.format("%d-ea+%d", runtimeJavaProperty, buildNumber);
-        Jdk jdk = container.create("ea_jdk_" + runtimeJavaProperty, j -> {
-            j.setVersion(eaVersionString);
+        var jdkbuild = buildNumber == null ? findLatestPreReleaseBuild(major) : findPreReleaseBuild(major, buildNumber);
+        String prVersionString = String.format("%d-%s+%d", major.asInt(), jdkbuild.type(), jdkbuild.buildNumber());
+        return resolveJavaHomeFromJdkDownloadPlugin(jdkbuild.type(), major, prVersionString);
+
+    }
+
+    private RuntimeJava resolveJavaHomeFromJdkDownloadPlugin(String preReleaseType, JavaLanguageVersion major, String prVersionString) {
+        NamedDomainObjectContainer<Jdk> container = (NamedDomainObjectContainer<Jdk>) project.getExtensions().getByName("jdks");
+        Jdk jdk = container.create(preReleaseType + "_" + major.asInt(), j -> {
+            j.setVersion(prVersionString);
             j.setVendor("openjdk");
             j.setPlatform(OS.current().javaOsReference);
             j.setArchitecture(Architecture.current().javaClassifier);
-            j.setDistributionVersion("ea");
+            j.setDistributionVersion(preReleaseType);
         });
-        return providers.provider(() -> new File(jdk.getJavaHomePath().toString()));
+        // We on purpose resolve this here eagerly to ensure we resolve the jdk configuration in the context of the root project.
+        // If we keep this lazy we can not guarantee in which project context this is resolved which will fail the build.
+        File file = new File(jdk.getJavaHomePath().toString());
+        return runtimeJavaHome(providers.provider(() -> file), true, preReleaseType);
     }
 
-    @NotNull
     private Provider<File> resolveJavaHomeFromToolChainService(String version) {
         Property<JavaLanguageVersion> value = objectFactory.property(JavaLanguageVersion.class).value(JavaLanguageVersion.of(version));
         return toolChainService.launcherFor(javaToolchainSpec -> javaToolchainSpec.getLanguageVersion().value(value))
@@ -444,4 +450,5 @@ public class GlobalBuildInfoPlugin implements Plugin<Project> {
             spec.getLanguageVersion().set(expectedJavaLanguageVersion);
         }
     }
+
 }

+ 66 - 0
build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/info/RuntimeJava.java

@@ -0,0 +1,66 @@
+/*
+ * 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.gradle.api.JavaVersion;
+import org.gradle.api.provider.Provider;
+
+import java.io.File;
+
+public class RuntimeJava {
+
+    private final Provider<File> javahome;
+    private final Provider<JavaVersion> javaVersion;
+    private final boolean explicitlySet;
+    private final String preReleaseType;
+    private final Provider<String> vendorDetails;
+
+    RuntimeJava(Provider<File> javahome, Provider<JavaVersion> javaVersion, Provider<String> vendorDetails, boolean explicitlySet) {
+        this(javahome, javaVersion, vendorDetails, explicitlySet, null);
+    }
+
+    RuntimeJava(
+        Provider<File> javahome,
+        Provider<JavaVersion> javaVersion,
+        Provider<String> vendorDetails,
+        boolean explicitlySet,
+        String preReleaseType
+    ) {
+        this.javahome = javahome;
+        this.javaVersion = javaVersion;
+        this.vendorDetails = vendorDetails;
+        this.explicitlySet = explicitlySet;
+        this.preReleaseType = preReleaseType;
+    }
+
+    public Provider<File> getJavahome() {
+        return javahome;
+    }
+
+    public boolean isPreRelease() {
+        return preReleaseType != null;
+    }
+
+    public Provider<JavaVersion> getJavaVersion() {
+        return javaVersion;
+    }
+
+    public Provider<String> getVendorDetails() {
+        return vendorDetails;
+    }
+
+    public boolean isExplicitlySet() {
+        return explicitlySet;
+    }
+
+    public String getPreReleaseType() {
+        return preReleaseType;
+    }
+}

+ 38 - 19
build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/toolchain/EarlyAccessCatalogJdkToolchainResolver.java

@@ -12,6 +12,7 @@ package org.elasticsearch.gradle.internal.toolchain;
 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.VersionProperties;
 import org.gradle.jvm.toolchain.JavaLanguageVersion;
@@ -28,6 +29,7 @@ import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.List;
 import java.util.Optional;
 
@@ -43,6 +45,8 @@ import java.util.Optional;
  */
 public abstract class EarlyAccessCatalogJdkToolchainResolver extends AbstractCustomJavaToolchainResolver {
 
+    public static final String RECENT_JDK_RELEASES_CATALOG_URL = "https://builds.es-jdk-archive.com/jdks/openjdk/recent.json";
+
     interface JdkBuild {
         JavaLanguageVersion languageVersion();
 
@@ -51,29 +55,33 @@ public abstract class EarlyAccessCatalogJdkToolchainResolver extends AbstractCus
 
     @FunctionalInterface
     interface EarlyAccessJdkBuildResolver {
-        Optional<EarlyAccessJdkBuild> findLatestEABuild(JavaLanguageVersion languageVersion);
+        PreReleaseJdkBuild findLatestEABuild(JavaLanguageVersion languageVersion);
     }
 
     // allow overriding for testing
-    EarlyAccessJdkBuildResolver earlyAccessJdkBuildResolver = (languageVersion) -> findLatestEABuild(languageVersion);
+    EarlyAccessJdkBuildResolver earlyAccessJdkBuildResolver = (languageVersion) -> findLatestPreReleaseBuild(languageVersion);
 
-    record EarlyAccessJdkBuild(JavaLanguageVersion languageVersion, int buildNumber) implements JdkBuild {
+    public record PreReleaseJdkBuild(JavaLanguageVersion languageVersion, int buildNumber, String type) implements JdkBuild {
         @Override
         public String url(String os, String arch, String extension) {
             // example:
             // https://builds.es-jdk-archive.com/jdks/openjdk/26/openjdk-26-ea+6/openjdk-26-ea+6_linux-aarch64_bin.tar.gz
+
+            // RCs don't attach a special suffix to the artifact name
+            String releaseTypeSuffix = type.equals("ea") ? "-" + type + "+" + buildNumber : "";
             return "https://builds.es-jdk-archive.com/jdks/openjdk/"
                 + languageVersion.asInt()
                 + "/"
                 + "openjdk-"
                 + languageVersion.asInt()
-                + "-ea+"
+                + "-"
+                + type
+                + "+"
                 + buildNumber
                 + "/"
                 + "openjdk-"
                 + languageVersion.asInt()
-                + "-ea+"
-                + buildNumber
+                + releaseTypeSuffix
                 + "_"
                 + os
                 + "-"
@@ -98,7 +106,6 @@ public abstract class EarlyAccessCatalogJdkToolchainResolver extends AbstractCus
             .getLanguageVersion()
             .get()
             .asInt()) {
-            // This resolver only handles early access builds, that are beyond the last bundled jdk version
         }
         return findSupportedBuild(request).map(build -> {
             OperatingSystem operatingSystem = request.getBuildPlatform().getOperatingSystem();
@@ -113,7 +120,7 @@ public abstract class EarlyAccessCatalogJdkToolchainResolver extends AbstractCus
      * Check if request can be full-filled by this resolver:
      * 1. Aarch64 windows images are not supported
      */
-    private Optional<EarlyAccessJdkBuild> findSupportedBuild(JavaToolchainRequest request) {
+    private Optional<PreReleaseJdkBuild> findSupportedBuild(JavaToolchainRequest request) {
         JavaToolchainSpec javaToolchainSpec = request.getJavaToolchainSpec();
         BuildPlatform buildPlatform = request.getBuildPlatform();
         Architecture architecture = buildPlatform.getArchitecture();
@@ -125,36 +132,48 @@ public abstract class EarlyAccessCatalogJdkToolchainResolver extends AbstractCus
         }
 
         JavaLanguageVersion languageVersion = javaToolchainSpec.getLanguageVersion().get();
-        return earlyAccessJdkBuildResolver.findLatestEABuild(languageVersion);
+        return Optional.of(earlyAccessJdkBuildResolver.findLatestEABuild(languageVersion));
     }
 
-    private static Optional<EarlyAccessJdkBuild> findLatestEABuild(JavaLanguageVersion languageVersion) {
+    static List<PreReleaseJdkBuild> findRecentPreReleaseBuild(JavaLanguageVersion languageVersion) {
         try {
-            URL url = new URL("https://storage.googleapis.com/elasticsearch-jdk-archive/jdks/openjdk/latest.json");
+            URL url = new URL(RECENT_JDK_RELEASES_CATALOG_URL);
             try (InputStream is = url.openStream()) {
                 ObjectMapper mapper = new ObjectMapper();
                 JsonNode node = mapper.readTree(is);
-                ArrayNode buildsNode = (ArrayNode) node.get("builds");
+                ObjectNode majors = (ObjectNode) node.get("majors");
+                System.out.println(majors.getClass());
+                ObjectNode perVersion = (ObjectNode) majors.get("" + languageVersion.asInt());
+                ArrayNode buildsNode = (ArrayNode) perVersion.get("builds");
                 List<JsonNode> buildsList = new ArrayList<>();
                 buildsNode.forEach(buildsList::add);
-                List<EarlyAccessJdkBuild> eaBuilds = buildsList.stream()
+                List<PreReleaseJdkBuild> eaBuilds = buildsList.stream()
                     .map(
-                        n -> new EarlyAccessJdkBuild(
+                        n -> new PreReleaseJdkBuild(
                             JavaLanguageVersion.of(n.get("major").asText()),
-                            Integer.parseInt(n.get("build").asText())
+                            Integer.parseInt(n.get("build").asText()),
+                            n.get("type").asText()
                         )
                     )
                     .toList();
-                return eaBuilds.stream().filter(ea -> ea.languageVersion().equals(languageVersion)).findFirst();
+                return eaBuilds.stream().filter(ea -> ea.languageVersion().equals(languageVersion)).toList();
             } catch (IOException e) {
                 throw new RuntimeException(e);
             }
         } catch (MalformedURLException e) {
-            return Optional.empty();
+            throw new RuntimeException(e);
         }
     }
 
-    public static int findLatestEABuildNumber(int languageVersion) {
-        return findLatestEABuild(JavaLanguageVersion.of(languageVersion)).map(ea -> ea.buildNumber()).get();
+    public static PreReleaseJdkBuild findPreReleaseBuild(JavaLanguageVersion languageVersion, int buildNumber) {
+        return findRecentPreReleaseBuild(languageVersion).stream()
+            .filter(preReleaseJdkBuild -> preReleaseJdkBuild.buildNumber == buildNumber)
+            .max(Comparator.comparingInt(PreReleaseJdkBuild::buildNumber))
+            .get();
+    }
+
+    public static PreReleaseJdkBuild findLatestPreReleaseBuild(JavaLanguageVersion languageVersion) {
+        return findRecentPreReleaseBuild(languageVersion).stream().max(Comparator.comparingInt(PreReleaseJdkBuild::buildNumber)).get();
     }
+
 }

+ 36 - 4
build-tools-internal/src/test/groovy/org/elasticsearch/gradle/internal/toolchain/EarlyAccessCatalogJdkToolchainResolverSpec.groovy

@@ -12,8 +12,8 @@ package org.elasticsearch.gradle.internal.toolchain
 import org.elasticsearch.gradle.VersionProperties
 import org.gradle.api.services.BuildServiceParameters
 import org.gradle.jvm.toolchain.JavaLanguageVersion
+import org.gradle.jvm.toolchain.JavaToolchainDownload
 import org.gradle.jvm.toolchain.JavaToolchainResolver
-import org.gradle.jvm.toolchain.JvmVendorSpec
 
 import static org.gradle.platform.Architecture.AARCH64
 import static org.gradle.platform.Architecture.X86_64
@@ -29,13 +29,45 @@ class EarlyAccessCatalogJdkToolchainResolverSpec extends AbstractToolchainResolv
             }
         }
         resolver.earlyAccessJdkBuildResolver = (JavaLanguageVersion version) -> {
-            return Optional.of(
-                new EarlyAccessCatalogJdkToolchainResolver.EarlyAccessJdkBuild(version, 30)
-            )
+            new EarlyAccessCatalogJdkToolchainResolver.PreReleaseJdkBuild(version, 30, 'ea')
         }
         return resolver
     }
 
+    def "resolves rc versions #os #arch #vendor jdk #langVersion"() {
+        given:
+        def resolver = new EarlyAccessCatalogJdkToolchainResolver() {
+            @Override
+            BuildServiceParameters.None getParameters() {
+                return null
+            }
+        }
+        resolver.earlyAccessJdkBuildResolver = (JavaLanguageVersion version) -> {
+            new EarlyAccessCatalogJdkToolchainResolver.PreReleaseJdkBuild(version, 30, 'rc')
+        }
+        when:
+        Optional<JavaToolchainDownload> download = resolver.resolve(request(JavaLanguageVersion.of(langVersion), vendor, platform(os, arch)))
+
+        then:
+        download.get().uri == URI.create(expectedUrl)
+        where:
+
+        [langVersion, vendor, os, arch, expectedUrl] << [
+            [25, anyVendor(), LINUX, X86_64, "https://builds.es-jdk-archive.com/jdks/openjdk/25/openjdk-25-rc+30/openjdk-25_linux-x64_bin.tar.gz"],
+            [25, anyVendor(), LINUX, AARCH64, "https://builds.es-jdk-archive.com/jdks/openjdk/25/openjdk-25-rc+30/openjdk-25_linux-aarch64_bin.tar.gz"],
+            [25, anyVendor(), MAC_OS, X86_64, "https://builds.es-jdk-archive.com/jdks/openjdk/25/openjdk-25-rc+30/openjdk-25_macos-x64_bin.tar.gz"],
+            [25, anyVendor(), MAC_OS, AARCH64, "https://builds.es-jdk-archive.com/jdks/openjdk/25/openjdk-25-rc+30/openjdk-25_macos-aarch64_bin.tar.gz"],
+            [25, anyVendor(), WINDOWS, X86_64, "https://builds.es-jdk-archive.com/jdks/openjdk/25/openjdk-25-rc+30/openjdk-25_windows-x64_bin.zip"],
+
+            [26, anyVendor(), LINUX, X86_64, "https://builds.es-jdk-archive.com/jdks/openjdk/26/openjdk-26-rc+30/openjdk-26_linux-x64_bin.tar.gz"],
+            [26, anyVendor(), LINUX, AARCH64, "https://builds.es-jdk-archive.com/jdks/openjdk/26/openjdk-26-rc+30/openjdk-26_linux-aarch64_bin.tar.gz"],
+            [26, anyVendor(), MAC_OS, X86_64, "https://builds.es-jdk-archive.com/jdks/openjdk/26/openjdk-26-rc+30/openjdk-26_macos-x64_bin.tar.gz"],
+            [26, anyVendor(), MAC_OS, AARCH64, "https://builds.es-jdk-archive.com/jdks/openjdk/26/openjdk-26-rc+30/openjdk-26_macos-aarch64_bin.tar.gz"],
+            [26, anyVendor(), WINDOWS, X86_64, "https://builds.es-jdk-archive.com/jdks/openjdk/26/openjdk-26-rc+30/openjdk-26_windows-x64_bin.zip"]
+        ]
+    }
+
+
     @Override
     def supportedRequests() {
         return [

+ 63 - 33
distribution/build.gradle

@@ -20,6 +20,8 @@ import org.elasticsearch.gradle.transform.FilteringJarTransform
 import java.nio.file.Files
 import java.nio.file.Path
 
+import static org.elasticsearch.gradle.internal.toolchain.EarlyAccessCatalogJdkToolchainResolver.findLatestPreReleaseBuildNumber
+
 plugins {
   id 'base'
   id 'elasticsearch.distro'
@@ -48,9 +50,9 @@ dependencies {
 
 def thisProj = project
 rootProject.allprojects { proj ->
-    proj.plugins.withType(DependenciesInfoPlugin) {
-      thisProj.dependencies.add("dependencyInfos", project.dependencies.project(path: proj.path))
-    }
+  proj.plugins.withType(DependenciesInfoPlugin) {
+    thisProj.dependencies.add("dependencyInfos", project.dependencies.project(path: proj.path))
+  }
 }
 
 /*****************************************************************************
@@ -61,9 +63,10 @@ rootProject.allprojects { proj ->
 tasks.register("generateDependenciesReport", ConcatFilesTask) {
   files = configurations.dependencyInfos
   headerLine = "name,version,url,license,sourceURL"
-  target = new File(providers.systemProperty('csv')
-                      .orElse("${project.buildDir}/reports/dependencies/es-dependencies.csv")
-                      .get()
+  target = new File(
+    providers.systemProperty('csv')
+      .orElse("${project.buildDir}/reports/dependencies/es-dependencies.csv")
+      .get()
   )
   // explicitly add our dependency on the JDK
   String jdkVersion = VersionProperties.versions.get('bundled_jdk').split('@')[0]
@@ -246,20 +249,46 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) {
   apply plugin: 'elasticsearch.jdk-download'
   apply plugin: 'elasticsearch.repositories'
 
-  // Setup all required JDKs
-  project.jdks {
-    ['darwin', 'windows', 'linux'].each { platform ->
-      (platform == 'linux' || platform == 'darwin' ? ['x64', 'aarch64'] : ['x64']).each { architecture ->
-        "bundled_${platform}_${architecture}" {
-          it.platform = platform
-          it.version = VersionProperties.bundledJdkVersion
-          it.vendor = VersionProperties.bundledJdkVendor
-          it.architecture = architecture
+  if (buildParams.runtimeJava.preRelease) {
+    Integer buildNumber = Integer.getInteger("runtime.java.build")
+    String preReleaseType = buildParams.runtimeJava.preReleaseType
+    def runtimeJavaMajorVersion = Integer.parseInt(buildParams.runtimeJavaVersion.get().getMajorVersion())
+    if (buildNumber == null) {
+      buildNumber = findLatestPreReleaseBuildNumber(runtimeJavaMajorVersion, preReleaseType);
+    }
+    String preReleaseVersionString = String.format("%d-%s+%d", runtimeJavaMajorVersion, preReleaseType, buildNumber);
+
+    project.jdks {
+      ['darwin', 'windows', 'linux'].each { platform ->
+        (platform == 'linux' || platform == 'darwin' ? ['x64', 'aarch64'] : ['x64']).each { architecture ->
+          "bundled_${platform}_${architecture}" {
+            it.version = preReleaseVersionString;
+            it.vendor = "openjdk"
+            it.platform = platform
+            it.architecture = architecture
+            it.distributionVersion = preReleaseType
+          }
+        }
+      }
+    }
+  } else {
+    // Setup all required JDKs
+    project.jdks {
+      ['darwin', 'windows', 'linux'].each { platform ->
+        (platform == 'linux' || platform == 'darwin' ? ['x64', 'aarch64'] : ['x64']).each { architecture ->
+          "bundled_${platform}_${architecture}" {
+            it.platform = platform
+            it.version = VersionProperties.bundledJdkVersion
+            it.vendor = VersionProperties.bundledJdkVendor
+            it.architecture = architecture
+          }
         }
       }
     }
+
   }
 
+
   // TODO: the map needs to be an input of the tasks, so that when it changes, the task will re-run...
   /*****************************************************************************
    *             Properties to expand when copying packaging files             *
@@ -288,7 +317,8 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) {
     }
     all {
       resolutionStrategy.dependencySubstitution {
-        substitute module("org.apache.logging.log4j:log4j-core") using project(":libs:log4j") because "patched to remove JndiLookup class"}
+        substitute module("org.apache.logging.log4j:log4j-core") using project(":libs:log4j") because "patched to remove JndiLookup class"
+      }
     }
   }
 
@@ -354,7 +384,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) {
         into('platform') {
           from(configurations.libsNative)
           if (os != null) {
-            include (os + '-' + architecture + '/*')
+            include(os + '-' + architecture + '/*')
           }
         }
         into('entitlement-agent') {
@@ -416,7 +446,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) {
           // main config files, processed with distribution specific substitutions
           from '../src/config'
           exclude 'log4j2.properties' // this is handled separately below
-          filter("tokens" : expansionsForDistribution(distributionType, isTestDistro), ReplaceTokens.class)
+          filter("tokens": expansionsForDistribution(distributionType, isTestDistro), ReplaceTokens.class)
         }
         from buildDefaultLog4jConfigTaskProvider
         from isTestDistro ? integTestConfigFiles : defaultConfigFiles
@@ -431,11 +461,11 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) {
           exclude '*.exe'
           exclude '*.bat'
           eachFile {
-            it.permissions{
+            it.permissions {
               unix(0755)
             }
           }
-          filter("tokens" : expansionsForDistribution(distributionType, testDistro), ReplaceTokens.class)
+          filter("tokens": expansionsForDistribution(distributionType, testDistro), ReplaceTokens.class)
         }
         // windows files, only for zip
         if (distributionType == 'zip') {
@@ -443,7 +473,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) {
             from '../src/bin'
             include '*.bat'
             filter(FixCrLfFilter, eol: FixCrLfFilter.CrLf.newInstance('crlf'))
-            filter("tokens" : expansionsForDistribution(distributionType, testDistro), ReplaceTokens.class)
+            filter("tokens": expansionsForDistribution(distributionType, testDistro), ReplaceTokens.class)
           }
           with copySpec {
             from '../src/bin'
@@ -466,7 +496,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) {
         if (testDistro) {
           from buildServerNoticeTaskProvider
         } else {
-          from (buildDefaultNoticeTaskProvider) {
+          from(buildDefaultNoticeTaskProvider) {
             filePermissions {
               unix(0644)
             }
@@ -547,57 +577,57 @@ subprojects {
     String footer = "# Built for ${project.name}-${project.version} " +
       "(${distributionType})"
     Map<String, Object> expansions = [
-      'project.name': project.name,
-      'project.version': version,
+      'project.name'         : project.name,
+      'project.version'      : version,
       'project.minor.version': "${VersionProperties.elasticsearchVersion.major}.${VersionProperties.elasticsearchVersion.minor}",
 
-      'path.conf': [
+      'path.conf'            : [
         'deb': '/etc/elasticsearch',
         'rpm': '/etc/elasticsearch',
         'def': '"$ES_HOME"/config'
       ],
-      'path.data': [
+      'path.data'            : [
         'deb': packagingPathData,
         'rpm': packagingPathData,
         'def': '#path.data: /path/to/data'
       ],
-      'path.env': [
+      'path.env'             : [
         'deb': '/etc/default/elasticsearch',
         'rpm': '/etc/sysconfig/elasticsearch',
         /* There isn't one of these files for tar or zip but its important to
           make an empty string here so the script can properly skip it. */
         'def': 'if [ -z "$ES_PATH_CONF" ]; then ES_PATH_CONF="$ES_HOME"/config; done',
       ],
-      'source.path.env': [
+      'source.path.env'      : [
         'deb': 'source /etc/default/elasticsearch',
         'rpm': 'source /etc/sysconfig/elasticsearch',
         'def': 'if [ -z "$ES_PATH_CONF" ]; then ES_PATH_CONF="$ES_HOME"/config; fi',
       ],
-      'path.logs': [
+      'path.logs'            : [
         'deb': packagingPathLogs,
         'rpm': packagingPathLogs,
         'def': '#path.logs: /path/to/logs'
       ],
 
-      'scripts.footer': [
+      'scripts.footer'       : [
         /* Debian needs exit 0 on these scripts so we add it here and preserve
           the pretty footer. */
         'deb': "exit 0\n${footer}",
         'def': footer
       ],
 
-      'es.distribution.type': [
+      'es.distribution.type' : [
         'deb': 'deb',
         'rpm': 'rpm',
         'tar': 'tar',
         'zip': 'zip'
       ],
 
-      'license.name': [
+      'license.name'         : [
         'deb': 'Elastic-License'
       ],
 
-      'license.text': [
+      'license.text'         : [
         'deb': licenseText,
       ],
     ]

+ 4 - 2
test/framework/src/main/java/org/elasticsearch/test/junit/listeners/ReproduceInfoPrinter.java

@@ -190,8 +190,10 @@ public class ReproduceInfoPrinter extends RunListener {
             appendOpt("tests.locale", Locale.getDefault().toLanguageTag());
             appendOpt("tests.timezone", TimeZone.getDefault().getID());
             appendOpt("tests.distribution", System.getProperty("tests.distribution"));
-            if (Runtime.version().build().isPresent() && "ea".equalsIgnoreCase(Runtime.version().pre().orElse(""))) {
-                appendOpt("runtime.java", Runtime.version().feature() + "-ea");
+            if (Runtime.version().build().isPresent()
+                && ("ea".equalsIgnoreCase(Runtime.version().pre().orElse(""))
+                    || ("rc".equalsIgnoreCase(Runtime.version().pre().orElse(""))))) {
+                appendOpt("runtime.java", Runtime.version().feature() + "-pre");
                 appendOpt("runtime.java.build", Integer.toString(Runtime.version().build().get()));
             } else {
                 appendOpt("runtime.java", Integer.toString(Runtime.version().feature()));

+ 2 - 2
test/immutable-collections-patch/build.gradle

@@ -17,8 +17,8 @@ configurations {
 }
 
 dependencies {
-  implementation 'org.ow2.asm:asm:9.7.1'
-  implementation 'org.ow2.asm:asm-tree:9.7.1'
+  implementation 'org.ow2.asm:asm:9.8'
+  implementation 'org.ow2.asm:asm-tree:9.8'
 }
 
 def outputDir = layout.buildDirectory.dir("jdk-patches")