From 4c6672ac995c912ca7ae94aa73d7a4ac7c33cd27 Mon Sep 17 00:00:00 2001 From: Valentin Delaye Date: Fri, 10 Jan 2025 06:01:08 +0100 Subject: [PATCH] Add a Java ISO visitor for metadata collection --- plugin-modernizer-core/pom.xml | 11 +++++ .../core/extractor/JavaFileVisitor.java | 32 ++++++++++++++ .../extractor/MetadataFinalizerVisitor.java | 21 +++++---- .../core/extractor/MetadataVisitor.java | 8 ++++ .../core/extractor/PluginMetadata.java | 14 ++++++ .../core/extractor/FetchMetadataTest.java | 44 +++++++++++++++++-- 6 files changed, 119 insertions(+), 11 deletions(-) create mode 100644 plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/extractor/JavaFileVisitor.java diff --git a/plugin-modernizer-core/pom.xml b/plugin-modernizer-core/pom.xml index e14e03b3..74d4ac17 100644 --- a/plugin-modernizer-core/pom.xml +++ b/plugin-modernizer-core/pom.xml @@ -260,6 +260,17 @@ javax.servlet-api 4.0.1 + + + org.testcontainers + testcontainers + 1.20.4 + + + org.jenkins-ci.test + docker-fixtures + 200.v22a_e8766731c + org.jenkins-ci.plugins diff --git a/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/extractor/JavaFileVisitor.java b/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/extractor/JavaFileVisitor.java new file mode 100644 index 00000000..24692c27 --- /dev/null +++ b/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/extractor/JavaFileVisitor.java @@ -0,0 +1,32 @@ +package io.jenkins.tools.pluginmodernizer.core.extractor; + +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.tree.J; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A visitor to extract metadata from Java files. + */ +public class JavaFileVisitor extends JavaIsoVisitor { + + /** + * LOGGER. + */ + private static final Logger LOG = LoggerFactory.getLogger(JavaFileVisitor.class); + + @Override + public J.Import visitImport(J.Import _import, PluginMetadata pluginMetadata) { + _import = super.visitImport(_import, pluginMetadata); + // Rather a naive approach, but let's assume we can detect testcontainers usage by the package name + if (_import.getPackageName().startsWith("org.testcontainers.containers")) { + LOG.info("Found testcontainers import: {}. Plugin is using container tests", _import.getPackageName()); + pluginMetadata.setUseContainerTests(true); + } + if (_import.getPackageName().startsWith("org.jenkinsci.test.acceptance.docker")) { + LOG.info("Found docker-fixtures import: {}. Plugin is using container tests", _import.getPackageName()); + pluginMetadata.setUseContainerTests(true); + } + return _import; + } +} diff --git a/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/extractor/MetadataFinalizerVisitor.java b/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/extractor/MetadataFinalizerVisitor.java index 7193d95d..903d4bf4 100644 --- a/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/extractor/MetadataFinalizerVisitor.java +++ b/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/extractor/MetadataFinalizerVisitor.java @@ -1,6 +1,9 @@ package io.jenkins.tools.pluginmodernizer.core.extractor; -import io.jenkins.tools.pluginmodernizer.core.utils.JsonUtils; +import static io.jenkins.tools.pluginmodernizer.core.utils.JsonUtils.fromJson; +import static io.jenkins.tools.pluginmodernizer.core.utils.JsonUtils.merge; +import static io.jenkins.tools.pluginmodernizer.core.utils.JsonUtils.toJson; + import org.openrewrite.ExecutionContext; import org.openrewrite.Tree; import org.openrewrite.TreeVisitor; @@ -22,22 +25,24 @@ public Tree visit(Tree tree, ExecutionContext ctx) { PluginMetadata mergedMetadata = ctx.getMessage("mergedMetadata", new PluginMetadata()); PluginMetadata commonMetadata = ctx.getMessage("commonMetadata", new PluginMetadata()); - PluginMetadata pluginMetadata = ctx.getMessage("pomMetadata", new PluginMetadata()); + PluginMetadata pomMetadata = ctx.getMessage("pomMetadata", new PluginMetadata()); + PluginMetadata javaMetadata = ctx.getMessage("javaMetadata", new PluginMetadata()); PluginMetadata jenkinsFileMetadata = ctx.getMessage("jenkinsFileMetadata", new PluginMetadata()); // Merge the metadata - PluginMetadata merged = JsonUtils.fromJson( - JsonUtils.merge(pluginMetadata.toJson(), jenkinsFileMetadata.toJson()), PluginMetadata.class); - merged = JsonUtils.fromJson(JsonUtils.merge(commonMetadata.toJson(), merged.toJson()), PluginMetadata.class); - merged = JsonUtils.fromJson(JsonUtils.merge(mergedMetadata.toJson(), merged.toJson()), PluginMetadata.class); + PluginMetadata merged = + fromJson(merge(pomMetadata.toJson(), jenkinsFileMetadata.toJson()), PluginMetadata.class); + merged = fromJson(merge(commonMetadata.toJson(), merged.toJson()), PluginMetadata.class); + merged = fromJson(merge(javaMetadata.toJson(), merged.toJson()), PluginMetadata.class); + merged = fromJson(merge(mergedMetadata.toJson(), merged.toJson()), PluginMetadata.class); - LOG.debug("Merged metadata: {}", JsonUtils.toJson(merged)); + LOG.debug("Merged metadata: {}", toJson(merged)); // Write the metadata to a file for later use by the plugin modernizer. merged.save(); LOG.debug("Plugin metadata written to {}", merged.getRelativePath()); ctx.putMessage("mergedMetadata", merged); - LOG.debug(JsonUtils.toJson(merged)); + LOG.debug(toJson(merged)); return tree; } diff --git a/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/extractor/MetadataVisitor.java b/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/extractor/MetadataVisitor.java index 87bc1730..d43c4ef1 100644 --- a/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/extractor/MetadataVisitor.java +++ b/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/extractor/MetadataVisitor.java @@ -55,6 +55,14 @@ else if (PathUtils.matchesGlob(sourceFile.getSourcePath(), "**/pom.xml")) { executionContext.putMessage("pomMetadata", pomMetadata); // Is there better than context messaging ? return tree; } + // Extract metadata from java file + else if (PathUtils.matchesGlob(sourceFile.getSourcePath(), "**/*.java")) { + LOG.debug("Visiting Java file {}", sourceFile.getSourcePath()); + PluginMetadata javaMetadata = new JavaFileVisitor().reduce(tree, commonMetadata); + LOG.debug("Java metadata: {}", JsonUtils.toJson(javaMetadata)); + executionContext.putMessage("javaMetadata", javaMetadata); // Is there better than context messaging ? + return tree; + } // Just add the common else { diff --git a/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/extractor/PluginMetadata.java b/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/extractor/PluginMetadata.java index 275f0b11..8dafda28 100644 --- a/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/extractor/PluginMetadata.java +++ b/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/extractor/PluginMetadata.java @@ -16,6 +16,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; /** @@ -53,6 +54,11 @@ public class PluginMetadata extends CacheEntry { */ private Boolean useContainerAgent; + /** + * If the plugin is using container tests + */ + private Boolean useContainerTests; + /** * forkCount extracted from Jenkinsfile */ @@ -240,6 +246,14 @@ public void setUseContainerAgent(Boolean useContainerAgent) { this.useContainerAgent = useContainerAgent; } + public Boolean isUseContainerTests() { + return Objects.requireNonNullElse(useContainerTests, false); + } + + public void setUseContainerTests(Boolean useContainerTests) { + this.useContainerTests = useContainerTests; + } + public String getForkCount() { return forkCount; } diff --git a/plugin-modernizer-core/src/test/java/io/jenkins/tools/pluginmodernizer/core/extractor/FetchMetadataTest.java b/plugin-modernizer-core/src/test/java/io/jenkins/tools/pluginmodernizer/core/extractor/FetchMetadataTest.java index 22a45161..e7b4a167 100644 --- a/plugin-modernizer-core/src/test/java/io/jenkins/tools/pluginmodernizer/core/extractor/FetchMetadataTest.java +++ b/plugin-modernizer-core/src/test/java/io/jenkins/tools/pluginmodernizer/core/extractor/FetchMetadataTest.java @@ -9,6 +9,7 @@ import io.jenkins.tools.pluginmodernizer.core.model.JDK; import io.jenkins.tools.pluginmodernizer.core.model.Platform; +import io.jenkins.tools.pluginmodernizer.core.recipes.DeclarativeRecipesTest; import io.jenkins.tools.pluginmodernizer.core.recipes.FetchMetadata; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -19,6 +20,7 @@ import org.intellij.lang.annotations.Language; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.openrewrite.java.JavaParser; import org.openrewrite.test.RewriteTest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,6 +42,7 @@ public class FetchMetadataTest implements RewriteTest { EXPECTED_METADATA.setBomArtifactId("bom-2.414.x"); EXPECTED_METADATA.setBomVersion("2950.va_633b_f42f759"); EXPECTED_METADATA.setUseContainerAgent(null); + EXPECTED_METADATA.setUseContainerTests(null); EXPECTED_METADATA.setForkCount(null); Map properties = new LinkedHashMap<>(); properties.put("revision", "1.8.1"); @@ -295,9 +298,15 @@ class FooBar {} } @Test - void testWithManyCommonFiles() throws Exception { + void testWithManyCommonFilesAndTestContainers() throws Exception { rewriteRun( - recipeSpec -> recipeSpec.recipe(new FetchMetadata()), + spec -> { + var parser = JavaParser.fromJavaVersion().logCompilationWarningsAndErrors(true); + DeclarativeRecipesTest.collectRewriteTestDependencies().stream() + .filter(entry -> entry.getFileName().toString().contains("testcontainers")) + .forEach(parser::addClasspathEntry); + spec.recipe(new FetchMetadata()).parser(parser); + }, pomXml(POM_XML), // language=groovy groovy( @@ -314,6 +323,7 @@ void testWithManyCommonFiles() throws Exception { java( """ package com.uppercase.camelcase; + import org.testcontainers.containers.GenericContainer; class FooBar {} """), // language=yaml @@ -356,7 +366,8 @@ class FooBar {} // Check rest Set jdkVersion = pluginMetadata.getJdks(); assertEquals(2, jdkVersion.size()); - assertTrue(pluginMetadata.isUseContainerAgent()); + assertTrue(pluginMetadata.isUseContainerAgent(), "Should use container agent"); + assertTrue(pluginMetadata.isUseContainerTests(), "Should use container tests"); assertEquals(EXPECTED_METADATA.getParentVersion(), pluginMetadata.getParentVersion()); assertEquals(EXPECTED_METADATA.getPluginName(), pluginMetadata.getPluginName()); assertEquals(EXPECTED_METADATA.getJenkinsVersion(), pluginMetadata.getJenkinsVersion()); @@ -365,6 +376,31 @@ class FooBar {} assertEquals(EXPECTED_METADATA.getFlags(), pluginMetadata.getFlags()); } + @Test + void testWithDockerFixtures() throws Exception { + rewriteRun( + spec -> { + var parser = JavaParser.fromJavaVersion().logCompilationWarningsAndErrors(true); + DeclarativeRecipesTest.collectRewriteTestDependencies().stream() + .filter(entry -> entry.getFileName().toString().contains("docker-fixtures")) + .forEach(parser::addClasspathEntry); + spec.recipe(new FetchMetadata()).parser(parser); + }, + // language=java + java( + """ + package com.uppercase.camelcase; + import org.jenkinsci.test.acceptance.docker.DockerClassRule; + class FooBar {} + """)); + + PluginMetadata pluginMetadata = new PluginMetadata().refresh(); + assertNotNull(pluginMetadata, "Plugin metadata was not written by the recipe"); + + // Check use container test + assertTrue(pluginMetadata.isUseContainerTests(), "Should use container tests"); + } + @Test void testWithJenkinsfileOnly() throws Exception { rewriteRun( @@ -398,6 +434,8 @@ void testWithJenkinsfileOnly() throws Exception { assertEquals(2, platforms.size()); assertTrue(platforms.contains(Platform.WINDOWS)); assertTrue(platforms.contains(Platform.LINUX)); + + assertFalse(pluginMetadata.isUseContainerTests()); } @Test