From 63ae7ade3930d55d8cce602b933265427135098c Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Wed, 22 Jun 2022 16:27:17 -0400 Subject: [PATCH 1/3] [BUG] Custom POM configuration for ZIP publication produces duplicit tags (url, scm) Signed-off-by: Andriy Redko --- .../org/opensearch/gradle/PublishPlugin.java | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/buildSrc/src/main/java/org/opensearch/gradle/PublishPlugin.java b/buildSrc/src/main/java/org/opensearch/gradle/PublishPlugin.java index a28015784c4be..fe03605c830d2 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/PublishPlugin.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/PublishPlugin.java @@ -36,6 +36,8 @@ import com.github.jengelman.gradle.plugins.shadow.ShadowExtension; import groovy.util.Node; import groovy.util.NodeList; +import groovy.xml.QName; + import org.opensearch.gradle.info.BuildParams; import org.opensearch.gradle.precommit.PomValidationPrecommitPlugin; import org.opensearch.gradle.util.Util; @@ -146,9 +148,36 @@ public String call() throws Exception { private static void addScmInfo(XmlProvider xml) { Node root = xml.asNode(); - root.appendNode("url", Util.urlFromOrigin(BuildParams.getGitOrigin())); - Node scmNode = root.appendNode("scm"); - scmNode.appendNode("url", BuildParams.getGitOrigin()); + Node url = null, scm = null; + + for (final Object child : root.children()) { + if (child instanceof Node) { + final Node node = (Node) child; + if (node.name() instanceof QName) { + final QName qname = (QName) node.name(); + if (qname.matches("url")) { + url = node; + } else if (qname.matches("scm")) { + scm = node; + } + } else if ("url".equals(node.name())) { + url = node; + } else if ("scm".equals(node.name())) { + scm = node; + } + } + } + + // Only include URL section if it is not provided in the POM already + if (url == null) { + root.appendNode("url", Util.urlFromOrigin(BuildParams.getGitOrigin())); + } + + // Only include SCM section if it is not provided in the POM already + if (scm == null) { + Node scmNode = root.appendNode("scm"); + scmNode.appendNode("url", BuildParams.getGitOrigin()); + } } /** Adds a javadocJar task to generate a jar containing javadocs. */ From dcbb3a5f20b6c4b84f0c1d800a319ed6c99f035c Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Thu, 23 Jun 2022 09:29:38 -0400 Subject: [PATCH 2/3] Added test case for pluginZip with POM Signed-off-by: Andriy Redko --- .../gradle/pluginzip/PublishTests.java | 118 +++++++++++++++--- 1 file changed, 100 insertions(+), 18 deletions(-) diff --git a/buildSrc/src/test/java/org/opensearch/gradle/pluginzip/PublishTests.java b/buildSrc/src/test/java/org/opensearch/gradle/pluginzip/PublishTests.java index 851c450699bd7..8c1314c4b4394 100644 --- a/buildSrc/src/test/java/org/opensearch/gradle/pluginzip/PublishTests.java +++ b/buildSrc/src/test/java/org/opensearch/gradle/pluginzip/PublishTests.java @@ -52,26 +52,62 @@ public void tearDown() { @Test public void testZipPublish() throws IOException, XmlPullParserException { - Project project = ProjectBuilder.builder().build(); String zipPublishTask = "publishPluginZipPublicationToZipStagingRepository"; - // Apply the opensearch.pluginzip plugin - project.getPluginManager().apply("opensearch.pluginzip"); - // Check if the plugin has been applied to the project - assertTrue(project.getPluginManager().hasPlugin("opensearch.pluginzip")); - // Check if the project has the task from class PublishToMavenRepository after plugin apply - assertNotNull(project.getTasks().withType(PublishToMavenRepository.class)); - // Create a mock bundlePlugin task - Zip task = project.getTasks().create("bundlePlugin", Zip.class); - Publish.configMaven(project); - // Check if the main task publishPluginZipPublicationToZipStagingRepository exists after plugin apply - assertTrue(project.getTasks().getNames().contains(zipPublishTask)); - assertNotNull("Task to generate: ", project.getTasks().getByName(zipPublishTask)); - // Run Gradle functional tests, but calling a build.gradle file, that resembles the plugin publish behavior + prepareProjectForPublishTask(zipPublishTask); + + // Generate the build.gradle file + String buildFileContent = "apply plugin: 'maven-publish' \n" + + "apply plugin: 'java' \n" + + "publishing {\n" + + " repositories {\n" + + " maven {\n" + + " url = 'local-staging-repo/'\n" + + " name = 'zipStaging'\n" + + " }\n" + + " }\n" + + " publications {\n" + + " pluginZip(MavenPublication) {\n" + + " groupId = 'org.opensearch.plugin' \n" + + " artifactId = 'sample-plugin' \n" + + " version = '2.0.0.0' \n" + + " artifact('sample-plugin.zip') \n" + + " }\n" + + " }\n" + + "}"; + writeString(projectDir.newFile("build.gradle"), buildFileContent); + // Execute the task publishPluginZipPublicationToZipStagingRepository + List allArguments = new ArrayList(); + allArguments.add("build"); + allArguments.add(zipPublishTask); + GradleRunner runner = GradleRunner.create(); + runner.forwardOutput(); + runner.withPluginClasspath(); + runner.withArguments(allArguments); + runner.withProjectDir(projectDir.getRoot()); + BuildResult result = runner.build(); + // Check if task publishMavenzipPublicationToZipstagingRepository has ran well + assertEquals(SUCCESS, result.task(":" + zipPublishTask).getOutcome()); + // check if the zip has been published to local staging repo + assertTrue( + new File(projectDir.getRoot(), "local-staging-repo/org/opensearch/plugin/sample-plugin/2.0.0.0/sample-plugin-2.0.0.0.zip") + .exists() + ); + assertEquals(SUCCESS, result.task(":" + "build").getOutcome()); + // Parse the maven file and validate the groupID to org.opensearch.plugin + MavenXpp3Reader reader = new MavenXpp3Reader(); + Model model = reader.read( + new FileReader( + new File(projectDir.getRoot(), "local-staging-repo/org/opensearch/plugin/sample-plugin/2.0.0.0/sample-plugin-2.0.0.0.pom") + ) + ); + assertEquals(model.getGroupId(), "org.opensearch.plugin"); + } + + @Test + public void testZipPublishWithPom() throws IOException, XmlPullParserException { + String zipPublishTask = "publishPluginZipPublicationToZipStagingRepository"; + Project project = prepareProjectForPublishTask(zipPublishTask); - // Create a sample plugin zip file - File sampleZip = new File(projectDir.getRoot(), "sample-plugin.zip"); - Files.createFile(sampleZip.toPath()); - writeString(projectDir.newFile("settings.gradle"), ""); // Generate the build.gradle file String buildFileContent = "apply plugin: 'maven-publish' \n" + "apply plugin: 'java' \n" @@ -88,6 +124,26 @@ public void testZipPublish() throws IOException, XmlPullParserException { + " artifactId = 'sample-plugin' \n" + " version = '2.0.0.0' \n" + " artifact('sample-plugin.zip') \n" + + " pom {\n" + + " name = 'sample-plugin'\n" + + " description = 'sample-description'\n" + + " licenses {\n" + + " license {\n" + + " name = \"The Apache License, Version 2.0\"\n" + + " url = \"http://www.apache.org/licenses/LICENSE-2.0.txt\"\n" + + " }\n" + + " }\n" + + " developers {\n" + + " developer {\n" + + " name = 'opensearch'\n" + + " url = 'https://github.com/opensearch-project/OpenSearch'\n" + + " }\n" + + " }\n" + + " url = 'https://github.com/opensearch-project/OpenSearch'\n" + + " scm {\n" + + " url = 'https://github.com/opensearch-project/OpenSearch'\n" + + " }\n" + + " }" + " }\n" + " }\n" + "}"; @@ -118,6 +174,32 @@ public void testZipPublish() throws IOException, XmlPullParserException { ) ); assertEquals(model.getGroupId(), "org.opensearch.plugin"); + assertEquals(model.getUrl(), "https://github.com/opensearch-project/OpenSearch"); + } + + protected Project prepareProjectForPublishTask(String zipPublishTask) throws IOException { + Project project = ProjectBuilder.builder().build(); + + // Apply the opensearch.pluginzip plugin + project.getPluginManager().apply("opensearch.pluginzip"); + // Check if the plugin has been applied to the project + assertTrue(project.getPluginManager().hasPlugin("opensearch.pluginzip")); + // Check if the project has the task from class PublishToMavenRepository after plugin apply + assertNotNull(project.getTasks().withType(PublishToMavenRepository.class)); + // Create a mock bundlePlugin task + Zip task = project.getTasks().create("bundlePlugin", Zip.class); + Publish.configMaven(project); + // Check if the main task publishPluginZipPublicationToZipStagingRepository exists after plugin apply + assertTrue(project.getTasks().getNames().contains(zipPublishTask)); + assertNotNull("Task to generate: ", project.getTasks().getByName(zipPublishTask)); + // Run Gradle functional tests, but calling a build.gradle file, that resembles the plugin publish behavior + + // Create a sample plugin zip file + File sampleZip = new File(projectDir.getRoot(), "sample-plugin.zip"); + Files.createFile(sampleZip.toPath()); + writeString(projectDir.newFile("settings.gradle"), ""); + + return project; } private void writeString(File file, String string) throws IOException { From 1743571fc326593fcdabf7bd51d85c047218708d Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Thu, 23 Jun 2022 12:08:29 -0400 Subject: [PATCH 3/3] Support both Gradle 6.8.x and Gradle 7.4.x Signed-off-by: Andriy Redko --- .../org/opensearch/gradle/PublishPlugin.java | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/buildSrc/src/main/java/org/opensearch/gradle/PublishPlugin.java b/buildSrc/src/main/java/org/opensearch/gradle/PublishPlugin.java index fe03605c830d2..2bdef8e4cd244 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/PublishPlugin.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/PublishPlugin.java @@ -36,7 +36,6 @@ import com.github.jengelman.gradle.plugins.shadow.ShadowExtension; import groovy.util.Node; import groovy.util.NodeList; -import groovy.xml.QName; import org.opensearch.gradle.info.BuildParams; import org.opensearch.gradle.precommit.PomValidationPrecommitPlugin; @@ -57,6 +56,9 @@ import org.gradle.api.tasks.bundling.Jar; import org.gradle.language.base.plugins.LifecycleBasePlugin; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.util.concurrent.Callable; import static org.opensearch.gradle.util.GradleUtils.maybeConfigure; @@ -153,16 +155,29 @@ private static void addScmInfo(XmlProvider xml) { for (final Object child : root.children()) { if (child instanceof Node) { final Node node = (Node) child; - if (node.name() instanceof QName) { - final QName qname = (QName) node.name(); - if (qname.matches("url")) { - url = node; - } else if (qname.matches("scm")) { - scm = node; + final Object name = node.name(); + + try { + // For Gradle 6.8 and below, the class is groovy.xml.QName + // For Gradle 7.4 and above, the class is groovy.namespace.QName + if (name != null && name.getClass().getSimpleName().equals("QName")) { + final MethodHandle handle = MethodHandles.publicLookup() + .findVirtual(name.getClass(), "matches", MethodType.methodType(boolean.class, Object.class)) + .bindTo(name); + + if ((boolean) handle.invoke("url")) { + url = node; + } else if ((boolean) handle.invoke("scm")) { + scm = node; + } } - } else if ("url".equals(node.name())) { + } catch (final Throwable ex) { + // Not a suitable QName type we could use ... + } + + if ("url".equals(name)) { url = node; - } else if ("scm".equals(node.name())) { + } else if ("scm".equals(name)) { scm = node; } }