Skip to content
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

Add recipe for index.jelly and to replace a test constructor on outdated plugin #571

Merged
merged 1 commit into from
Jan 4, 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
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>

Choose a reason for hiding this comment

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

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
Loading