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

feat(recipes): Add jelly xml declaration #355

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package io.jenkins.tools.pluginmodernizer.core.recipe;

import io.jenkins.tools.pluginmodernizer.core.utils.PomModifier;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.text.PlainText;
import org.openrewrite.text.PlainTextVisitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Recipe to add an XML declaration to Jelly files.
*/
public class AddJellyXmlDeclaration extends Recipe {

private static final Logger LOG = LoggerFactory.getLogger(AddJellyXmlDeclaration.class);

/**
* Returns the display name of the recipe.
*
* @return the display name of the recipe
*/
@Override
public String getDisplayName() {
return "Add XML declaration to Jelly files";
}

/**
* Returns the description of the recipe.
*
* @return the description of the recipe
*/
@Override
public String getDescription() {
return "Ensure the XML declaration `<?jelly escape-by-default='true'?>` is present in all `.jelly` files.";
}

/**
* Returns the visitor that will be used to apply the recipe.
*
* @return the visitor that will be used to apply the recipe
*/
@Override
public PlainTextVisitor<ExecutionContext> getVisitor() {
return new PlainTextVisitor<ExecutionContext>() {
public static final String JELLY_DECLARATION = "<?jelly escape-by-default='true'?>";

/**
* Visits the text and adds the XML declaration if it is a Jelly file and the declaration is not already present.
*
* @param text the text to visit
* @param executionContext the execution context
* @return the modified text if the XML declaration was added, otherwise the original text
*/
@Override
public PlainText visitText(PlainText text, ExecutionContext executionContext) {
if (text == null || text.getSourcePath() == null) {
return text;
}
if (text.getSourcePath().toString().endsWith(".jelly")) {
LOG.debug("Processing Jelly file: {}", text.getSourcePath());
String content = text.getText();
// Handle empty files
if (content.trim().isEmpty()) {
LOG.debug("Adding declaration to empty file");
return text.withText(JELLY_DECLARATION);
}
// Detect line ending style
String lineEnding = content.contains("\r\n") ? "\r\n" : "\n";

// Check for and handle malformed declarations
if (content.trim().toLowerCase().matches("^<\\?jelly\\s+[^>]*>") && !content.startsWith(JELLY_DECLARATION)) {
LOG.warn("Found malformed Jelly declaration in {}", text.getSourcePath());
LOG.debug("Adding missing declaration");
// Remove existing malformed declaration up to first line ending
content = content.substring(content.indexOf(lineEnding) + lineEnding.length());
} // Add declaration if missing
if (!content.startsWith(JELLY_DECLARATION)) {
LOG.debug("Declaration already present");
content = JELLY_DECLARATION + lineEnding + content;
return text.withText(content);
}
}
return text;
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,11 @@ recipeList:
interval: monthly
relativeFileName: .github/dependabot.yml
overwriteExisting: false
---
type: specs.openrewrite.org/v1beta/recipe
name: io.jenkins.tools.pluginmodernizer.AddJellyXmlDeclaration
displayName: Add XML declaration to Jelly files
description: Ensure the XML declaration `<?jelly escape-by-default='true'?>` is present in all `.jelly` files.
tags: ['chore']
recipeList:
- io.jenkins.tools.pluginmodernizer.core.recipe.AddJellyXmlDeclaration
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package io.jenkins.tools.pluginmodernizer.core.recipe;

import static org.openrewrite.test.SourceSpecs.text;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.openrewrite.Recipe;
import org.openrewrite.test.RewriteTest;
import org.openrewrite.text.PlainTextParser;

/**
* Test class for the AddJellyXmlDeclaration recipe.
*/
public class AddJellyXmlDeclarationTest implements RewriteTest {

/**
* Returns the recipe to be tested.
*
* @return the AddJellyXmlDeclaration recipe
*/
public Recipe getRecipe() {
return new AddJellyXmlDeclaration();
}

/**
* Test to verify that the XML declaration is added to a Jelly file.
*
* @param tempDir the temporary directory provided by JUnit
* @throws IOException if an I/O error occurs
*/
@Test
void addXmlDeclarationToJellyFile(@TempDir Path tempDir) throws IOException {
Path inputFile = tempDir.resolve("example.jelly");
Files.writeString(
inputFile,
"""
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define">
<st:contentType value="text/html"/>
<h1>Hello, World!</h1>
</j:jelly>
""");

Path expectedFile = tempDir.resolve("expected.jelly");
Files.writeString(
expectedFile,
"""
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define">
<st:contentType value="text/html"/>
<h1>Hello, World!</h1>
</j:jelly>
""");

rewriteRun(
spec -> spec.recipe(new AddJellyXmlDeclaration())
.parser(PlainTextParser.builder())
.expectedCyclesThatMakeChanges(1)
.cycles(1),
text(Files.readString(inputFile), Files.readString(expectedFile)));
}

/**
* Test to verify that the XML declaration is not added if it is already present in the Jelly file.
*
* @param tempDir the temporary directory provided by JUnit
* @throws IOException if an I/O error occurs
*/
@Test
void doNotAddXmlDeclarationIfAlreadyPresent(@TempDir Path tempDir) throws IOException {
Path inputFile = tempDir.resolve("example.jelly");
Files.writeString(
inputFile,
"""
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define">
<st:contentType value="text/html"/>
<h1>Hello, World!</h1>
</j:jelly>
""");

rewriteRun(
spec -> spec.recipe(new AddJellyXmlDeclaration())
.parser(PlainTextParser.builder())
.expectedCyclesThatMakeChanges(1)
.cycles(0),
text(Files.readString(inputFile)));
}
}
1 change: 0 additions & 1 deletion plugin-modernizer-core/src/test/resources/test-pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>1.554.1</version>
<relativePath />
</parent>

<properties>
Expand Down