Skip to content

Commit

Permalink
feat: Generated comment recipes for deprecated properties without rep…
Browse files Browse the repository at this point in the history
…lacement

Refs: openrewrite#634
  • Loading branch information
Andrei Shakirin authored and timtebeek committed Nov 26, 2024
1 parent c8c4b41 commit 4ca06a9
Showing 1 changed file with 107 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.github.classgraph.ClassGraph;
import io.github.classgraph.ResourceList;
import io.github.classgraph.ScanResult;
import org.jetbrains.annotations.NotNull;
import org.jspecify.annotations.Nullable;
import org.openrewrite.maven.tree.Version;

Expand All @@ -27,6 +29,7 @@
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.LocalDate;
Expand All @@ -35,16 +38,13 @@

import static java.util.Collections.emptySet;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toSet;
import static java.util.stream.Collectors.*;

class GeneratePropertiesMigratorConfiguration {
private static final ObjectMapper objectMapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

public static void main(String[] args) throws IOException {
var springBootReleases = new SpringBootReleases(true); // `true` for release candidates

var objectMapper = new ObjectMapper()
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

var releasesDir = new File(".boot-releases");
//noinspection ResultOfMethodCallIgnored
releasesDir.mkdirs();
Expand Down Expand Up @@ -78,69 +78,116 @@ public static void main(String[] args) throws IOException {
}

System.out.println("Scanning version " + version);

try (ScanResult scanResult = new ClassGraph()
.overrideClasspath(Arrays.stream(requireNonNull(versionDir.listFiles())).map(File::toURI).collect(Collectors.toList()))
.acceptPaths("META-INF")
.enableMemoryMapping()
.scan()) {
var replacements = scanResult.getResourcesMatchingWildcard("**/*spring-configuration-metadata.json").stream()
.flatMap(res -> {
try (InputStream inputStream = res.open()) {
var metadata = objectMapper.readValue(inputStream, SpringConfigurationMetadata.class);
return metadata.properties().stream()
.filter(p -> p.deprecation() != null && p.deprecation().replacement() != null);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
})
.filter(p -> alreadyDefined.add(p.name()))
.sorted(Comparator.comparing(SpringConfigurationMetadata.ConfigurationProperty::name))
.toList();

if (!replacements.isEmpty()) {
var majorMinor = version.split("\\.");
if (semanticVersion.compareTo(new Version("3.1")) < 0) {
// Don't override manual fixes to the unsupported 2.x and 3.0 versions anymore
continue;
}

var config = Paths.get("src/main/resources/META-INF/rewrite/spring-boot-%s%s-properties.yml".formatted(majorMinor[0], majorMinor[1]));
Files.writeString(config, "#\n" +
Files.readAllLines(Paths.get("gradle/licenseHeader.txt"))
.stream()
.map(str -> str.replaceAll("^", "# "))
.map(str -> str.replace("${year}", LocalDate.now().getYear() + ""))
.collect(Collectors.joining("\n")) + "\n#\n");

Files.writeString(config, """
# This file is automatically generated by the GeneratePropertiesMigratorConfiguration class.
# Do not edit this file manually. Update the Spring Boot property metadata upstream instead.
---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.spring.boot%1$s.SpringBootProperties_%1$s_%2$s
displayName: Migrate Spring Boot properties to %1$s.%2$s
description: Migrate properties found in `application.properties` and `application.yml`.
tags:
- spring
- boot
recipeList:""".formatted(majorMinor[0], majorMinor[1]),
StandardOpenOption.APPEND);

Files.writeString(config, replacements.stream()
.map(r -> """
- org.openrewrite.java.spring.ChangeSpringPropertyKey:
oldPropertyKey: %s
newPropertyKey: %s
""".formatted(
r.name(), requireNonNull(r.deprecation()).replacement())
)
.collect(joining("", "\n", "\n")),
StandardOpenOption.APPEND);
List<SpringConfigurationMetadata.ConfigurationProperty> deprecations = getDeprecations(scanResult, alreadyDefined);
if (deprecations.isEmpty()) {
continue;
}
var majorMinor = version.split("\\.");
if (semanticVersion.compareTo(new Version("3.1")) < 0) {
// Don't override manual fixes to the unsupported 2.x and 3.0 versions anymore
continue;
}
var recipePath = Paths.get("src/main/resources/META-INF/rewrite/spring-boot-%s%s-properties.yml".formatted(majorMinor[0], majorMinor[1]));
writeFileHeader(majorMinor, recipePath);
generateReplacementRecipes(deprecations, recipePath);
generateCommentRecipesForDeprecations(deprecations, recipePath);
}
}
}

private static @NotNull List<SpringConfigurationMetadata.ConfigurationProperty> getDeprecations(ScanResult scanResult, HashSet<Object> alreadyDefined) {
ResourceList resources = scanResult.getResourcesMatchingWildcard("**/*spring-configuration-metadata.json");
return resources.stream()
.flatMap(res -> {
try (InputStream inputStream = res.open()) {
var metadata = objectMapper.readValue(inputStream, SpringConfigurationMetadata.class);
return metadata.properties().stream()
.filter(p -> p.deprecation() != null)
.filter(p -> alreadyDefined.add(getPropertyKey(p)));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
})
.toList();
}

private static void generateReplacementRecipes(List<SpringConfigurationMetadata.ConfigurationProperty> properties, Path recipePath) throws IOException {
var replacements = properties.stream()
.filter(d -> (d.deprecation() != null) && d.deprecation().replacement() != null)
.sorted(Comparator.comparing(SpringConfigurationMetadata.ConfigurationProperty::name))
.toList();

if (!replacements.isEmpty()) {
Files.writeString(recipePath, replacements.stream()
.map(r -> """
- org.openrewrite.java.spring.ChangeSpringPropertyKey:
oldPropertyKey: %s
newPropertyKey: %s
""".formatted(
r.name(), requireNonNull(r.deprecation()).replacement())
)
.collect(joining("", "\n", "\n")),
StandardOpenOption.APPEND);
}
}

private static void generateCommentRecipesForDeprecations(List<SpringConfigurationMetadata.ConfigurationProperty> properties, Path recipePath) throws IOException {
var deprecationsWithoutReplacement = properties.stream()
.filter(d -> (d.deprecation() != null) && d.deprecation().replacement() == null)
.sorted(Comparator.comparing(SpringConfigurationMetadata.ConfigurationProperty::name))
.toList();

if (!deprecationsWithoutReplacement.isEmpty()) {
Files.writeString(recipePath, """
- org.openrewrite.java.spring.InlineCommentSpringProperties:
comment: "This property is deprecated and will be removed in future Spring Boot versions"
propertyKeys:
""",
StandardOpenOption.APPEND);

Files.writeString(recipePath, deprecationsWithoutReplacement.stream()
.map(r -> """
- %s
""".formatted(
r.name())
)
.collect(joining("", "", "\n")),
StandardOpenOption.APPEND);
}
}

private static String getPropertyKey(SpringConfigurationMetadata.ConfigurationProperty property) {
String replacementSuffix = (property.deprecation() != null) && (property.deprecation().replacement() != null)? "->" + property.deprecation().replacement() : "";
return property.name() + replacementSuffix;
}

private static void writeFileHeader(String[] majorMinor, Path recipePath) throws IOException {
Files.writeString(recipePath, "#\n" +
Files.readAllLines(Paths.get("gradle/licenseHeader.txt"))
.stream()
.map(str -> str.replaceAll("^", "# "))
.map(str -> str.replace("${year}", LocalDate.now().getYear() + ""))
.collect(Collectors.joining("\n")) + "\n#\n");

Files.writeString(recipePath, """
# This file is automatically generated by the GeneratePropertiesMigratorConfiguration class.
# Do not edit this file manually. Update the Spring Boot property metadata upstream instead.
---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.spring.boot%1$s.SpringBootProperties_%1$s_%2$s
displayName: Migrate Spring Boot properties to %1$s.%2$s
description: Migrate properties found in `application.properties` and `application.yml`.
tags:
- spring
- boot
recipeList:""".formatted(majorMinor[0], majorMinor[1]),
StandardOpenOption.APPEND);
}
}

record SpringConfigurationMetadata(List<ConfigurationProperty> properties) {
Expand Down

0 comments on commit 4ca06a9

Please sign in to comment.