From faf353b0794a3f3f88ef126ce64f562764171c0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20Kr=C3=BCger?= Date: Wed, 18 Oct 2023 15:01:40 +0200 Subject: [PATCH] WIP: Example app running Spring Boot Upgrade --- sbm-support-rewrite/pom.xml | 16 ++-- .../project/resource/ProjectResourceSet.java | 34 +++++++ .../sbm/example/BootUpgradeExample.java | 88 +++++++++++++++++++ .../sbm}/example/recipes/DummyRecipe.java | 2 +- .../parsers/RewriteRecipeDiscoveryTest.java | 8 +- .../maven/RewriteMavenProjectParserTest.java | 27 +----- .../sbm/test/util/GitTestHelper.java | 64 ++++++++++++++ 7 files changed, 204 insertions(+), 35 deletions(-) create mode 100644 sbm-support-rewrite/src/test/java/org/springframework/sbm/example/BootUpgradeExample.java rename sbm-support-rewrite/src/test/java/{com => org/springframework/sbm}/example/recipes/DummyRecipe.java (94%) create mode 100644 sbm-support-rewrite/src/test/java/org/springframework/sbm/test/util/GitTestHelper.java diff --git a/sbm-support-rewrite/pom.xml b/sbm-support-rewrite/pom.xml index 4be8876c6..46a7aec14 100644 --- a/sbm-support-rewrite/pom.xml +++ b/sbm-support-rewrite/pom.xml @@ -110,11 +110,6 @@ rewrite-maven ${rewrite.version} - - org.openrewrite - rewrite-java - ${rewrite.version} - org.openrewrite rewrite-java-17 @@ -183,6 +178,17 @@ commons-cli 1.4 + + org.openrewrite.recipe + rewrite-migrate-java + 2.1.0 + + + org.openrewrite.recipe + rewrite-spring + 5.0.10 + + org.openrewrite.maven diff --git a/sbm-support-rewrite/src/main/java/org/springframework/sbm/project/resource/ProjectResourceSet.java b/sbm-support-rewrite/src/main/java/org/springframework/sbm/project/resource/ProjectResourceSet.java index bc5bc5a2d..a7c8917fa 100644 --- a/sbm-support-rewrite/src/main/java/org/springframework/sbm/project/resource/ProjectResourceSet.java +++ b/sbm-support-rewrite/src/main/java/org/springframework/sbm/project/resource/ProjectResourceSet.java @@ -19,6 +19,7 @@ import org.openrewrite.Recipe; import org.openrewrite.Result; import org.openrewrite.SourceFile; +import org.openrewrite.config.RecipeDescriptor; import org.openrewrite.internal.InMemoryLargeSourceSet; import java.nio.file.Path; @@ -26,6 +27,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static java.util.stream.Collectors.joining; + // TODO: make package private public class ProjectResourceSet { @@ -103,9 +106,40 @@ public List getRecipeList() { .getChangeset() .getAllResults(); + results.forEach(r -> logRecipesThatMadeChanges(r)); + migrationResultMerger.mergeResults(this, results); } + protected void logRecipesThatMadeChanges(Result result) { + String indent = " "; + String prefix = " "; + for (RecipeDescriptor recipeDescriptor : result.getRecipeDescriptorsThatMadeChanges()) { + logRecipe(recipeDescriptor, prefix); + prefix = prefix + indent; + } + } + + private void logRecipe(RecipeDescriptor rd, String prefix) { + StringBuilder recipeString = new StringBuilder(prefix + rd.getName()); + if (!rd.getOptions().isEmpty()) { + String opts = rd.getOptions().stream().map(option -> { + if (option.getValue() != null) { + return option.getName() + "=" + option.getValue(); + } + return null; + } + ).filter(Objects::nonNull).collect(joining(", ")); + if (!opts.isEmpty()) { + recipeString.append(": {").append(opts).append("}"); + } + } + System.out.println(recipeString); + for (RecipeDescriptor rchild : rd.getRecipeList()) { + logRecipe(rchild, prefix + " "); + } + } + void clearDeletedResources() { Iterator> iterator = this.projectResources.iterator(); while (iterator.hasNext()) { diff --git a/sbm-support-rewrite/src/test/java/org/springframework/sbm/example/BootUpgradeExample.java b/sbm-support-rewrite/src/test/java/org/springframework/sbm/example/BootUpgradeExample.java new file mode 100644 index 000000000..eae9d02e1 --- /dev/null +++ b/sbm-support-rewrite/src/test/java/org/springframework/sbm/example/BootUpgradeExample.java @@ -0,0 +1,88 @@ +/* + * Copyright 2021 - 2023 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.springframework.sbm.example; + +import org.apache.commons.io.FileUtils; +import org.openrewrite.Recipe; +import org.openrewrite.SourceFile; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.sbm.parsers.ProjectScanner; +import org.springframework.sbm.parsers.RewriteProjectParser; +import org.springframework.sbm.parsers.RewriteProjectParsingResult; +import org.springframework.sbm.project.resource.ProjectResourceSet; +import org.springframework.sbm.project.resource.ProjectResourceSetFactory; +import org.springframework.sbm.project.resource.ProjectResourceSetSerializer; +import org.springframework.sbm.recipes.RewriteRecipeDiscovery; +import org.springframework.sbm.test.util.GitTestHelper; + +import java.nio.file.Path; +import java.util.List; + +import static java.util.stream.Collectors.joining; + +@SpringBootApplication +public class BootUpgradeExample implements CommandLineRunner { + + public static void main(String[] args) { + SpringApplication.run(BootUpgradeExample.class, args); + } + + @Autowired + ProjectScanner scanner; + @Autowired + RewriteProjectParser parser; + @Autowired + RewriteRecipeDiscovery discovery; + @Autowired + ProjectResourceSetSerializer serializer; + @Autowired + ProjectResourceSetFactory factory; + + @Override + public void run(String... args) throws Exception { + + // Clone Spring PetClinic using Boot 2.7 + String tmpDir = System.getProperty("java.io.tmpdir"); + String target = tmpDir + "/cloned"; + FileUtils.forceDelete(Path.of(target).toFile()); + Path baseDir = GitTestHelper.cloneProjectCommit("https://github.com/spring-projects/spring-petclinic.git", target, "9ecdc1111e3da388a750ace41a125287d9620534"); + + // parse + RewriteProjectParsingResult parsingResult = parser.parse(baseDir); + List sourceFiles = parsingResult.sourceFiles(); + + // create ProjectResourceSet + ProjectResourceSet projectResourceSet = factory.create(baseDir, sourceFiles); + + // find recipe + String recipeName = "org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_1"; + List recipes = discovery.discoverRecipes(); + System.out.println("Discovered %d recipes.".formatted(recipes.size())); + Recipe recipe = recipes.stream().filter(r -> r.getName().equals(recipeName)).findFirst().get(); + + // apply recipe + System.out.println("Apply recipe '%s'".formatted(recipe.getName())); + projectResourceSet.apply(recipe); + + // write changes to fs + serializer.writeChanges(projectResourceSet); + } + +} \ No newline at end of file diff --git a/sbm-support-rewrite/src/test/java/com/example/recipes/DummyRecipe.java b/sbm-support-rewrite/src/test/java/org/springframework/sbm/example/recipes/DummyRecipe.java similarity index 94% rename from sbm-support-rewrite/src/test/java/com/example/recipes/DummyRecipe.java rename to sbm-support-rewrite/src/test/java/org/springframework/sbm/example/recipes/DummyRecipe.java index 17d61aae3..4c89d2559 100644 --- a/sbm-support-rewrite/src/test/java/com/example/recipes/DummyRecipe.java +++ b/sbm-support-rewrite/src/test/java/org/springframework/sbm/example/recipes/DummyRecipe.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.example.recipes; +package org.springframework.sbm.example.recipes; import org.openrewrite.Recipe; diff --git a/sbm-support-rewrite/src/test/java/org/springframework/sbm/parsers/RewriteRecipeDiscoveryTest.java b/sbm-support-rewrite/src/test/java/org/springframework/sbm/parsers/RewriteRecipeDiscoveryTest.java index 7662eeeed..fd6c64bc1 100644 --- a/sbm-support-rewrite/src/test/java/org/springframework/sbm/parsers/RewriteRecipeDiscoveryTest.java +++ b/sbm-support-rewrite/src/test/java/org/springframework/sbm/parsers/RewriteRecipeDiscoveryTest.java @@ -15,7 +15,7 @@ */ package org.springframework.sbm.parsers; -import com.example.recipes.DummyRecipe; +import org.springframework.sbm.example.recipes.DummyRecipe; import io.example.recipes.AnotherDummyRecipe; import org.assertj.core.data.Index; import org.jetbrains.annotations.NotNull; @@ -79,17 +79,17 @@ void shouldDiscoverDummyRecipes() { void providingAcceptedPackagesShouldOnlyShowRecipesWithMatchingPackage() { ClasspathScanningLoader resourceLoader1 = new ClasspathScanningLoader(new Properties(), new String[]{"com.example"}); Collection recipes = resourceLoader1.listRecipes(); - assertThat(recipes).anyMatch(r -> com.example.recipes.DummyRecipe.class == r.getClass()); + assertThat(recipes).anyMatch(r -> DummyRecipe.class == r.getClass()); assertThat(recipes).noneMatch(r -> io.example.recipes.AnotherDummyRecipe.class == r.getClass()); ClasspathScanningLoader resourceLoader2 = new ClasspathScanningLoader(new Properties(), new String[]{"io.example"}); Collection recipes2 = resourceLoader2.listRecipes(); - assertThat(recipes2).noneMatch(r -> com.example.recipes.DummyRecipe.class == r.getClass()); + assertThat(recipes2).noneMatch(r -> DummyRecipe.class == r.getClass()); assertThat(recipes2).anyMatch(r -> io.example.recipes.AnotherDummyRecipe.class == r.getClass()); ClasspathScanningLoader resourceLoader3 = new ClasspathScanningLoader(new Properties(), new String[]{"io.example", "com.example"}); Collection recipes3 = resourceLoader3.listRecipes(); - assertThat(recipes3).anyMatch(r -> com.example.recipes.DummyRecipe.class == r.getClass()); + assertThat(recipes3).anyMatch(r -> DummyRecipe.class == r.getClass()); assertThat(recipes3).anyMatch(r -> io.example.recipes.AnotherDummyRecipe.class == r.getClass()); } diff --git a/sbm-support-rewrite/src/test/java/org/springframework/sbm/parsers/maven/RewriteMavenProjectParserTest.java b/sbm-support-rewrite/src/test/java/org/springframework/sbm/parsers/maven/RewriteMavenProjectParserTest.java index 325b683f9..2a2bcaaed 100644 --- a/sbm-support-rewrite/src/test/java/org/springframework/sbm/parsers/maven/RewriteMavenProjectParserTest.java +++ b/sbm-support-rewrite/src/test/java/org/springframework/sbm/parsers/maven/RewriteMavenProjectParserTest.java @@ -16,7 +16,6 @@ package org.springframework.sbm.parsers.maven; import org.intellij.lang.annotations.Language; -import org.jetbrains.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; @@ -40,8 +39,6 @@ import org.openrewrite.maven.MavenExecutionContextView; import org.openrewrite.maven.MavenSettings; import org.openrewrite.maven.cache.*; -import org.openrewrite.maven.cache.LocalMavenArtifactCache; -import org.openrewrite.maven.cache.MavenArtifactCache; import org.openrewrite.maven.tree.MavenResolutionResult; import org.openrewrite.shaded.jgit.api.Git; import org.openrewrite.shaded.jgit.api.errors.GitAPIException; @@ -58,13 +55,12 @@ import org.springframework.sbm.parsers.events.RewriteParsingEventListenerAdapter; import org.springframework.sbm.scopes.ScanScope; import org.springframework.sbm.test.util.DummyResource; +import org.springframework.sbm.test.util.GitTestHelper; import org.springframework.sbm.utils.ResourceUtil; import org.springframework.test.util.ReflectionTestUtils; -import java.io.File; import java.nio.charset.Charset; import java.nio.file.Path; -import java.nio.file.Paths; import java.time.Duration; import java.time.Instant; import java.time.ZoneId; @@ -302,7 +298,7 @@ void parseMultiModule1_WithCustomParser() { @Disabled("https://github.com/openrewrite/rewrite/issues/3409") void parseComplexMavenReactorProject() { String target = "./testcode/maven-projects/cwa-server"; - cloneProject("https://github.com/corona-warn-app/cwa-server.git", target, "v3.2.0"); + GitTestHelper.cloneProjectTag("https://github.com/corona-warn-app/cwa-server.git", target, "v3.2.0"); Path projectRoot = Path.of(target).toAbsolutePath().normalize(); // SBM root RewriteMavenProjectParser projectParser = sut; ExecutionContext executionContext = new InMemoryExecutionContext(t -> t.printStackTrace()); @@ -434,25 +430,6 @@ private static void verifyExecutionContext(RewriteProjectParsingResult parsingRe assertThat(MavenExecutionContextView.view(resultingExecutionContext).getRepositories()).isEmpty(); } - private void cloneProject(String url, String target, String tag) { - File directory = Path.of(target).toFile(); - if (directory.exists()) { - return; - } - try { - Git git = Git.cloneRepository() - .setDirectory(directory) - .setURI(url) - .call(); - - git.checkout() - .setName("refs/tags/" + tag) - .call(); - - } catch (GitAPIException e) { - throw new RuntimeException(e); - } - } private void verifyMavenParser(RewriteProjectParsingResult parsingResult) { verify(parsingResult.sourceFiles().get(0), Xml.Document.class, "pom.xml", document -> { diff --git a/sbm-support-rewrite/src/test/java/org/springframework/sbm/test/util/GitTestHelper.java b/sbm-support-rewrite/src/test/java/org/springframework/sbm/test/util/GitTestHelper.java new file mode 100644 index 000000000..31744071f --- /dev/null +++ b/sbm-support-rewrite/src/test/java/org/springframework/sbm/test/util/GitTestHelper.java @@ -0,0 +1,64 @@ +/* + * Copyright 2021 - 2023 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.springframework.sbm.test.util; + +import org.apache.commons.io.FileUtils; +import org.jetbrains.annotations.NotNull; +import org.openrewrite.shaded.jgit.api.Git; +import org.openrewrite.shaded.jgit.api.errors.GitAPIException; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; + +/** + * @author Fabian Krüger + */ +public class GitTestHelper { + + public static Path cloneProjectCommit(String url, String target, String commitHash) { + return checkout(url, target, commitHash); + } + + public static Path cloneProjectTag(String url, String target, String tag) { + String ref = "refs/tags/" + tag; + return checkout(url, target, ref); + } + + @NotNull + private static Path checkout(String url, String target, String ref) { + try { + File directory = Path.of(target).toFile(); + if (!directory.exists()) { + FileUtils.forceMkdir(directory.toPath().toAbsolutePath().normalize().toFile()); + } + Git git = Git.cloneRepository() + .setDirectory(directory) + .setURI(url) + .call(); + + git.checkout() + .setName(ref) + .call(); + + return git.getRepository().getDirectory().toPath().getParent(); + } catch (GitAPIException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +}