Skip to content

[Build] Add support for publishing to maven central #128659

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

Merged
merged 15 commits into from
Jun 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions build-conventions/build.gradle
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ group = "org.elasticsearch"


// This project contains Checkstyle rule implementations used by IDEs which use a Java 11 runtime // This project contains Checkstyle rule implementations used by IDEs which use a Java 11 runtime
java { java {
targetCompatibility = 11 targetCompatibility = 17
Copy link
Contributor Author

Choose a reason for hiding this comment

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

nmcp plugin does not support java 11, so we can bump

sourceCompatibility = 11 sourceCompatibility = 17
} }


gradlePlugin { gradlePlugin {
Expand Down Expand Up @@ -75,6 +75,7 @@ dependencies {
api buildLibs.maven.model api buildLibs.maven.model
api buildLibs.shadow.plugin api buildLibs.shadow.plugin
api buildLibs.apache.rat api buildLibs.apache.rat
api buildLibs.nmcp
Copy link
Contributor Author

Choose a reason for hiding this comment

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

required by the PublishPlugin

compileOnly buildLibs.checkstyle compileOnly buildLibs.checkstyle
constraints { constraints {
api("org.eclipse.platform:org.eclipse.osgi:3.18.300") { api("org.eclipse.platform:org.eclipse.osgi:3.18.300") {
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import com.github.jengelman.gradle.plugins.shadow.ShadowExtension; import com.github.jengelman.gradle.plugins.shadow.ShadowExtension;
import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin; import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin;


import nmcp.NmcpPlugin;

import org.elasticsearch.gradle.internal.conventions.info.GitInfo; import org.elasticsearch.gradle.internal.conventions.info.GitInfo;
import org.elasticsearch.gradle.internal.conventions.precommit.PomValidationPrecommitPlugin; import org.elasticsearch.gradle.internal.conventions.precommit.PomValidationPrecommitPlugin;
import org.elasticsearch.gradle.internal.conventions.util.Util; import org.elasticsearch.gradle.internal.conventions.util.Util;
Expand All @@ -27,6 +29,7 @@
import org.gradle.api.plugins.ExtensionContainer; import org.gradle.api.plugins.ExtensionContainer;
import org.gradle.api.plugins.JavaLibraryPlugin; import org.gradle.api.plugins.JavaLibraryPlugin;
import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.provider.MapProperty; import org.gradle.api.provider.MapProperty;
import org.gradle.api.provider.Provider; import org.gradle.api.provider.Provider;
import org.gradle.api.provider.ProviderFactory; import org.gradle.api.provider.ProviderFactory;
Expand Down Expand Up @@ -65,6 +68,7 @@ public void apply(Project project) {
project.getPluginManager().apply(MavenPublishPlugin.class); project.getPluginManager().apply(MavenPublishPlugin.class);
project.getPluginManager().apply(PomValidationPrecommitPlugin.class); project.getPluginManager().apply(PomValidationPrecommitPlugin.class);
project.getPluginManager().apply(LicensingPlugin.class); project.getPluginManager().apply(LicensingPlugin.class);
project.getPluginManager().apply(NmcpPlugin.class);
configureJavadocJar(project); configureJavadocJar(project);
configureSourcesJar(project); configureSourcesJar(project);
configurePomGeneration(project); configurePomGeneration(project);
Expand All @@ -82,6 +86,11 @@ private void configurePublications(Project project) {
publication.from(project.getComponents().getByName("java")); publication.from(project.getComponents().getByName("java"));
} }
}); });
project.getPlugins().withType(JavaPlugin.class, plugin -> {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ensure we include sources and javadoc jars as maven artifacts

var javaPluginExtension = project.getExtensions().getByType(JavaPluginExtension.class);
javaPluginExtension.withJavadocJar();
javaPluginExtension.withSourcesJar();
});
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
var projectLicenses = (MapProperty<String, Provider<String>>) project.getExtensions().getExtraProperties().get("projectLicenses"); var projectLicenses = (MapProperty<String, Provider<String>>) project.getExtensions().getExtraProperties().get("projectLicenses");
publication.getPom().withXml(xml -> { publication.getPom().withXml(xml -> {
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -123,8 +123,6 @@ class BuildPluginFuncTest extends AbstractGradleFuncTest {
then: then:
result.task(":assemble").outcome == TaskOutcome.SUCCESS result.task(":assemble").outcome == TaskOutcome.SUCCESS
file("build/distributions/hello-world.jar").exists() file("build/distributions/hello-world.jar").exists()
file("build/distributions/hello-world-javadoc.jar").exists()
file("build/distributions/hello-world-sources.jar").exists()
Comment on lines -126 to -127
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are these files no longer produced? I couldn't pinpoint the change which disable that.
I can only see, there is a change which adds tasks for packaging javadoc and sources in the PublishPlugin.java.
Or is it removed just because this check is unrelated to this specific test?

Copy link
Contributor Author

@breskeby breskeby Jun 6, 2025

Choose a reason for hiding this comment

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

Its related to removing the publish plugin from the build plugin. see #128659 (comment)

And there's now better PublishPlugin test coverage for this in PublishPluginFunc

assertValidJar(file("build/distributions/hello-world.jar")) assertValidJar(file("build/distributions/hello-world.jar"))
} }


Expand Down Expand Up @@ -162,7 +160,6 @@ class BuildPluginFuncTest extends AbstractGradleFuncTest {
result.task(":forbiddenPatterns").outcome == TaskOutcome.SUCCESS result.task(":forbiddenPatterns").outcome == TaskOutcome.SUCCESS
result.task(":validateModule").outcome == TaskOutcome.SUCCESS result.task(":validateModule").outcome == TaskOutcome.SUCCESS
result.task(":splitPackagesAudit").outcome == TaskOutcome.SUCCESS result.task(":splitPackagesAudit").outcome == TaskOutcome.SUCCESS
result.task(":validateElasticPom").outcome == TaskOutcome.SUCCESS
// disabled but check for being on the task graph // disabled but check for being on the task graph
result.task(":forbiddenApisMain").outcome == TaskOutcome.SKIPPED result.task(":forbiddenApisMain").outcome == TaskOutcome.SKIPPED
result.task(":checkstyleMain").outcome == TaskOutcome.SKIPPED result.task(":checkstyleMain").outcome == TaskOutcome.SKIPPED
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -23,6 +23,209 @@ class PublishPluginFuncTest extends AbstractGradleFuncTest {
configurationCacheCompatible = false configurationCacheCompatible = false
} }


def "project with plugin applied is considered for maven central publication"() {
given:
// required for JarHell to work
subProject(":libs:some-public-lib") << """
plugins {
id 'elasticsearch.java'
id 'elasticsearch.publish'
}

group = 'org.acme'
version = '1.0'
"""

subProject(":libs:some-other-lib") << """
plugins {
id 'elasticsearch.java'
id 'elasticsearch.publish'
}

group = 'org.acme.xpack'
version = '1.0'
"""

subProject(":libs:some-private-lib") << """
plugins {
id 'elasticsearch.java'
}

group = 'org.acme.xpack'
version = '1.0'
"""

buildFile << """
plugins {
id 'com.gradleup.nmcp.aggregation'
}

version = "1.0"
group = 'org.acme'
description = "custom project description"
nmcpAggregation {
centralPortal {
username = 'acme'
password = 'acmepassword'
// publish manually from the portal
publishingType = "USER_MANAGED"
}
// this breaks project isolation but this is broken in elasticsearch build atm anyhow.
publishAllProjectsProbablyBreakingProjectIsolation()
}
"""

when:
def result = gradleRunner(':zipAggregation').build()

then:
result.task(":zipAggregation").outcome == TaskOutcome.SUCCESS
file("build/nmcp/zip/aggregation.zip").exists()


def zip = zip("build/nmcp/zip/aggregation.zip")
zip.files().findAll { it.isDirectory() == false }.collect { it.name }.sort() == [
"org/acme/some-public-lib/1.0/some-public-lib-1.0-javadoc.jar",
"org/acme/some-public-lib/1.0/some-public-lib-1.0-javadoc.jar.md5",
"org/acme/some-public-lib/1.0/some-public-lib-1.0-javadoc.jar.sha1",
"org/acme/some-public-lib/1.0/some-public-lib-1.0-javadoc.jar.sha256",
"org/acme/some-public-lib/1.0/some-public-lib-1.0-javadoc.jar.sha512",
"org/acme/some-public-lib/1.0/some-public-lib-1.0-sources.jar",
"org/acme/some-public-lib/1.0/some-public-lib-1.0-sources.jar.md5",
"org/acme/some-public-lib/1.0/some-public-lib-1.0-sources.jar.sha1",
"org/acme/some-public-lib/1.0/some-public-lib-1.0-sources.jar.sha256",
"org/acme/some-public-lib/1.0/some-public-lib-1.0-sources.jar.sha512",
"org/acme/some-public-lib/1.0/some-public-lib-1.0.jar",
"org/acme/some-public-lib/1.0/some-public-lib-1.0.jar.md5",
"org/acme/some-public-lib/1.0/some-public-lib-1.0.jar.sha1",
"org/acme/some-public-lib/1.0/some-public-lib-1.0.jar.sha256",
"org/acme/some-public-lib/1.0/some-public-lib-1.0.jar.sha512",
"org/acme/some-public-lib/1.0/some-public-lib-1.0.module",
"org/acme/some-public-lib/1.0/some-public-lib-1.0.module.md5",
"org/acme/some-public-lib/1.0/some-public-lib-1.0.module.sha1",
"org/acme/some-public-lib/1.0/some-public-lib-1.0.module.sha256",
"org/acme/some-public-lib/1.0/some-public-lib-1.0.module.sha512",
"org/acme/some-public-lib/1.0/some-public-lib-1.0.pom",
"org/acme/some-public-lib/1.0/some-public-lib-1.0.pom.md5",
"org/acme/some-public-lib/1.0/some-public-lib-1.0.pom.sha1",
"org/acme/some-public-lib/1.0/some-public-lib-1.0.pom.sha256",
"org/acme/some-public-lib/1.0/some-public-lib-1.0.pom.sha512",

"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0-javadoc.jar",
"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0-javadoc.jar.md5",
"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0-javadoc.jar.sha1",
"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0-javadoc.jar.sha256",
"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0-javadoc.jar.sha512",
"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0-sources.jar",
"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0-sources.jar.md5",
"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0-sources.jar.sha1",
"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0-sources.jar.sha256",
"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0-sources.jar.sha512",
"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0.jar",
"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0.jar.md5",
"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0.jar.sha1",
"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0.jar.sha256",
"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0.jar.sha512",
"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0.module",
"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0.module.md5",
"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0.module.sha1",
"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0.module.sha256",
"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0.module.sha512",
"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0.pom",
"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0.pom.md5",
"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0.pom.sha1",
"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0.pom.sha256",
"org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0.pom.sha512"
]

assertXmlEquals(zip.file("org/acme/some-public-lib/1.0/some-public-lib-1.0.pom").read(),"""
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<!-- This module was also published with a richer model, Gradle metadata, -->
<!-- which should be used instead. Do not delete the following line which -->
<!-- is to indicate to Gradle or any Gradle module metadata file consumer -->
<!-- that they should prefer consuming it instead. -->
<!-- do_not_remove: published-with-gradle-metadata -->
<modelVersion>4.0.0</modelVersion>
<groupId>org.acme</groupId>
<artifactId>some-public-lib</artifactId>
<version>1.0</version>
<name>some-public-lib</name>
<description/>
<url>unknown</url>
<scm>
<url>unknown</url>
</scm>
<inceptionYear>2009</inceptionYear>
<licenses>
<license>
<name>Elastic License 2.0</name>
<url>https://raw.githubusercontent.com/elastic/elasticsearch/v1.0/licenses/ELASTIC-LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
<license>
<name>GNU Affero General Public License Version 3</name>
<url>https://raw.githubusercontent.com/elastic/elasticsearch/v1.0/licenses/AGPL-3.0+SSPL-1.0+ELASTIC-LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
<license>
<name>Server Side Public License, v 1</name>
<url>https://www.mongodb.com/licensing/server-side-public-license</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<name>Elastic</name>
<url>https://www.elastic.co</url>
</developer>
</developers>
</project>
""")
assertXmlEquals(zip.file("org/acme/xpack/some-other-lib/1.0/some-other-lib-1.0.pom").read(),"""
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<!-- This module was also published with a richer model, Gradle metadata, -->
<!-- which should be used instead. Do not delete the following line which -->
<!-- is to indicate to Gradle or any Gradle module metadata file consumer -->
<!-- that they should prefer consuming it instead. -->
<!-- do_not_remove: published-with-gradle-metadata -->
<modelVersion>4.0.0</modelVersion>
<groupId>org.acme.xpack</groupId>
<artifactId>some-other-lib</artifactId>
<version>1.0</version>
<name>some-other-lib</name>
<description/>
<url>unknown</url>
<scm>
<url>unknown</url>
</scm>
<inceptionYear>2009</inceptionYear>
<licenses>
<license>
<name>Elastic License 2.0</name>
<url>https://raw.githubusercontent.com/elastic/elasticsearch/v1.0/licenses/ELASTIC-LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
<license>
<name>GNU Affero General Public License Version 3</name>
<url>https://raw.githubusercontent.com/elastic/elasticsearch/v1.0/licenses/AGPL-3.0+SSPL-1.0+ELASTIC-LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
<license>
<name>Server Side Public License, v 1</name>
<url>https://www.mongodb.com/licensing/server-side-public-license</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<name>Elastic</name>
<url>https://www.elastic.co</url>
</developer>
</developers>
</project>
""")
}

def "artifacts and tweaked pom is published"() { def "artifacts and tweaked pom is published"() {
given: given:
buildFile << """ buildFile << """
Expand Down Expand Up @@ -520,4 +723,5 @@ $expected
assert diff.hasDifferences() == false assert diff.hasDifferences() == false
true true
} }

} }
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@


package org.elasticsearch.gradle.internal; package org.elasticsearch.gradle.internal;


import org.elasticsearch.gradle.internal.conventions.LicensingPlugin;
import org.elasticsearch.gradle.internal.info.GlobalBuildInfoPlugin; import org.elasticsearch.gradle.internal.info.GlobalBuildInfoPlugin;
import org.elasticsearch.gradle.internal.precommit.InternalPrecommitTasks; import org.elasticsearch.gradle.internal.precommit.InternalPrecommitTasks;
import org.elasticsearch.gradle.internal.snyk.SnykDependencyMonitoringGradlePlugin; import org.elasticsearch.gradle.internal.snyk.SnykDependencyMonitoringGradlePlugin;
Expand Down Expand Up @@ -59,9 +60,9 @@ public void apply(final Project project) {
} }


project.getPluginManager().apply("elasticsearch.java"); project.getPluginManager().apply("elasticsearch.java");
project.getPluginManager().apply("elasticsearch.publish");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

we should not apply the publish plugin by default when applying the build plugin:

  • we don't want all artifacts being published (we only intent to publish ~35 projects instead of 175 having using the build plugin
  • by relying on explicitly applying the publish plugin we have more control over what projects we publish

project.getPluginManager().apply(ElasticsearchJavadocPlugin.class); project.getPluginManager().apply(ElasticsearchJavadocPlugin.class);
project.getPluginManager().apply(DependenciesInfoPlugin.class); project.getPluginManager().apply(DependenciesInfoPlugin.class);
project.getPluginManager().apply(LicensingPlugin.class);
project.getPluginManager().apply(SnykDependencyMonitoringGradlePlugin.class); project.getPluginManager().apply(SnykDependencyMonitoringGradlePlugin.class);
project.getPluginManager().apply(ClusterFeaturesMetadataPlugin.class); project.getPluginManager().apply(ClusterFeaturesMetadataPlugin.class);
InternalPrecommitTasks.create(project, true); InternalPrecommitTasks.create(project, true);
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
package org.elasticsearch.gradle.fixtures package org.elasticsearch.gradle.fixtures


import org.apache.commons.io.FileUtils 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.BuildConfigurationAwareGradleRunner
import org.elasticsearch.gradle.internal.test.InternalAwareGradleRunner import org.elasticsearch.gradle.internal.test.InternalAwareGradleRunner
import org.elasticsearch.gradle.internal.test.NormalizeOutputGradleRunner import org.elasticsearch.gradle.internal.test.NormalizeOutputGradleRunner
Expand All @@ -23,11 +24,14 @@ import spock.lang.Specification
import spock.lang.TempDir import spock.lang.TempDir


import java.lang.management.ManagementFactory import java.lang.management.ManagementFactory
import java.nio.charset.StandardCharsets
import java.nio.file.Files import java.nio.file.Files
import java.io.File import java.io.File
import java.nio.file.Path import java.nio.file.Path
import java.util.jar.JarEntry import java.util.jar.JarEntry
import java.util.jar.JarOutputStream import java.util.jar.JarOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipFile


import static org.elasticsearch.gradle.internal.test.TestUtils.normalizeString import static org.elasticsearch.gradle.internal.test.TestUtils.normalizeString


Expand Down Expand Up @@ -234,6 +238,64 @@ checkstyle = "com.puppycrawl.tools:checkstyle:10.3"
(it as TestResultExtension.ErrorListener).errorInfo != null } (it as TestResultExtension.ErrorListener).errorInfo != null }
} }


ZipAssertion zip(String relativePath) {
File archiveFile = file(relativePath);
try (ZipFile zipFile = new ZipFile(archiveFile)) {
Map<String, ZipAssertionFile> files = zipFile.entries().collectEntries { ZipEntry entry ->
[(entry.name): new ZipAssertionFile(archiveFile, entry)]
}
return new ZipAssertion(files);
}
}

static class ZipAssertion {
private Map<String, ZipAssertionFile> files = new HashMap<>()

ZipAssertion(Map<String, ZipAssertionFile> files) {
this.files = files;
}

ZipAssertionFile file(String path) {
return this.files.get(path)
}

Collection<ZipAssertionFile> files() {
return files.values()
}
}

static class ZipAssertionFile {

private ZipEntry entry;
private File zipFile;

ZipAssertionFile(File zipFile, ZipEntry entry) {
this.entry = entry
this.zipFile = zipFile
}

boolean exists() {
entry == null
}

String getName() {
return entry.name
}

boolean isDirectory() {
return entry.isDirectory()
}

String read() {
try(ZipFile zipFile1 = new ZipFile(zipFile)) {
def inputStream = zipFile1.getInputStream(entry)
return IOUtils.toString(inputStream, StandardCharsets.UTF_8.name())
} catch (IOException e) {
throw new RuntimeException("Failed to read entry ${entry.name} from zip file ${zipFile.name}", e)
}
}
}

static class ProjectConfigurer { static class ProjectConfigurer {
private File projectDir private File projectDir


Expand Down
Loading