Skip to content
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package io.jenkins.tools.pluginmodernizer.core.recipes;

import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Option;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.maven.AddDependencyVisitor;
import org.openrewrite.maven.MavenVisitor;
import org.openrewrite.maven.RemoveDependency;
import org.openrewrite.maven.tree.ResolvedDependency;
import org.openrewrite.xml.AddToTagVisitor;
import org.openrewrite.xml.XPathMatcher;
import org.openrewrite.xml.tree.Xml;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ReplaceLibrariesWithApiPlugin extends Recipe {
/**
* Logger
*/
private static final Logger LOG = LoggerFactory.getLogger(ReplaceLibrariesWithApiPlugin.class);

private static final XPathMatcher DEPENDENCIES_MATCHER = new XPathMatcher("/project/dependencies");

@Option(
displayName = "API Plugin's groupId",
description = "The first part of a dependency coordinate 'io.jenkins.plugins:ARTIFACT_ID:VERSION'.",
example = "io.jenkins.plugins")
String pluginGroupId;

@Option(
displayName = "API Plugin's artifactId",
description = "The second part of a dependency coordinate 'GROUP_ID:jackson2-api:VERSION'.",
example = "jackson2-api")
String pluginArtifactId;

@Option(
displayName = "API Plugin's version",
description = "An exact version number.",
example = "1981.v17df70e84a_a_1")
String pluginVersion;

@Option(
displayName = "Replaced Libraries",
description = "The set of library coordinates replaced by this API Plugin.",
example = "groupId: org.apache.commons\nartifactId: commons-text")
Set<Library> replaces;

/**
* The groupId:artifactId combos to be replaced if present.
*/
public static final class Library {
private final String groupId;
private final String artifactId;

public Library(String groupId, String artifactId) {
this.groupId = groupId;
this.artifactId = artifactId;
}
}

/**
* Replaces a set of libraries with an api plugin.
* @param pluginGroupId api plugin's groupId
* @param pluginArtifactId api plugin's artifactId
* @param pluginVersion api plugin's version
* @param replaces set of libraries included in the api plugin
*/
public ReplaceLibrariesWithApiPlugin(
String pluginGroupId, String pluginArtifactId, String pluginVersion, Set<Library> replaces) {
this.pluginGroupId = pluginGroupId;
this.pluginArtifactId = pluginArtifactId;
this.pluginVersion = pluginVersion;
this.replaces = replaces;
}

@Override
public String getDisplayName() {
return "Use Jenkins API plugin instead of libraries";
}

@Override
public String getDescription() {
return "Prefer Jenkins API plugins over bundling libraries for slimmer plugins.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new MavenVisitor<ExecutionContext>() {
@Override
public Xml visitTag(Xml.Tag tag, ExecutionContext ctx) {
if (isDependencyTag()) {
ResolvedDependency dependency = findDependency(tag);
if (dependency != null && !isApiPlugin(dependency)) {
for (Library replaced : replaces) {
String groupId = replaced.groupId;
String artifactId = replaced.artifactId;
ResolvedDependency found = dependency.findDependency(groupId, artifactId);
if (found == null) {
continue;
}
doAfterVisit(new AddDependencyVisitor(
pluginGroupId,
pluginArtifactId,
pluginVersion,
null,
null,
true,
null,
null,
false,
null));
doAfterVisit(new RemoveDependency(groupId, artifactId, null).getVisitor());
// transitive dependency
if (found != dependency) {
if (isApiProvidedLibrary(groupId, artifactId)) {
continue; // Skip exclusions if the library is already part of the API plugin
}
Optional<Xml.Tag> maybeExclusions = tag.getChild("exclusions");
if (maybeExclusions.isPresent()) {
Xml.Tag exclusions = maybeExclusions.get();

List<Xml.Tag> individualExclusions = exclusions.getChildren("exclusion");
boolean alreadyExcluded = individualExclusions.stream()
.anyMatch(exclusion -> groupId.equals(exclusion
.getChildValue("groupId")
.orElse(null))
&& artifactId.equals(exclusion
.getChildValue("artifactId")
.orElse(null)));
if (!alreadyExcluded) {
doAfterVisit(new AddToTagVisitor<>(
exclusions,
Xml.Tag.build("<exclusion>\n"
+ "<!-- brought in by " + pluginGroupId + ":" + pluginArtifactId
+ " -->\n"
+ "<groupId>" + groupId + "</groupId>\n"
+ "<artifactId>" + artifactId + "</artifactId>\n"
+ "</exclusion>")));
}
} else {
doAfterVisit(new AddToTagVisitor<>(
tag,
Xml.Tag.build("<exclusions>\n"
+ "<exclusion>\n"
+ "<!-- brought in by " + pluginGroupId + ":" + pluginArtifactId
+ " -->\n"
+ "<groupId>" + groupId + "</groupId>\n"
+ "<artifactId>" + artifactId + "</artifactId>\n"
+ "</exclusion>\n"
+ "</exclusions>")));
}
maybeUpdateModel();
}
}
}
}
return super.visitTag(tag, ctx);
}

private boolean isApiProvidedLibrary(String groupId, String artifactId) {
return replaces.stream()
.anyMatch(
replaced -> replaced.groupId.equals(groupId) && replaced.artifactId.equals(artifactId));
}

private boolean isApiPlugin(ResolvedDependency dependency) {
return pluginGroupId.equals(dependency.getGroupId())
&& pluginArtifactId.equals(dependency.getArtifactId());
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ tags: ['developer']
preconditions:
- io.jenkins.tools.pluginmodernizer.conditions.IsUsingRecommendCoreVersion
recipeList:
- org.openrewrite.jenkins.ReplaceLibrariesWithApiPlugin:
- io.jenkins.tools.pluginmodernizer.core.recipes.ReplaceLibrariesWithApiPlugin:
pluginGroupId: io.jenkins.plugins
pluginArtifactId: json-api
pluginVersion: 20250107-125.v28b_a_ffa_eb_f01
Expand All @@ -249,7 +249,7 @@ tags: ['developer']
preconditions:
- io.jenkins.tools.pluginmodernizer.conditions.IsUsingRecommendCoreVersion
recipeList:
- org.openrewrite.jenkins.ReplaceLibrariesWithApiPlugin:
- io.jenkins.tools.pluginmodernizer.core.recipes.ReplaceLibrariesWithApiPlugin:
pluginGroupId: io.jenkins.plugins
pluginArtifactId: json-path-api
pluginVersion: 2.9.0-138.vc943da_d833b_6
Expand All @@ -265,7 +265,7 @@ tags: ['developer']
preconditions:
- io.jenkins.tools.pluginmodernizer.conditions.IsUsingCoreVersionWithASMRemoved
recipeList:
- org.openrewrite.jenkins.ReplaceLibrariesWithApiPlugin:
- io.jenkins.tools.pluginmodernizer.core.recipes.ReplaceLibrariesWithApiPlugin:
pluginGroupId: io.jenkins.plugins
pluginArtifactId: asm-api
pluginVersion: 9.7.1-97.v4cc844130d97
Expand All @@ -289,7 +289,7 @@ tags: ['developer']
preconditions:
- io.jenkins.tools.pluginmodernizer.conditions.IsUsingRecommendCoreVersion
recipeList:
- org.openrewrite.jenkins.ReplaceLibrariesWithApiPlugin:
- io.jenkins.tools.pluginmodernizer.core.recipes.ReplaceLibrariesWithApiPlugin:
pluginGroupId: io.jenkins.plugins
pluginArtifactId: joda-time-api
pluginVersion: 2.13.1-115.va_6b_5f8efb_1d8
Expand All @@ -305,7 +305,7 @@ tags: ['developer']
preconditions:
- io.jenkins.tools.pluginmodernizer.conditions.IsUsingRecommendCoreVersion
recipeList:
- org.openrewrite.jenkins.ReplaceLibrariesWithApiPlugin:
- io.jenkins.tools.pluginmodernizer.core.recipes.ReplaceLibrariesWithApiPlugin:
pluginGroupId: io.jenkins.plugins
pluginArtifactId: gson-api
pluginVersion: 2.12.1-113.v347686d6729f
Expand All @@ -321,7 +321,7 @@ tags: ['developer']
preconditions:
- io.jenkins.tools.pluginmodernizer.conditions.IsUsingRecommendCoreVersion
recipeList:
- org.openrewrite.jenkins.ReplaceLibrariesWithApiPlugin:
- io.jenkins.tools.pluginmodernizer.core.recipes.ReplaceLibrariesWithApiPlugin:
pluginGroupId: org.jenkins-ci.plugins
pluginArtifactId: jsoup
pluginVersion: 1.18.3-30.v952e9442d416
Expand All @@ -337,7 +337,7 @@ tags: ['developer']
preconditions:
- io.jenkins.tools.pluginmodernizer.conditions.IsUsingCoreVersionWithCommonsCompressRemoved
recipeList:
- org.openrewrite.jenkins.ReplaceLibrariesWithApiPlugin:
- io.jenkins.tools.pluginmodernizer.core.recipes.ReplaceLibrariesWithApiPlugin:
pluginGroupId: io.jenkins.plugins
pluginArtifactId: commons-compress-api
pluginVersion: 1.27.1-2
Expand All @@ -356,7 +356,7 @@ preconditions:
- org.openrewrite.jenkins.IsJenkinsPlugin:
version: "[2.361.4,)"
recipeList:
- org.openrewrite.jenkins.ReplaceLibrariesWithApiPlugin:
- io.jenkins.tools.pluginmodernizer.core.recipes.ReplaceLibrariesWithApiPlugin:
pluginGroupId: io.jenkins.plugins
pluginArtifactId: commons-lang3-api
pluginVersion: 3.17.0-84.vb_b_938040b_078
Expand All @@ -372,7 +372,7 @@ tags: ['developer']
preconditions:
- io.jenkins.tools.pluginmodernizer.conditions.IsUsingRecommendCoreVersion
recipeList:
- org.openrewrite.jenkins.ReplaceLibrariesWithApiPlugin:
- io.jenkins.tools.pluginmodernizer.core.recipes.ReplaceLibrariesWithApiPlugin:
pluginGroupId: io.jenkins.plugins
pluginArtifactId: byte-buddy-api
pluginVersion: 1.17.0-117.v025ccb_c559d7
Expand All @@ -388,7 +388,7 @@ tags: ['developer']
preconditions:
- io.jenkins.tools.pluginmodernizer.conditions.IsUsingRecommendCoreVersion
recipeList:
- org.openrewrite.jenkins.ReplaceLibrariesWithApiPlugin:
- io.jenkins.tools.pluginmodernizer.core.recipes.ReplaceLibrariesWithApiPlugin:
pluginGroupId: io.jenkins.plugins
pluginArtifactId: commons-text-api
pluginVersion: 1.13.0-153.v91dcd89e2a_22
Expand Down
Loading