Rewrite 7 to 8 migration. 8.1.2 Release (2023-06-13)
OpenRewrite 8 makes some substantial changes in order to support large monorepos that can not fit into memory. All recipes will need to be migrated in order for them to keep working. Specific migration instructions can be found in the migrating your recipes section.
Below, we'll detail the key changes being made and provide guidance for how you can update your recipes to ensure they keep working. At the bottom you'll find the standard new/changed/deleted recipe list.
Recipe no longer exposes a getSingleSourceApplicableTest or a getApplicableTest method. Recipe authors should, instead, use Preconditions.check(TreeVisitor check, TreeVisitor visitor) to conditionally apply the visitor only if the check makes sense.
As this is semantically equivalent to the OpenRewrite 7 single source applicability test, recipe authors will generally just have to copy the body of their old applicability test method into the first argument of the call to
Preconditions.check()
.In OpenRewrite 7,
Recipe.getApplicabilityTest()
was rarely used as it was confusing to most users. If your recipe uses it, you will need to convert your recipe to a ScanningRecipe.There is, unfortunately, no way for a YAML recipe to use
Preconditions
. We hope to support such a feature for them eventually, though.Example:
Before
After
package org.openrewrite.java.cleanup;
import org.openrewrite.Applicability;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.MethodMatcher;
import org.openrewrite.java.search.UsesMethod;
import org.openrewrite.java.tree.*;
import org.openrewrite.java.PartProvider;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
public class ChainStringBuilderAppendCalls extends Recipe {
private static final MethodMatcher STRING_BUILDER_APPEND = new MethodMatcher("java.lang.StringBuilder append(String)");
private static J.Binary additiveBinaryTemplate = null;
@Override
public String getDisplayName() {
return "Chain `StringBuilder.append()` calls";
}
@Override
protected @Nullable TreeVisitor<?, ExecutionContext> getSingleSourceApplicableTest() {
return Applicability.or(new UsesMethod<>(STRING_BUILDER_APPEND),
new UsesMethod<>(STRING_BUILDER_APPEND));
}
@Override
protected JavaIsoVisitor<ExecutionContext> getVisitor() {
return new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
J.MethodInvocation m = super.visitMethodInvocation(method, ctx);
// do something
return m;
}
};
}
}
package org.openrewrite.java.cleanup;
import org.openrewrite.*;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.MethodMatcher;
import org.openrewrite.java.search.UsesMethod;
import org.openrewrite.java.tree.*;
import org.openrewrite.java.PartProvider;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
public class ChainStringBuilderAppendCalls extends Recipe {
private static final MethodMatcher STRING_BUILDER_APPEND = new MethodMatcher("java.lang.StringBuilder append(String)");
private static J.Binary additiveBinaryTemplate = null;
@Override
public String getDisplayName() {
return "Chain `StringBuilder.append()` calls";
}
@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(
Preconditions.or(new UsesMethod<>(STRING_BUILDER_APPEND),
new UsesMethod<>(STRING_BUILDER_APPEND)), new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
J.MethodInvocation m = super.visitMethodInvocation(method, ctx);
// do something
return m;
}
});
}
}
In the previous version of OpenRewrite, developers could use Recipe.visit(List ...) to randomly access different files when their recipes needed them. Unfortunately, this random access took a considerable amount of time and was not scalable for large source sets. To address that problem, OpenRewrite 8 introduces a new type of recipe that separates the run functionality into three, scalable, phases.
For recipes that only change a single source file, you won't need to use the new recipe type. Instead, you will need to update the
Recipe.getVisitor()
method to be public
instead of protected
.For recipes that change many source files, you will need to convert the recipe to be a ScanningRecipe.
A
ScanningRecipe
extends the normal Recipe
and adds two key objects: an accumulator and a scanner. The accumulator
object is a custom data structure defined by the recipe itself to store any information the recipe needs to function. The scanner
object is a visitor
which populates the accumulator
with data.Scanning recipes have three phases:
- 1.A scanning phase that collects information while making no new code changes. In this phase, the
scanner
is called for each source file and information is added to theaccumulator
that the recipe will need for future steps.- For example, a recipe might want to detect whether a project is a Maven project or not. The
scanner
could detect apom.xml
file and add a flag to theaccumulator
so that future steps know this.
- 2.An optional generating phase where new files are created (if any are needed). In this phase, the
accumulator
can be accessed to determine whether or not a file should be created. - 3.An editing phase where the recipe makes changes (as it would before). Like the generating phase, an
accumulator
can be accessed to make changes – but you can not randomly access other source code or files.
As converting a
Recipe
to a ScanningRecipe
is a substantial change, the migration recipe is not able to automate this. You will need to rewrite your recipes that need to be ScanningRecipes
on your own.Below is an example of what a recipe might look like before/after this change. You can find the converted recipe in it's entirety here.
Before
After
// imports
@Value
@EqualsAndHashCode(callSuper = true)
public class AddManagedDependency extends Recipe {
// Standard methods such as displayName and description
@Override
protected List<SourceFile> visit(List<SourceFile> before, ExecutionContext ctx) {
List<SourceFile> rootPoms = new ArrayList<>();
for (SourceFile source : before) {
source.getMarkers().findFirst(MavenResolutionResult.class).ifPresent(mavenResolutionResult -> {
if (mavenResolutionResult.getParent() == null) {
rootPoms.add(source);
}
});
}
return ListUtils.map(before, s -> s.getMarkers().findFirst(MavenResolutionResult.class)
.map(javaProject -> (Tree) new MavenVisitor<ExecutionContext>() {
@Override
public Xml visitDocument(Xml.Document document, ExecutionContext executionContext) {
Xml maven = super.visitDocument(document, executionContext);
if (!Boolean.TRUE.equals(addToRootPom) || rootPoms.contains(document)) {
Validated versionValidation = Semver.validate(version, versionPattern);
if (versionValidation.isValid()) {
VersionComparator versionComparator = requireNonNull(versionValidation.getValue());
try {
String versionToUse = findVersionToUse(versionComparator, ctx);
if (!Objects.equals(versionToUse, existingManagedDependencyVersion())) {
doAfterVisit(new AddManagedDependencyVisitor(groupId, artifactId,
versionToUse, scope, type, classifier));
maybeUpdateModel();
}
} catch (MavenDownloadingException e) {
return e.warn(document);
}
}
}
return maven;
}
@Nullable
private String existingManagedDependencyVersion() {
return getResolutionResult().getPom().getDependencyManagement().stream()
.map(resolvedManagedDep -> {
if (resolvedManagedDep.matches(groupId, artifactId, type, classifier)) {
return resolvedManagedDep.getGav().getVersion();
} else if (resolvedManagedDep.getRequestedBom() != null
&& resolvedManagedDep.getRequestedBom().getGroupId().equals(groupId)
&& resolvedManagedDep.getRequestedBom().getArtifactId().equals(artifactId)) {
return resolvedManagedDep.getRequestedBom().getVersion();
}
return null;
})
.filter(Objects::nonNull)
.findFirst().orElse(null);
}
@Nullable
private String findVersionToUse(VersionComparator versionComparator, ExecutionContext ctx) throws MavenDownloadingException {
MavenMetadata mavenMetadata = metadataFailures.insertRows(ctx, () -> downloadMetadata(groupId, artifactId, ctx));
LatestRelease latest = new LatestRelease(versionPattern);
return mavenMetadata.getVersioning().getVersions().stream()
.filter(v -> versionComparator.isValid(null, v))
.filter(v -> !Boolean.TRUE.equals(releasesOnly) || latest.isValid(null, v))
.max((v1, v2) -> versionComparator.compare(null, v1, v2))
.orElse(null);
}
}.visit(s, ctx))
.map(SourceFile.class::cast)
.orElse(s)
);
}
}
// imports
@Value
@EqualsAndHashCode(callSuper = true)
public class AddManagedDependency extends ScanningRecipe<AddManagedDependency.Scanned> {
// Standard methods such as displayName and description
static class Scanned {
boolean usingType;
List<SourceFile> rootPoms = new ArrayList<>();
}
@Override
public Scanned getInitialValue(ExecutionContext ctx) {
Scanned scanned = new Scanned();
scanned.usingType = onlyIfUsing == null;
return scanned;
}
@Override
public TreeVisitor<?, ExecutionContext> getScanner(Scanned acc) {
return Preconditions.check(acc.usingType || (!StringUtils.isNullOrEmpty(onlyIfUsing) && onlyIfUsing.contains(":")), new MavenIsoVisitor<ExecutionContext>() {
@Override
public Xml.Document visitDocument(Xml.Document document, ExecutionContext ctx) {
document.getMarkers().findFirst(MavenResolutionResult.class).ifPresent(mavenResolutionResult -> {
if (mavenResolutionResult.getParent() == null) {
acc.rootPoms.add(document);
}
});
if(acc.usingType) {
return SearchResult.found(document);
}
return super.visitDocument(document, ctx);
}
@Override
public Xml.Tag visitTag(Xml.Tag tag, ExecutionContext ctx) {
Xml.Tag t = super.visitTag(tag, ctx);
if (isDependencyTag()) {
ResolvedDependency dependency = findDependency(t, null);
if (dependency != null) {
String[] ga = requireNonNull(onlyIfUsing).split(":");
ResolvedDependency match = dependency.findDependency(ga[0], ga[1]);
if (match != null) {
acc.usingType = true;
}
}
}
return t;
}
});
}
@Override
public TreeVisitor<?, ExecutionContext> getVisitor(Scanned acc) {
return Preconditions.check(acc.usingType, new MavenVisitor<ExecutionContext>() {
@Override
public Xml visitDocument(Xml.Document document, ExecutionContext ctx) {
Xml maven = super.visitDocument(document, ctx);
if (!Boolean.TRUE.equals(addToRootPom) || acc.rootPoms.contains(document)) {
Validated versionValidation = Semver.validate(version, versionPattern);
if (versionValidation.isValid()) {
VersionComparator versionComparator = requireNonNull(versionValidation.getValue());
try {
String versionToUse = findVersionToUse(versionComparator, ctx);
if (!Objects.equals(versionToUse, existingManagedDependencyVersion())) {
doAfterVisit(new AddManagedDependencyVisitor(groupId, artifactId,
versionToUse, scope, type, classifier));
maybeUpdateModel();
}
} catch (MavenDownloadingException e) {
return e.warn(document);
}
}
}
return maven;
}
@Nullable
private String existingManagedDependencyVersion() {
return getResolutionResult().getPom().getDependencyManagement().stream()
.map(resolvedManagedDep -> {
if (resolvedManagedDep.matches(groupId, artifactId, type, classifier)) {
return resolvedManagedDep.getGav().getVersion();
} else if (resolvedManagedDep.getRequestedBom() != null
&& resolvedManagedDep.getRequestedBom().getGroupId().equals(groupId)
&& resolvedManagedDep.getRequestedBom().getArtifactId().equals(artifactId)) {
return resolvedManagedDep.getRequestedBom().getVersion();
}
return null;
})
.filter(Objects::nonNull)
.findFirst().orElse(null);
}
@Nullable
private String findVersionToUse(VersionComparator versionComparator, ExecutionContext ctx) throws MavenDownloadingException {
MavenMetadata mavenMetadata = metadataFailures.insertRows(ctx, () -> downloadMetadata(groupId, artifactId, ctx));
LatestRelease latest = new LatestRelease(versionPattern);
return mavenMetadata.getVersioning().getVersions().stream()
.filter(v -> versionComparator.isValid(null, v))
.filter(v -> !Boolean.TRUE.equals(releasesOnly) || latest.isValid(null, v))
.max((v1, v2) -> versionComparator.compare(null, v1, v2))
.orElse(null);
}
});
}
}
Up until now,
JavaTemplate
has had a single purpose: for creating LST subtrees based on template code and input parameters. In OpenRewrite 8, however, a second use case has been added – JavaTemplate
can now be defined as a pattern that matches against an LST subtree, similar to a regular expression (regexp). To make this work, the template needs to be context-free, meaning it doesn't refer to surrounding code but, instead, only to template parameters and statically available elements like classes and static members.This context-free nature allows us to embed the template into a skeleton Java class, compile it once to an LST, and then compare it with other LSTs to determine if they match. The template parameters in the LST act as wildcards in a regexp-like manner. This approach significantly improves efficiency compared to replacing each subtree separately.
The context-free templates also benefit the first use case of LST substitution, although the impact may not be as significant since template application is relatively infrequent. However, for template matching, where potentially thousands of subtrees in a single source file need to be matched, the context-free nature is crucial.
To support this new functionality, we've had to redesign the
JavaTemplate
API. New methods, such as matches()
and matcher()
, have been introduced for templating matching (similar to Java's regex functionality). Furthermore, the builder now produces context-free templates by default. If a template requires context sensitivity, the contextSensitive()
method of the builder must be invoked.You will need to update all references to
JavaTemplate
in your recipes. You will need to:- 1.Double-check whether or not
contextSensitive()
makes sense for your recipe. It is added by default – but if your recipe doesn't refer to the surrounding code and, instead, only refers to template parameters or statically available elements like classes and static methods, you can remove it. - 2.Determine what type of
Cursor
should go into theJavaTemplate.apply()
method. Typically this should begetCursor()
. However, if theJ
instance is updated in the method, the cursor on this visitor will need to be updated. In that case, you should use updateCursor instead.
Example:
Before
After
package org.openrewrite.java;
import lombok.EqualsAndHashCode;
import lombok.Value;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;
import java.util.List;
@Value
@EqualsAndHashCode(callSuper = true)
public class AddOrUpdateAnnotationAttribute extends Recipe {
@Override
public String getDisplayName() {
return "Add or update annotation attribute";
}
@Override
public String getDescription() {
return "Add or update annotation attribute.";
}
@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.Annotation visitAnnotation(J.Annotation a, ExecutionContext context) {
String param1 = "test parameter 1";
String param2 = "test parameter 2";
List<Expression> currentArgs = a.getArguments();
if (currentArgs == null || currentArgs.isEmpty()) {
return a.withTemplate(
JavaTemplate.builder(this::getCursor, "#{}")
.build(),
a.getCoordinates().replaceArguments(),
param1,
param2);
}
return a;
}
};
}
}
package org.openrewrite.java;
import lombok.EqualsAndHashCode;
import lombok.Value;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;
import java.util.List;
@Value
@EqualsAndHashCode(callSuper = true)
public class AddOrUpdateAnnotationAttribute extends Recipe {
@Override
public String getDisplayName() {
return "Add or update annotation attribute";
}
@Override
public String getDescription() {
return "Add or update annotation attribute.";
}
@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.Annotation visitAnnotation(J.Annotation a, ExecutionContext context) {
String param1 = "test parameter 1";
String param2 = "test parameter 2";
List<Expression> currentArgs = a.getArguments();
if (currentArgs == null || currentArgs.isEmpty()) {
return JavaTemplate.builder("#{}")/*[Rewrite8 migration]`contextSensitive()` could be unnecessary and can be removed, please double-check manually*/.contextSensitive()
.build().apply(/*[Rewrite8 migration] please double-check correctness of this parameter manually, it could be updateCursor() if the value is updated somewhere*/getCursor(),
a.getCoordinates().replaceArguments(),
param1,
param2);
}
return a;
}
};
}
}
All cleanup recipes have been moved to the rewrite-static-analysis repository. The package name has also changed from
org.openrewrite.java.cleanup
to org.openrewrite.staticanalysis
.The JavaVisitor.visitJavaSourceFile(JavaSourceFile cu, P p) method was removed in favor of the TreeVisitor.visit(@Nullable Tree tree, P p) method.
Example:
Before
After
package org.openrewrite.staticanalysis;
import org.openrewrite.*;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.RenameVariable;
import org.openrewrite.java.tree.Flag;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaSourceFile;
import java.time.Duration;
import java.util.*;
import static org.openrewrite.internal.NameCaseConvention.LOWER_CAMEL;
public class RenamePrivateFieldsToCamelCase extends Recipe {
@Override
public String getDisplayName() {
return "Reformat private field names to camelCase";
}
@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new RenameNonCompliantNames();
}
private static class RenameNonCompliantNames extends JavaIsoVisitor<ExecutionContext> {
@Override
public JavaSourceFile visitJavaSourceFile(JavaSourceFile cu, ExecutionContext ctx) {
Map<J.VariableDeclarations.NamedVariable, String> renameVariablesMap = new LinkedHashMap<>();
Set<String> hasNameSet = new HashSet<>();
getCursor().putMessage("RENAME_VARIABLES_KEY", renameVariablesMap);
getCursor().putMessage("HAS_NAME_KEY", hasNameSet);
super.visitJavaSourceFile(cu, ctx);
renameVariablesMap.forEach((key, value) -> {
if (!hasNameSet.contains(value) && !hasNameSet.contains(key.getSimpleName())) {
doAfterVisit(new RenameVariable<>(key, value));
hasNameSet.add(value);
}
});
return cu;
}
}
}
package org.openrewrite.staticanalysis;
import org.openrewrite.*;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.RenameVariable;
import org.openrewrite.java.tree.Flag;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaSourceFile;
import java.time.Duration;
import java.util.*;
import static org.openrewrite.internal.NameCaseConvention.LOWER_CAMEL;
public class RenamePrivateFieldsToCamelCase extends Recipe {
@Override
public String getDisplayName() {
return "Reformat private field names to camelCase";
}
@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new RenameNonCompliantNames();
}
private static class RenameNonCompliantNames extends JavaIsoVisitor<ExecutionContext> {
@Override
public @Nullable J visit(@Nullable Tree tree, ExecutionContext ctx) {
if (tree instanceof JavaSourceFile) {
JavaSourceFile cu = (JavaSourceFile) tree;
Map<J.VariableDeclarations.NamedVariable, String> renameVariablesMap = new LinkedHashMap<>();
Set<String> hasNameSet = new HashSet<>();
getCursor().putMessage("RENAME_VARIABLES_KEY", renameVariablesMap);
getCursor().putMessage("HAS_NAME_KEY", hasNameSet);
super.visit(cu, ctx);
renameVariablesMap.forEach((key, value) -> {
if (!hasNameSet.contains(value) && !hasNameSet.contains(key.getSimpleName())) {
doAfterVisit(new RenameVariable<>(key, value));
hasNameSet.add(value);
}
});
}
return super.visit(tree, ctx);
}
}
}
The deprecated Markers#searchResult methods have been removed in favor of the SearchResult#found methods.
Example:
Before
After
package org.openrewrite.kubernetes.resource;
import lombok.EqualsAndHashCode;
import lombok.Value;
import org.openrewrite.*;
import org.openrewrite.yaml.YamlIsoVisitor;
import org.openrewrite.yaml.tree.Yaml;
@Value
@EqualsAndHashCode(callSuper = true)
public class FindExceedsResourceRatio extends Recipe {
@Override
public String getDisplayName() {
return "Find exceeds resource ratio";
}
@Override
protected TreeVisitor<?, ExecutionContext> getVisitor() {
return new YamlIsoVisitor<ExecutionContext>() {
@Override
public Yaml.Mapping.Entry visitMappingEntry(Yaml.Mapping.Entry entry, ExecutionContext ctx) {
Yaml.Mapping.Entry e = super.visitMappingEntry(entry, ctx);
return e.withMarkers(e.getMarkers().searchResult("foo"));
}
};
}
}
package org.openrewrite.kubernetes.resource;
import lombok.EqualsAndHashCode;
import lombok.Value;
import org.openrewrite.*;
import org.openrewrite.marker.SearchResult;
import org.openrewrite.yaml.YamlIsoVisitor;
import org.openrewrite.yaml.tree.Yaml;
@Value
@EqualsAndHashCode(callSuper = true)
public class FindExceedsResourceRatio extends Recipe {
@Override
public String getDisplayName() {
return "Find exceeds resource ratio";
}
@Override
protected TreeVisitor<?, ExecutionContext> getVisitor() {
return new YamlIsoVisitor<ExecutionContext>() {
@Override
public Yaml.Mapping.Entry visitMappingEntry(Yaml.Mapping.Entry entry, ExecutionContext ctx) {
Yaml.Mapping.Entry e = super.visitMappingEntry(entry, ctx);
return SearchResult.found(e, "foo");
}
};
}
}
The org.openrewrite.Recipe doNext(..) method has been removed. In most situations, it should be replaced with TreeVisitor#doAfterVisit(Visitor).
However, as the
doAfterVisit
method only changes the current source file, if your recipe needs to change other files, it will need to be rewritten as a ScanningRecipe.Similarly, the
doAfterVisit(Recipe)
method has been removed in favor of doAfterVisit(Visitor)
.We've created a migration recipe that will assist you with migrating your recipes to the latest version. You should run this recipe against all of your existing recipes. If the migration recipe is not able to fully migrate your recipe, comments will be added to the code to request a human to review it.
Do not attempt to bump the version of OpenRewrite or any of your dependencies before running this recipe. The flow for the upgrade should look like this:
- 1.Go to the MigrateToRewrite8 recipe doc. In the usage section, there are instructions for how to add this recipe to your repository. Either add it directly to your Maven or Gradle project or use the Maven command line.
- 2.Run the recipe.
- 3.Look over the recipe. Some pieces may have been directly changed whereas other parts may have just had comments added.
- 4.Bump your dependencies to the latest version and attempt to address the comments.
- 5.Run your tests and recipe to ensure it continues working as expected. Make changes until it works.
- 6.
You can find some examples of how this migration recipe works in the Migrate Rewrite recipes from 7 to 8 recipe page
- rewrite-analysis
- rewrite-cucumber-jvm
- rewrite-hibernate
- rewrite-recommendations
- rewrite-sql
- rewrite-static-analysis
- org.openrewrite.FindCollidingSourceFiles: Finds source files which share a path with another source file. There should always be exactly one source file per path within a repository. This is a diagnostic for finding problems in OpenRewrite parsers/build plugins.
- org.openrewrite.analysis.controlflow.ControlFlowVisualization: Visualize the control flow of a Java program.
- org.openrewrite.analysis.search.FindFlowBetweenMethods: Takes two patterns for the start/end methods to find flow between.
- org.openrewrite.analysis.search.UriCreatedWithHttpScheme: This is a sample recipe demonstrating a simple application of local data flow analysis.
- org.openrewrite.cucumber.jvm.CucumberAnnotationToSuite: Replace @Cucumber with @Suite and @SelectClasspathResource("cucumber/annotated/class/package").
- org.openrewrite.cucumber.jvm.CucumberJava8HookDefinitionToCucumberJava: Replace LambdaGlue hook definitions with new annotated methods with the same body.
- org.openrewrite.cucumber.jvm.CucumberJava8StepDefinitionToCucumberJava: Replace StepDefinitionBody methods with StepDefinitionAnnotations on new methods with the same body.
- org.openrewrite.cucumber.jvm.CucumberJava8ToJava: Migrates Cucumber-Java8 step definitions and LambdaGlue hooks to Cucumber-Java annotated methods.
- org.openrewrite.cucumber.jvm.CucumberToJunitPlatformSuite: Migrates Cucumber tests to JUnit Test Suites.
- org.openrewrite.cucumber.jvm.DropSummaryPrinter: Replace SummaryPrinter with Plugin, if not already present.
- org.openrewrite.cucumber.jvm.RegexToCucumberExpression: Strip regex prefix and suffix from step annotation expressions arguments where possible.
- org.openrewrite.cucumber.jvm.UpgradeCucumber2x: Upgrade to Cucumber-JVM 2.x from any previous version.
- org.openrewrite.cucumber.jvm.UpgradeCucumber5x: Upgrade to Cucumber-JVM 5.x from any previous version.
- org.openrewrite.cucumber.jvm.UpgradeCucumber7x: Upgrade to Cucumber-JVM 7.x from any previous version.
- org.openrewrite.github.SetupJavaAdoptOpenJDKToTemurin: Adopt OpenJDK got moved to Eclipse Temurin and won't be updated anymore. It is highly recommended to migrate workflows from adopt to temurin to keep receiving software and security updates. See more details in the Good-bye AdoptOpenJDK post.
- org.openrewrite.github.SetupJavaAdoptOpenj9ToSemeru: Adopt OpenJDK got moved to Eclipse Temurin and won't be updated anymore. It is highly recommended to migrate workflows from adopt-openj9 to IBM semeru to keep receiving software and security updates. See more details in the Good-bye AdoptOpenJDK post.
- org.openrewrite.gradle.RemoveDependency: Removes a single dependency from the dependencies section of the
build.gradle
.