17 changed files with 735 additions and 543 deletions
@ -0,0 +1,64 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012-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.boot.configurationmetadata.changelog; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; |
||||||
|
import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository; |
||||||
|
|
||||||
|
/** |
||||||
|
* A changelog containing differences computed from two repositories of configuration |
||||||
|
* metadata. |
||||||
|
* |
||||||
|
* @param oldVersionNumber the name of the old version |
||||||
|
* @param newVersionNumber the name of the new version |
||||||
|
* @param differences the differences |
||||||
|
* @author Stephane Nicoll |
||||||
|
* @author Andy Wilkinson |
||||||
|
* @author Phillip Webb |
||||||
|
*/ |
||||||
|
record Changelog(String oldVersionNumber, String newVersionNumber, List<Difference> differences) { |
||||||
|
|
||||||
|
static Changelog of(String oldVersionNumber, ConfigurationMetadataRepository oldMetadata, String newVersionNumber, |
||||||
|
ConfigurationMetadataRepository newMetadata) { |
||||||
|
return new Changelog(oldVersionNumber, newVersionNumber, computeDifferences(oldMetadata, newMetadata)); |
||||||
|
} |
||||||
|
|
||||||
|
static List<Difference> computeDifferences(ConfigurationMetadataRepository oldMetadata, |
||||||
|
ConfigurationMetadataRepository newMetadata) { |
||||||
|
List<String> seenIds = new ArrayList<>(); |
||||||
|
List<Difference> differences = new ArrayList<>(); |
||||||
|
for (ConfigurationMetadataProperty oldProperty : oldMetadata.getAllProperties().values()) { |
||||||
|
String id = oldProperty.getId(); |
||||||
|
seenIds.add(id); |
||||||
|
ConfigurationMetadataProperty newProperty = newMetadata.getAllProperties().get(id); |
||||||
|
Difference difference = Difference.compute(oldProperty, newProperty); |
||||||
|
if (difference != null) { |
||||||
|
differences.add(difference); |
||||||
|
} |
||||||
|
} |
||||||
|
for (ConfigurationMetadataProperty newProperty : newMetadata.getAllProperties().values()) { |
||||||
|
if ((!seenIds.contains(newProperty.getId())) && (!newProperty.isDeprecated())) { |
||||||
|
differences.add(new Difference(DifferenceType.ADDED, null, newProperty)); |
||||||
|
} |
||||||
|
} |
||||||
|
return List.copyOf(differences); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,79 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012-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.boot.configurationmetadata.changelog; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.io.IOException; |
||||||
|
import java.util.jar.JarEntry; |
||||||
|
import java.util.jar.JarFile; |
||||||
|
|
||||||
|
import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository; |
||||||
|
import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepositoryJsonBuilder; |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a configuration metadata changelog. Requires three arguments: |
||||||
|
* |
||||||
|
* <ol> |
||||||
|
* <li>The path of a directory containing jar files of the old version |
||||||
|
* <li>The path of a directory containing jar files of the new version |
||||||
|
* <li>The path of a file to which the asciidoc changelog will be written |
||||||
|
* </ol> |
||||||
|
* |
||||||
|
* The name of each directory will be used as version numbers in generated changelog. |
||||||
|
* |
||||||
|
* @author Andy Wilkinson |
||||||
|
* @author Phillip Webb |
||||||
|
* @since 3.2.0 |
||||||
|
*/ |
||||||
|
public final class ChangelogGenerator { |
||||||
|
|
||||||
|
private ChangelogGenerator() { |
||||||
|
} |
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException { |
||||||
|
generate(new File(args[0]), new File(args[1]), new File(args[2])); |
||||||
|
} |
||||||
|
|
||||||
|
private static void generate(File oldDir, File newDir, File out) throws IOException { |
||||||
|
String oldVersionNumber = oldDir.getName(); |
||||||
|
ConfigurationMetadataRepository oldMetadata = buildRepository(oldDir); |
||||||
|
String newVersionNumber = newDir.getName(); |
||||||
|
ConfigurationMetadataRepository newMetadata = buildRepository(newDir); |
||||||
|
Changelog changelog = Changelog.of(oldVersionNumber, oldMetadata, newVersionNumber, newMetadata); |
||||||
|
try (ChangelogWriter writer = new ChangelogWriter(out)) { |
||||||
|
writer.write(changelog); |
||||||
|
} |
||||||
|
System.out.println("%nConfiguration metadata changelog written to '%s'".formatted(out)); |
||||||
|
} |
||||||
|
|
||||||
|
static ConfigurationMetadataRepository buildRepository(File directory) { |
||||||
|
ConfigurationMetadataRepositoryJsonBuilder builder = ConfigurationMetadataRepositoryJsonBuilder.create(); |
||||||
|
for (File file : directory.listFiles()) { |
||||||
|
try (JarFile jarFile = new JarFile(file)) { |
||||||
|
JarEntry metadataEntry = jarFile.getJarEntry("META-INF/spring-configuration-metadata.json"); |
||||||
|
if (metadataEntry != null) { |
||||||
|
builder.withJsonResource(jarFile.getInputStream(metadataEntry)); |
||||||
|
} |
||||||
|
} |
||||||
|
catch (IOException ex) { |
||||||
|
throw new RuntimeException(ex); |
||||||
|
} |
||||||
|
} |
||||||
|
return builder.build(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,224 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012-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.boot.configurationmetadata.changelog; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.io.FileWriter; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.PrintWriter; |
||||||
|
import java.io.Writer; |
||||||
|
import java.text.BreakIterator; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.Comparator; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Locale; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Objects; |
||||||
|
import java.util.function.Consumer; |
||||||
|
import java.util.function.Function; |
||||||
|
import java.util.function.Predicate; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
import java.util.stream.Stream; |
||||||
|
|
||||||
|
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; |
||||||
|
import org.springframework.boot.configurationmetadata.Deprecation; |
||||||
|
|
||||||
|
/** |
||||||
|
* Writes a {@link Changelog} using asciidoc markup. |
||||||
|
* |
||||||
|
* @author Stephane Nicoll |
||||||
|
* @author Andy Wilkinson |
||||||
|
* @author Phillip Webb |
||||||
|
*/ |
||||||
|
class ChangelogWriter implements AutoCloseable { |
||||||
|
|
||||||
|
private static final Comparator<ConfigurationMetadataProperty> COMPARING_ID = Comparator |
||||||
|
.comparing(ConfigurationMetadataProperty::getId); |
||||||
|
|
||||||
|
private final PrintWriter out; |
||||||
|
|
||||||
|
ChangelogWriter(File out) throws IOException { |
||||||
|
this(new FileWriter(out)); |
||||||
|
} |
||||||
|
|
||||||
|
ChangelogWriter(Writer out) { |
||||||
|
this.out = new PrintWriter(out); |
||||||
|
} |
||||||
|
|
||||||
|
void write(Changelog changelog) { |
||||||
|
String oldVersionNumber = changelog.oldVersionNumber(); |
||||||
|
String newVersionNumber = changelog.newVersionNumber(); |
||||||
|
Map<DifferenceType, List<Difference>> differencesByType = collateByType(changelog); |
||||||
|
write("Configuration property changes between `%s` and `%s`%n", oldVersionNumber, newVersionNumber); |
||||||
|
write("%n%n%n== Deprecated in %s%n", newVersionNumber); |
||||||
|
writeDeprecated(differencesByType.get(DifferenceType.DEPRECATED)); |
||||||
|
write("%n%n%n== Added in %s%n", newVersionNumber); |
||||||
|
writeAdded(differencesByType.get(DifferenceType.ADDED)); |
||||||
|
write("%n%n%n== Removed in %s%n", newVersionNumber); |
||||||
|
writeRemoved(differencesByType.get(DifferenceType.DELETED), differencesByType.get(DifferenceType.DEPRECATED)); |
||||||
|
} |
||||||
|
|
||||||
|
private Map<DifferenceType, List<Difference>> collateByType(Changelog differences) { |
||||||
|
Map<DifferenceType, List<Difference>> byType = new HashMap<>(); |
||||||
|
for (DifferenceType type : DifferenceType.values()) { |
||||||
|
byType.put(type, new ArrayList<>()); |
||||||
|
} |
||||||
|
for (Difference difference : differences.differences()) { |
||||||
|
byType.get(difference.type()).add(difference); |
||||||
|
} |
||||||
|
return byType; |
||||||
|
} |
||||||
|
|
||||||
|
private void writeDeprecated(List<Difference> differences) { |
||||||
|
List<Difference> rows = sortProperties(differences, Difference::newProperty).stream() |
||||||
|
.filter(this::isDeprecatedInRelease) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
writeTable("| Key | Replacement | Reason", rows, this::writeDeprecated); |
||||||
|
} |
||||||
|
|
||||||
|
private void writeDeprecated(Difference difference) { |
||||||
|
writeDeprecatedPropertyRow(difference.newProperty()); |
||||||
|
} |
||||||
|
|
||||||
|
private void writeAdded(List<Difference> differences) { |
||||||
|
List<Difference> rows = sortProperties(differences, Difference::newProperty); |
||||||
|
writeTable("| Key | Default value | Description", rows, this::writeAdded); |
||||||
|
} |
||||||
|
|
||||||
|
private void writeAdded(Difference difference) { |
||||||
|
writeRegularPropertyRow(difference.newProperty()); |
||||||
|
} |
||||||
|
|
||||||
|
private void writeRemoved(List<Difference> deleted, List<Difference> deprecated) { |
||||||
|
List<Difference> rows = getRemoved(deleted, deprecated); |
||||||
|
writeTable("| Key | Replacement | Reason", rows, this::writeRemoved); |
||||||
|
} |
||||||
|
|
||||||
|
private List<Difference> getRemoved(List<Difference> deleted, List<Difference> deprecated) { |
||||||
|
List<Difference> result = new ArrayList<>(deleted); |
||||||
|
deprecated.stream().filter(Predicate.not(this::isDeprecatedInRelease)).forEach(result::remove); |
||||||
|
return sortProperties(result, |
||||||
|
(difference) -> getFirstNonNull(difference, Difference::oldProperty, Difference::newProperty)); |
||||||
|
} |
||||||
|
|
||||||
|
private void writeRemoved(Difference difference) { |
||||||
|
writeDeprecatedPropertyRow(getFirstNonNull(difference, Difference::newProperty, Difference::oldProperty)); |
||||||
|
} |
||||||
|
|
||||||
|
private List<Difference> sortProperties(List<Difference> differences, |
||||||
|
Function<Difference, ConfigurationMetadataProperty> extractor) { |
||||||
|
return differences.stream().sorted(Comparator.comparing(extractor, COMPARING_ID)).toList(); |
||||||
|
} |
||||||
|
|
||||||
|
@SafeVarargs |
||||||
|
@SuppressWarnings("varargs") |
||||||
|
private <T, P> P getFirstNonNull(T t, Function<T, P>... extractors) { |
||||||
|
return Stream.of(extractors) |
||||||
|
.map((extractor) -> extractor.apply(t)) |
||||||
|
.filter(Objects::nonNull) |
||||||
|
.findFirst() |
||||||
|
.orElse(null); |
||||||
|
} |
||||||
|
|
||||||
|
private void writeTable(String header, List<Difference> rows, Consumer<Difference> action) { |
||||||
|
if (rows.isEmpty()) { |
||||||
|
write("_None_.%n"); |
||||||
|
} |
||||||
|
else { |
||||||
|
writeTableBreak(); |
||||||
|
write(header + "%n%n"); |
||||||
|
for (Iterator<Difference> iterator = rows.iterator(); iterator.hasNext();) { |
||||||
|
action.accept(iterator.next()); |
||||||
|
write((!iterator.hasNext()) ? null : "%n"); |
||||||
|
} |
||||||
|
writeTableBreak(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void writeTableBreak() { |
||||||
|
write("|======================%n"); |
||||||
|
} |
||||||
|
|
||||||
|
private void writeRegularPropertyRow(ConfigurationMetadataProperty property) { |
||||||
|
writeCell(monospace(property.getId())); |
||||||
|
writeCell(monospace(asString(property.getDefaultValue()))); |
||||||
|
writeCell(property.getShortDescription()); |
||||||
|
} |
||||||
|
|
||||||
|
private void writeDeprecatedPropertyRow(ConfigurationMetadataProperty property) { |
||||||
|
Deprecation deprecation = (property.getDeprecation() != null) ? property.getDeprecation() : new Deprecation(); |
||||||
|
writeCell(monospace(property.getId())); |
||||||
|
writeCell(monospace(deprecation.getReplacement())); |
||||||
|
writeCell(getFirstSentence(deprecation.getReason())); |
||||||
|
} |
||||||
|
|
||||||
|
private String getFirstSentence(String text) { |
||||||
|
if (text == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
int dot = text.indexOf('.'); |
||||||
|
if (dot != -1) { |
||||||
|
BreakIterator breakIterator = BreakIterator.getSentenceInstance(Locale.US); |
||||||
|
breakIterator.setText(text); |
||||||
|
String sentence = text.substring(breakIterator.first(), breakIterator.next()).trim(); |
||||||
|
return removeSpaceBetweenLine(sentence); |
||||||
|
} |
||||||
|
String[] lines = text.split(System.lineSeparator()); |
||||||
|
return lines[0].trim(); |
||||||
|
} |
||||||
|
|
||||||
|
private String removeSpaceBetweenLine(String text) { |
||||||
|
String[] lines = text.split(System.lineSeparator()); |
||||||
|
return Arrays.stream(lines).map(String::trim).collect(Collectors.joining(" ")); |
||||||
|
} |
||||||
|
|
||||||
|
private boolean isDeprecatedInRelease(Difference difference) { |
||||||
|
Deprecation deprecation = difference.newProperty().getDeprecation(); |
||||||
|
return (deprecation != null) && (deprecation.getLevel() != Deprecation.Level.ERROR); |
||||||
|
} |
||||||
|
|
||||||
|
private String monospace(String value) { |
||||||
|
return (value != null) ? "`%s`".formatted(value) : null; |
||||||
|
} |
||||||
|
|
||||||
|
private void writeCell(String format, Object... args) { |
||||||
|
write((format != null) ? "| %s%n".formatted(format) : "|%n", args); |
||||||
|
} |
||||||
|
|
||||||
|
private void write(String format, Object... args) { |
||||||
|
if (format != null) { |
||||||
|
Object[] strings = Arrays.stream(args).map(this::asString).toArray(); |
||||||
|
this.out.append(format.formatted(strings)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private String asString(Object value) { |
||||||
|
if (value instanceof Object[] array) { |
||||||
|
return Stream.of(array).map(this::asString).collect(Collectors.joining(", ")); |
||||||
|
} |
||||||
|
return (value != null) ? value.toString() : null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void close() { |
||||||
|
this.out.close(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -1,56 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright 2012-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.boot.configurationmetadata.changelog; |
|
||||||
|
|
||||||
import java.io.File; |
|
||||||
import java.io.FileWriter; |
|
||||||
import java.io.IOException; |
|
||||||
|
|
||||||
/** |
|
||||||
* Generates a configuration metadata changelog. Requires three arguments: |
|
||||||
* |
|
||||||
* <ol> |
|
||||||
* <li>The path of a directory containing jar files from which the old metadata will be |
|
||||||
* extracted |
|
||||||
* <li>The path of a directory containing jar files from which the new metadata will be |
|
||||||
* extracted |
|
||||||
* <li>The path of a file to which the changelog will be written |
|
||||||
* </ol> |
|
||||||
* |
|
||||||
* The name of each directory will be used to name the old and new metadata in the |
|
||||||
* generated changelog |
|
||||||
* |
|
||||||
* @author Andy Wilkinson |
|
||||||
*/ |
|
||||||
final class ConfigurationMetadataChangelogGenerator { |
|
||||||
|
|
||||||
private ConfigurationMetadataChangelogGenerator() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException { |
|
||||||
ConfigurationMetadataDiff diff = ConfigurationMetadataDiff.of( |
|
||||||
NamedConfigurationMetadataRepository.from(new File(args[0])), |
|
||||||
NamedConfigurationMetadataRepository.from(new File(args[1]))); |
|
||||||
try (ConfigurationMetadataChangelogWriter writer = new ConfigurationMetadataChangelogWriter( |
|
||||||
new FileWriter(new File(args[2])))) { |
|
||||||
writer.write(diff); |
|
||||||
} |
|
||||||
System.out.println("\nConfiguration metadata changelog written to '" + args[2] + "'"); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -1,204 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright 2012-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.boot.configurationmetadata.changelog; |
|
||||||
|
|
||||||
import java.io.PrintWriter; |
|
||||||
import java.io.Writer; |
|
||||||
import java.text.BreakIterator; |
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.HashMap; |
|
||||||
import java.util.List; |
|
||||||
import java.util.Locale; |
|
||||||
import java.util.Map; |
|
||||||
import java.util.function.Function; |
|
||||||
import java.util.stream.Collectors; |
|
||||||
import java.util.stream.Stream; |
|
||||||
|
|
||||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; |
|
||||||
import org.springframework.boot.configurationmetadata.Deprecation; |
|
||||||
import org.springframework.boot.configurationmetadata.changelog.ConfigurationMetadataDiff.Difference; |
|
||||||
import org.springframework.boot.configurationmetadata.changelog.ConfigurationMetadataDiff.Difference.Type; |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes a configuration metadata changelog from a {@link ConfigurationMetadataDiff}. |
|
||||||
* |
|
||||||
* @author Stephane Nicoll |
|
||||||
* @author Andy Wilkinson |
|
||||||
*/ |
|
||||||
class ConfigurationMetadataChangelogWriter implements AutoCloseable { |
|
||||||
|
|
||||||
private final PrintWriter out; |
|
||||||
|
|
||||||
ConfigurationMetadataChangelogWriter(Writer out) { |
|
||||||
this.out = new PrintWriter(out); |
|
||||||
} |
|
||||||
|
|
||||||
void write(ConfigurationMetadataDiff diff) { |
|
||||||
this.out.append(String.format("Configuration property changes between `%s` and " + "`%s`%n", diff.leftName(), |
|
||||||
diff.rightName())); |
|
||||||
this.out.append(System.lineSeparator()); |
|
||||||
this.out.append(String.format("== Deprecated in `%s`%n", diff.rightName())); |
|
||||||
Map<Type, List<Difference>> differencesByType = differencesByType(diff); |
|
||||||
writeDeprecatedProperties(differencesByType.get(Type.DEPRECATED)); |
|
||||||
this.out.append(System.lineSeparator()); |
|
||||||
this.out.append(String.format("== New in `%s`%n", diff.rightName())); |
|
||||||
writeAddedProperties(differencesByType.get(Type.ADDED)); |
|
||||||
this.out.append(System.lineSeparator()); |
|
||||||
this.out.append(String.format("== Removed in `%s`%n", diff.rightName())); |
|
||||||
writeRemovedProperties(differencesByType.get(Type.DELETED), differencesByType.get(Type.DEPRECATED)); |
|
||||||
} |
|
||||||
|
|
||||||
private Map<Type, List<Difference>> differencesByType(ConfigurationMetadataDiff diff) { |
|
||||||
Map<Type, List<Difference>> differencesByType = new HashMap<>(); |
|
||||||
for (Type type : Type.values()) { |
|
||||||
differencesByType.put(type, new ArrayList<>()); |
|
||||||
} |
|
||||||
for (Difference difference : diff.differences()) { |
|
||||||
differencesByType.get(difference.type()).add(difference); |
|
||||||
} |
|
||||||
return differencesByType; |
|
||||||
} |
|
||||||
|
|
||||||
private void writeDeprecatedProperties(List<Difference> differences) { |
|
||||||
if (differences.isEmpty()) { |
|
||||||
this.out.append(String.format("None.%n")); |
|
||||||
} |
|
||||||
else { |
|
||||||
List<Difference> properties = sortProperties(differences, Difference::right).stream() |
|
||||||
.filter(this::isDeprecatedInRelease) |
|
||||||
.collect(Collectors.toList()); |
|
||||||
this.out.append(String.format("|======================%n")); |
|
||||||
this.out.append(String.format("|Key |Replacement |Reason%n")); |
|
||||||
properties.forEach((diff) -> { |
|
||||||
ConfigurationMetadataProperty property = diff.right(); |
|
||||||
writeDeprecatedProperty(property); |
|
||||||
}); |
|
||||||
this.out.append(String.format("|======================%n")); |
|
||||||
} |
|
||||||
this.out.append(String.format("%n%n")); |
|
||||||
} |
|
||||||
|
|
||||||
private boolean isDeprecatedInRelease(Difference difference) { |
|
||||||
return difference.right().getDeprecation() != null |
|
||||||
&& Deprecation.Level.ERROR != difference.right().getDeprecation().getLevel(); |
|
||||||
} |
|
||||||
|
|
||||||
private void writeAddedProperties(List<Difference> differences) { |
|
||||||
if (differences.isEmpty()) { |
|
||||||
this.out.append(String.format("None.%n")); |
|
||||||
} |
|
||||||
else { |
|
||||||
List<Difference> properties = sortProperties(differences, Difference::right); |
|
||||||
this.out.append(String.format("|======================%n")); |
|
||||||
this.out.append(String.format("|Key |Default value |Description%n")); |
|
||||||
properties.forEach((diff) -> writeRegularProperty(diff.right())); |
|
||||||
this.out.append(String.format("|======================%n")); |
|
||||||
} |
|
||||||
this.out.append(String.format("%n%n")); |
|
||||||
} |
|
||||||
|
|
||||||
private void writeRemovedProperties(List<Difference> deleted, List<Difference> deprecated) { |
|
||||||
List<Difference> removed = getRemovedProperties(deleted, deprecated); |
|
||||||
if (removed.isEmpty()) { |
|
||||||
this.out.append(String.format("None.%n")); |
|
||||||
} |
|
||||||
else { |
|
||||||
this.out.append(String.format("|======================%n")); |
|
||||||
this.out.append(String.format("|Key |Replacement |Reason%n")); |
|
||||||
removed.forEach((property) -> writeDeprecatedProperty( |
|
||||||
(property.right() != null) ? property.right() : property.left())); |
|
||||||
this.out.append(String.format("|======================%n")); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private List<Difference> getRemovedProperties(List<Difference> deleted, List<Difference> deprecated) { |
|
||||||
List<Difference> properties = new ArrayList<>(deleted); |
|
||||||
properties.addAll(deprecated.stream().filter((p) -> !isDeprecatedInRelease(p)).collect(Collectors.toList())); |
|
||||||
return sortProperties(properties, |
|
||||||
(difference) -> (difference.left() != null) ? difference.left() : difference.right()); |
|
||||||
} |
|
||||||
|
|
||||||
private void writeRegularProperty(ConfigurationMetadataProperty property) { |
|
||||||
this.out.append("|`").append(property.getId()).append("` |"); |
|
||||||
if (property.getDefaultValue() != null) { |
|
||||||
this.out.append("`").append(defaultValueToString(property.getDefaultValue())).append("`"); |
|
||||||
} |
|
||||||
this.out.append(" |"); |
|
||||||
if (property.getDescription() != null) { |
|
||||||
this.out.append(property.getShortDescription()); |
|
||||||
} |
|
||||||
this.out.append(System.lineSeparator()); |
|
||||||
} |
|
||||||
|
|
||||||
private void writeDeprecatedProperty(ConfigurationMetadataProperty property) { |
|
||||||
Deprecation deprecation = (property.getDeprecation() != null) ? property.getDeprecation() : new Deprecation(); |
|
||||||
this.out.append("|`").append(property.getId()).append("` |"); |
|
||||||
if (deprecation.getReplacement() != null) { |
|
||||||
this.out.append("`").append(deprecation.getReplacement()).append("`"); |
|
||||||
} |
|
||||||
this.out.append(" |"); |
|
||||||
if (deprecation.getReason() != null) { |
|
||||||
this.out.append(getFirstSentence(deprecation.getReason())); |
|
||||||
} |
|
||||||
this.out.append(System.lineSeparator()); |
|
||||||
} |
|
||||||
|
|
||||||
private String getFirstSentence(String text) { |
|
||||||
int dot = text.indexOf('.'); |
|
||||||
if (dot != -1) { |
|
||||||
BreakIterator breakIterator = BreakIterator.getSentenceInstance(Locale.US); |
|
||||||
breakIterator.setText(text); |
|
||||||
String sentence = text.substring(breakIterator.first(), breakIterator.next()).trim(); |
|
||||||
return removeSpaceBetweenLine(sentence); |
|
||||||
} |
|
||||||
else { |
|
||||||
String[] lines = text.split(System.lineSeparator()); |
|
||||||
return lines[0].trim(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private static String removeSpaceBetweenLine(String text) { |
|
||||||
String[] lines = text.split(System.lineSeparator()); |
|
||||||
StringBuilder sb = new StringBuilder(); |
|
||||||
for (String line : lines) { |
|
||||||
sb.append(line.trim()).append(" "); |
|
||||||
} |
|
||||||
return sb.toString().trim(); |
|
||||||
} |
|
||||||
|
|
||||||
private List<Difference> sortProperties(List<Difference> properties, |
|
||||||
Function<Difference, ConfigurationMetadataProperty> property) { |
|
||||||
List<Difference> sorted = new ArrayList<>(properties); |
|
||||||
sorted.sort((o1, o2) -> property.apply(o1).getId().compareTo(property.apply(o2).getId())); |
|
||||||
return sorted; |
|
||||||
} |
|
||||||
|
|
||||||
private static String defaultValueToString(Object defaultValue) { |
|
||||||
if (defaultValue instanceof Object[]) { |
|
||||||
return Stream.of((Object[]) defaultValue).map(Object::toString).collect(Collectors.joining(", ")); |
|
||||||
} |
|
||||||
else { |
|
||||||
return defaultValue.toString(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void close() { |
|
||||||
this.out.close(); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -1,109 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright 2012-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.boot.configurationmetadata.changelog; |
|
||||||
|
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.List; |
|
||||||
import java.util.Map; |
|
||||||
|
|
||||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; |
|
||||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository; |
|
||||||
import org.springframework.boot.configurationmetadata.Deprecation.Level; |
|
||||||
import org.springframework.boot.configurationmetadata.changelog.ConfigurationMetadataDiff.Difference.Type; |
|
||||||
|
|
||||||
/** |
|
||||||
* A diff of two repositories of configuration metadata. |
|
||||||
* |
|
||||||
* @param leftName the name of the left-hand side of the diff |
|
||||||
* @param rightName the name of the right-hand side of the diff |
|
||||||
* @param differences the differences |
|
||||||
* @author Stephane Nicoll |
|
||||||
* @author Andy Wilkinson |
|
||||||
*/ |
|
||||||
record ConfigurationMetadataDiff(String leftName, String rightName, List<Difference> differences) { |
|
||||||
|
|
||||||
static ConfigurationMetadataDiff of(NamedConfigurationMetadataRepository left, |
|
||||||
NamedConfigurationMetadataRepository right) { |
|
||||||
return new ConfigurationMetadataDiff(left.getName(), right.getName(), differences(left, right)); |
|
||||||
} |
|
||||||
|
|
||||||
private static List<Difference> differences(ConfigurationMetadataRepository left, |
|
||||||
ConfigurationMetadataRepository right) { |
|
||||||
List<Difference> differences = new ArrayList<>(); |
|
||||||
List<String> matches = new ArrayList<>(); |
|
||||||
Map<String, ConfigurationMetadataProperty> leftProperties = left.getAllProperties(); |
|
||||||
Map<String, ConfigurationMetadataProperty> rightProperties = right.getAllProperties(); |
|
||||||
for (ConfigurationMetadataProperty leftProperty : leftProperties.values()) { |
|
||||||
String id = leftProperty.getId(); |
|
||||||
matches.add(id); |
|
||||||
ConfigurationMetadataProperty rightProperty = rightProperties.get(id); |
|
||||||
if (rightProperty == null) { |
|
||||||
if (!(leftProperty.isDeprecated() && leftProperty.getDeprecation().getLevel() == Level.ERROR)) { |
|
||||||
differences.add(new Difference(Type.DELETED, leftProperty, null)); |
|
||||||
} |
|
||||||
} |
|
||||||
else if (rightProperty.isDeprecated() && !leftProperty.isDeprecated()) { |
|
||||||
differences.add(new Difference(Type.DEPRECATED, leftProperty, rightProperty)); |
|
||||||
} |
|
||||||
else if (leftProperty.isDeprecated() && leftProperty.getDeprecation().getLevel() == Level.WARNING |
|
||||||
&& rightProperty.isDeprecated() && rightProperty.getDeprecation().getLevel() == Level.ERROR) { |
|
||||||
differences.add(new Difference(Type.DELETED, leftProperty, rightProperty)); |
|
||||||
} |
|
||||||
} |
|
||||||
for (ConfigurationMetadataProperty rightProperty : rightProperties.values()) { |
|
||||||
if ((!matches.contains(rightProperty.getId())) && (!rightProperty.isDeprecated())) { |
|
||||||
differences.add(new Difference(Type.ADDED, null, rightProperty)); |
|
||||||
} |
|
||||||
} |
|
||||||
return differences; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* A difference in the metadata. |
|
||||||
* |
|
||||||
* @param type the type of the difference |
|
||||||
* @param left the left-hand side of the difference |
|
||||||
* @param right the right-hand side of the difference |
|
||||||
*/ |
|
||||||
static record Difference(Type type, ConfigurationMetadataProperty left, ConfigurationMetadataProperty right) { |
|
||||||
|
|
||||||
/** |
|
||||||
* The type of a difference in the metadata. |
|
||||||
*/ |
|
||||||
enum Type { |
|
||||||
|
|
||||||
/** |
|
||||||
* The entry has been added. |
|
||||||
*/ |
|
||||||
ADDED, |
|
||||||
|
|
||||||
/** |
|
||||||
* The entry has been made deprecated. It may or may not still exist in the |
|
||||||
* previous version. |
|
||||||
*/ |
|
||||||
DEPRECATED, |
|
||||||
|
|
||||||
/** |
|
||||||
* The entry has been deleted. |
|
||||||
*/ |
|
||||||
DELETED |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -0,0 +1,52 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012-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.boot.configurationmetadata.changelog; |
||||||
|
|
||||||
|
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; |
||||||
|
import org.springframework.boot.configurationmetadata.Deprecation.Level; |
||||||
|
|
||||||
|
/** |
||||||
|
* A difference the metadata. |
||||||
|
* |
||||||
|
* @param type the type of the difference |
||||||
|
* @param oldProperty the old property |
||||||
|
* @param newProperty the new property |
||||||
|
* @author Stephane Nicoll |
||||||
|
* @author Andy Wilkinson |
||||||
|
* @author Phillip Webb |
||||||
|
*/ |
||||||
|
record Difference(DifferenceType type, ConfigurationMetadataProperty oldProperty, |
||||||
|
ConfigurationMetadataProperty newProperty) { |
||||||
|
|
||||||
|
static Difference compute(ConfigurationMetadataProperty oldProperty, ConfigurationMetadataProperty newProperty) { |
||||||
|
if (newProperty == null) { |
||||||
|
if (!(oldProperty.isDeprecated() && oldProperty.getDeprecation().getLevel() == Level.ERROR)) { |
||||||
|
return new Difference(DifferenceType.DELETED, oldProperty, null); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
if (newProperty.isDeprecated() && !oldProperty.isDeprecated()) { |
||||||
|
return new Difference(DifferenceType.DEPRECATED, oldProperty, newProperty); |
||||||
|
} |
||||||
|
if (oldProperty.isDeprecated() && oldProperty.getDeprecation().getLevel() == Level.WARNING |
||||||
|
&& newProperty.isDeprecated() && newProperty.getDeprecation().getLevel() == Level.ERROR) { |
||||||
|
return new Difference(DifferenceType.DELETED, oldProperty, newProperty); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,42 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012-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.boot.configurationmetadata.changelog; |
||||||
|
|
||||||
|
/** |
||||||
|
* The type of a difference in the metadata. |
||||||
|
* |
||||||
|
* @author Andy Wilkinson |
||||||
|
*/ |
||||||
|
enum DifferenceType { |
||||||
|
|
||||||
|
/** |
||||||
|
* The entry has been added. |
||||||
|
*/ |
||||||
|
ADDED, |
||||||
|
|
||||||
|
/** |
||||||
|
* The entry has been made deprecated. It may or may not still exist in the previous |
||||||
|
* version. |
||||||
|
*/ |
||||||
|
DEPRECATED, |
||||||
|
|
||||||
|
/** |
||||||
|
* The entry has been deleted. |
||||||
|
*/ |
||||||
|
DELETED |
||||||
|
|
||||||
|
} |
||||||
@ -1,80 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright 2012-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.boot.configurationmetadata.changelog; |
|
||||||
|
|
||||||
import java.io.File; |
|
||||||
import java.io.IOException; |
|
||||||
import java.util.Map; |
|
||||||
import java.util.jar.JarEntry; |
|
||||||
import java.util.jar.JarFile; |
|
||||||
|
|
||||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataGroup; |
|
||||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; |
|
||||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository; |
|
||||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepositoryJsonBuilder; |
|
||||||
|
|
||||||
/** |
|
||||||
* A {@link ConfigurationMetadataRepository} with a name. |
|
||||||
* |
|
||||||
* @author Andy Wilkinson |
|
||||||
*/ |
|
||||||
class NamedConfigurationMetadataRepository implements ConfigurationMetadataRepository { |
|
||||||
|
|
||||||
private final String name; |
|
||||||
|
|
||||||
private final ConfigurationMetadataRepository delegate; |
|
||||||
|
|
||||||
NamedConfigurationMetadataRepository(String name, ConfigurationMetadataRepository delegate) { |
|
||||||
this.name = name; |
|
||||||
this.delegate = delegate; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* The name of the metadata held in the repository. |
|
||||||
* @return the name of the metadata |
|
||||||
*/ |
|
||||||
String getName() { |
|
||||||
return this.name; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public Map<String, ConfigurationMetadataGroup> getAllGroups() { |
|
||||||
return this.delegate.getAllGroups(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public Map<String, ConfigurationMetadataProperty> getAllProperties() { |
|
||||||
return this.delegate.getAllProperties(); |
|
||||||
} |
|
||||||
|
|
||||||
static NamedConfigurationMetadataRepository from(File metadataDir) { |
|
||||||
ConfigurationMetadataRepositoryJsonBuilder builder = ConfigurationMetadataRepositoryJsonBuilder.create(); |
|
||||||
for (File jar : metadataDir.listFiles()) { |
|
||||||
try (JarFile jarFile = new JarFile(jar)) { |
|
||||||
JarEntry jsonMetadata = jarFile.getJarEntry("META-INF/spring-configuration-metadata.json"); |
|
||||||
if (jsonMetadata != null) { |
|
||||||
builder.withJsonResource(jarFile.getInputStream(jsonMetadata)); |
|
||||||
} |
|
||||||
} |
|
||||||
catch (IOException ex) { |
|
||||||
throw new RuntimeException(ex); |
|
||||||
} |
|
||||||
} |
|
||||||
return new NamedConfigurationMetadataRepository(metadataDir.getName(), builder.build()); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -0,0 +1,68 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012-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.boot.configurationmetadata.changelog; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.io.FileInputStream; |
||||||
|
import java.io.FileOutputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
import java.nio.charset.StandardCharsets; |
||||||
|
import java.util.jar.JarOutputStream; |
||||||
|
import java.util.zip.ZipEntry; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
import org.junit.jupiter.api.io.TempDir; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests for {@link ChangelogGenerator}. |
||||||
|
* |
||||||
|
* @author Phillip Webb |
||||||
|
*/ |
||||||
|
class ChangelogGeneratorTests { |
||||||
|
|
||||||
|
@TempDir |
||||||
|
File temp; |
||||||
|
|
||||||
|
@Test |
||||||
|
void generateChangeLog() throws IOException { |
||||||
|
File oldJars = new File(this.temp, "1.0"); |
||||||
|
addJar(oldJars, "sample-1.0.json"); |
||||||
|
File newJars = new File(this.temp, "2.0"); |
||||||
|
addJar(newJars, "sample-2.0.json"); |
||||||
|
File out = new File(this.temp, "changes.adoc"); |
||||||
|
String[] args = new String[] { oldJars.getAbsolutePath(), newJars.getAbsolutePath(), out.getAbsolutePath() }; |
||||||
|
ChangelogGenerator.main(args); |
||||||
|
assertThat(out).usingCharset(StandardCharsets.UTF_8) |
||||||
|
.hasSameTextualContentAs(new File("src/test/resources/sample.adoc")); |
||||||
|
} |
||||||
|
|
||||||
|
private void addJar(File directory, String filename) throws IOException { |
||||||
|
directory.mkdirs(); |
||||||
|
try (JarOutputStream out = new JarOutputStream(new FileOutputStream(new File(directory, "sample.jar")))) { |
||||||
|
out.putNextEntry(new ZipEntry("META-INF/spring-configuration-metadata.json")); |
||||||
|
try (InputStream in = new FileInputStream("src/test/resources/" + filename)) { |
||||||
|
in.transferTo(out); |
||||||
|
out.closeEntry(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,73 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012-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.boot.configurationmetadata.changelog; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
|
||||||
|
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests for {@link Changelog}. |
||||||
|
* |
||||||
|
* @author Stephane Nicoll |
||||||
|
* @author Andy Wilkinson |
||||||
|
*/ |
||||||
|
class ChangelogTests { |
||||||
|
|
||||||
|
@Test |
||||||
|
void diffContainsDifferencesBetweenLeftAndRightInputs() { |
||||||
|
Changelog differences = TestChangelog.load(); |
||||||
|
assertThat(differences).isNotNull(); |
||||||
|
assertThat(differences.oldVersionNumber()).isEqualTo("1.0"); |
||||||
|
assertThat(differences.newVersionNumber()).isEqualTo("2.0"); |
||||||
|
assertThat(differences.differences()).hasSize(4); |
||||||
|
List<Difference> added = differences.differences() |
||||||
|
.stream() |
||||||
|
.filter((difference) -> difference.type() == DifferenceType.ADDED) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
assertThat(added).hasSize(1); |
||||||
|
assertProperty(added.get(0).newProperty(), "test.add", String.class, "new"); |
||||||
|
List<Difference> deleted = differences.differences() |
||||||
|
.stream() |
||||||
|
.filter((difference) -> difference.type() == DifferenceType.DELETED) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
assertThat(deleted).hasSize(2) |
||||||
|
.anySatisfy((entry) -> assertProperty(entry.oldProperty(), "test.delete", String.class, "delete")) |
||||||
|
.anySatisfy( |
||||||
|
(entry) -> assertProperty(entry.newProperty(), "test.delete.deprecated", String.class, "delete")); |
||||||
|
List<Difference> deprecated = differences.differences() |
||||||
|
.stream() |
||||||
|
.filter((difference) -> difference.type() == DifferenceType.DEPRECATED) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
assertThat(deprecated).hasSize(1); |
||||||
|
assertProperty(deprecated.get(0).oldProperty(), "test.deprecate", String.class, "wrong"); |
||||||
|
assertProperty(deprecated.get(0).newProperty(), "test.deprecate", String.class, "wrong"); |
||||||
|
} |
||||||
|
|
||||||
|
private void assertProperty(ConfigurationMetadataProperty property, String id, Class<?> type, Object defaultValue) { |
||||||
|
assertThat(property).isNotNull(); |
||||||
|
assertThat(property.getId()).isEqualTo(id); |
||||||
|
assertThat(property.getType()).isEqualTo(type.getName()); |
||||||
|
assertThat(property.getDefaultValue()).isEqualTo(defaultValue); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,45 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012-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.boot.configurationmetadata.changelog; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.io.StringWriter; |
||||||
|
import java.nio.charset.StandardCharsets; |
||||||
|
|
||||||
|
import org.assertj.core.util.Files; |
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests for {@link ChangelogWriter}. |
||||||
|
* |
||||||
|
* @author Phillip Webb |
||||||
|
*/ |
||||||
|
class ChangelogWriterTests { |
||||||
|
|
||||||
|
@Test |
||||||
|
void writeChangelog() { |
||||||
|
StringWriter out = new StringWriter(); |
||||||
|
try (ChangelogWriter writer = new ChangelogWriter(out)) { |
||||||
|
writer.write(TestChangelog.load()); |
||||||
|
} |
||||||
|
String expected = Files.contentOf(new File("src/test/resources/sample.adoc"), StandardCharsets.UTF_8); |
||||||
|
assertThat(out).hasToString(expected); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -1,92 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright 2012-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.boot.configurationmetadata.changelog; |
|
||||||
|
|
||||||
import java.io.FileInputStream; |
|
||||||
import java.io.IOException; |
|
||||||
import java.io.InputStream; |
|
||||||
import java.util.List; |
|
||||||
import java.util.stream.Collectors; |
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test; |
|
||||||
|
|
||||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; |
|
||||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository; |
|
||||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepositoryJsonBuilder; |
|
||||||
import org.springframework.boot.configurationmetadata.changelog.ConfigurationMetadataDiff.Difference; |
|
||||||
import org.springframework.boot.configurationmetadata.changelog.ConfigurationMetadataDiff.Difference.Type; |
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat; |
|
||||||
|
|
||||||
/** |
|
||||||
* Tests for {@link ConfigurationMetadataDiff}. |
|
||||||
* |
|
||||||
* @author Stephane Nicoll |
|
||||||
* @author Andy Wilkinson |
|
||||||
*/ |
|
||||||
class ConfigurationMetadataDiffTests { |
|
||||||
|
|
||||||
@Test |
|
||||||
void diffContainsDifferencesBetweenLeftAndRightInputs() { |
|
||||||
NamedConfigurationMetadataRepository left = new NamedConfigurationMetadataRepository("1.0", |
|
||||||
load("sample-1.0.json")); |
|
||||||
NamedConfigurationMetadataRepository right = new NamedConfigurationMetadataRepository("2.0", |
|
||||||
load("sample-2.0.json")); |
|
||||||
ConfigurationMetadataDiff diff = ConfigurationMetadataDiff.of(left, right); |
|
||||||
assertThat(diff).isNotNull(); |
|
||||||
assertThat(diff.leftName()).isEqualTo("1.0"); |
|
||||||
assertThat(diff.rightName()).isEqualTo("2.0"); |
|
||||||
assertThat(diff.differences()).hasSize(4); |
|
||||||
List<Difference> added = diff.differences() |
|
||||||
.stream() |
|
||||||
.filter((difference) -> difference.type() == Type.ADDED) |
|
||||||
.collect(Collectors.toList()); |
|
||||||
assertThat(added).hasSize(1); |
|
||||||
assertProperty(added.get(0).right(), "test.add", String.class, "new"); |
|
||||||
List<Difference> deleted = diff.differences() |
|
||||||
.stream() |
|
||||||
.filter((difference) -> difference.type() == Type.DELETED) |
|
||||||
.collect(Collectors.toList()); |
|
||||||
assertThat(deleted).hasSize(2) |
|
||||||
.anySatisfy((entry) -> assertProperty(entry.left(), "test.delete", String.class, "delete")) |
|
||||||
.anySatisfy((entry) -> assertProperty(entry.right(), "test.delete.deprecated", String.class, "delete")); |
|
||||||
List<Difference> deprecated = diff.differences() |
|
||||||
.stream() |
|
||||||
.filter((difference) -> difference.type() == Type.DEPRECATED) |
|
||||||
.collect(Collectors.toList()); |
|
||||||
assertThat(deprecated).hasSize(1); |
|
||||||
assertProperty(deprecated.get(0).left(), "test.deprecate", String.class, "wrong"); |
|
||||||
assertProperty(deprecated.get(0).right(), "test.deprecate", String.class, "wrong"); |
|
||||||
} |
|
||||||
|
|
||||||
private void assertProperty(ConfigurationMetadataProperty property, String id, Class<?> type, Object defaultValue) { |
|
||||||
assertThat(property).isNotNull(); |
|
||||||
assertThat(property.getId()).isEqualTo(id); |
|
||||||
assertThat(property.getType()).isEqualTo(type.getName()); |
|
||||||
assertThat(property.getDefaultValue()).isEqualTo(defaultValue); |
|
||||||
} |
|
||||||
|
|
||||||
private ConfigurationMetadataRepository load(String filename) { |
|
||||||
try (InputStream inputStream = new FileInputStream("src/test/resources/" + filename)) { |
|
||||||
return ConfigurationMetadataRepositoryJsonBuilder.create(inputStream).build(); |
|
||||||
} |
|
||||||
catch (IOException ex) { |
|
||||||
throw new RuntimeException(ex); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -0,0 +1,52 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012-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.boot.configurationmetadata.changelog; |
||||||
|
|
||||||
|
import java.io.FileInputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
|
||||||
|
import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository; |
||||||
|
import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepositoryJsonBuilder; |
||||||
|
|
||||||
|
/** |
||||||
|
* Factory to create test {@link Changelog} instance. |
||||||
|
* |
||||||
|
* @author Andy Wilkinson |
||||||
|
* @author Phillip Webb |
||||||
|
*/ |
||||||
|
final class TestChangelog { |
||||||
|
|
||||||
|
private TestChangelog() { |
||||||
|
} |
||||||
|
|
||||||
|
static Changelog load() { |
||||||
|
ConfigurationMetadataRepository previousRepository = load("sample-1.0.json"); |
||||||
|
ConfigurationMetadataRepository repository = load("sample-2.0.json"); |
||||||
|
return Changelog.of("1.0", previousRepository, "2.0", repository); |
||||||
|
} |
||||||
|
|
||||||
|
private static ConfigurationMetadataRepository load(String filename) { |
||||||
|
try (InputStream inputStream = new FileInputStream("src/test/resources/" + filename)) { |
||||||
|
return ConfigurationMetadataRepositoryJsonBuilder.create(inputStream).build(); |
||||||
|
} |
||||||
|
catch (IOException ex) { |
||||||
|
throw new RuntimeException(ex); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,32 @@ |
|||||||
|
Configuration property changes between `1.0` and `2.0` |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
== Deprecated in 2.0 |
||||||
|
_None_. |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
== Added in 2.0 |
||||||
|
|====================== |
||||||
|
| Key | Default value | Description |
||||||
|
|
||||||
|
| `test.add` |
||||||
|
| `new` |
||||||
|
| Test add. |
||||||
|
|====================== |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
== Removed in 2.0 |
||||||
|
|====================== |
||||||
|
| Key | Replacement | Reason |
||||||
|
|
||||||
|
| `test.delete` |
||||||
|
| |
||||||
|
| |
||||||
|
|
||||||
|
| `test.delete.deprecated` |
||||||
|
| `test.add` |
||||||
|
| it was just bad |
||||||
|
|====================== |
||||||
Loading…
Reference in new issue