Merge branch '3.0.x'

Closes gh-34335
pull/34342/head
Andy Wilkinson 2 years ago
commit 7d697310a0

@ -57,15 +57,11 @@ import org.w3c.dom.Document;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
import org.springframework.boot.build.DeployedPlugin; import org.springframework.boot.build.DeployedPlugin;
import org.springframework.boot.build.bom.Library.DependencyConstraintsDependencyVersions;
import org.springframework.boot.build.bom.Library.DependencyLockDependencyVersions;
import org.springframework.boot.build.bom.Library.DependencyVersions;
import org.springframework.boot.build.bom.Library.Exclusion; import org.springframework.boot.build.bom.Library.Exclusion;
import org.springframework.boot.build.bom.Library.Group; import org.springframework.boot.build.bom.Library.Group;
import org.springframework.boot.build.bom.Library.LibraryVersion; import org.springframework.boot.build.bom.Library.LibraryVersion;
import org.springframework.boot.build.bom.Library.Module; import org.springframework.boot.build.bom.Library.Module;
import org.springframework.boot.build.bom.Library.ProhibitedVersion; import org.springframework.boot.build.bom.Library.ProhibitedVersion;
import org.springframework.boot.build.bom.Library.VersionAlignment;
import org.springframework.boot.build.bom.bomr.version.DependencyVersion; import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
import org.springframework.boot.build.mavenplugin.MavenExec; import org.springframework.boot.build.mavenplugin.MavenExec;
import org.springframework.util.FileCopyUtils; import org.springframework.util.FileCopyUtils;
@ -114,13 +110,10 @@ public class BomExtension {
public void library(String name, String version, Action<LibraryHandler> action) { public void library(String name, String version, Action<LibraryHandler> action) {
ObjectFactory objects = this.project.getObjects(); ObjectFactory objects = this.project.getObjects();
LibraryHandler libraryHandler = objects.newInstance(LibraryHandler.class, (version != null) ? version : "", LibraryHandler libraryHandler = objects.newInstance(LibraryHandler.class, (version != null) ? version : "");
objects);
action.execute(libraryHandler); action.execute(libraryHandler);
LibraryVersion libraryVersion = new LibraryVersion(DependencyVersion.parse(libraryHandler.version), LibraryVersion libraryVersion = new LibraryVersion(DependencyVersion.parse(libraryHandler.version));
libraryHandler.versionAlignment); addLibrary(new Library(name, libraryVersion, libraryHandler.groups, libraryHandler.prohibitedVersions));
addLibrary(new Library(name, libraryVersion, libraryHandler.groups, libraryHandler.prohibitedVersions,
libraryHandler.dependencyVersions));
} }
public void effectiveBomArtifact() { public void effectiveBomArtifact() {
@ -220,25 +213,15 @@ public class BomExtension {
private final List<ProhibitedVersion> prohibitedVersions = new ArrayList<>(); private final List<ProhibitedVersion> prohibitedVersions = new ArrayList<>();
private final ObjectFactory objectFactory;
private String version; private String version;
private VersionAlignment versionAlignment;
private DependencyVersions dependencyVersions;
@Inject @Inject
public LibraryHandler(String version, ObjectFactory objectFactory) { public LibraryHandler(String version) {
this.version = version; this.version = version;
this.objectFactory = objectFactory;
} }
public void version(String version, Action<VersionHandler> action) { public void version(String version) {
this.version = version; this.version = version;
VersionHandler versionHandler = new VersionHandler();
action.execute(versionHandler);
this.versionAlignment = new VersionAlignment(versionHandler.libraryName);
} }
public void group(String id, Action<GroupHandler> action) { public void group(String id, Action<GroupHandler> action) {
@ -255,23 +238,6 @@ public class BomExtension {
handler.endsWith, handler.contains, handler.reason)); handler.endsWith, handler.contains, handler.reason));
} }
public void dependencyVersions(Action<DependencyVersionsHandler> action) {
DependencyVersionsHandler dependencyVersionsHandler = this.objectFactory
.newInstance(DependencyVersionsHandler.class, this.version);
action.execute(dependencyVersionsHandler);
this.dependencyVersions = dependencyVersionsHandler.dependencyVersions;
}
public static class VersionHandler {
private String libraryName;
public void shouldAlignWithVersionFrom(String libraryName) {
this.libraryName = libraryName;
}
}
public static class ProhibitedHandler { public static class ProhibitedHandler {
private String reason; private String reason;
@ -389,37 +355,6 @@ public class BomExtension {
} }
public static class DependencyVersionsHandler {
private final String libraryVersion;
private DependencyVersions dependencyVersions;
@Inject
public DependencyVersionsHandler(String libraryVersion) {
this.libraryVersion = libraryVersion;
}
public void extractFrom(Action<ExtractFromHandler> action) {
action.execute(new ExtractFromHandler());
}
public class ExtractFromHandler {
public void dependencyLock(String location) {
DependencyVersionsHandler.this.dependencyVersions = new DependencyLockDependencyVersions(location,
DependencyVersionsHandler.this.libraryVersion);
}
public void dependencyConstraints(String location) {
DependencyVersionsHandler.this.dependencyVersions = new DependencyConstraintsDependencyVersions(
location, DependencyVersionsHandler.this.libraryVersion);
}
}
}
} }
public static class UpgradeHandler { public static class UpgradeHandler {

@ -16,20 +16,11 @@
package org.springframework.boot.build.bom; package org.springframework.boot.build.bom;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.maven.artifact.versioning.VersionRange; import org.apache.maven.artifact.versioning.VersionRange;
import org.gradle.api.GradleException;
import org.springframework.boot.build.bom.bomr.version.DependencyVersion; import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
@ -51,8 +42,6 @@ public class Library {
private final List<ProhibitedVersion> prohibitedVersions; private final List<ProhibitedVersion> prohibitedVersions;
private final DependencyVersions dependencyVersions;
/** /**
* Create a new {@code Library} with the given {@code name}, {@code version}, and * Create a new {@code Library} with the given {@code name}, {@code version}, and
* {@code groups}. * {@code groups}.
@ -60,17 +49,15 @@ public class Library {
* @param version version of the library * @param version version of the library
* @param groups groups in the library * @param groups groups in the library
* @param prohibitedVersions version of the library that are prohibited * @param prohibitedVersions version of the library that are prohibited
* @param dependencyVersions the library's dependency versions
*/ */
public Library(String name, LibraryVersion version, List<Group> groups, List<ProhibitedVersion> prohibitedVersions, public Library(String name, LibraryVersion version, List<Group> groups,
DependencyVersions dependencyVersions) { List<ProhibitedVersion> prohibitedVersions) {
this.name = name; this.name = name;
this.version = version; this.version = version;
this.groups = groups; this.groups = groups;
this.versionProperty = "Spring Boot".equals(name) ? null this.versionProperty = "Spring Boot".equals(name) ? null
: name.toLowerCase(Locale.ENGLISH).replace(' ', '-') + ".version"; : name.toLowerCase(Locale.ENGLISH).replace(' ', '-') + ".version";
this.prohibitedVersions = prohibitedVersions; this.prohibitedVersions = prohibitedVersions;
this.dependencyVersions = dependencyVersions;
} }
public String getName() { public String getName() {
@ -93,10 +80,6 @@ public class Library {
return this.prohibitedVersions; return this.prohibitedVersions;
} }
public DependencyVersions getDependencyVersions() {
return this.dependencyVersions;
}
/** /**
* A version or range of versions that are prohibited from being used in a bom. * A version or range of versions that are prohibited from being used in a bom.
*/ */
@ -147,21 +130,14 @@ public class Library {
private final DependencyVersion version; private final DependencyVersion version;
private final VersionAlignment versionAlignment; public LibraryVersion(DependencyVersion version) {
public LibraryVersion(DependencyVersion version, VersionAlignment versionAlignment) {
this.version = version; this.version = version;
this.versionAlignment = versionAlignment;
} }
public DependencyVersion getVersion() { public DependencyVersion getVersion() {
return this.version; return this.version;
} }
public VersionAlignment getVersionAlignment() {
return this.versionAlignment;
}
} }
/** /**
@ -276,128 +252,4 @@ public class Library {
} }
public interface DependencyVersions {
String getVersion(String groupId, String artifactId);
default boolean available() {
return true;
}
}
public static class DependencyLockDependencyVersions implements DependencyVersions {
private final Map<String, Map<String, String>> dependencyVersions = new HashMap<>();
private final String sourceTemplate;
private final String libraryVersion;
public DependencyLockDependencyVersions(String sourceTemplate, String libraryVersion) {
this.sourceTemplate = sourceTemplate;
this.libraryVersion = libraryVersion;
}
@Override
public boolean available() {
return !this.libraryVersion.contains("-SNAPSHOT");
}
@Override
public String getVersion(String groupId, String artifactId) {
if (this.dependencyVersions.isEmpty()) {
loadVersions();
}
return this.dependencyVersions.computeIfAbsent(groupId, (key) -> Collections.emptyMap()).get(artifactId);
}
private void loadVersions() {
String source = this.sourceTemplate.replace("<libraryVersion>", this.libraryVersion);
try {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(URI.create(source).toURL().openStream()))) {
String line;
while ((line = reader.readLine()) != null) {
if (!line.startsWith("#")) {
String[] components = line.split(":");
Map<String, String> groupDependencies = this.dependencyVersions
.computeIfAbsent(components[0], (key) -> new HashMap<>());
groupDependencies.put(components[1], components[2]);
}
}
}
}
catch (IOException ex) {
throw new GradleException("Failed to load versions from dependency lock file '" + source + "'", ex);
}
}
}
public static class DependencyConstraintsDependencyVersions implements DependencyVersions {
private static final Pattern CONSTRAINT_PATTERN = Pattern.compile("api \"(.+):(.+):(.+)\"");
private final Map<String, Map<String, String>> dependencyVersions = new HashMap<>();
private final String sourceTemplate;
private final String libraryVersion;
public DependencyConstraintsDependencyVersions(String sourceTemplate, String libraryVersion) {
this.sourceTemplate = sourceTemplate;
this.libraryVersion = libraryVersion;
}
@Override
public String getVersion(String groupId, String artifactId) {
if (this.dependencyVersions.isEmpty()) {
loadVersions();
}
return this.dependencyVersions.computeIfAbsent(groupId, (key) -> Collections.emptyMap()).get(artifactId);
}
private void loadVersions() {
String version = this.libraryVersion;
if (version.endsWith("-SNAPSHOT")) {
version = version.substring(0, version.lastIndexOf('.')) + ".x";
}
String source = this.sourceTemplate.replace("<libraryVersion>", version);
try {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(URI.create(source).toURL().openStream()))) {
String line;
while ((line = reader.readLine()) != null) {
Matcher matcher = CONSTRAINT_PATTERN.matcher(line.trim());
if (matcher.matches()) {
Map<String, String> groupDependencies = this.dependencyVersions
.computeIfAbsent(matcher.group(1), (key) -> new HashMap<>());
groupDependencies.put(matcher.group(2), matcher.group(3));
}
}
}
}
catch (IOException ex) {
throw new GradleException(
"Failed to load versions from dependency constraints declared in '" + source + "'", ex);
}
}
}
public static class VersionAlignment {
private final String libraryName;
public VersionAlignment(String libraryName) {
this.libraryName = libraryName;
}
public String getLibraryName() {
return this.libraryName;
}
}
} }

@ -20,25 +20,20 @@ import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion; import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.gradle.api.InvalidUserDataException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.boot.build.bom.Library; import org.springframework.boot.build.bom.Library;
import org.springframework.boot.build.bom.Library.DependencyVersions;
import org.springframework.boot.build.bom.Library.Group; import org.springframework.boot.build.bom.Library.Group;
import org.springframework.boot.build.bom.Library.Module; import org.springframework.boot.build.bom.Library.Module;
import org.springframework.boot.build.bom.Library.ProhibitedVersion; import org.springframework.boot.build.bom.Library.ProhibitedVersion;
import org.springframework.boot.build.bom.Library.VersionAlignment;
import org.springframework.boot.build.bom.UpgradePolicy; import org.springframework.boot.build.bom.UpgradePolicy;
import org.springframework.boot.build.bom.bomr.version.DependencyVersion; import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
@ -83,9 +78,6 @@ class StandardLibraryUpdateResolver implements LibraryUpdateResolver {
} }
protected List<VersionOption> getVersionOptions(Library library, Map<String, Library> libraries) { protected List<VersionOption> getVersionOptions(Library library, Map<String, Library> libraries) {
if (library.getVersion().getVersionAlignment() != null) {
return determineAlignedVersionOption(library, libraries);
}
return determineResolvedVersionOptions(library); return determineResolvedVersionOptions(library);
} }
@ -121,51 +113,6 @@ class StandardLibraryUpdateResolver implements LibraryUpdateResolver {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
private List<VersionOption> determineAlignedVersionOption(Library library, Map<String, Library> libraries) {
VersionOption alignedVersionOption = alignedVersionOption(library, libraries);
if (alignedVersionOption == null) {
return Collections.emptyList();
}
if (!isPermitted(alignedVersionOption.getVersion(), library.getProhibitedVersions())) {
throw new InvalidUserDataException("Version alignment failed. Version " + alignedVersionOption.getVersion()
+ " from " + library.getName() + " is prohibited");
}
return Collections.singletonList(alignedVersionOption);
}
private VersionOption alignedVersionOption(Library library, Map<String, Library> libraries) {
VersionAlignment versionAlignment = library.getVersion().getVersionAlignment();
Library alignmentLibrary = libraries.get(versionAlignment.getLibraryName());
DependencyVersions dependencyVersions = alignmentLibrary.getDependencyVersions();
if (dependencyVersions == null) {
throw new InvalidUserDataException("Cannot align with library '" + versionAlignment.getLibraryName()
+ "' as it does not define any dependency versions");
}
if (!dependencyVersions.available()) {
return null;
}
Set<String> versions = new HashSet<>();
for (Group group : library.getGroups()) {
for (Module module : group.getModules()) {
String version = dependencyVersions.getVersion(group.getId(), module.getName());
if (version != null) {
versions.add(version);
}
}
}
if (versions.isEmpty()) {
throw new InvalidUserDataException("Cannot align with library '" + versionAlignment.getLibraryName()
+ "' as its dependency versions do not include any of this library's modules");
}
if (versions.size() > 1) {
throw new InvalidUserDataException("Cannot align with library '" + versionAlignment.getLibraryName()
+ "' as it uses multiple different versions of this library's modules");
}
DependencyVersion version = DependencyVersion.parse(versions.iterator().next());
return library.getVersion().getVersion().equals(version) ? null
: new VersionOption.AlignedVersionOption(version, alignmentLibrary);
}
private boolean isPermitted(DependencyVersion dependencyVersion, List<ProhibitedVersion> prohibitedVersions) { private boolean isPermitted(DependencyVersion dependencyVersion, List<ProhibitedVersion> prohibitedVersions) {
for (ProhibitedVersion prohibitedVersion : prohibitedVersions) { for (ProhibitedVersion prohibitedVersion : prohibitedVersions) {
String dependencyVersionToString = dependencyVersion.toString(); String dependencyVersionToString = dependencyVersion.toString();

@ -46,31 +46,16 @@ class UpgradeApplicatorTests {
@Test @Test
void whenUpgradeIsAppliedToLibraryWithVersionThenBomIsUpdated() throws IOException { void whenUpgradeIsAppliedToLibraryWithVersionThenBomIsUpdated() throws IOException {
File bom = new File(this.temp, "bom.gradle");
FileCopyUtils.copy(new File("src/test/resources/bom.gradle"), bom);
String originalContents = Files.readString(bom.toPath());
File gradleProperties = new File(this.temp, "gradle.properties");
FileCopyUtils.copy(new File("src/test/resources/gradle.properties"), gradleProperties);
new UpgradeApplicator(bom.toPath(), gradleProperties.toPath()).apply(new Upgrade(
new Library("ActiveMQ", new LibraryVersion(DependencyVersion.parse("5.15.11"), null), null, null, null),
DependencyVersion.parse("5.16")));
String bomContents = Files.readString(bom.toPath());
assertThat(bomContents).hasSize(originalContents.length() - 3);
}
@Test
void whenUpgradeIsAppliedToLibraryWithAlignedVersionThenBomIsUpdated() throws IOException {
File bom = new File(this.temp, "bom.gradle"); File bom = new File(this.temp, "bom.gradle");
FileCopyUtils.copy(new File("src/test/resources/bom.gradle"), bom); FileCopyUtils.copy(new File("src/test/resources/bom.gradle"), bom);
String originalContents = Files.readString(bom.toPath()); String originalContents = Files.readString(bom.toPath());
File gradleProperties = new File(this.temp, "gradle.properties"); File gradleProperties = new File(this.temp, "gradle.properties");
FileCopyUtils.copy(new File("src/test/resources/gradle.properties"), gradleProperties); FileCopyUtils.copy(new File("src/test/resources/gradle.properties"), gradleProperties);
new UpgradeApplicator(bom.toPath(), gradleProperties.toPath()).apply( new UpgradeApplicator(bom.toPath(), gradleProperties.toPath()).apply(
new Upgrade(new Library("OAuth2 OIDC SDK", new LibraryVersion(DependencyVersion.parse("8.36.1"), null), new Upgrade(new Library("ActiveMQ", new LibraryVersion(DependencyVersion.parse("5.15.11")), null, null),
null, null, null), DependencyVersion.parse("8.36.2"))); DependencyVersion.parse("5.16")));
String bomContents = Files.readString(bom.toPath()); String bomContents = Files.readString(bom.toPath());
assertThat(bomContents).hasSameSizeAs(originalContents); assertThat(bomContents).hasSize(originalContents.length() - 3);
assertThat(bomContents).contains("version(\"8.36.2\")");
} }
@Test @Test
@ -79,9 +64,9 @@ class UpgradeApplicatorTests {
FileCopyUtils.copy(new File("src/test/resources/bom.gradle"), bom); FileCopyUtils.copy(new File("src/test/resources/bom.gradle"), bom);
File gradleProperties = new File(this.temp, "gradle.properties"); File gradleProperties = new File(this.temp, "gradle.properties");
FileCopyUtils.copy(new File("src/test/resources/gradle.properties"), gradleProperties); FileCopyUtils.copy(new File("src/test/resources/gradle.properties"), gradleProperties);
new UpgradeApplicator(bom.toPath(), gradleProperties.toPath()).apply(new Upgrade( new UpgradeApplicator(bom.toPath(), gradleProperties.toPath())
new Library("Kotlin", new LibraryVersion(DependencyVersion.parse("1.3.70"), null), null, null, null), .apply(new Upgrade(new Library("Kotlin", new LibraryVersion(DependencyVersion.parse("1.3.70")), null, null),
DependencyVersion.parse("1.4"))); DependencyVersion.parse("1.4")));
Properties properties = new Properties(); Properties properties = new Properties();
try (InputStream in = new FileInputStream(gradleProperties)) { try (InputStream in = new FileInputStream(gradleProperties)) {
properties.load(in); properties.load(in);

Loading…
Cancel
Save