GithubHelp home page GithubHelp logo

openrewrite / rewrite-gradle-plugin Goto Github PK

View Code? Open in Web Editor NEW
57.0 7.0 36.0 1.28 MB

OpenRewrite's Gradle plugin.

License: Apache License 2.0

Kotlin 30.24% Groovy 2.85% Java 66.91%
openrewrite

rewrite-gradle-plugin's People

Contributors

aegershman avatar anuebel avatar dependabot[bot] avatar guylabs avatar jkschneider avatar knutwannheden avatar koppor avatar kunli2 avatar magicalash avatar moderneapp[bot] avatar natedanner avatar nicolasb29 avatar pstreef avatar pway99 avatar quaff avatar radoslaw-panuszewski avatar reka18 avatar renegrob avatar renovate[bot] avatar rpau avatar ryanwalker avatar sambsnyd avatar shanman190 avatar sjungling avatar sofiabrittoschwartz avatar sullis avatar timtebeek avatar tkvangorder avatar traceyyoshima avatar yeikel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

rewrite-gradle-plugin's Issues

Warn when rewrite.yml is non-null and doesn't exist

Specifying a rewrite.yml is optional, so we don't warn/error if it doesn't exist.
But if someone has specified a non-existent rewrite.yml we should raise some warning.

Difficulty: We check for its existence per task, and there's one task per source set, and many source sets per project. The warning should be displayed exactly once per missing rewrite config file.

Update Gradle Plugin's RewriteFixTask to deal with generated or deleted files

Visitors can now override RefactorVisitor.generate() to produce new sources.

It is the responsibility of the caller, in this case the Gradle plugin, to respond to these new sources and write them to disk in an appropriate location.

When iterating over Change inside of RewriteFixTask whenever the change has a null original and a non-null fixed, it is a new file which needs to be written to disk. SourceFile.getSourcePath() may be used to determine where this file should go.

Similarly, a change with a non-null original and a null fixed indicates that a source file should be deleted.

RewriteWarnTask should continue not to make any changes to disk, but the text it outputs to the console should be updated to accommodate file generation/deletion

Expose option to fail build if `rewriteDryRun` fails

At the moment, if one runs gradle rewriteDryRun, then it reports any code that hasn't been refactored.

However, I'd love for the build to fail if rewriteDryRun reports any such code, so that I know about it at CI time.

It seems to me that a good way forward would be to expose a flag in the extension that, when enabled, makes the build fail if a dry run reports any problems. It would be false by default to maintain backwards compatibility. So something like:

rewrite {
  activeRecipe("com.yourorg.TheRecipe")

  failOnBadDryRun = true
}

But I'm open to other ideas!

What do you think, Rewrite team?

Plugin fails to generate changes when using rewrite-gradle

Given the sample:
https://github.com/shanman190/openrewrite-gradle-issue-gh-83

And executing ./gradlew rewriteDryRun the plugin outputs:

> Task :compileJava NO-SOURCE
> Task :processResources NO-SOURCE
> Task :classes UP-TO-DATE
> Task :compileTestJava NO-SOURCE
> Task :rewriteResolveDependencies

> Task :rewriteDryRun
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/C:/Users/<removed>/.gradle/caches/modules-2/files-2.1/org.openrewrite/rewrite-gradle/7.16.0/1a7a48ffbfdaeeb2ba79b1a0714f4e69d82b67e3/rewrite-gradle-7.16.0.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/C:/Users/<removed>/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-simple/1.7.30/e606eac955f55ecf1d8edcccba04eb8ac98088dd/slf4j-simple-1.7.30.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.gradle.internal.logging.slf4j.OutputEventListenerBackedLoggerContext]
Using active recipe(s) [com.example.VersionRelocation]
Using active styles(s) []
Validating active recipes
Running recipe(s)...
Applying recipes would make no changes. No report generated.

Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/6.9.1/userguide/command_line_interface.html#sec:command_line_warnings

BUILD SUCCESSFUL in 24s
2 actionable tasks: 2 executed

As you can see in the sample, it should have rewritten the version from 2.3.12.RELEASE to 2.4.13. This same behavior also happens when trying to do ChangeDependencyGroupId and ChangeDependencyArtifactId. It feels like it may be that something with org.openrewrite.gradle effecting the build.gradle of itself is at play here?

Plugin classpath is leaking out when unintended

Presently, the plugin needs to compile against several different libraries (rewrite, jackson, etc). These of course are needed for compilation. However, in order to work around the plugin needing Jackson and not wanting to conflict with the shared plugin classpath, internally it uses it's own dependency configuration and isolated classloader. This works great.

However, we are still leaking these dependencies via the plugin's compileClasspath/runtimeClasspath out to the Gradle plugin classpath. This means that if another plugin depends on Jackson, this plugin influences it's version that it receives.

To correct this, we should move from implementation to compileOnly and testImplementation/runtimeOnly so that we no longer leak out the internal classpath that the plugin needs at runtime and will gain via it's internal dependency configuration rewriteDependencies.
See: https://github.com/openrewrite/rewrite-gradle-plugin/blob/main/plugin/src/main/java/org/openrewrite/gradle/AbstractRewriteTask.java#L77-L80

Re-evaluate plugin publishing name/id

Consider updating the generated name/id for gradlePlugin to set the metadata for rewrite-gradle-plugin to result in a GAV similar to Maven, e.g. org.openrewrite.maven:rewrite-maven-plugin, but for gradle; so, org.openrewrite.gradle:rewrite-gradle-plugin. Currently, the generated metadata for the rewrite-gradle-plugin results in referencing the plugin as org.openrewrite.rewrite. This is acceptable for gradle, but when publishing this to Maven, it results in gav coordinates of org.openrewrite:plugin (https://oss.sonatype.org/service/local/repositories/snapshots/content/org/openrewrite/plugin/maven-metadata.xml). And having to do something a little peculiar with pluginManagement when publishing snapshots:

pluginManagement {
    resolutionStrategy {
        eachPlugin {
            if (requested.id.namespace == "org.openrewrite") {
                useModule("org.openrewrite:plugin:${requested.version}")
            }
        }
    }
    ...
}

From #84

CPU Bottleneck when calling `toRealPath` during parsing

The following thread dump was collected and while working with a design partner. This may be due to Virus scanning software installed on the machine, but we should investigate how to speed this up.

"Execution worker for ':' Thread 13" #466 prio=5 os_prio=31 cpu=633447.32ms elapsed=730.02s tid=0x00007ffdc8f87000 nid=0x57c03 runnable  [0x000070002d677000]
   java.lang.Thread.State: RUNNABLE
        at sun.nio.fs.UnixNativeDispatcher.realpath0([email protected]/Native Method)
        at sun.nio.fs.UnixNativeDispatcher.realpath([email protected]/UnixNativeDispatcher.java:272)
        at sun.nio.fs.UnixPath.toRealPath([email protected]/UnixPath.java:857)
        at org.openrewrite.gradle.AbstractRewriteTask.toRealPath(AbstractRewriteTask.java:448)
        at org.openrewrite.gradle.AbstractRewriteTask$$Lambda$1135/0x0000000800e69c40.apply(Unknown Source)
        at java.util.stream.ReferencePipeline$3$1.accept([email protected]/ReferencePipeline.java:195)
        at java.util.stream.ReferencePipeline$3$1.accept([email protected]/ReferencePipeline.java:195)
        at java.util.Iterator.forEachRemaining([email protected]/Iterator.java:133)
        at java.util.Spliterators$IteratorSpliterator.forEachRemaining([email protected]/Spliterators.java:1801)
        at java.util.stream.AbstractPipeline.copyInto([email protected]/AbstractPipeline.java:484)
        at java.util.stream.AbstractPipeline.wrapAndCopyInto([email protected]/AbstractPipeline.java:474)
        at java.util.stream.ReduceOps$ReduceOp.evaluateSequential([email protected]/ReduceOps.java:913)
        at java.util.stream.AbstractPipeline.evaluate([email protected]/AbstractPipeline.java:234)
        at java.util.stream.ReferencePipeline.collect([email protected]/ReferencePipeline.java:578)
        at org.openrewrite.gradle.AbstractRewriteTask.parse(AbstractRewriteTask.java:251)
        at org.openrewrite.gradle.AbstractRewriteTask.lambda$listResults$3(AbstractRewriteTask.java:191)
        at org.openrewrite.gradle.AbstractRewriteTask$$Lambda$1103/0x0000000800e66040.apply(Unknown Source)
        at java.util.stream.ReferencePipeline$7$1.accept([email protected]/ReferencePipeline.java:271)
        at java.util.ArrayList$ArrayListSpliterator.forEachRemaining([email protected]/ArrayList.java:1655)
        at java.util.stream.AbstractPipeline.copyInto([email protected]/AbstractPipeline.java:484)
        at java.util.stream.AbstractPipeline.wrapAndCopyInto([email protected]/AbstractPipeline.java:474)
        at java.util.stream.ReduceOps$ReduceOp.evaluateSequential([email protected]/ReduceOps.java:913)
        at java.util.stream.AbstractPipeline.evaluate([email protected]/AbstractPipeline.java:234)
        at java.util.stream.ReferencePipeline.collect([email protected]/ReferencePipeline.java:578)
        at org.openrewrite.gradle.AbstractRewriteTask.listResults(AbstractRewriteTask.java:192)
        at org.openrewrite.gradle.RewriteDryRunTask.run(RewriteDryRunTask.java:69)
        at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0([email protected]/Native Method)
        at jdk.internal.reflect.NativeMethodAccessorImpl.invoke([email protected]/NativeMethodAccessorImpl.java:62)
        at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke([email protected]/DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke([email protected]/Method.java:566)
        at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:104)
        at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:58)
        at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:51)
        at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:29)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$2.run(ExecuteActionsTaskExecuter.java:494)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
        at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
        at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)

Gradle Rewrite config location should be a list, allow URLs as well as file paths

Currently the Gradle plugin allows only a single config file to be specified. Typically something like:

rewrite {
    configLocation = "rewrite.yaml"
}

This config location is always interpreted as a file path on the local machine.

To allow for many repositories to easily share centrally hosted configuration, we should allow URIs in this field.
Similarly, there might be one (or more) common configs in addition to one (or more) local, project-specific configurations.
Therefore configLocation should accept a list, rather than only a singular entry.

No one really uses the Gradle plugin right now - we haven't even published it properly to the gradle plugin portal - so backwards compatibility isn't particularly important in this case.

rewrite-gradle-plugin snapshots to ossrh

See: openrewrite/rewrite-maven-plugin#265

Publish rewrite-gradle-plugin snapshots to ossrh snapshots.
This will make it more convenient to consume and validate changes prior to release.

Be aware that to resolve plugins from anywhere other than the gradle plugin portal, you need a snippet like this in the settings.gradle.kts

pluginManagement {
    repositories {
        // ...
        maven {
            url = uri("https://oss.sonatype.org/content/repositories/snapshots/")
        }
        // ...
        // you'll likely also need this if you don't have a pluginManagement section already:
        gradlePluginPortal()
        // ...
    }
}

Gradle plugin portal doesn't accept snapshots, but that's alright. We'll push snapshots to OSSRH, and can keep publishing releases just to gradlePluginPortal.

Ensure that rewrite dependency resolution is unaffected by outside dependency resolution customizations

The Gradle plugin creates a dependency configuration called rewrite. This is where users declare any recipe modules they want to be able to run. That configuration is resolved when any of the rewrite tasks run and the resulting jars stuffed into a class loader.
This works pretty well unless custom dependency resolution policies or a plugin like the spring dependency management plugin interfere with normal dependency resolution.

We should try packing the core rewrite dependencies and jackson into the plugin's own jar and loading them from there.

Based on this discussion in the community slack.

Plugin version 5.5.0+ fails with NoSuchMethodException

I'm trying to run some Java recipes. With v5.5.0 or v5.6.0 running rewriteRun or rewriteDryRun fails with

Caused by: java.lang.RuntimeException: java.lang.NoSuchMethodException: org.openrewrite.java.marker.JavaProvenance.<init>(java.util.UUID, java.lang.String, java.lang.String, org.openrewrite.java.marker.JavaProvenance$BuildTool, org.openrewrite.java.marker.JavaProvenance$JavaVersion, java.util.Set, org.openrewrite.java.marker.JavaProvenance$Publication)
        at org.openrewrite.gradle.RewriteReflectiveFacade$JavaProvenanceBuilder.build(RewriteReflectiveFacade.java:375)
        at org.openrewrite.gradle.AbstractRewriteTask.parse(AbstractRewriteTask.java:212)
        ... 125 more
Caused by: java.lang.NoSuchMethodException: org.openrewrite.java.marker.JavaProvenance.<init>(java.util.UUID, java.lang.String, java.lang.String, org.openrewrite.java.marker.JavaProvenance$BuildTool, org.openrewrite.java.marker.JavaProvenance$JavaVersion, java.util.Set, org.openrewrite.java.marker.JavaProvenance$Publication)
        at org.openrewrite.gradle.RewriteReflectiveFacade$JavaProvenanceBuilder.build(RewriteReflectiveFacade.java:371)
        ... 126 more

I tried this in a larger project but a barebones project with no source, settings.gradle

rootProject.name = 'foo'

and build.gradle

plugins {
    id 'java'
    id 'org.openrewrite.rewrite' version '5.5.0'
}

repositories {
    mavenCentral()
}

rewrite {
    activeRecipe('org.openrewrite.java.cleanup.UnnecessaryThrows')
}

fails the same way. Downgrading to version 5.4.1 works.

Isolate classpath/classloader for rewrite from other gradle plugins

All gradle plugins included in the same build share the same classpath at runtime.
This can be problematic when plugins used in the same build depend on different versions of the same dependencies. We've persistently had issues with our dependency on jackson conflicting with other gradle plugins that also depend on jackson.

To resolve this, we should remove our compile-time dependencies on rewrite and instead construct a detached Gradle dependency configuration at runtime, resolve the rewrite dependencies there, and construct our own isolated classloader through which we load & use rewrite at runtime.

Revisit recipe runtime classpath contributions

Currently, documentation for rewrite-gradle-plugin instructs users to add rewrite recipe dependencies (i.e. rewrite-testing-frameworks) as compileOnly dependencies on the project being mutated. It's not entirely clear why, but compileOnly doesn't work when running rewrite-testing-frameworks with netflix zuul; using implementation scope does work. However, needing to use implementation scoped dependencies is not ideal because it will pollute the application's real dependencies. 2 options were discussed as potential solutions with different benefits.

  1. Annotation processor approach: investigate running rewrite as an annotation processor in a gradle build. Rewrite will have access to JCTree objects and could bypass compilation in JavaParser (compilation has already been run before annotation processing). This would support other annotation processors (i.e. lombok), and mixed language projects.
  2. Detached configurations. Use gradle detached configuration scopes to strictly control the classpath that's used in the rewrite gradle plugin. The detached configuration we use could extend from compile, so that the projects dependencies would be available, and we'd pick up any recipe jars referred to by the project.

Add a "Discover" task that lists all of the Recipes available on the classpath

Create a new type of task that lists all of the available Recipes and their visitors.
When this task is complete running ./gradlew rewriteDiscover on a project which has this on its classpath and applies this plugin should result in console output similar to:

>./gradlew rewriteDiscover

Found 1 active recipes and 1 total recipes. 

Active Recipe Names:
    org.openrewrite.mockito

Recipes:
    name: org.openrewrite.mockito
    include: org.openrewrite.mockito.*
    exclude: 
    visitors:
        org.openrewrite.mockito.MockAsOuterClass
        org.openrewrite.mockito.ArgumentMatchersAny
        org.openrewrite.mockito.InvocationOnMockGetArgument

Implementing this will involve creating a class called RewriteDiscoverTask, the start of which will probably look something like this:

import org.gradle.api.logging.Logger;
import org.gradle.api.logging.Logging;
import org.gradle.api.tasks.TaskAction;
import org.openrewrite.RefactorPlan;
import org.openrewrite.RefactorVisitor;

import java.util.Collection;
import java.util.List;
import java.util.Set;

public class RewriteDiscoverTask extends AbstractRewriteTask {
    private static Logger log = Logging.getLogger(RewriteDiscoverTask.class);
    @Override
    protected Logger getLog() {
        return log;
    }

    @TaskAction
    public void run() {
        RefactorPlan plan = plan();

        Set<String> activeRecipes = getActiveRecipes();
        // Print active recipes via log.quiet("message to print")

        List<GradleRecipeConfiguration> recipes = getRecipes();
        for(GradleRecipeConfiguration recipe : recipes) {
            // Print recipe name
            Collection<RefactorVisitor<?>> visitors = plan.visitors(recipe.name);
            for(RefactorVisitor<?> visitor : visitors) {
                // Print visitor name
            }
        }
    }
}

Tasks of this type must then be registered on the Gradle project inside of RewritePlugin.
The configuration of these newly registered RewriteDiscoverTasks should end up being very similar to the configuration for the RewriteFixTasks in the same area.

Gradle plugin should print SearchResult markers as comments

Right now if you set a search recipe as active it will run but produce no output. This is because the plugins are not configured to use any SearchResultPrinters. The plugins should use the default SearchResultPrinter for a given SourceFile (see openrewrite/rewrite#412) to accomplish this, so that when the marker is reified to a comment it does not break the resulting source code.

Add dependency configuration just for recipes, while retaining compileOny and testCompileOnly as valid sources

rewrite-testing-frameworks takes a dependency on junit4 so that junit4 can be removed. But if people leave rewrite-testing-frameworks as a compileOnly or testCompileOnly dependency then junit4 will always be on their class path.

So this is issue is to add a new configuration just for recipes, call it "rewrite" or "rewriteRecipe" or something along those lines.
No other configuration will inherit from that one so we wont have to worry about cluttering up any classpaths but our own.

We should still also look at the compile-time dependencies since we have a longer term aspiration that framework/library authors package upgrade recipes inside their jars. For that to work recipes need to continue to be discoverable from the compile classpath.

Rewrite-spring yaml deserialization issue

Rewrite-spring recipes RemoveObsoleteRunners and RunnerToExtension are both unable to be deserialized from declarative recipe lists in rewrite-gradle-plugin. We've observed that both contain an @Option annotated property that is of type List<String>. The exception observed is

java.lang.IllegalArgumentException: Could not resolve type id 'org.openrewrite.java.testing.junit5.RemoveObsoleteRunners' as a subtype of `org.openrewrite.Recipe`: no such class found

though based on observing the classpath, Recipe and RemoveObsoleteRunners are both definitely present, so we think this exception may be a red herring for the real issue.

Default to locating rewrite.yml relative to the root project

On the assumption that most builds, even larger ones, will want to configure rewrite in a single place by default. Will continue to be over-rideble on a per-project basis.

To affect this change in RewriteExtension we just need to change this line from:

configFile = project.file("rewrite.yml");

to this:

configFile = project.getRootProject().file("rewrite.yml");

Re-Enable Metrics in gradle plugin again

Users should be able to specify custom endpoint the metrics should be sent to.
We have some defunct infrastructure for this already, but it needs to be updated and turned back no.

Compilation errors in netflix zuul

When running rewrite-testing-frameworks against netflix zuul via rewrite-gradle-plugin gw rewriteRun, the recipes make modifications to multiple source files, and then a compile is run with many errors due to the fact that we don't currently have a way to change the junit dependencies in build.gradle. As far as I can tell, the rewriteRun task should not be running a compile. This may be due to some gradle configuration in netflix zuul, or perhaps something in the rewrite gradle plugin is running a build after rewriteRun. Investigate, and If it's the latter, prevent rewriteRun from running compile.

Consider hooking `rewriteDryRun` into the `check` task

At the moment, when I import rewrite-gradle-plugin and run check, it doesn't seem to run any task from rewrite-gradle-plugin such as rewriteDryRun. This is counter-intuitive for me, because I'm used to Spotless - a code formatting plugin - automatically checking if my code is formatted if I run check.

It's not a big problem, because it's easy to tell rewriteDryRun to run when check is called, like so:

tasks.named("check").configure {
    dependsOn(tasks.named("rewriteDryRun"))
}

However, I'd love for the status quo to change so that I don't need this piece of code anymore.

Proposal

Consider hooking rewriteDryRun into check so that when check runs, then rewriteDryRun does too.

Alternatively, consider exposing a new flag in the extension that does all this when enabled. For example:

rewrite {
  activeRecipe(...)

  doDryRunOnCheck = true
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.