diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 83d0bfede..234b3d021 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3.6.0 + - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-java@v3 diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 5d53aaeed..323cefe4e 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -20,11 +20,11 @@ jobs: name: itest-${{ matrix.java }} steps: - name: checkout-openrewrite/spring-petclinic-migration - uses: actions/checkout@v3.6.0 + uses: actions/checkout@v4 with: repository: openrewrite/spring-petclinic-migration - name: setup-plugin-checkout-source - uses: actions/checkout@v3.6.0 + uses: actions/checkout@v4 with: path: rewrite-gradle-plugin - name: setup-plugin-setup-java-11 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 72f71d683..5c243aedb 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -16,7 +16,7 @@ jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3.6.0 + - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-java@v3 diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 63387828f..18d993678 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -7,7 +7,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import java.util.* plugins { - kotlin("jvm") version("1.8.0") + kotlin("jvm") version("1.9.0") id("com.gradle.plugin-publish") version "1.1.0" id("com.github.hierynomus.license") version "0.16.1" } @@ -67,7 +67,6 @@ configurations.all { } } - java { toolchain { languageVersion.set(JavaLanguageVersion.of(17)) @@ -109,6 +108,7 @@ dependencies { "rewriteDependencies"("org.openrewrite.gradle.tooling:model:$latest") "rewriteDependencies"("org.openrewrite:rewrite-maven") // Newer versions of checkstyle are compiled with a newer version of Java than is supported with gradle 4.x + @Suppress("VulnerableLibrariesLocal", "RedundantSuppression") "rewriteDependencies"("com.puppycrawl.tools:checkstyle:9.3") "rewriteDependencies"("com.fasterxml.jackson.module:jackson-module-kotlin:latest.release") @@ -126,6 +126,7 @@ dependencies { compileOnly("org.openrewrite:rewrite-xml") compileOnly("org.openrewrite:rewrite-yaml") compileOnly("org.openrewrite:rewrite-polyglot:$latest") + @Suppress("VulnerableLibrariesLocal", "RedundantSuppression") compileOnly("com.puppycrawl.tools:checkstyle:9.3") compileOnly("org.jetbrains.kotlin:kotlin-gradle-plugin:latest.release") @@ -156,7 +157,7 @@ tasks.withType { } val gVP = tasks.register("generateVersionsProperties") { - val outputFile = file("$buildDir/rewrite/versions.properties") + val outputFile = file("${project.layout.buildDirectory.get().asFile}/rewrite/versions.properties") description = "Creates a versions.properties in $outputFile" group = "Build" diff --git a/plugin/src/main/java/org/openrewrite/gradle/AbstractRewriteTask.java b/plugin/src/main/java/org/openrewrite/gradle/AbstractRewriteTask.java index 54ec6beab..2865a40af 100755 --- a/plugin/src/main/java/org/openrewrite/gradle/AbstractRewriteTask.java +++ b/plugin/src/main/java/org/openrewrite/gradle/AbstractRewriteTask.java @@ -58,8 +58,8 @@ public boolean isDumpGcActivity() { @Internal protected T getProjectParser() { - if(gpp == null) { - if(extension == null) { + if (gpp == null) { + if (extension == null) { throw new IllegalArgumentException("Must configure extension"); } if (resolvedDependencies == null) { diff --git a/plugin/src/main/java/org/openrewrite/gradle/DefaultRewriteExtension.java b/plugin/src/main/java/org/openrewrite/gradle/DefaultRewriteExtension.java index 2208aa03c..2ae0f78f5 100644 --- a/plugin/src/main/java/org/openrewrite/gradle/DefaultRewriteExtension.java +++ b/plugin/src/main/java/org/openrewrite/gradle/DefaultRewriteExtension.java @@ -38,7 +38,7 @@ public class DefaultRewriteExtension implements RewriteExtension { private File configFile; private Provider checkstyleConfigProvider; - private Provider> checkstylePropertiesProvider; + private Provider> checkstylePropertiesProvider; private File checkstyleConfigFile; private String metricsUri = magicalMetricsLogString; private boolean enableExperimentalGradleBuildScriptParsing = true; @@ -98,10 +98,10 @@ public void setCheckstyleConfigFile(File configFile) { @Override @Nullable public File getCheckstyleConfigFile() { - if(checkstyleConfigFile == null && checkstyleConfigProvider != null) { + if (checkstyleConfigFile == null && checkstyleConfigProvider != null) { try { return checkstyleConfigProvider.get(); - } catch(Exception e) { + } catch (Exception e) { return null; } } @@ -110,7 +110,7 @@ public File getCheckstyleConfigFile() { @Override public Map getCheckstyleProperties() { - if(checkstyleConfigProvider == null) { + if (checkstyleConfigProvider == null) { return emptyMap(); } return checkstylePropertiesProvider.get(); @@ -195,11 +195,11 @@ public List getActiveRecipes() { } private Properties getVersionProps() { - if(versionProps == null) { - try(InputStream is = DefaultRewriteExtension.class.getResourceAsStream("/rewrite/versions.properties")) { + if (versionProps == null) { + try (InputStream is = DefaultRewriteExtension.class.getResourceAsStream("/rewrite/versions.properties")) { versionProps = new Properties(); versionProps.load(is); - } catch(IOException e) { + } catch (IOException e) { throw new RuntimeException(e); } } @@ -211,24 +211,25 @@ private Properties getVersionProps() { */ @Override public String getRewriteVersion() { - if(rewriteVersion == null) { + if (rewriteVersion == null) { return getVersionProps().getProperty("org.openrewrite:rewrite-core"); } return rewriteVersion; } + private String rewritePolyglotVersion; @Override public String getRewritePolyglotVersion() { - if(rewriteVersion == null) { + if (rewritePolyglotVersion == null) { return getVersionProps().getProperty("org.openrewrite:rewrite-polyglot"); } - return rewriteVersion; + return rewritePolyglotVersion; } private String rewriteGradleModelVersion; @Override public String getRewriteGradleModelVersion() { - if(rewriteGradleModelVersion == null) { + if (rewriteGradleModelVersion == null) { rewriteGradleModelVersion = getVersionProps().getProperty("org.openrewrite.gradle.tooling:model"); } return rewriteGradleModelVersion; @@ -237,7 +238,7 @@ public String getRewriteGradleModelVersion() { private String rewriteKotlinVersion; @Override public String getRewriteKotlinVersion() { - if(rewriteKotlinVersion == null) { + if (rewriteKotlinVersion == null) { rewriteKotlinVersion = getVersionProps().getProperty("org.openrewrite:rewrite-kotlin"); } return rewriteKotlinVersion; @@ -343,7 +344,6 @@ public List getPlainTextMasks() { "**.gitattributes", "**.gitignore", "**.java-version", - "**Jenkinsfile", "**.jsp", "**.ksh", "**.kts", @@ -385,7 +385,7 @@ public String getJacksonModuleKotlinVersion() { @Override public boolean getThrowOnParseFailures() { - if(project.getProperties().containsKey("rewrite.throwOnParseFailures")) { + if (project.getProperties().containsKey("rewrite.throwOnParseFailures")) { return true; } return throwOnParseFailures; diff --git a/plugin/src/main/java/org/openrewrite/gradle/DelegatingProjectParser.java b/plugin/src/main/java/org/openrewrite/gradle/DelegatingProjectParser.java index 77ae7fac4..2b33d8726 100755 --- a/plugin/src/main/java/org/openrewrite/gradle/DelegatingProjectParser.java +++ b/plugin/src/main/java/org/openrewrite/gradle/DelegatingProjectParser.java @@ -54,7 +54,7 @@ public DelegatingProjectParser(Project project, RewriteExtension extension, Set< .getResource("/org/openrewrite/gradle/isolated/DefaultProjectParser.class") .toString()); classpathUrls.add(currentJar); - if(rewriteClassLoader == null || !classpathUrls.equals(rewriteClasspath)) { + if (rewriteClassLoader == null || !classpathUrls.equals(rewriteClasspath)) { if (rewriteClassLoader != null) { rewriteClassLoader.close(); } diff --git a/plugin/src/main/java/org/openrewrite/gradle/RewritePlugin.java b/plugin/src/main/java/org/openrewrite/gradle/RewritePlugin.java index d7432d3fc..ee0f353e9 100644 --- a/plugin/src/main/java/org/openrewrite/gradle/RewritePlugin.java +++ b/plugin/src/main/java/org/openrewrite/gradle/RewritePlugin.java @@ -42,6 +42,11 @@ import static org.gradle.api.attributes.Bundling.BUNDLING_ATTRIBUTE; import static org.gradle.api.attributes.java.TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE; +import java.io.File; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Set; + /** * When applied to the root project of a multi-project build, applies to all subprojects. * When applied to the root project the "rewrite" configuration and "rewrite" DSL created in the root project apply to @@ -59,7 +64,7 @@ public void apply(Project project) { if (!isRootProject && project.getRootProject().getPluginManager().hasPlugin("org.openrewrite.rewrite")) { return; } - if(project.getRootProject().getPluginManager().hasPlugin("io.moderne.rewrite")) { + if (project.getRootProject().getPluginManager().hasPlugin("io.moderne.rewrite")) { // Moderne plugin provides superset of rewrite plugin functionality, no need to apply both return; } @@ -85,7 +90,7 @@ public void apply(Project project) { .setResolvedDependencies(resolvedDependenciesProvider); rewriteDiscover.dependsOn(rewriteConf); - if(isRootProject) { + if (isRootProject) { project.allprojects(subproject -> configureProject(subproject, extension, rewriteDryRun, rewriteRun)); } else { configureProject(project, extension, rewriteDryRun, rewriteRun); @@ -96,14 +101,14 @@ private static void configureProject(Project project, RewriteExtension extension // DomainObjectCollection.all() accepts a function to be applied to both existing and subsequently added members of the collection // Do not replace all() with any form of collection iteration which does not share this important property project.getPlugins().all(plugin -> { - if(plugin instanceof CheckstylePlugin) { + if (plugin instanceof CheckstylePlugin) { // A multi-project build could hypothetically have different checkstyle configuration per-project // In practice all projects tend to have the same configuration CheckstyleExtension checkstyleExtension = project.getExtensions().getByType(CheckstyleExtension.class); extension.setCheckstyleConfigProvider(checkstyleExtension::getConfigFile); extension.setCheckstylePropertiesProvider(checkstyleExtension::getConfigProperties); } - if(!(plugin instanceof JavaBasePlugin)) { + if (!(plugin instanceof JavaBasePlugin)) { return; } @@ -118,6 +123,27 @@ private static void configureProject(Project project, RewriteExtension extension rewriteRun.dependsOn(compileTask); rewriteDryRun.dependsOn(compileTask); }); + + // Detect SourceSets which overlap other sourceSets and disable the compilation task of the overlapping + // source set. Some plugins will create source sets not intended to be compiled for their own purposes. + Set sourceDirs = new HashSet<>(); + project.afterEvaluate(unused -> javaConvention.getSourceSets().stream() + .sorted(Comparator.comparingInt(sourceSet -> { + if ("main".equals(sourceSet.getName())) { + return 0; + } else if ("test".equals(sourceSet.getName())) { + return 1; + } else { + return 2; + } + })).forEach(sourceSet -> { + for (File file : sourceSet.getAllJava().getSourceDirectories().getFiles()) { + if (!sourceDirs.add(file.getAbsolutePath())) { + Task compileTask = project.getTasks().getByPath(sourceSet.getCompileJavaTaskName()); + compileTask.setEnabled(false); + } + } + })); }); } diff --git a/plugin/src/main/java/org/openrewrite/gradle/SanitizedMarkerPrinter.java b/plugin/src/main/java/org/openrewrite/gradle/SanitizedMarkerPrinter.java index 0d89b75af..131c01f41 100644 --- a/plugin/src/main/java/org/openrewrite/gradle/SanitizedMarkerPrinter.java +++ b/plugin/src/main/java/org/openrewrite/gradle/SanitizedMarkerPrinter.java @@ -29,7 +29,7 @@ public class SanitizedMarkerPrinter implements PrintOutputCapture.MarkerPrinter { @Override public String beforeSyntax(Marker marker, Cursor cursor, UnaryOperator commentWrapper) { - if(marker instanceof SearchResult) { + if (marker instanceof SearchResult) { return DEFAULT.beforeSyntax(marker, cursor, commentWrapper); } return ""; @@ -37,7 +37,7 @@ public String beforeSyntax(Marker marker, Cursor cursor, UnaryOperator c @Override public String afterSyntax(Marker marker, Cursor cursor, UnaryOperator commentWrapper) { - if(marker instanceof SearchResult) { + if (marker instanceof SearchResult) { return DEFAULT.afterSyntax(marker, cursor, commentWrapper); } return ""; diff --git a/plugin/src/main/java/org/openrewrite/gradle/TimeUtils.java b/plugin/src/main/java/org/openrewrite/gradle/TimeUtils.java index 063101748..271c5194b 100644 --- a/plugin/src/main/java/org/openrewrite/gradle/TimeUtils.java +++ b/plugin/src/main/java/org/openrewrite/gradle/TimeUtils.java @@ -27,39 +27,39 @@ public static String prettyPrint(Duration duration) { StringBuilder result = new StringBuilder(); long days = duration.getSeconds() / SECONDS_PER_DAY; boolean startedPrinting = false; - if(days > 0) { + if (days > 0) { startedPrinting = true; result.append(days); result.append(" day"); - if(days != 1) { + if (days != 1) { result.append("s"); } result.append(" "); } - long hours = duration.toHours() % 24; - if(startedPrinting || hours > 0) { + long hours = duration.toHours() % 24; + if (startedPrinting || hours > 0) { startedPrinting = true; result.append(hours); result.append(" hour"); - if(hours != 1) { + if (hours != 1) { result.append("s"); } result.append(" "); } long minutes = (duration.getSeconds() / SECONDS_PER_MINUTE) % MINUTES_PER_HOUR; - if(startedPrinting || minutes > 0) { + if (startedPrinting || minutes > 0) { result.append(minutes); result.append(" minute"); - if(minutes != 1) { + if (minutes != 1) { result.append("s"); } result.append(" "); } long seconds = duration.getSeconds() % SECONDS_PER_MINUTE; - if(startedPrinting || seconds > 0) { + if (startedPrinting || seconds > 0) { result.append(seconds); result.append(" second"); if (seconds != 1) { @@ -71,7 +71,7 @@ public static String prettyPrint(Duration duration) { long millis = duration.getNano() / 1000_000; result.append(millis); result.append(" millisecond"); - if(millis != 1) { + if (millis != 1) { result.append("s"); } diff --git a/plugin/src/main/java/org/openrewrite/gradle/isolated/DefaultProjectParser.java b/plugin/src/main/java/org/openrewrite/gradle/isolated/DefaultProjectParser.java index 4ae1ebc87..eee2b7b14 100644 --- a/plugin/src/main/java/org/openrewrite/gradle/isolated/DefaultProjectParser.java +++ b/plugin/src/main/java/org/openrewrite/gradle/isolated/DefaultProjectParser.java @@ -80,6 +80,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.PathMatcher; +import java.nio.file.Paths; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; @@ -119,7 +120,7 @@ public DefaultProjectParser(Project project, RewriteExtension extension) { OperatingSystemProvenance.current(), new BuildTool(randomId(), BuildTool.Type.Gradle, project.getGradle().getGradleVersion())) .filter(Objects::nonNull) - .collect(Collectors.toList()); + .collect(toList()); } /** @@ -185,7 +186,7 @@ public List getActiveStyles() { } public List getAvailableStyles() { - return environment().listStyles().stream().map(NamedStyles::getName).collect(Collectors.toList()); + return environment().listStyles().stream().map(NamedStyles::getName).collect(toList()); } public void discoverRecipes(boolean interactive, ServiceRegistry serviceRegistry) { @@ -269,13 +270,12 @@ private static StringBuilder repeat(int repeat) { public Collection listSources() { // Use a sorted collection so that gradle input detection isn't thrown off by ordering - Set result = new TreeSet<>(omniParser(emptySet()).acceptedPaths(project.getProjectDir().toPath())); + Set result = new TreeSet<>(omniParser(emptySet()).acceptedPaths(baseDir, project.getProjectDir().toPath())); //noinspection deprecation JavaPluginConvention javaConvention = project.getConvention().findPlugin(JavaPluginConvention.class); if (javaConvention != null) { for (SourceSet sourceSet : javaConvention.getSourceSets()) { - sourceSet.getAllJava().getFiles().stream() - .filter(it -> it.isFile() && (it.getName().endsWith(".java") || it.getName().endsWith(".kt"))) + sourceSet.getAllSource().getFiles().stream() .map(File::toPath) .map(Path::toAbsolutePath) .map(Path::normalize) @@ -293,6 +293,7 @@ public void dryRun(Path reportPath, boolean dumpGcActivity, Consumer heapMetrics.bindTo(meterRegistry); new JvmMemoryMetrics().bindTo(meterRegistry); + //noinspection deprecation File rewriteBuildDir = new File(project.getBuildDir(), "/rewrite"); if (rewriteBuildDir.exists() || rewriteBuildDir.mkdirs()) { File rewriteGcLog = new File(rewriteBuildDir, "rewrite-gc.csv"); @@ -614,35 +615,46 @@ public Stream parse(Project subproject, Set alreadyParsed, Exe Collection exclusions = extension.getExclusions().stream() .map(pattern -> subproject.getProjectDir().toPath().getFileSystem().getPathMatcher("glob:" + pattern)) .collect(toList()); - if (isExcluded(exclusions, subproject.getProjectDir().toPath())) { + if (isExcluded(exclusions, baseDir.relativize(subproject.getProjectDir().toPath()))) { logger.lifecycle("Skipping project {} because it is excluded", subproject.getPath()); return Stream.empty(); } logger.lifecycle("Scanning sources in project {}", subproject.getPath()); List styles = getStyles(); + logger.lifecycle("Using active styles {}", styles.stream().map(NamedStyles::getName).collect(toList())); @SuppressWarnings("deprecation") JavaPluginConvention javaConvention = subproject.getConvention().findPlugin(JavaPluginConvention.class); - Set sourceSets; + List sourceSets; List projectProvenance; if (javaConvention == null) { projectProvenance = sharedProvenance; - sourceSets = Collections.emptySet(); + sourceSets = emptyList(); } else { projectProvenance = new ArrayList<>(sharedProvenance); projectProvenance.add(new JavaProject(randomId(), subproject.getName(), new JavaProject.Publication(subproject.getGroup().toString(), subproject.getName(), subproject.getVersion().toString()))); - sourceSets = javaConvention.getSourceSets(); + sourceSets = javaConvention.getSourceSets().stream() + .sorted(Comparator.comparingInt(sourceSet -> { + if ("main".equals(sourceSet.getName())) { + return 0; + } else if ("test".equals(sourceSet.getName())) { + return 1; + } else { + return 2; + } + })).collect(toList()); } if (subproject.getPlugins().hasPlugin("org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension") || subproject.getExtensions().findByName("kotlin") != null && subproject.getExtensions().getByName("kotlin").getClass() .getCanonicalName().startsWith("org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension")) { - sourceFileStream = sourceFileStream.concat(parseMultiplatformKotlinProject(subproject, exclusions, alreadyParsed, projectProvenance, ctx)); + sourceFileStream = sourceFileStream.concat(parseMultiplatformKotlinProject(subproject, exclusions, alreadyParsed, ctx)); } + Set sourceDirs = new HashSet<>(); for (SourceSet sourceSet : sourceSets) { Stream sourceSetSourceFiles = Stream.of(); int sourceSetSize = 0; @@ -654,11 +666,25 @@ public Stream parse(Project subproject, Set alreadyParsed, Exe javaCompileTask.getSourceCompatibility(), javaCompileTask.getTargetCompatibility()); - List javaPaths = sourceSet.getAllJava().getFiles().stream() - .filter(it -> it.isFile() && it.getName().endsWith(".java")) - .map(File::toPath) + List unparsedSources = sourceSet.getAllSource() + .getSourceDirectories() + .filter(it -> it.exists() && !alreadyParsed.contains(it.toPath())) + .getFiles() + .stream() + .flatMap(sourceDir -> { + try { + return Files.walk(sourceDir.toPath()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }) + .filter(Files::isRegularFile) .map(Path::toAbsolutePath) .map(Path::normalize) + .distinct() + .collect(Collectors.toList()); + List javaPaths = unparsedSources.stream() + .filter(it -> it.toString().endsWith(".java") && !alreadyParsed.contains(it)) .collect(toList()); // classpath doesn't include the transitive dependencies of the implementation configuration @@ -698,6 +724,7 @@ public Stream parse(Project subproject, Set alreadyParsed, Exe .map(Supplier::get) .flatMap(jp -> jp.parse(javaPaths, baseDir, ctx)) .map(cu -> { + //noinspection deprecation if (isExcluded(exclusions, cu.getSourcePath()) || cu.getSourcePath().startsWith(baseDir.relativize(subproject.getBuildDir().toPath()))) { return null; @@ -712,11 +739,10 @@ public Stream parse(Project subproject, Set alreadyParsed, Exe } if (subproject.getPlugins().hasPlugin("org.jetbrains.kotlin.jvm")) { - List kotlinPaths = sourceSet.getAllSource().getFiles().stream() - .filter(it -> it.isFile() && it.getName().endsWith(".kt")) - .map(File::toPath) - .map(Path::toAbsolutePath) - .map(Path::normalize) + String excludedProtosPath = subproject.getProjectDir().getPath() + "/protos/build/generated"; + List kotlinPaths = unparsedSources.stream() + .filter(it -> !it.toString().startsWith(excludedProtosPath)) + .filter(it -> it.toString().endsWith(".kt")) .collect(toList()); if (!kotlinPaths.isEmpty()) { @@ -743,13 +769,9 @@ public Stream parse(Project subproject, Set alreadyParsed, Exe logger.info("Scanned {} Kotlin sources in {}/{}", kotlinPaths.size(), subproject.getPath(), sourceSet.getName()); } } - if (subproject.getPlugins().hasPlugin(GroovyPlugin.class)) { - List groovyPaths = sourceSet.getAllSource().getFiles().stream() - .filter(it -> it.isFile() && it.getName().endsWith(".groovy")) - .map(File::toPath) - .map(Path::toAbsolutePath) - .map(Path::normalize) + List groovyPaths = unparsedSources.stream() + .filter(it -> it.toString().endsWith(".groovy")) .collect(toList()); if (!groovyPaths.isEmpty()) { @@ -786,29 +808,36 @@ public Stream parse(Project subproject, Set alreadyParsed, Exe } for (File resourcesDir : sourceSet.getResources().getSourceDirectories()) { - if (resourcesDir.exists()) { + if (resourcesDir.exists() && !alreadyParsed.contains(resourcesDir.toPath())) { OmniParser omniParser = omniParser(alreadyParsed); - List accepted = omniParser.acceptedPaths(resourcesDir.toPath()); + List accepted = omniParser.acceptedPaths(baseDir, resourcesDir.toPath()); sourceSetSourceFiles = Stream.concat( sourceSetSourceFiles, - omniParser.parse(accepted, resourcesDir.toPath(), new InMemoryExecutionContext()) + omniParser.parse(accepted, baseDir, new InMemoryExecutionContext()) ); + alreadyParsed.addAll(accepted); sourceSetSize += accepted.size(); } } JavaSourceSet sourceSetProvenance = JavaSourceSet.build(sourceSet.getName(), dependencyPaths, javaTypeCache, false); - sourceFileStream = sourceFileStream.concat(sourceSetSourceFiles.map(addProvenance(projectProvenance, sourceSetProvenance)), sourceSetSize); + sourceFileStream = sourceFileStream.concat(sourceSetSourceFiles.map(addProvenance(sourceSetProvenance)), sourceSetSize); + // Some source sets get misconfigured to have the same directories as other source sets + // This causes duplicate source files to be parsed, so once a source set has been parsed exclude it from future parsing + for (File file : sourceSet.getAllSource().getSourceDirectories().getFiles()) { + alreadyParsed.add(file.toPath()); + } } - - sourceFileStream = sourceFileStream.concat(parseGradleFiles(exclusions, alreadyParsed, ctx)); + SourceFileStream gradleFiles = parseGradleFiles(exclusions, alreadyParsed, ctx); + sourceFileStream = sourceFileStream.concat(gradleFiles, gradleFiles.size()); SourceFileStream nonProjectResources = parseNonProjectResources(subproject, alreadyParsed, ctx, projectProvenance, sourceFileStream); - sourceFileStream = sourceFileStream.concat(nonProjectResources - .map(addProvenance(projectProvenance, null)), nonProjectResources.size()); + sourceFileStream = sourceFileStream.concat(nonProjectResources, nonProjectResources.size()); progressBar.setMax(sourceFileStream.size()); - return sourceFileStream.peek(it -> progressBar.step()); + return sourceFileStream + .map(addProvenance(projectProvenance)) + .peek(it -> progressBar.step()); } catch (Exception e) { throw new RuntimeException(e); } @@ -859,14 +888,13 @@ private SourceFileStream parseGradleFiles( if (buildScriptPath.toString().endsWith(".gradle")) { gradleParser = gradleParser(); sourceFiles = gradleParser.parse(singleton(buildGradleFile.toPath()), baseDir, ctx); - gradleFileCount++; } else { sourceFiles = PlainTextParser.builder().build() .parse(singleton(buildGradleFile.toPath()), baseDir, ctx); - gradleFileCount++; } + gradleFileCount++; sourceFiles = sourceFiles.map(sourceFile -> sourceFile.withMarkers(sourceFile.getMarkers().add(gp))); - alreadyParsed.add(buildScriptPath); + alreadyParsed.add(project.getBuildscript().getSourceFile().toPath()); } } @@ -883,32 +911,36 @@ private SourceFileStream parseGradleFiles( if (gradleParser == null) { gradleParser = gradleParser(); } - sourceFiles = Stream.concat( - sourceFiles, - gradleParser - .parse(singleton(settingsGradleFile.toPath()), baseDir, ctx) - .map(sourceFile -> { - if (finalGs == null) { - return sourceFile; - } - return sourceFile.withMarkers(sourceFile.getMarkers().add(finalGs)); - })); - gradleFileCount++; - alreadyParsed.add(settingsPath); + if(!isExcluded(exclusions, settingsPath)) { + sourceFiles = Stream.concat( + sourceFiles, + gradleParser + .parse(singleton(settingsGradleFile.toPath()), baseDir, ctx) + .map(sourceFile -> { + if (finalGs == null) { + return sourceFile; + } + return sourceFile.withMarkers(sourceFile.getMarkers().add(finalGs)); + })); + gradleFileCount++; + } + alreadyParsed.add(settingsGradleFile.toPath()); } else if (settingsGradleKtsFile.exists()) { Path settingsPath = baseDir.relativize(settingsGradleKtsFile.toPath()); - sourceFiles = Stream.concat( - sourceFiles, - PlainTextParser.builder().build() - .parse(singleton(settingsGradleKtsFile.toPath()), baseDir, ctx) - .map(sourceFile -> { - if (finalGs == null) { - return sourceFile; - } - return sourceFile.withMarkers(sourceFile.getMarkers().add(finalGs)); - })); - gradleFileCount++; - alreadyParsed.add(settingsPath); + if(!isExcluded(exclusions, settingsPath)) { + sourceFiles = Stream.concat( + sourceFiles, + PlainTextParser.builder().build() + .parse(singleton(settingsGradleKtsFile.toPath()), baseDir, ctx) + .map(sourceFile -> { + if (finalGs == null) { + return sourceFile; + } + return sourceFile.withMarkers(sourceFile.getMarkers().add(finalGs)); + })); + gradleFileCount++; + } + alreadyParsed.add(settingsGradleKtsFile.toPath()); } } @@ -919,14 +951,14 @@ private SourceFileStream parseGradleFiles( protected SourceFileStream parseNonProjectResources(Project subproject, Set alreadyParsed, ExecutionContext ctx, List projectProvenance, Stream sourceFiles) { //Collect any additional yaml/properties/xml files that are NOT already in a source set. OmniParser omniParser = omniParser(alreadyParsed); - List accepted = omniParser.acceptedPaths(subproject.getProjectDir().toPath()); - return SourceFileStream.build("", s -> { - }).concat(omniParser.parse(accepted, subproject.getProjectDir().toPath(), ctx), accepted.size()); + List accepted = omniParser.acceptedPaths(baseDir, subproject.getProjectDir().toPath()); + return SourceFileStream.build("", s -> {}) + .concat(omniParser.parse(accepted, baseDir, ctx), accepted.size()); } private OmniParser omniParser(Set alreadyParsed) { return OmniParser.builder( - OmniParser.RESOURCE_PARSERS, + OmniParser.defaultResourceParsers(), PlainTextParser.builder() .plainTextMasks(baseDir, extension.getPlainTextMasks()) .build(), @@ -949,10 +981,10 @@ private static Collection mergeExclusions(Project project, Path baseDir, private Collection pathMatchers(Path basePath, Collection pathExpressions) { return pathExpressions.stream() .map(o -> basePath.getFileSystem().getPathMatcher("glob:" + o)) - .collect(Collectors.toList()); + .collect(toList()); } - private SourceFileStream parseMultiplatformKotlinProject(Project subproject, Collection exclusions, Set alreadyParsed, List projectProvenance, ExecutionContext ctx) { + private SourceFileStream parseMultiplatformKotlinProject(Project subproject, Collection exclusions, Set alreadyParsed, ExecutionContext ctx) { Object kotlinExtension = subproject.getExtensions().getByName("kotlin"); NamedDomainObjectContainer sourceSets; try { @@ -1044,10 +1076,9 @@ private SourceFileStream parseMultiplatformKotlinProject(Project subproject, Col }).filter(Objects::nonNull); JavaSourceSet sourceSetProvenance = JavaSourceSet.build(sourceSetName, dependencyPaths, javaTypeCache, false); - sourceFileStream = sourceFileStream.concat(cus.map(addProvenance(projectProvenance, sourceSetProvenance)), kotlinPaths.size()); + sourceFileStream = sourceFileStream.concat(cus.map(addProvenance(sourceSetProvenance)), kotlinPaths.size()); logger.info("Scanned {} Kotlin sources in {}/{}", kotlinPaths.size(), subproject.getPath(), kotlinDirectorySet.getName()); } - return sourceFileStream; } catch (Exception e) { logger.warn("Failed to resolve sourceSet from {}:{}. Some type information may be incomplete", subproject.getPath(), sourceSetName); @@ -1073,6 +1104,11 @@ private boolean isExcluded(Collection exclusions, Path path) { return true; } } + // PathMather will not evaluate the path "build.gradle" to be matched by the pattern "**/build.gradle" + // This is counter-intuitive for most users and would otherwise require separate exclusions for files at the root and files in subdirectories + if(!path.isAbsolute() && !path.startsWith(File.separator)) { + return isExcluded(exclusions, Paths.get("/" + path)); + } return false; } @@ -1150,19 +1186,25 @@ private UnaryOperator applyAutodetectedStyle(Map UnaryOperator addProvenance(List projectProvenance, @Nullable Marker sourceSet) { + private UnaryOperator addProvenance(List projectProvenance) { return s -> { Markers m = s.getMarkers(); for (Marker marker : projectProvenance) { - m = m.add(marker); - } - if (sourceSet != null) { - m = m.add(sourceSet); + m = m.addIfAbsent(marker); } return s.withMarkers(m); }; } + private UnaryOperator addProvenance(Marker sourceSet) { + return s -> { + Markers m = s.getMarkers(); + m = m.addIfAbsent(sourceSet); + return s.withMarkers(m); + }; + } + + protected void logRecipesThatMadeChanges(Result result) { String indent = " "; String prefix = " "; diff --git a/plugin/src/main/kotlin/org/openrewrite/gradle/GradleProjectSpec.kt b/plugin/src/main/kotlin/org/openrewrite/gradle/GradleProjectSpec.kt index a0acd9647..92620ec2f 100644 --- a/plugin/src/main/kotlin/org/openrewrite/gradle/GradleProjectSpec.kt +++ b/plugin/src/main/kotlin/org/openrewrite/gradle/GradleProjectSpec.kt @@ -22,10 +22,10 @@ import java.io.File * Utility to help with writing gradle projects to disk to assist with plugin testing */ class GradleProjectSpec( - val dir: File + private val dir: File ) { - val subprojects: MutableList = mutableListOf() - val sourceSets: MutableList = mutableListOf() + private val subprojects: MutableList = mutableListOf() + private val sourceSets: MutableList = mutableListOf() @Language("groovy") var groovyBuildScript: String? = null @@ -37,7 +37,7 @@ class GradleProjectSpec( @Language("groovy") var settingsGradle: String? = null fun settingsGradle(@Language("groovy") text: String) { - settingsGradle = text.trimIndent(); + settingsGradle = text.trimIndent() } @Language("yaml") @@ -52,12 +52,12 @@ class GradleProjectSpec( checkstyleXml = text.trimIndent() } - val propertiesFiles: MutableMap = mutableMapOf() + private val propertiesFiles: MutableMap = mutableMapOf() fun propertiesFile(name: String, @Language("properties") text: String) { propertiesFiles[name] = text } - val textFiles: MutableMap = mutableMapOf() + private val textFiles: MutableMap = mutableMapOf() fun textFile(name: String, text: String) { textFiles[name] = text } @@ -77,82 +77,82 @@ class GradleProjectSpec( fun build(): GradleProjectSpec { dir.mkdirs() - if(settingsGradle != null) { - File(dir, "settings.gradle").writeText(settingsGradle!!) + val settings = File(dir, "settings.gradle") + if(settingsGradle == null) { + val settingsText = "rootProject.name = \"${dir.name}\"\n" + if (subprojects.isEmpty()) { + settings.writeText("rootProject.name = \"${dir.name}\"\n") + } else { + val subprojectsDeclarations = subprojects.joinToString("\n") { subproject -> "include('${subproject.dir.name}')" } + settings.writeText(settingsText + subprojectsDeclarations) + } + } else { + settings.writeText(settingsGradle!!) } - if(groovyBuildScript != null) { + if (groovyBuildScript != null) { File(dir, "build.gradle").writeText(groovyBuildScript!!) } - if(rewriteYaml != null) { + if (rewriteYaml != null) { File(dir, "rewrite.yml").writeText(rewriteYaml!!) } - if(checkstyleXml != null) { - File(dir, "config/checkstyle/checkstyle.xml").apply { + if (checkstyleXml != null) { + File(dir, "config/checkstyle/checkstyle.xml").apply{ parentFile.mkdirs() writeText(checkstyleXml!!) } } - for(props in propertiesFiles.entries) { - File(dir, props.key).apply { + for (props in propertiesFiles.entries) { + File(dir, props.key).apply{ parentFile.mkdirs() writeText(props.value) } } - for(text in textFiles.entries) { - File(dir, text.key).apply { + for (text in textFiles.entries) { + File(dir, text.key).apply{ parentFile.mkdirs() writeText(text.value) } } - for(sourceSet in sourceSets) { + for (sourceSet in sourceSets) { sourceSet.build(File(dir, "src")) } - - val settings = File(dir, "settings.gradle") - val settingsText = "rootProject.name = \"${dir.name}\"\n" - if(subprojects.isEmpty()) { - settings.writeText("rootProject.name = \"${dir.name}\"\n") - } else { - val subprojectsDeclarations = subprojects.joinToString("\n") { subproject -> "include('${subproject.dir.name}')" } - settings.writeText(settingsText + subprojectsDeclarations) - for(subproject in subprojects) { - subproject.build() - } + for (subproject in subprojects) { + subproject.build() } return this } } class GradleSourceSetSpec( - val name: String + private val name: String ) { - val javaSources: MutableList = mutableListOf() + private val javaSources: MutableList = mutableListOf() fun java(@Language("java") source: String) { javaSources.add(source.trimIndent()) } - val kotlinSources: MutableList = mutableListOf() + private val kotlinSources: MutableList = mutableListOf() fun kotlin(@Language("kotlin") source: String) { kotlinSources.add(source.trimIndent()) } - val propertiesFiles: MutableMap = mutableMapOf() + private val propertiesFiles: MutableMap = mutableMapOf() fun propertiesFile(name: String, @Language("properties") text: String) { propertiesFiles[name] = text } - val yamlFiles: MutableMap = mutableMapOf() + private val yamlFiles: MutableMap = mutableMapOf() fun yamlFile(name: String, @Language("yaml") text: String) { yamlFiles[name] = text } - val groovyClasses: MutableList = mutableListOf() + private val groovyClasses: MutableList = mutableListOf() fun groovyClass(@Language("groovy") source: String) { groovyClasses.add(source.trimIndent()) } @@ -161,8 +161,8 @@ class GradleSourceSetSpec( @Suppress("RegExpSimplifiable") fun build(dir: File): GradleSourceSetSpec { dir.mkdirs() - for(javaSource in javaSources) { - val peckage = if(javaSource.startsWith("package")) { + for (javaSource in javaSources) { + val packageDecl = if (javaSource.startsWith("package")) { "package\\s+([a-zA-Z0-9.]+);".toRegex(RegexOption.MULTILINE) .find(javaSource)!! .groupValues[1] @@ -170,18 +170,18 @@ class GradleSourceSetSpec( "" }.replace(".", "/") val clazz = ".*(class|interface|enum)\\s+([a-zA-Z0-9-_]+)".toRegex(RegexOption.MULTILINE).find(javaSource)!!.groupValues[2] - val path = if(peckage.isEmpty()) { + val path = if (packageDecl.isEmpty()) { "$name/java/$clazz.java" } else { - "$name/java/$peckage/$clazz.java" + "$name/java/$packageDecl/$clazz.java" } - File(dir, path).apply { + File(dir, path).apply{ parentFile.mkdirs() writeText(javaSource) } } - for(kotlinSource in kotlinSources) { - val peckage = if(kotlinSource.startsWith("package")) { + for (kotlinSource in kotlinSources) { + val packageDecl = if (kotlinSource.startsWith("package")) { "package\\s+([a-zA-Z0-9.]+)".toRegex(RegexOption.MULTILINE) .find(kotlinSource)!! .groupValues[1] @@ -189,18 +189,18 @@ class GradleSourceSetSpec( "" }.replace(".", "/") val clazz = ".*(class|interface|enum)\\s+([a-zA-Z0-9-_]+)".toRegex(RegexOption.MULTILINE).find(kotlinSource)!!.groupValues[2] - val path = if(peckage.isEmpty()) { + val path = if (packageDecl.isEmpty()) { "$name/kotlin/$clazz.kt" } else { - "$name/kotlin/$peckage/$clazz.kt" + "$name/kotlin/$packageDecl/$clazz.kt" } - File(dir, path).apply { + File(dir, path).apply{ parentFile.mkdirs() writeText(kotlinSource) } } - for(groovySource in groovyClasses) { - val peckage = if(groovySource.startsWith("package")) { + for (groovySource in groovyClasses) { + val packageDecl = if (groovySource.startsWith("package")) { "package\\s+([a-zA-Z0-9.]+);?".toRegex(RegexOption.MULTILINE) .find(groovySource)!! .groupValues[1] @@ -208,27 +208,27 @@ class GradleSourceSetSpec( "" }.replace(".", "/") val clazz = ".*(class|interface|enum)\\s+([a-zA-Z0-9-_]+)".toRegex(RegexOption.MULTILINE).find(groovySource)!!.groupValues[2] - val path = if(peckage.isEmpty()) { + val path = if (packageDecl.isEmpty()) { "$name/groovy/$clazz.groovy" } else { - "$name/groovy/$peckage/$clazz.groovy" + "$name/groovy/$packageDecl/$clazz.groovy" } - File(dir, path).apply { + File(dir, path).apply{ parentFile.mkdirs() writeText(groovySource) } } - if(propertiesFiles.isNotEmpty()) { - for(props in propertiesFiles.entries) { - File(dir, "$name/resources/${props.key}").apply { + if (propertiesFiles.isNotEmpty()) { + for (props in propertiesFiles.entries) { + File(dir, "$name/resources/${props.key}").apply{ parentFile.mkdirs() writeText(props.value) } } } - if(yamlFiles.isNotEmpty()) { - for(yaml in yamlFiles.entries) { - File(dir, "$name/resources/${yaml.key}").apply { + if (yamlFiles.isNotEmpty()) { + for (yaml in yamlFiles.entries) { + File(dir, "$name/resources/${yaml.key}").apply{ parentFile.mkdirs() writeText(yaml.value) } diff --git a/plugin/src/test/kotlin/org/openrewrite/gradle/RewriteDiscoverTest.kt b/plugin/src/test/kotlin/org/openrewrite/gradle/RewriteDiscoverTest.kt index 2d7ba3e57..b1a28b9fe 100644 --- a/plugin/src/test/kotlin/org/openrewrite/gradle/RewriteDiscoverTest.kt +++ b/plugin/src/test/kotlin/org/openrewrite/gradle/RewriteDiscoverTest.kt @@ -29,7 +29,7 @@ class RewriteDiscoverTest : RewritePluginTest { fun `rewriteDiscover prints recipes from external dependencies`( @TempDir projectDir: File ) { - gradleProject(projectDir) { + gradleProject(projectDir) { buildGradle(""" plugins { id("java") diff --git a/plugin/src/test/kotlin/org/openrewrite/gradle/RewriteDryRunTest.kt b/plugin/src/test/kotlin/org/openrewrite/gradle/RewriteDryRunTest.kt index 6bea64338..c948c3427 100644 --- a/plugin/src/test/kotlin/org/openrewrite/gradle/RewriteDryRunTest.kt +++ b/plugin/src/test/kotlin/org/openrewrite/gradle/RewriteDryRunTest.kt @@ -36,7 +36,7 @@ class RewriteDryRunTest : RewritePluginTest { }public static void main(String[] args) { sayGoodbye(); } } """.trimIndent() - gradleProject(projectDir) { + gradleProject(projectDir) { rewriteYaml(""" type: specs.openrewrite.org/v1beta/recipe name: org.openrewrite.gradle.SayHello @@ -66,7 +66,7 @@ class RewriteDryRunTest : RewritePluginTest { activeRecipe("org.openrewrite.gradle.SayHello", "org.openrewrite.java.format.AutoFormat") } """) - sourceSet("main") { + sourceSet("main") { java(helloWorld) } } @@ -83,7 +83,7 @@ class RewriteDryRunTest : RewritePluginTest { fun `A recipe with optional configuration can be activated directly`( @TempDir projectDir: File ) { - gradleProject(projectDir) { + gradleProject(projectDir) { buildGradle(""" plugins { id("java") @@ -98,7 +98,7 @@ class RewriteDryRunTest : RewritePluginTest { } } """) - sourceSet("main") { + sourceSet("main") { java(""" package org.openrewrite.before; @@ -128,7 +128,7 @@ class RewriteDryRunTest : RewritePluginTest { @DisabledIf("lessThanGradle6_1") @Test fun testMultiplatform(@TempDir projectDir: File) { - gradleProject(projectDir) { + gradleProject(projectDir) { buildGradle(""" plugins { id("java") @@ -178,7 +178,7 @@ class RewriteDryRunTest : RewritePluginTest { } rootProject.name = "example" """) - sourceSet("commonMain") { + sourceSet("commonMain") { kotlin(""" class HelloWorld { fun sayHello() { diff --git a/plugin/src/test/kotlin/org/openrewrite/gradle/RewritePluginTest.kt b/plugin/src/test/kotlin/org/openrewrite/gradle/RewritePluginTest.kt index 06ec0fee1..79fbfb88e 100644 --- a/plugin/src/test/kotlin/org/openrewrite/gradle/RewritePluginTest.kt +++ b/plugin/src/test/kotlin/org/openrewrite/gradle/RewritePluginTest.kt @@ -29,7 +29,7 @@ interface RewritePluginTest { GradleRunner.create() .withDebug(ManagementFactory.getRuntimeMXBean().inputArguments.toString().indexOf("-agentlib:jdwp") > 0) .withProjectDir(testDir) - .apply { + .apply{ if (gradleVersion != null) { withGradleVersion(gradleVersion) } @@ -43,4 +43,9 @@ interface RewritePluginTest { val currentVersion = if (gradleVersion == null) GradleVersion.current() else GradleVersion.version(gradleVersion) return currentVersion < GradleVersion.version("6.1") } + + fun lessThanGradle6_8(): Boolean { + val currentVersion = if (gradleVersion == null) GradleVersion.current() else GradleVersion.version(gradleVersion) + return currentVersion < GradleVersion.version("6.8") + } } diff --git a/plugin/src/test/kotlin/org/openrewrite/gradle/RewriteResolveDependenciesTest.kt b/plugin/src/test/kotlin/org/openrewrite/gradle/RewriteResolveDependenciesTest.kt new file mode 100644 index 000000000..d18446ad3 --- /dev/null +++ b/plugin/src/test/kotlin/org/openrewrite/gradle/RewriteResolveDependenciesTest.kt @@ -0,0 +1,55 @@ +/* + * Copyright ${year} the original author or authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.openrewrite.gradle + +import org.assertj.core.api.Assertions.assertThat +import org.gradle.testkit.runner.TaskOutcome +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir +import java.io.File + +class RewriteResolveDependenciesTest : RewritePluginTest { + @Test + fun `Specifying a rewriteVersion does not cause build failures`( + @TempDir projectDir: File + ) { + gradleProject(projectDir) { + buildGradle(""" + plugins { + id("java") + id("org.openrewrite.rewrite") + } + + repositories { + mavenLocal() + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } + } + + rewrite { + rewriteVersion = "8.6.1" + } + """) + } + + val result = runGradle(projectDir, "rewriteResolveDependencies") + val rewriteDryRunResult = result.task(":rewriteResolveDependencies")!! + assertThat(rewriteDryRunResult.outcome).isEqualTo(TaskOutcome.SUCCESS) + } + +} diff --git a/plugin/src/test/kotlin/org/openrewrite/gradle/RewriteRunTest.kt b/plugin/src/test/kotlin/org/openrewrite/gradle/RewriteRunTest.kt index e37de46d8..f56122b18 100644 --- a/plugin/src/test/kotlin/org/openrewrite/gradle/RewriteRunTest.kt +++ b/plugin/src/test/kotlin/org/openrewrite/gradle/RewriteRunTest.kt @@ -21,7 +21,6 @@ package org.openrewrite.gradle import org.assertj.core.api.Assertions.assertThat import org.gradle.testkit.runner.TaskOutcome -import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.junit.jupiter.api.condition.DisabledIf import org.junit.jupiter.api.condition.DisabledOnOs @@ -156,12 +155,12 @@ class RewriteRunTest : RewritePluginTest { } @Test - fun `rewriteRun applies built-in AutoFormat to a multi-project build`( + fun `rewriteRun applies recipe to a multi-project build`( @TempDir projectDir: File ) { val bTestClassExpected = """ package com.foo; - + import org.junit.Test; public class BTestClass { @@ -171,6 +170,15 @@ class RewriteRunTest : RewritePluginTest { } """.trimIndent() gradleProject(projectDir) { + rewriteYaml(""" + type: specs.openrewrite.org/v1beta/recipe + name: org.openrewrite.FormatAndAddProperty + recipeList: + - org.openrewrite.java.format.AutoFormat + - org.openrewrite.properties.ChangePropertyKey: + oldPropertyKey: foo + newPropertyKey: bar + """) buildGradle(""" plugins { id("org.openrewrite.rewrite") @@ -178,7 +186,7 @@ class RewriteRunTest : RewritePluginTest { } rewrite { - activeRecipe("org.openrewrite.java.format.AutoFormat") + activeRecipe("org.openrewrite.FormatAndAddProperty") exclusion("**/BTestClass.java") } @@ -216,6 +224,7 @@ class RewriteRunTest : RewritePluginTest { public void passes() { } } """) + propertiesFile("test.properties", "foo=baz\n") } } subproject("b") { @@ -231,7 +240,7 @@ class RewriteRunTest : RewritePluginTest { //language=java val aTestClassExpected = """ package com.foo; - + import org.junit.Test; public class ATestClass { @@ -245,6 +254,9 @@ class RewriteRunTest : RewritePluginTest { assertThat(aTestClassFile.readText()).isEqualTo(aTestClassExpected) val bTestClassFile = File(projectDir, "b/src/test/java/com/foo/BTestClass.java") assertThat(bTestClassFile.readText()).isEqualTo(bTestClassExpected) + + val propertiesFile = File(projectDir, "a/src/test/resources/test.properties") + assertThat(propertiesFile.readText()).isEqualTo("bar=baz\n") } @Suppress("ClassInitializerMayBeStatic", "StatementWithEmptyBody", "ConstantConditions") @@ -301,7 +313,7 @@ class RewriteRunTest : RewritePluginTest { } } - val result = runGradle(projectDir,"rewriteRun") + val result = runGradle(projectDir, "rewriteRun") val rewriteRunResult = result.task(":rewriteRun")!! assertThat(rewriteRunResult.outcome).isEqualTo(TaskOutcome.SUCCESS) @@ -433,8 +445,8 @@ class RewriteRunTest : RewritePluginTest { rewriteYaml(""" type: specs.openrewrite.org/v1beta/recipe name: org.openrewrite.test.RemoveJacksonCore - displayName: Rename build.gradle to build.gradle.kts - description: Rename build.gradle to build.gradle.kts + displayName: Remove jackson-core + description: Remove jackson-core recipeList: - org.openrewrite.gradle.RemoveDependency: groupId: com.fasterxml.jackson.core @@ -503,6 +515,73 @@ class RewriteRunTest : RewritePluginTest { """.trimIndent()) } + + @DisabledIf("lessThanGradle6_8") + @Test + fun dependencyRepositoriesDeclaredInSettings( + @TempDir projectDir: File + ) { + gradleProject(projectDir) { + rewriteYaml(""" + type: specs.openrewrite.org/v1beta/recipe + name: org.openrewrite.test.UpgradeJacksonCore + displayName: Remove jackson-core + description: Remove jackson-core + recipeList: + - org.openrewrite.gradle.UpgradeDependencyVersion: + groupId: com.fasterxml.jackson.core + artifactId: jackson-core + nevVersion: 2.16.0 + """) + settingsGradle(""" + dependencyResolutionManagement { + repositories { + mavenLocal() + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } + } + } + """) + buildGradle(""" + plugins { + id("java") + id("org.openrewrite.rewrite") + } + + dependencies { + implementation("com.fasterxml.jackson.core:jackson-core:2.15.1") + } + + rewrite { + activeRecipe("org.openrewrite.test.UpgradeJacksonCore") + } + """) + } + + val result = runGradle(projectDir, "rewriteRun") + val rewriteRunResult = result.task(":rewriteRun")!! + assertThat(rewriteRunResult.outcome).isEqualTo(TaskOutcome.SUCCESS) + + assertThat(projectDir.resolve("build.gradle").readText()) + //language=groovy + .isEqualTo(""" + plugins { + id("java") + id("org.openrewrite.rewrite") + } + + dependencies { + implementation("com.fasterxml.jackson.core:jackson-core:2.16.0") + } + + rewrite { + activeRecipe("org.openrewrite.test.UpgradeJacksonCore") + } + """.trimIndent()) + } + @Test fun mergeConfiguredAndAutodetectedStyles(@TempDir projectDir: File) { gradleProject(projectDir) { @@ -823,25 +902,25 @@ class RewriteRunTest : RewritePluginTest { @Test fun `build root and repository root do not need to be the same`(@TempDir repositoryRoot: File) { - repositoryRoot.apply { - resolve(".git").apply { + repositoryRoot.apply{ + resolve(".git").apply{ mkdirs() - resolve("HEAD").apply { + resolve("HEAD").apply{ createNewFile() writeText("ref: refs/heads/main") } - resolve("objects").apply { + resolve("objects").apply{ mkdir() } - resolve("refs").apply { + resolve("refs").apply{ mkdir() } - resolve("reftable").apply { + resolve("reftable").apply{ mkdir() } } } - val buildRoot = repositoryRoot.resolve("test-project").apply { mkdirs() } + val buildRoot = repositoryRoot.resolve("test-project").apply{ mkdirs() } gradleProject(buildRoot) { buildGradle(""" plugins { @@ -900,7 +979,6 @@ class RewriteRunTest : RewritePluginTest { ) } - @Disabled("Applicability tests are no longer supported for YAML recipes") @DisabledOnOs(OS.WINDOWS) // A file handle I haven't been able to track down is left open, causing JUnit to fail to clean up the directory on Windows @Issue("https://github.com/openrewrite/rewrite-gradle-plugin/issues/176") @Test @@ -924,10 +1002,9 @@ class RewriteRunTest : RewritePluginTest { name: com.example.TextToSam displayName: Changes contents of sam.txt description: Change contents of sam.txt to "sam" - applicability: - singleSource: - - org.openrewrite.FindSourceFiles: - filePattern: "**/sam.txt" + preconditions: + - org.openrewrite.FindSourceFiles: + filePattern: "**/sam.txt" recipeList: - org.openrewrite.text.ChangeText: toText: sam @@ -973,6 +1050,85 @@ class RewriteRunTest : RewritePluginTest { .isEqualTo("jonathan") } + @Test + fun overlappingSourceSet(@TempDir buildRoot: File) { + gradleProject(buildRoot) { + rewriteYaml(""" + type: specs.openrewrite.org/v1beta/recipe + name: org.openrewrite.Overlaps + displayName: Find overlaps + description: Find lombok SneakyThrows annotation and duplicate source files + recipeList: + - org.openrewrite.java.search.FindTypes: + fullyQualifiedTypeName: lombok.SneakyThrows + - org.openrewrite.FindDuplicateSourceFiles + """) + buildGradle(""" + plugins { + id("java") + id("org.openrewrite.rewrite") + } + + repositories { + mavenLocal() + mavenCentral() + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots") + } + } + + sourceSets { + create("overlapping") { + java.srcDir("src/test/java") + } + } + + rewrite { + activeRecipe("org.openrewrite.Overlaps") + } + + dependencies { + rewrite("org.openrewrite.recipe:rewrite-all:latest.integration") + testImplementation("org.projectlombok:lombok:latest.release") + } + """) + sourceSet("test") { + java(""" + package com.foo; + + import lombok.SneakyThrows; + + public class ATest { + + @SneakyThrows + public void fail() { + } + } + """) + } + } + + val result = runGradle(buildRoot, "rewriteRun") + val rewriteRunResult = result.task(":rewriteRun")!! + assertThat(rewriteRunResult.outcome).isEqualTo(TaskOutcome.SUCCESS) + val javaFile = buildRoot.resolve("src/test/java/com/foo/ATest.java") + assertThat(javaFile.readText()) + //language=java + .isEqualTo(""" + package com.foo; + + import lombok.SneakyThrows; + + public class ATest { + + @/*~~>*/SneakyThrows + public void fail() { + } + } + """.trimIndent() + ) + } + // TODO: Extract out into RewritePluginTest? Does JUnit support that? @Test @Disabled diff --git a/scripts/spring-petclinic-test.init.gradle b/scripts/spring-petclinic-test.init.gradle index 6e3e02dd1..93d09cc05 100755 --- a/scripts/spring-petclinic-test.init.gradle +++ b/scripts/spring-petclinic-test.init.gradle @@ -1,62 +1,62 @@ -initscript { - repositories { - repositories { +initscript{ + repositories{ + repositories{ mavenLocal() - maven { + maven{ url = uri("https://oss.sonatype.org/content/repositories/snapshots") } mavenCentral() } } - dependencies { + dependencies{ classpath("org.openrewrite:plugin:latest.integration") } } -projectsEvaluated { - rootProject { - apply plugin: org.openrewrite.gradle.RewritePlugin +projectsEvaluated{ + rootProject{ + applyplugin: org.openrewrite.gradle.RewritePlugin - repositories { + repositories{ mavenLocal() - maven { + maven{ url = uri("https://oss.sonatype.org/content/repositories/snapshots") } mavenCentral() } - rewrite { + rewrite{ failOnInvalidActiveRecipes = true activeRecipe("org.openrewrite.java.spring.boot2.SpringBoot1To2Migration") } - dependencies { + dependencies{ rewrite("org.openrewrite.recipe:rewrite-spring:latest.integration") } tasks.create("validateRewrite") { dependsOn(tasks.getByName("rewriteDryRun")) - doLast { + doLast{ File patchFile = file("build/reports/rewrite/rewrite.patch") - if(!patchFile.exists()) { + if (!patchFile.exists()) { throw new RuntimeException("Running rewrite dryRun failed to produce a patch report at " + patchFile.absolutePath); } // Spot-check for some expected changes String patch = patchFile.text - if(!patch.contains('+++ b/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java')) { + if (!patch.contains('+++ b/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java')) { throw new RuntimeException("Expected the recipe to make changes to OwnerController.java"); } - if(!patch.contains('- @RequestMapping(value = "/owners/what")\n+ @GetMapping("/owners/what")')) { + if (!patch.contains('- @RequestMapping(value = "/owners/what")\n+ @GetMapping("/owners/what")')) { throw new RuntimeException("Expected the recipe to change @RequestMapping into @GetMapping") } - if(!patch.contains('- public JCacheManagerCustomizer cacheManagerCustomizer() {\n+ JCacheManagerCustomizer cacheManagerCustomizer() {')) { + if (!patch.contains('- public JCacheManagerCustomizer cacheManagerCustomizer() {\n+ JCacheManagerCustomizer cacheManagerCustomizer() {')) { throw new RuntimeException("Expected the recipe to reduce visibility of bean methods that don't need to be public") } - if(!patch.contains('-@RunWith(MockitoJUnitRunner.class)\n+@ExtendWith(MockitoExtension.class)\n public class PetTypeFormatterTests {')) { + if (!patch.contains('-@RunWith(MockitoJUnitRunner.class)\n+@ExtendWith(MockitoExtension.class)\n public class PetTypeFormatterTests {')) { throw new RuntimeException("Expected the recipe to swap MockitoJUnitRunner for MockitoExtension") } - if(!patch.contains('-spring.datasource.schema=classpath*:db/${database}/schema.sql\n' + + if (!patch.contains('-spring.datasource.schema=classpath*:db/${database}/schema.sql\n' + '-spring.datasource.data=classpath*:db/${database}/data.sql\n' + '+spring.sql.init.schema-locations=classpath*:db/${database}/schema.sql\n' + '+spring.sql.init.data-locations=classpath*:db/${database}/data.sql')) {