Skip to content

Commit

Permalink
Merge branch 'main' into fix/3932
Browse files Browse the repository at this point in the history
  • Loading branch information
ammachado authored Feb 15, 2024
2 parents 67700ef + 904ca30 commit f684837
Show file tree
Hide file tree
Showing 39 changed files with 1,002 additions and 191 deletions.
38 changes: 24 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
<h1 align="center">OpenRewrite</h1>
<p align="center"><i><b>🎉 Automate software refactoring 🎉</b></i></p>

---

<p align="center">
<img src="doc/OpenRewrite.gif" alt="OpenRewrite"/>
<img src="./doc/logo-oss.png" alt="OpenRewrite"/>
</p>

<div align="center">
Expand All @@ -17,23 +12,38 @@
[![Contributing Guide](https://img.shields.io/badge/Contributing-Guide-informational)](https://github.com/openrewrite/.github/blob/main/CONTRIBUTING.md)
</div>

## More tech. Less debt.
<h1 align="center">Fast, repeatable refactoring for developers</h1>

The OpenRewrite project is an automated refactoring ecosystem for source code, enabling developers to effectively eliminate technical debt within their repositories.

It consists of an auto-refactoring engine that runs prepackaged, open source refactoring recipes for common framework migrations, security fixes, and stylistic consistency tasks—reducing your coding effort from hours or days to minutes. Build tool plugins like [OpenRewrite Gradle Plugin](https://docs.openrewrite.org/reference/gradle-plugin-configuration) and [OpenRewrite Maven Plugin](https://docs.openrewrite.org/reference/rewrite-maven-plugin) help you run these recipes on one repository at a time.

The OpenRewrite project is a mass source code refactoring ecosystem. Reduce 1000s of hours of static code analysis fixes to minutes. Turn a four-month migration project into four hours of work. Patch security vulnerabilities across 100s of repositories at once. OpenRewrite automates code refactoring and remediation tasks for you, enabling developers to deliver more business value.
OpenRewrite recipes have bench strength in Java and are part of a growing community of languages, frameworks, and tech. It’s thousands of great individuals and teams working to make software seamless to update and continuously secure. You can also easily customize recipes for your code.

Start with our [quickstart guide](https://docs.openrewrite.org/running-recipes/getting-started) and let OpenRewrite start handling the boring parts of software development for you. Full documentation available at [docs.openrewrite.org](https://docs.openrewrite.org/).
Start with our [quickstart guide](https://docs.openrewrite.org/running-recipes/getting-started) and let OpenRewrite handle the boring parts of software development for you.

Feel free to join us on [Slack](https://join.slack.com/t/rewriteoss/shared_invite/zt-nj42n3ea-b~62rIHzb3Vo0E1APKCXEA) or [Discord](https://discord.gg/xk3ZKrhWAb)! We're happy to answer your questions directly. Also, follow us on [Twitter](https://twitter.com/moderneinc) and [LinkedIn](https://www.linkedin.com/company/moderneinc).
Get and stay informed:
* Read the [documentation](http://docs.openrewrite.org).
* Join us on [Slack](https://join.slack.com/t/rewriteoss/shared_invite/zt-nj42n3ea-b~62rIHzb3Vo0E1APKCXEA) or [Discord](https://discord.gg/xk3ZKrhWAb)! We're happy to answer your questions directly.
* Check out [Community Office Hours](https://www.youtube.com/@moderne-auto-remediation/streams) where we deep dive topics and answer questions.
* Subscribe to our [YouTube](https://www.youtube.com/@moderne-auto-remediation) channel for great videos on OpenRewrite recipes.
* Follow us on [Twitter](https://twitter.com/openrewrite) and [LinkedIn](https://www.linkedin.com/company/moderneinc).

See this [doc page](https://docs.openrewrite.org/reference/building-openrewrite-from-source) for more on building this repository from source.
OpenRewrite is Apache2 licensed and maintained by Moderne. OpenRewrite's refactoring engine and recipes will always be open source.

## Refactoring at scale with Moderne

OpenRewrite's refactoring engine and recipes will always be open source. Build tool plugins like [OpenRewrite Gradle Plugin](https://docs.openrewrite.org/reference/gradle-plugin-configuration) and [OpenRewrite Maven Plugin](https://docs.openrewrite.org/reference/rewrite-maven-plugin) help you run these recipes on one repository at a time. Moderne is a complementary commercial product that executes OpenRewrite recipes at scale on hundreds of millions of lines of code and enables mass-committing of results and large scale impact analysis. Moderne freely runs an [open public service](https://app.moderne.io) for the benefit of tens of thousands of open source projects.
[Moderne](https://www.moderne.io/) is a commercial platform designed to run the OpenRewrite recipe catalog with incredible efficiency, supporting large codebases and multiple repositories. It’s a place where platform teams and developers can collaborate to drive migrations across their codebases, mass-commit code fixes, and perform large-scale impact analyses.

See how Moderne and OpenRewrite work in different ways to transform your code.

[![Moderne](./doc/openrewrite_v_moderne.png)](https://www.youtube.com/watch?v=Q-ej2lCJHRs)

Moderne freely runs an [open public service](https://www.moderne.io/try-moderne) for the benefit of tens of thousands of open source projects. The platform can streamline your recipe customization and creation, enabling quick assembly and testing. It also includes a multi-repo command line interface (CLI) to enable developers to run and customize recipes locally.

To understand better how Moderne can help your team, [contact us here](https://www.moderne.io/try-moderne).
[![Moderne](./doc/moderne_cli.png)](https://www.youtube.com/watch?v=cs-6FJ_mtro)

[![Moderne](./doc/video_preview.png)](https://youtu.be/cs-6FJ_mtro)
To understand better how Moderne can help your team, [contact us](https://www.moderne.io/contact-us).

## Contributing

Expand Down
Binary file added doc/moderne_cli.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/openrewrite_v_moderne.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed doc/video_preview.png
Binary file not shown.
16 changes: 16 additions & 0 deletions rewrite-core/src/main/java/org/openrewrite/LargeSourceSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,25 @@ public interface LargeSourceSet {

/**
* Called by {@link RecipeScheduler} at the beginning of a scan/generate/edit cycle.
*
* @deprecated Override {@link #beforeCycle(boolean)} instead
*/
@Deprecated
default void beforeCycle() {
}

/**
* Called by {@link RecipeScheduler} at the beginning of a scan/generate/edit cycle.
*
* @param definitelyLastCycle {@code true} if this is definitely the last cycle of a recipe; when called with a
* value of {@code false} the next call to {@link #afterCycle(boolean)} could still be
* called with {@code true}. This depends on whether any of the recipes requested an
* additional cycle.
*/
default void beforeCycle(boolean definitelyLastCycle) {
beforeCycle();
}

/**
* Maintain context about what recipe is performing an edit or generating code.
*
Expand Down Expand Up @@ -78,6 +93,7 @@ default void afterCycle(boolean lastCycle) {

/**
* Get the original source file, before any edits. Returns null if it is not present.
*
* @param sourcePath The path of the source file to retrieve.
* @return The original source file. Null if not present.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ private LargeSourceSet runRecipeCycles(Recipe recipe, LargeSourceSet sourceSet,
RecipeRunCycle<LargeSourceSet> cycle = new RecipeRunCycle<>(recipe, i, rootCursor, ctxWithWatch,
recipeRunStats, sourceFileResults, errorsTable, LargeSourceSet::edit);
ctxWithWatch.putCycle(cycle);
after.beforeCycle();
after.beforeCycle(i == maxCycles);

// pre-transformation scanning phase where there can only be modifications to capture exceptions
// occurring during the scanning phase
Expand Down
65 changes: 62 additions & 3 deletions rewrite-core/src/main/java/org/openrewrite/Result.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,21 @@
package org.openrewrite;

import lombok.Getter;
import org.eclipse.jgit.lib.*;
import org.eclipse.jgit.lib.FileMode;
import org.openrewrite.config.RecipeDescriptor;
import org.openrewrite.internal.InMemoryDiffEntry;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.marker.RecipesThatMadeChanges;
import org.openrewrite.marker.SearchResult;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

import static java.util.Objects.requireNonNull;

public class Result {
/**
Expand Down Expand Up @@ -71,11 +76,65 @@ public Result(@Nullable SourceFile before, @Nullable SourceFile after, Collectio
public Result(@Nullable SourceFile before, SourceFile after) {
this(before, after, after.getMarkers()
.findFirst(RecipesThatMadeChanges.class)
.orElseThrow(() -> new IllegalStateException("SourceFile changed but no recipe " +
"reported making a change"))
.orElseThrow(() -> new IllegalStateException(
String.format(
"Source file changed but no recipe " +
"reported making a change. %s",
explainWhatChanged(before, after)
)
))
.getRecipes());
}

private static String explainWhatChanged(@Nullable SourceFile before, SourceFile after) {
if (before == null) {
return String.format("A new file %s was generated but no recipe reported generating it. This is likely a bug in OpenRewrite itself.",
after.getSourcePath());
}
Map<UUID, Tree> beforeTrees = new HashMap<>();
new TreeVisitor<Tree, Integer>() {
@Override
public @Nullable Tree visit(@Nullable Tree tree, Integer integer) {
if (tree != null) {
beforeTrees.put(tree.getId(), tree);
}
return super.visit(tree, integer);
}
}.visit(before, 0);

SourceFile changesMarked = (SourceFile) new TreeVisitor<Tree, Integer>() {
@Override
public @Nullable Tree visit(@Nullable Tree tree, Integer p) {
if (tree != null &&
beforeTrees.get(tree.getId()) != tree &&
!subtreeChanged(tree, beforeTrees)) {
return SearchResult.found(tree);
}
return super.visit(tree, p);
}
}.visitNonNull(after, 0);

String diff = diff(before.printAllTrimmed(), changesMarked.printAllTrimmed(), after.getSourcePath());
return "The following diff highlights the places where unexpected changes were made:\n" +
Arrays.stream(requireNonNull(diff).split("\n"))
.map(l -> " " + l)
.collect(Collectors.joining("\n"));
}

private static boolean subtreeChanged(Tree root, Map<UUID, Tree> beforeTrees) {
return new TreeVisitor<Tree, AtomicBoolean>() {
@Override
public @Nullable Tree visit(@Nullable Tree tree, AtomicBoolean changed) {
if (tree != null && tree != root) {
if (beforeTrees.get(tree.getId()) != tree) {
changed.set(true);
}
}
return super.visit(tree, changed);
}
}.reduce(root, new AtomicBoolean(false)).get();
}

/**
* Return a list of recipes that have made changes as a hierarchy of descriptors.
* The method transforms the flat, stack-based representation into descriptors where children are grouped under their common parents.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuf
this.valueSeparator = valueSeparator;
}

public boolean hasPlaceholders(@Nullable String value) {
if (value == null) {
return false;
}
int startIndex = value.indexOf(placeholderPrefix);
return startIndex > -1 && value.indexOf(placeholderSuffix, startIndex) > startIndex;
}

public String replacePlaceholders(String value, final Properties properties) {
return replacePlaceholders(value, properties::getProperty);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,11 +227,15 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Execu
}

if (version != null) {
try {
resolvedVersion = resolveDependencyVersion(groupId, artifactId, "0", version, versionPattern, gp.getMavenRepositories(), metadataFailures, ctx)
.orElse(null);
} catch (MavenDownloadingException e) {
return e.warn(m);
if (version.startsWith("$")) {
resolvedVersion = version;
} else {
try {
resolvedVersion = resolveDependencyVersion(groupId, artifactId, "0", version, versionPattern, gp.getMavenRepositories(), metadataFailures, ctx)
.orElse(null);
} catch (MavenDownloadingException e) {
return e.warn(m);
}
}
}

Expand Down Expand Up @@ -370,7 +374,7 @@ public static Optional<String> resolveDependencyVersion(String groupId, String a

Optional<String> version;
if (versionComparator instanceof ExactVersion) {
version = Optional.of(newVersion);
version = versionComparator.upgrade(currentVersion, singletonList(newVersion));
} else if (versionComparator instanceof LatestPatch && !versionComparator.isValid(currentVersion, currentVersion)) {
// in the case of "latest.patch", a new version can only be derived if the
// current version is a semantic version
Expand Down
Loading

0 comments on commit f684837

Please sign in to comment.