Skip to content

Commit

Permalink
Add recipe for IndexJelly and to replace a test constructor on outdat…
Browse files Browse the repository at this point in the history
…ed plugins
  • Loading branch information
jonesbusy committed Jan 4, 2025
1 parent f1ccf6f commit 5dacba8
Show file tree
Hide file tree
Showing 8 changed files with 707 additions and 32 deletions.
11 changes: 10 additions & 1 deletion plugin-modernizer-cli/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,17 @@
<groupId>io.jenkins.plugin-modernizer</groupId>
<artifactId>plugin-modernizer-core</artifactId>
<version>${changelist}</version>
<!-- We never use OpenRewrite directly from CLI because of maven invoker which will download the core module and it's dependencies -->
<!-- We never use OpenRewrite or it's dependencies when running recipes
directly from CLI because of maven invoker which will download the core module and it's dependencies -->
<exclusions>
<exclusion>
<groupId>io.jenkins.plugins</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>org.openrewrite</groupId>
<artifactId>*</artifactId>
Expand Down
91 changes: 66 additions & 25 deletions plugin-modernizer-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,19 @@
<artifactId>plugin-modernizer-core</artifactId>
<name>Plugin Modernizer Core</name>

<!-- For plugin code migration -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.jenkins.tools.bom</groupId>
<artifactId>bom-2.479.x</artifactId>
<version>3875.v1df09947cde6</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
Expand Down Expand Up @@ -160,6 +173,7 @@
<artifactId>jjwt-impl</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
Expand All @@ -171,26 +185,6 @@
<version>0.0.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.kohsuke.stapler</groupId>
<artifactId>stapler</artifactId>
<version>1928.v9115fe47607f</version>
<scope>test</scope>
</dependency>

<!-- Test dependencies -->
<dependency>
<groupId>org.openrewrite</groupId>
<artifactId>rewrite-test</artifactId>
Expand All @@ -202,6 +196,7 @@
<version>2.1.7</version>
<scope>test</scope>
</dependency>

</dependencies>

<build>
Expand Down Expand Up @@ -239,15 +234,44 @@
<version>3.8.1</version>
<executions>
<execution>
<id>copy-dependencies</id>
<id>copy</id>
<goals>
<goal>copy-dependencies</goal>
<goal>copy</goal>
</goals>
<phase>validate</phase>
<configuration>
<outputDirectory>${project.build.directory}/openrewrite-classpath</outputDirectory>
<!-- Adapt when testing java transformation -->
<includeGroupIds>javax.servlet,jakarta.servlet,org.kohsuke.stapler</includeGroupIds>
<outputDirectory>${project.build.directory}/openrewrite-jars</outputDirectory>

<!-- Only for rewrite test -->
<!-- Several version can be provided to test migration -->
<artifactItems>
<artifactItem>
<groupId>org.kohsuke.stapler</groupId>
<artifactId>stapler</artifactId>
<version>1928.v9115fe47607f</version>
</artifactItem>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.1.0</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<!-- SSH slave migration from 1.12 to latest -->
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>ssh-slaves</artifactId>
<version>1.12</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>ssh-slaves</artifactId>
<version>3.1021.va_cc11b_de26a_e</version>
</dependency>
</artifactItems>
</configuration>
</execution>
</executions>
Expand Down Expand Up @@ -315,4 +339,21 @@
</plugins>
</build>

<!-- Only to group recipe/plugin/core dependencies -->
<!-- This must be excluded from CLI packaging -->
<profiles>
<profile>
<id>recipes-dependencies</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<dependencies>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>ssh-slaves</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package io.jenkins.tools.pluginmodernizer.core.recipes;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.jenkins.tools.pluginmodernizer.core.extractor.ArchetypeCommonFile;
import io.jenkins.tools.pluginmodernizer.core.extractor.PluginMetadata;
import io.jenkins.tools.pluginmodernizer.core.extractor.PomResolutionVisitor;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.intellij.lang.annotations.Language;
import org.openrewrite.Cursor;
import org.openrewrite.ExecutionContext;
import org.openrewrite.ScanningRecipe;
import org.openrewrite.SourceFile;
import org.openrewrite.Tree;
import org.openrewrite.TreeVisitor;
import org.openrewrite.maven.MavenIsoVisitor;
import org.openrewrite.text.PlainText;
import org.openrewrite.xml.tree.Xml;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Ensure `index.jelly` exists
*/
public class EnsureIndexJelly extends ScanningRecipe<EnsureIndexJelly.ShouldCreate> {

/**
* Jelly file
*/
@Language("xml")
public static final String JELLY_FILE =
"""
<?jelly escape-by-default='true'?>
<div>
DESCRIPTION
</div>
""";

/**
* LOGGER.
*/
private static final Logger LOG = LoggerFactory.getLogger(EnsureIndexJelly.class);

@Override
public String getDisplayName() {
return "Create `index.jelly` if it doesn't exist";
}

@Override
public String getDescription() {
return "Jenkins tooling [requires](https://github.com/jenkinsci/maven-hpi-plugin/pull/302) "
+ "`src/main/resources/index.jelly` exists with a description.";
}

@Override
public ShouldCreate getInitialValue(ExecutionContext ctx) {
return new ShouldCreate();
}

@Override
@SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
public TreeVisitor<?, ExecutionContext> getScanner(ShouldCreate shouldCreate) {
PluginMetadata metadata = new PluginMetadata();
return new TreeVisitor<>() {
@Override
public Tree visit(Tree tree, ExecutionContext ctx) {
SourceFile sourceFile = (SourceFile) tree;
// We visit a jelly
if (sourceFile.getSourcePath().endsWith(ArchetypeCommonFile.INDEX_JELLY.getPath())) {
LOG.info("Found 1 index.jelly a Will not replace it");
shouldCreate.jelliesPath.add(sourceFile.getSourcePath());
return tree;
}
// We visit a pom
if (sourceFile.getSourcePath().endsWith(ArchetypeCommonFile.POM.getPath())) {
new PomResolutionVisitor().reduce(sourceFile, metadata);
if (metadata.getJenkinsVersion() == null) {
LOG.info("Skipping pom {} as it is not a Jenkins plugin", sourceFile.getSourcePath());
return tree;
}
Path jellyPath = sourceFile
.getSourcePath()
.resolve("..")
.resolve(ArchetypeCommonFile.INDEX_JELLY.getPath())
.normalize();
Xml.Document pom = (Xml.Document) sourceFile;
DescriptionVisitor descriptionVisitor = new DescriptionVisitor();
descriptionVisitor.visitNonNull(pom, ctx);
if (!descriptionVisitor.description.isEmpty()) {
shouldCreate.plugins.put(jellyPath, descriptionVisitor.description);
} else if (!descriptionVisitor.artifactId.isEmpty()) {
shouldCreate.plugins.put(jellyPath, descriptionVisitor.artifactId);
}
LOG.debug(shouldCreate.toString());
}
return tree;
}
};
}

/**
* Visitor to extract the metadata from the POM file.
*/
private static class DescriptionVisitor extends MavenIsoVisitor<ExecutionContext> {
private String artifactId = "";
private String description = "";

@Override
public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) {
Cursor parent = getCursor().getParentOrThrow();
if (!(parent.getValue() instanceof Xml.Tag)) {
return super.visitTag(tag, ctx);
}
Xml.Tag parentTag = parent.getValue();
if (!parentTag.getName().equals("project")) {
return super.visitTag(tag, ctx);
}
if ("description".equals(tag.getName())) {
description = tag.getValue().orElse("");
} else if ("artifactId".equals(tag.getName()) && !isManagedDependencyTag() && !isDependencyTag()) {
artifactId =
tag.getValue().orElseThrow(() -> new IllegalStateException("Expected to find an artifact id"));
}
return super.visitTag(tag, ctx);
}
}

/**
* Accumulator to know if the file should be created or not and with which description.
*/
public static class ShouldCreate {
private Map<Path, String> plugins = new HashMap<>();
private List<Path> jelliesPath = new LinkedList<>();
}

@Override
public Collection<SourceFile> generate(ShouldCreate shouldCreate, ExecutionContext ctx) {
if (shouldCreate.plugins.isEmpty()) {
return Collections.emptyList();
}
List<SourceFile> generated = new LinkedList<>();
for (Map.Entry<Path, String> plugin : shouldCreate.plugins.entrySet()) {
if (shouldCreate.jelliesPath.contains(plugin.getKey())) {
continue;
}
LOG.info("Creating index.jelly at " + plugin.getKey() + " with description " + plugin.getValue());
generated.add(PlainText.builder()
.sourcePath(plugin.getKey())
.text(JELLY_FILE.replace("DESCRIPTION", plugin.getValue()))
.build());
}
return generated;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package io.jenkins.tools.pluginmodernizer.core.recipes.code;

import org.openrewrite.ExecutionContext;
import org.openrewrite.NlsRewrite;
import org.openrewrite.Preconditions;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.search.IsLikelyTest;
import org.openrewrite.java.tree.J;

/**
* A recipe that update the bom version to latest available.
*/
public class ReplaceRemovedSSHLauncherConstructor extends Recipe {

@Override
public @NlsRewrite.DisplayName String getDisplayName() {
return "Replace a remove SSHLauncher constructor";
}

@Override
public @NlsRewrite.Description String getDescription() {
return "Replace a remove SSHLauncher constructor.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
// Only run on test files
return Preconditions.check(new IsLikelyTest(), new UseDataBoundConstructor());
}

/**
* Visitor that replace the removed SSHLauncher removed constructor by the new one.
*/
public static class UseDataBoundConstructor extends JavaIsoVisitor<ExecutionContext> {

// We will replace by the @DataBoundConstructor
JavaTemplate newConstructorTemplate = JavaTemplate.builder(
"new SSHLauncher(#{any(java.lang.String)}, #{any(int)}, null)")
.imports("hudson.plugins.sshslaves.SSHLauncher")
.javaParser(JavaParser.fromJavaVersion().classpath("ssh-slaves"))
.build();

@Override
public J.NewClass visitNewClass(J.NewClass newClass, ExecutionContext ctx) {
newClass = super.visitNewClass(newClass, ctx);
if (newClass.getConstructorType() == null) {
return newClass;
}
if (!newClass.getConstructorType()
.getDeclaringType()
.getFullyQualifiedName()
.equals("hudson.plugins.sshslaves.SSHLauncher")) {
return newClass;
}
// Replace removed 6 arguments constructor with 3 arguments constructor
// See https://github.com/jenkinsci/ssh-agents-plugin/commit/f540572d7819bec840605227636de319a192bc84
if (newClass.getArguments().size() == 6) {
return newConstructorTemplate.apply(
updateCursor(newClass),
newClass.getCoordinates().replace(),
newClass.getArguments().get(0),
newClass.getArguments().get(1));
}
return newClass;
}
}
}
Loading

0 comments on commit 5dacba8

Please sign in to comment.