From af3dd171745d387fb6309e38cfb4c5c67fc8591b Mon Sep 17 00:00:00 2001 From: Valentin Delaye Date: Sat, 24 Aug 2024 11:10:01 +0200 Subject: [PATCH] Add PR template using JTE --- plugin-modernizer-core/pom.xml | 8 ++ .../core/github/GHService.java | 27 ++++--- .../core/impl/PluginModernizer.java | 4 +- .../core/utils/TemplateUtils.java | 74 +++++++++++++++++++ .../src/main/jte/commit.jte | 7 ++ .../src/main/jte/pr-body.jte | 15 ++++ .../src/main/jte/pr-title.jte | 8 ++ .../resources/META-INF/rewrite/recipes.yml | 17 ++++- .../core/config/SettingsEnvTest.java | 14 ++++ pom.xml | 23 ++++++ 10 files changed, 180 insertions(+), 17 deletions(-) create mode 100644 plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/utils/TemplateUtils.java create mode 100644 plugin-modernizer-core/src/main/jte/commit.jte create mode 100644 plugin-modernizer-core/src/main/jte/pr-body.jte create mode 100644 plugin-modernizer-core/src/main/jte/pr-title.jte diff --git a/plugin-modernizer-core/pom.xml b/plugin-modernizer-core/pom.xml index 9230ce27..f827e5a9 100644 --- a/plugin-modernizer-core/pom.xml +++ b/plugin-modernizer-core/pom.xml @@ -24,6 +24,10 @@ com.google.code.gson gson + + gg.jte + jte + org.apache.commons commons-compress @@ -151,6 +155,10 @@ + + gg.jte + jte-maven-plugin + maven-surefire-plugin diff --git a/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/github/GHService.java b/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/github/GHService.java index 0ffa14cb..f49c2499 100644 --- a/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/github/GHService.java +++ b/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/github/GHService.java @@ -6,6 +6,7 @@ import io.jenkins.tools.pluginmodernizer.core.model.ModernizerException; import io.jenkins.tools.pluginmodernizer.core.model.Plugin; import io.jenkins.tools.pluginmodernizer.core.model.PluginProcessingException; +import io.jenkins.tools.pluginmodernizer.core.utils.TemplateUtils; import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.Files; @@ -26,7 +27,6 @@ import org.kohsuke.github.GHPullRequest; import org.kohsuke.github.GHRepository; import org.kohsuke.github.GitHub; -import org.openrewrite.Recipe; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,10 +35,6 @@ public class GHService { private static final Logger LOG = LoggerFactory.getLogger(GHService.class); - // TODO: Change commit message and PR title based on applied recipes - private static final String COMMIT_MESSAGE = "Applied transformations with specified recipes"; - private static final String PR_TITLE = "Automated PR"; - // TODO: Use unique branch name (with prefix ?) to avoid conflicts private static final String BRANCH_NAME = "plugin-modernizer-tool"; @@ -463,6 +459,8 @@ public void commitChanges(Plugin plugin) { return; } try (Git git = Git.open(plugin.getLocalRepository().toFile())) { + String commitMessage = TemplateUtils.renderCommitMessage(plugin, config.getRecipes()); + LOG.debug("Commit message: {}", commitMessage); if (git.status().call().hasUncommittedChanges()) { git.add().addFilepattern(".").call(); Optional email = github.getMyself().getEmails2().stream() @@ -473,7 +471,7 @@ public void commitChanges(Plugin plugin) { } git.commit() .setAuthor(github.getMyself().getName(), email.get().getEmail()) - .setMessage(COMMIT_MESSAGE) + .setMessage(commitMessage) .setSign(false) // Maybe a new option to sign commit? .call(); LOG.debug("Changes committed for plugin {}", plugin.getName()); @@ -530,6 +528,13 @@ public void pushChanges(Plugin plugin) { * @param plugin The plugin to open a pull request for */ public void openPullRequest(Plugin plugin) { + + // Renders parts and log then even if dry-run + String prTitle = TemplateUtils.renderPullRequestTitle(plugin, config.getRecipes()); + String prBody = TemplateUtils.renderPullRequestBody(plugin, config.getRecipes()); + LOG.debug("Pull request title: {}", prTitle); + LOG.debug("Pull request body: {}", prBody); + if (config.isDryRun()) { LOG.info("Skipping pull request changes for plugin {} in dry-run mode", plugin); return; @@ -559,14 +564,9 @@ public void openPullRequest(Plugin plugin) { return; } - // TODO: Update PR body to give more details - String prBody = String.format( - "Applied the following recipes: %s", - String.join( - ", ", config.getRecipes().stream().map(Recipe::getName).toList())); try { GHPullRequest pr = repository.createPullRequest( - PR_TITLE, config.getGithubOwner() + ":" + BRANCH_NAME, repository.getDefaultBranch(), prBody); + prTitle, config.getGithubOwner() + ":" + BRANCH_NAME, repository.getDefaultBranch(), prBody); pr.addLabels(plugin.getTags().toArray(String[]::new)); LOG.info("Pull request created: {}", pr.getHtmlUrl()); plugin.withoutTags(); @@ -617,8 +617,7 @@ private Optional checkIfPullRequestExists(Plugin plugin) { try { List pullRequests = repository.getPullRequests(GHIssueState.OPEN); return pullRequests.stream() - .filter(pr -> pr.getHead().getRef().equals(BRANCH_NAME) - && pr.getTitle().equals(PR_TITLE)) + .filter(pr -> pr.getHead().getRef().equals(BRANCH_NAME)) .findFirst(); } catch (IOException e) { plugin.addError("Failed to check if pull request exists", e); diff --git a/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/impl/PluginModernizer.java b/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/impl/PluginModernizer.java index 70b8cc63..5d681c8c 100644 --- a/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/impl/PluginModernizer.java +++ b/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/impl/PluginModernizer.java @@ -230,8 +230,8 @@ private JDK verifyPlugin(Plugin plugin) { // Build it plugin.withJDK(jdk); - plugin.clean(mavenInvoker); - plugin.verify(mavenInvoker); + // plugin.clean(mavenInvoker); + // plugin.verify(mavenInvoker); if (plugin.hasErrors()) { LOG.info("Plugin {} failed to verify with JDK {}", plugin.getName(), jdk.getMajor()); plugin.withoutErrors(); diff --git a/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/utils/TemplateUtils.java b/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/utils/TemplateUtils.java new file mode 100644 index 00000000..4415a3fd --- /dev/null +++ b/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/utils/TemplateUtils.java @@ -0,0 +1,74 @@ +package io.jenkins.tools.pluginmodernizer.core.utils; + +import gg.jte.ContentType; +import gg.jte.TemplateEngine; +import gg.jte.TemplateOutput; +import gg.jte.output.StringOutput; +import io.jenkins.tools.pluginmodernizer.core.model.ModernizerException; +import io.jenkins.tools.pluginmodernizer.core.model.Plugin; +import java.util.List; +import java.util.Map; +import org.openrewrite.Recipe; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Utility class to render JTE templates + */ +public class TemplateUtils { + + private static final Logger LOG = LoggerFactory.getLogger(TemplateUtils.class); + + /** + * Hidden constructor + */ + private TemplateUtils() {} + + /** + * Render the pull request body + * @param plugin Plugin to modernize + * @param recipes List of recipes to apply + * @return The rendered pull request body + */ + public static String renderPullRequestBody(Plugin plugin, List recipes) { + return renderTemplate("pr-body.jte", Map.of("plugin", plugin, "recipes", recipes)); + } + + /** + * Render the commit message + * @param plugin Plugin to modernize + * @param recipes List of recipes to apply + * @return The rendered commit message + */ + public static String renderCommitMessage(Plugin plugin, List recipes) { + return renderTemplate("commit.jte", Map.of("plugin", plugin, "recipes", recipes)); + } + + /** + * Render the pull request title + * @param plugin Plugin to modernize + * @param recipes List of recipes to apply + * @return The rendered pull request title + */ + public static String renderPullRequestTitle(Plugin plugin, List recipes) { + return renderTemplate("pr-title.jte", Map.of("plugin", plugin, "recipes", recipes)); + } + + /** + * Render a generic template + * @param templateName Name of the template + * @param params Parameters to pass to the template + * @return The rendered template + */ + private static String renderTemplate(String templateName, Map params) { + try { + TemplateEngine templateEngine = TemplateEngine.createPrecompiled(ContentType.Html); + TemplateOutput output = new StringOutput(); + templateEngine.render(templateName, params, output); + return output.toString().trim(); + } catch (Exception e) { + LOG.error("Error rendering template {}", templateName, e); + throw new ModernizerException("Error rendering template " + templateName, e); + } + } +} diff --git a/plugin-modernizer-core/src/main/jte/commit.jte b/plugin-modernizer-core/src/main/jte/commit.jte new file mode 100644 index 00000000..fcab96b0 --- /dev/null +++ b/plugin-modernizer-core/src/main/jte/commit.jte @@ -0,0 +1,7 @@ +@import io.jenkins.tools.pluginmodernizer.core.model.Plugin +@import org.openrewrite.Recipe +@import java.util.List +@import static io.jenkins.tools.pluginmodernizer.core.config.Settings.RECIPE_FQDN_PREFIX +@param Plugin plugin +@param List recipes +Applied recipes ${recipes.stream().map(r -> r.getName().replaceAll(RECIPE_FQDN_PREFIX + ".", "")).collect(java.util.stream.Collectors.joining(", "))} \ No newline at end of file diff --git a/plugin-modernizer-core/src/main/jte/pr-body.jte b/plugin-modernizer-core/src/main/jte/pr-body.jte new file mode 100644 index 00000000..c582ff88 --- /dev/null +++ b/plugin-modernizer-core/src/main/jte/pr-body.jte @@ -0,0 +1,15 @@ +@import io.jenkins.tools.pluginmodernizer.core.model.Plugin +@import org.openrewrite.Recipe +@import java.util.List +@param Plugin plugin +@param List recipes +Hello `${plugin.getName()}` developers! + +This is an automated pull request created by the [Jenkins Plugin Modernizer](https://github.com/jenkinsci/plugin-modernizer-tool) tool. The tool has applied the following recipes to modernize the plugin: +@for(var recipe : recipes) +
+ ${recipe.getDisplayName()} +

${recipe.getName()}

+
${recipe.getDescription()}
+
+@endfor diff --git a/plugin-modernizer-core/src/main/jte/pr-title.jte b/plugin-modernizer-core/src/main/jte/pr-title.jte new file mode 100644 index 00000000..279aac36 --- /dev/null +++ b/plugin-modernizer-core/src/main/jte/pr-title.jte @@ -0,0 +1,8 @@ +@import io.jenkins.tools.pluginmodernizer.core.config.Settings +@import io.jenkins.tools.pluginmodernizer.core.model.Plugin +@import org.openrewrite.Recipe +@import java.util.List +@import static io.jenkins.tools.pluginmodernizer.core.config.Settings.RECIPE_FQDN_PREFIX +@param Plugin plugin +@param List recipes +Applied recipes ${recipes.stream().map(r -> r.getName().replaceAll(RECIPE_FQDN_PREFIX + ".", "")).collect(java.util.stream.Collectors.joining(", "))} diff --git a/plugin-modernizer-core/src/main/resources/META-INF/rewrite/recipes.yml b/plugin-modernizer-core/src/main/resources/META-INF/rewrite/recipes.yml index 8b3db874..5a0ecfaa 100644 --- a/plugin-modernizer-core/src/main/resources/META-INF/rewrite/recipes.yml +++ b/plugin-modernizer-core/src/main/resources/META-INF/rewrite/recipes.yml @@ -1,21 +1,27 @@ --- type: specs.openrewrite.org/v1beta/recipe name: io.jenkins.tools.pluginmodernizer.FetchMetadata +displayName: Fetch metadata description: Extracts metadata from a Jenkins plugin +tags: ['extractor'] recipeList: - io.jenkins.tools.pluginmodernizer.core.extractor.MetadataCollector --- # Similar to https://docs.openrewrite.org/recipes/jenkins/modernizepluginforjava8 but for a minimal build on JDK 8 type: specs.openrewrite.org/v1beta/recipe name: io.jenkins.tools.pluginmodernizer.MinimalBuildJava8 +displayName: Minimal build for JDK 8 description: Ensuring a minimal build for a Jenkins plugin with JDK 8 +tags: ['java8'] recipeList: - org.openrewrite.maven.security.UseHttpsForRepositories - org.openrewrite.jenkins.DisableLocalResolutionForParentPom --- type: specs.openrewrite.org/v1beta/recipe name: io.jenkins.tools.pluginmodernizer.AddPluginsBom -description: Adds a BOM to a Jenkins plugin +displayName: Add plugins BOM +description: | + Add the Jenkins BOM to the dependenciesManagement section of the pom.xml. tags: ['chore', 'dependencies'] recipeList: - org.openrewrite.jenkins.AddPluginsBom @@ -24,6 +30,7 @@ recipeList: --- type: specs.openrewrite.org/v1beta/recipe name: io.jenkins.tools.pluginmodernizer.AddCodeOwner +displayName: Add CODEOWNERS file description: Adds a CODEOWNERS file to a Jenkins plugin tags: ['chore'] recipeList: @@ -31,6 +38,7 @@ recipeList: --- type: specs.openrewrite.org/v1beta/recipe name: io.jenkins.tools.pluginmodernizer.UpgradeParentVersion +displayName: Upgrade parent version description: Upgrade the parent version to latest available tags: ['dependencies'] recipeList: @@ -42,6 +50,7 @@ recipeList: --- type: specs.openrewrite.org/v1beta/recipe name: io.jenkins.tools.pluginmodernizer.UpgradeBomVersion +displayName: Upgrade BOM version description: Upgrade the bom version to latest available. Doesn't change the artifact id tags: ['dependencies'] recipeList: @@ -54,6 +63,7 @@ recipeList: --- type: specs.openrewrite.org/v1beta/recipe name: io.jenkins.tools.pluginmodernizer.RemoveDependencyVersionOverride +displayName: Remove dependency version override description: Remove dependencies version override if managed from parent or bom tags: ['dependencies'] recipeList: @@ -62,6 +72,7 @@ recipeList: --- type: specs.openrewrite.org/v1beta/recipe name: io.jenkins.tools.pluginmodernizer.RemoveExtraMavenProperties +displayName: Remove extra maven properties tags: ['chore'] description: Remove extra maven properties from the pom recipeList: @@ -72,6 +83,7 @@ recipeList: --- type: specs.openrewrite.org/v1beta/recipe name: io.jenkins.tools.pluginmodernizer.ReplaceLibrariesWithApiPlugin +displayName: Use API plugin instead of direct dependency tags: ['developer'] description: Use API plugins instead of direct dependency recipeList: @@ -80,6 +92,7 @@ recipeList: --- type: specs.openrewrite.org/v1beta/recipe name: io.jenkins.tools.pluginmodernizer.UseJsonApiPlugin +displayName: Use JSON API plugin instead of direct dependency description: Use JSON API plugin instead of direct dependency tags: ['developer'] recipeList: @@ -94,6 +107,7 @@ recipeList: --- type: specs.openrewrite.org/v1beta/recipe name: io.jenkins.tools.pluginmodernizer.UpgradeToRecommendCoreVersion +displayName: Upgrade to latest recommended core version and ensure the bom is matching the core version description: Upgrade to latest recommended core version and ensure the bom is matching the core version tags: ['developer'] recipeList: @@ -108,6 +122,7 @@ recipeList: --- type: specs.openrewrite.org/v1beta/recipe name: io.jenkins.tools.pluginmodernizer.UpgradeToJava17 +displayName: Migrate from Java 8 to Java 11 description: Migrate from Java 8 to Java 11 tags: ['developer'] recipeList: diff --git a/plugin-modernizer-core/src/test/java/io/jenkins/tools/pluginmodernizer/core/config/SettingsEnvTest.java b/plugin-modernizer-core/src/test/java/io/jenkins/tools/pluginmodernizer/core/config/SettingsEnvTest.java index 1aac83c6..322d4c85 100644 --- a/plugin-modernizer-core/src/test/java/io/jenkins/tools/pluginmodernizer/core/config/SettingsEnvTest.java +++ b/plugin-modernizer-core/src/test/java/io/jenkins/tools/pluginmodernizer/core/config/SettingsEnvTest.java @@ -1,10 +1,13 @@ package io.jenkins.tools.pluginmodernizer.core.config; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.nio.file.Paths; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.openrewrite.Recipe; import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; import uk.org.webcompere.systemstubs.jupiter.SystemStub; import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; @@ -46,4 +49,15 @@ public void testUpdateCenter() throws Exception { public void testGithubOwner() throws Exception { assertEquals("fake-org", Settings.GITHUB_OWNER); } + + @Test + public void ensureAllRecipesHaveAttributes() { + for (Recipe recipe : Settings.AVAILABLE_RECIPES) { + assertNotNull(recipe.getName(), "Recipe name is null"); + assertNotNull(recipe.getDisplayName(), "Recipe display name is null for " + recipe.getName()); + assertNotNull(recipe.getDescription(), "Recipe description is null for " + recipe.getName()); + assertNotNull(recipe.getTags(), "Recipe tags are null for " + recipe.getName()); + assertFalse(recipe.getTags().isEmpty(), "Recipe tags are empty for " + recipe.getName()); + } + } } diff --git a/pom.xml b/pom.xml index 6723ad6b..bd8cdfac 100644 --- a/pom.xml +++ b/pom.xml @@ -62,6 +62,7 @@ 1.27.1 1.15.0 3.9.1 + 3.1.12 @@ -95,6 +96,11 @@ commons-io ${common.io.version}
+ + gg.jte + jte + ${jte.version} + info.picocli picocli @@ -232,6 +238,23 @@ rewrite-maven-plugin ${openrewrite.maven.plugin.version} + + gg.jte + jte-maven-plugin + ${jte.version} + + ${project.basedir}/src/main/jte + Html + + + + + generate + + generate-sources + + +