Skip to content

Commit

Permalink
Merge pull request #513 from jonesbusy/feature/add-property-comment-v…
Browse files Browse the repository at this point in the history
…isitor

Add visitor to add and remove a comment for properties and `jenkins.baseline` migration
  • Loading branch information
jonesbusy authored Dec 26, 2024
2 parents d81a9e0 + 92035ce commit 3fda79b
Show file tree
Hide file tree
Showing 12 changed files with 1,006 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,13 @@ public void setProperties(Map<String, String> properties) {
this.properties = properties;
}

public void addProperty(String key, String value) {
if (properties == null) {
properties = new HashMap<>();
}
properties.put(key, value);
}

@Override
public UUID getId() {
return id;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ public class Recipe {
@JsonIgnore
private Object preconditions; // Use Object to avoid mapping complex nested structures.

@JsonIgnore
private Boolean causesAnotherCycle;

public String getName() {
return name;
}
Expand Down Expand Up @@ -70,4 +73,16 @@ public void setRecipeList(Object recipeList) {
public Object getPreconditions() {
return preconditions;
}

public void setPreconditions(Object preconditions) {
this.preconditions = preconditions;
}

public Boolean getCausesAnotherCycle() {
return causesAnotherCycle;
}

public void setCausesAnotherCycle(Boolean causesAnotherCycle) {
this.causesAnotherCycle = causesAnotherCycle;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

import io.jenkins.tools.pluginmodernizer.core.extractor.PluginMetadata;
import io.jenkins.tools.pluginmodernizer.core.extractor.PomVisitor;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import io.jenkins.tools.pluginmodernizer.core.visitors.AddBeforePropertyVisitor;
import io.jenkins.tools.pluginmodernizer.core.visitors.AddPropertyCommentVisitor;
import org.jspecify.annotations.Nullable;
import org.openrewrite.*;
import org.openrewrite.Cursor;
import org.openrewrite.maven.AddPropertyVisitor;
import org.openrewrite.maven.MavenIsoVisitor;
import org.openrewrite.xml.ChangeTagValueVisitor;
Expand Down Expand Up @@ -36,41 +38,67 @@ public TreeVisitor<?, ExecutionContext> getVisitor() {
*/
private static class MigrateToJenkinsBaseLinePropertyVisitor extends MavenIsoVisitor<ExecutionContext> {

@Override
public @Nullable Xml visit(@Nullable Tree tree, ExecutionContext executionContext, Cursor parent) {
return super.visit(tree, executionContext, parent);
}

@Override
public Xml.Document visitDocument(Xml.Document document, ExecutionContext ctx) {
Xml.Document d = super.visitDocument(document, ctx);

// Skip if not bom or weekly bom
PluginMetadata pluginMetadata = new PomVisitor().reduce(document, new PluginMetadata());

// Keep only major and minor and ignore patch version
String jenkinsBaseline = pluginMetadata.getJenkinsVersion();
String jenkinsPatch = null;
if (pluginMetadata.getJenkinsVersion().matches("\\d+\\.\\d+\\.\\d+")) {
jenkinsBaseline = jenkinsBaseline.substring(0, jenkinsBaseline.lastIndexOf('.'));
jenkinsPatch = pluginMetadata
.getJenkinsVersion()
.substring(pluginMetadata.getJenkinsVersion().lastIndexOf('.') + 1);
}
LOG.debug("Jenkins baseline version is {}", jenkinsBaseline);
LOG.debug("Jenkins patch version is {}", jenkinsPatch);

if (pluginMetadata.getBomArtifactId() == null || "bom-weekly".equals(pluginMetadata.getBomArtifactId())) {
LOG.debug("Project is using Jenkins weekly bom or not bom, skipping");
return d;
}

performUpdate(document, jenkinsBaseline, jenkinsPatch);
performUpdate(document);
return document;
}

private void performUpdate(Xml.Document document, String jenkinsBaseline, String jenkinsPatch) {
private void performUpdate(Xml.Document document) {

Xml.Tag jenkinsVersionTag = document.getRoot()
.getChild("properties")
.flatMap(props -> props.getChild("jenkins.version"))
.orElse(null);

if (jenkinsVersionTag == null) {
return;
}

// Keep only major and minor and ignore patch version
String jenkinsVersion = jenkinsVersionTag.getValue().get();
String jenkinsBaseline = jenkinsVersionTag.getValue().get();
String jenkinsPatch = null;

// It's a LTS, extract patch
if (jenkinsVersion.matches("\\d+\\.\\d+\\.\\d+")
|| jenkinsVersion.matches("\\$\\{jenkins.baseline}\\.\\d+")) {
jenkinsBaseline = jenkinsBaseline.substring(0, jenkinsBaseline.lastIndexOf('.'));
jenkinsPatch = jenkinsVersion.substring(jenkinsVersion.lastIndexOf('.') + 1);
}
LOG.debug("Jenkins baseline version is {}", jenkinsBaseline);
LOG.debug("Jenkins patch version is {}", jenkinsPatch);

String expectedVersion =
jenkinsPatch != null ? "${jenkins.baseline}." + jenkinsPatch : "${jenkins.baseline}";

// Add or changes properties
doAfterVisit(new AddPropertyVisitor("jenkins.baseline", jenkinsBaseline, false));
if (jenkinsPatch != null) {
doAfterVisit(new AddPropertyVisitor("jenkins.version", "${jenkins.baseline}." + jenkinsPatch, false));
} else {
doAfterVisit(new AddPropertyVisitor("jenkins.version", "${jenkins.baseline}", false));
doAfterVisit(new AddBeforePropertyVisitor("jenkins.version", "jenkins.baseline", jenkinsBaseline));
doAfterVisit(new AddPropertyCommentVisitor(
"jenkins.baseline",
" https://www.jenkins.io/doc/developer/plugin-development/choosing-jenkins-baseline/ "));

LOG.debug("Jenkins version is {}", jenkinsVersionTag.getValue().get());
LOG.debug("Expected version is {}", expectedVersion);

// Change the jenkins version
if (!expectedVersion.equals(jenkinsVersionTag.getValue().get())) {
doAfterVisit(new AddPropertyVisitor("jenkins.version", expectedVersion, false));
maybeUpdateModel();
}

// Change the bom artifact ID
Expand All @@ -84,7 +112,11 @@ private void performUpdate(Xml.Document document, String jenkinsBaseline, String
.flatMap(dep -> dep.getChild("artifactId"))
.orElseThrow();

doAfterVisit(new ChangeTagValueVisitor<>(artifactIdTag, "bom-${jenkins.baseline}.x"));
LOG.debug("Artifact ID is {}", artifactIdTag.getValue().get());

if (!artifactIdTag.getValue().get().equals("bom-${jenkins.baseline}.x")) {
doAfterVisit(new ChangeTagValueVisitor<>(artifactIdTag, "bom-${jenkins.baseline}.x"));
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package io.jenkins.tools.pluginmodernizer.core.visitors;

import java.util.ArrayList;
import java.util.List;
import org.openrewrite.ExecutionContext;
import org.openrewrite.maven.MavenIsoVisitor;
import org.openrewrite.xml.tree.Content;
import org.openrewrite.xml.tree.Xml;

/**
* A visitor that add a maven property before another property.
* If the property already exists, it will update its value.
* If the previous property does not exist, the new property will not be added.
*/
public class AddBeforePropertyVisitor extends MavenIsoVisitor<ExecutionContext> {

/**
* The previous property name to add the new property before.
*/
private final String previousPropertyName;

/**
* The property name to add.
*/
private final String propertyName;

/**
* The property value to add.
*/
private final String propertyValue;

/**
* Constructor of the visitor.
* @param previousPropertyName The previous property name to add the new property before.
* @param propertyName The property name to add.
* @param propertyValue The property value to add.
*/
public AddBeforePropertyVisitor(String previousPropertyName, String propertyName, String propertyValue) {
this.previousPropertyName = previousPropertyName;
this.propertyName = propertyName;
this.propertyValue = propertyValue;
}

@Override
public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext executionContext) {
tag = super.visitTag(tag, executionContext);
if (tag.getName().equals("properties")) {

// Ensure previous
Xml.Tag previousPropertyTag = tag.getChild(previousPropertyName).orElse(null);
if (previousPropertyTag == null) {
return tag;
}

// Ensure value if exists
Xml.Tag existingPropertyTag = tag.getChild(propertyName).orElse(null);
if (existingPropertyTag != null && existingPropertyTag.getValue().isPresent()) {
// doAfterVisit(new ChangeTagValueVisitor<>(existingPropertyTag, propertyValue));
return tag;
}

if (tag.getContent() == null || tag.getContent().isEmpty()) {
return tag;
}

List<Content> contents = new ArrayList<>(tag.getContent());
int propertyIndex = contents.indexOf(previousPropertyTag);

// If there are comments leave them as they are
while (propertyIndex > 0 && contents.get(propertyIndex - 1) instanceof Xml.Comment) {
propertyIndex--;
}

// Place the tag just before the property or on first element of the sequence
Xml.Tag propertyTag = Xml.Tag.build("<" + propertyName + ">" + propertyValue + "</" + propertyName + ">");
propertyTag = propertyTag.withPrefix(previousPropertyTag.getPrefix());
contents.add(propertyIndex, propertyTag);

tag = tag.withContent(contents);
}

return tag;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package io.jenkins.tools.pluginmodernizer.core.visitors;

import java.util.ArrayList;
import java.util.List;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Tree;
import org.openrewrite.marker.Markers;
import org.openrewrite.maven.MavenIsoVisitor;
import org.openrewrite.xml.tree.Content;
import org.openrewrite.xml.tree.Xml;

/**
* A visitor that add a comment before a maven property.
* If a comment already exists, it's updated.
*/
public class AddPropertyCommentVisitor extends MavenIsoVisitor<ExecutionContext> {

/**
* The property name to add the comment before.
*/
private final String propertyName;

/**
* The comment to add.
*/
private final String comment;

/**
* Constructor of the visitor.
* @param propertyName The property name to add the comment before.
* @param comment The comment to add.
*/
public AddPropertyCommentVisitor(String propertyName, String comment) {
this.propertyName = propertyName;
this.comment = comment;
}

@Override
public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext executionContext) {
tag = super.visitTag(tag, executionContext);
if (tag.getName().equals("properties")) {

// Ensure property exists
Xml.Tag propertyTag = tag.getChild(propertyName).orElse(null);
if (propertyTag == null) {
return tag;
}

if (tag.getContent() == null) {
return tag;
}

List<Content> contents = new ArrayList<>(tag.getContent());
int propertyIndex = contents.indexOf(propertyTag);

// Add or update comment
if (propertyTag.getContent() != null) {
boolean containsComment = contents.stream()
.anyMatch(c -> c instanceof Xml.Comment && comment.equals(((Xml.Comment) c).getText()));

// Add comment if not exists
if (!containsComment) {

// If there is a comment just before, remove it
if (propertyIndex > 0 && contents.get(propertyIndex - 1) instanceof Xml.Comment xmlComment) {
propertyIndex--;
contents.remove(propertyIndex);
doAfterVisit(new RemovePropertyCommentVisitor(xmlComment.getText()));
}

Xml.Comment customComment = new Xml.Comment(
Tree.randomId(), contents.get(propertyIndex).getPrefix(), Markers.EMPTY, comment);
contents.add(propertyIndex, customComment);
tag = tag.withContent(contents);
}
}
}
return tag;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.jenkins.tools.pluginmodernizer.core.visitors;

import java.util.ArrayList;
import java.util.List;
import org.openrewrite.ExecutionContext;
import org.openrewrite.maven.MavenIsoVisitor;
import org.openrewrite.xml.tree.Content;
import org.openrewrite.xml.tree.Xml;

public class RemovePropertyCommentVisitor extends MavenIsoVisitor<ExecutionContext> {

private String comment;

public RemovePropertyCommentVisitor(String comment) {
this.comment = comment;
}

@Override
public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext executionContext) {
tag = super.visitTag(tag, executionContext);
if (tag.getName().equals("properties")) {

List<Content> contents = new ArrayList<>(tag.getContent());

// Remove the comment if needed
boolean containsComment = contents.stream()
.anyMatch(c -> c instanceof Xml.Comment && comment.equals(((Xml.Comment) c).getText()));

// Add comment if not exists
if (containsComment) {
for (int i = 0; i < contents.size(); i++) {
if (contents.get(i) instanceof Xml.Comment
&& comment.equals(((Xml.Comment) contents.get(i)).getText())) {
contents.remove(i);
break;
}
}
tag = tag.withContent(contents);
}
}
return tag;
}
}
Loading

0 comments on commit 3fda79b

Please sign in to comment.