mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-09-03 04:26:12 +08:00
Add dependency lock and constraint version alignment to Bomr
Closes gh-27044
This commit is contained in:
parent
d0f12392ab
commit
363de4cfa5
@ -29,6 +29,7 @@ dependencies {
|
|||||||
testImplementation("org.assertj:assertj-core:3.11.1")
|
testImplementation("org.assertj:assertj-core:3.11.1")
|
||||||
testImplementation("org.apache.logging.log4j:log4j-core:2.12.1")
|
testImplementation("org.apache.logging.log4j:log4j-core:2.12.1")
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter:5.6.0")
|
testImplementation("org.junit.jupiter:junit-jupiter:5.6.0")
|
||||||
|
testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.6.3")
|
||||||
}
|
}
|
||||||
|
|
||||||
checkstyle {
|
checkstyle {
|
||||||
|
@ -56,10 +56,15 @@ 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.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;
|
||||||
@ -101,11 +106,17 @@ public class BomExtension {
|
|||||||
this.upgradeHandler.gitHub.repository, this.upgradeHandler.gitHub.issueLabels));
|
this.upgradeHandler.gitHub.repository, this.upgradeHandler.gitHub.issueLabels));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void library(String name, Closure<?> closure) {
|
||||||
|
this.library(name, null, closure);
|
||||||
|
}
|
||||||
|
|
||||||
public void library(String name, String version, Closure<?> closure) {
|
public void library(String name, String version, Closure<?> closure) {
|
||||||
LibraryHandler libraryHandler = new LibraryHandler();
|
LibraryHandler libraryHandler = new LibraryHandler(version);
|
||||||
ConfigureUtil.configure(closure, libraryHandler);
|
ConfigureUtil.configure(closure, libraryHandler);
|
||||||
addLibrary(new Library(name, DependencyVersion.parse(version), libraryHandler.groups,
|
LibraryVersion libraryVersion = new LibraryVersion(DependencyVersion.parse(libraryHandler.version),
|
||||||
libraryHandler.prohibitedVersions));
|
libraryHandler.versionAlignment);
|
||||||
|
addLibrary(new Library(name, libraryVersion, libraryHandler.groups, libraryHandler.prohibitedVersions,
|
||||||
|
libraryHandler.dependencyVersions));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void effectiveBomArtifact() {
|
public void effectiveBomArtifact() {
|
||||||
@ -171,17 +182,18 @@ public class BomExtension {
|
|||||||
this.libraries.add(library);
|
this.libraries.add(library);
|
||||||
String versionProperty = library.getVersionProperty();
|
String versionProperty = library.getVersionProperty();
|
||||||
if (versionProperty != null) {
|
if (versionProperty != null) {
|
||||||
this.properties.put(versionProperty, library.getVersion());
|
this.properties.put(versionProperty, library.getVersion().getVersion());
|
||||||
}
|
}
|
||||||
for (Group group : library.getGroups()) {
|
for (Group group : library.getGroups()) {
|
||||||
for (Module module : group.getModules()) {
|
for (Module module : group.getModules()) {
|
||||||
putArtifactVersionProperty(group.getId(), module.getName(), versionProperty);
|
putArtifactVersionProperty(group.getId(), module.getName(), versionProperty);
|
||||||
this.dependencyHandler.getConstraints().add(JavaPlatformPlugin.API_CONFIGURATION_NAME,
|
this.dependencyHandler.getConstraints().add(JavaPlatformPlugin.API_CONFIGURATION_NAME,
|
||||||
createDependencyNotation(group.getId(), module.getName(), library.getVersion()));
|
createDependencyNotation(group.getId(), module.getName(), library.getVersion().getVersion()));
|
||||||
}
|
}
|
||||||
for (String bomImport : group.getBoms()) {
|
for (String bomImport : group.getBoms()) {
|
||||||
putArtifactVersionProperty(group.getId(), bomImport, versionProperty);
|
putArtifactVersionProperty(group.getId(), bomImport, versionProperty);
|
||||||
String bomDependency = createDependencyNotation(group.getId(), bomImport, library.getVersion());
|
String bomDependency = createDependencyNotation(group.getId(), bomImport,
|
||||||
|
library.getVersion().getVersion());
|
||||||
this.dependencyHandler.add(JavaPlatformPlugin.API_CONFIGURATION_NAME,
|
this.dependencyHandler.add(JavaPlatformPlugin.API_CONFIGURATION_NAME,
|
||||||
this.dependencyHandler.platform(bomDependency));
|
this.dependencyHandler.platform(bomDependency));
|
||||||
this.dependencyHandler.add(BomPlugin.API_ENFORCED_CONFIGURATION_NAME,
|
this.dependencyHandler.add(BomPlugin.API_ENFORCED_CONFIGURATION_NAME,
|
||||||
@ -196,6 +208,23 @@ public class BomExtension {
|
|||||||
|
|
||||||
private final List<ProhibitedVersion> prohibitedVersions = new ArrayList<>();
|
private final List<ProhibitedVersion> prohibitedVersions = new ArrayList<>();
|
||||||
|
|
||||||
|
private String version;
|
||||||
|
|
||||||
|
private VersionAlignment versionAlignment;
|
||||||
|
|
||||||
|
private DependencyVersions dependencyVersions;
|
||||||
|
|
||||||
|
public LibraryHandler(String version) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void version(String version, Closure<?> closure) {
|
||||||
|
this.version = version;
|
||||||
|
VersionHandler versionHandler = new VersionHandler();
|
||||||
|
ConfigureUtil.configure(closure, versionHandler);
|
||||||
|
this.versionAlignment = new VersionAlignment(versionHandler.libraryName);
|
||||||
|
}
|
||||||
|
|
||||||
public void group(String id, Closure<?> closure) {
|
public void group(String id, Closure<?> closure) {
|
||||||
GroupHandler groupHandler = new GroupHandler(id);
|
GroupHandler groupHandler = new GroupHandler(id);
|
||||||
ConfigureUtil.configure(closure, groupHandler);
|
ConfigureUtil.configure(closure, groupHandler);
|
||||||
@ -215,6 +244,21 @@ public class BomExtension {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void dependencyVersions(Closure<?> closure) {
|
||||||
|
DependencyVersionsHandler dependencyVersionsHandler = new DependencyVersionsHandler();
|
||||||
|
ConfigureUtil.configure(closure, dependencyVersionsHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class VersionHandler {
|
||||||
|
|
||||||
|
private String libraryName;
|
||||||
|
|
||||||
|
public void shouldAlignWithVersionFrom(String libraryName) {
|
||||||
|
this.libraryName = libraryName;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static class ProhibitedVersionHandler {
|
public static class ProhibitedVersionHandler {
|
||||||
|
|
||||||
private String reason;
|
private String reason;
|
||||||
@ -277,6 +321,29 @@ public class BomExtension {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class DependencyVersionsHandler {
|
||||||
|
|
||||||
|
public void extractFrom(Closure<?> closure) {
|
||||||
|
ExtractFromHandler extractFromHandler = new ExtractFromHandler();
|
||||||
|
ConfigureUtil.configure(closure, extractFromHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ExtractFromHandler {
|
||||||
|
|
||||||
|
public void dependencyLock(String location) {
|
||||||
|
LibraryHandler.this.dependencyVersions = new DependencyLockDependencyVersions(location,
|
||||||
|
LibraryHandler.this.version);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dependencyConstraints(String location) {
|
||||||
|
LibraryHandler.this.dependencyVersions = new DependencyConstraintsDependencyVersions(location,
|
||||||
|
LibraryHandler.this.version);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class UpgradeHandler {
|
public static class UpgradeHandler {
|
||||||
|
@ -50,7 +50,7 @@ public class CheckBom extends DefaultTask {
|
|||||||
for (Group group : library.getGroups()) {
|
for (Group group : library.getGroups()) {
|
||||||
for (Module module : group.getModules()) {
|
for (Module module : group.getModules()) {
|
||||||
if (!module.getExclusions().isEmpty()) {
|
if (!module.getExclusions().isEmpty()) {
|
||||||
checkExclusions(group.getId(), module, library.getVersion());
|
checkExclusions(group.getId(), module, library.getVersion().getVersion());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,11 +16,20 @@
|
|||||||
|
|
||||||
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;
|
||||||
|
|
||||||
@ -34,7 +43,7 @@ public class Library {
|
|||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
private final DependencyVersion version;
|
private final LibraryVersion version;
|
||||||
|
|
||||||
private final List<Group> groups;
|
private final List<Group> groups;
|
||||||
|
|
||||||
@ -42,6 +51,8 @@ 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}.
|
||||||
@ -49,22 +60,24 @@ 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, DependencyVersion version, List<Group> groups,
|
public Library(String name, LibraryVersion version, List<Group> groups, List<ProhibitedVersion> prohibitedVersions,
|
||||||
List<ProhibitedVersion> prohibitedVersions) {
|
DependencyVersions dependencyVersions) {
|
||||||
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() {
|
||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DependencyVersion getVersion() {
|
public LibraryVersion getVersion() {
|
||||||
return this.version;
|
return this.version;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,6 +93,10 @@ 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.
|
||||||
*/
|
*/
|
||||||
@ -104,6 +121,27 @@ public class Library {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class LibraryVersion {
|
||||||
|
|
||||||
|
private final DependencyVersion version;
|
||||||
|
|
||||||
|
private final VersionAlignment versionAlignment;
|
||||||
|
|
||||||
|
public LibraryVersion(DependencyVersion version, VersionAlignment versionAlignment) {
|
||||||
|
this.version = version;
|
||||||
|
this.versionAlignment = versionAlignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DependencyVersion getVersion() {
|
||||||
|
return this.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VersionAlignment getVersionAlignment() {
|
||||||
|
return this.versionAlignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A collection of modules, Maven plugins, and Maven boms with the same group ID.
|
* A collection of modules, Maven plugins, and Maven boms with the same group ID.
|
||||||
*/
|
*/
|
||||||
@ -194,4 +232,128 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,19 +19,25 @@ package org.springframework.boot.build.bom.bomr;
|
|||||||
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.HashMap;
|
||||||
|
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.gradle.api.internal.tasks.userinput.UserInputHandler;
|
import org.gradle.api.internal.tasks.userinput.UserInputHandler;
|
||||||
|
|
||||||
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;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
@ -59,39 +65,91 @@ public final class InteractiveUpgradeResolver implements UpgradeResolver {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Upgrade> resolveUpgrades(Collection<Library> libraries) {
|
public List<Upgrade> resolveUpgrades(Collection<Library> libraries) {
|
||||||
return libraries.stream().map(this::resolveUpgrade).filter((upgrade) -> upgrade != null)
|
Map<String, Library> librariesByName = new HashMap<>();
|
||||||
.collect(Collectors.toList());
|
for (Library library : libraries) {
|
||||||
|
librariesByName.put(library.getName(), library);
|
||||||
|
}
|
||||||
|
return libraries.stream().map((library) -> resolveUpgrade(library, librariesByName))
|
||||||
|
.filter((upgrade) -> upgrade != null).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Upgrade resolveUpgrade(Library library) {
|
private Upgrade resolveUpgrade(Library library, Map<String, Library> libraries) {
|
||||||
|
List<VersionOption> versionOptions = getVersionOptions(library, libraries);
|
||||||
|
if (versionOptions.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
VersionOption current = new VersionOption(library.getVersion().getVersion());
|
||||||
|
VersionOption selected = this.userInputHandler
|
||||||
|
.selectOption(library.getName() + " " + library.getVersion().getVersion(), versionOptions, current);
|
||||||
|
return (selected.equals(current)) ? null : new Upgrade(library, selected.version);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<VersionOption> getVersionOptions(Library library, Map<String, Library> libraries) {
|
||||||
|
if (library.getVersion().getVersionAlignment() != null) {
|
||||||
|
VersionOption alignedVersionOption = alignedVersionOption(library, libraries);
|
||||||
|
if (!isPermitted(alignedVersionOption.version, library.getProhibitedVersions())) {
|
||||||
|
throw new InvalidUserDataException("Version alignment failed. Version " + alignedVersionOption.version
|
||||||
|
+ " from " + library.getName() + " is prohibited");
|
||||||
|
}
|
||||||
|
return Collections.singletonList(alignedVersionOption);
|
||||||
|
}
|
||||||
Map<String, SortedSet<DependencyVersion>> moduleVersions = new LinkedHashMap<>();
|
Map<String, SortedSet<DependencyVersion>> moduleVersions = new LinkedHashMap<>();
|
||||||
|
DependencyVersion libraryVersion = library.getVersion().getVersion();
|
||||||
for (Group group : library.getGroups()) {
|
for (Group group : library.getGroups()) {
|
||||||
for (Module module : group.getModules()) {
|
for (Module module : group.getModules()) {
|
||||||
moduleVersions.put(group.getId() + ":" + module.getName(),
|
moduleVersions.put(group.getId() + ":" + module.getName(),
|
||||||
getLaterVersionsForModule(group.getId(), module.getName(), library.getVersion()));
|
getLaterVersionsForModule(group.getId(), module.getName(), libraryVersion));
|
||||||
}
|
}
|
||||||
for (String bom : group.getBoms()) {
|
for (String bom : group.getBoms()) {
|
||||||
moduleVersions.put(group.getId() + ":" + bom,
|
moduleVersions.put(group.getId() + ":" + bom,
|
||||||
getLaterVersionsForModule(group.getId(), bom, library.getVersion()));
|
getLaterVersionsForModule(group.getId(), bom, libraryVersion));
|
||||||
}
|
}
|
||||||
for (String plugin : group.getPlugins()) {
|
for (String plugin : group.getPlugins()) {
|
||||||
moduleVersions.put(group.getId() + ":" + plugin,
|
moduleVersions.put(group.getId() + ":" + plugin,
|
||||||
getLaterVersionsForModule(group.getId(), plugin, library.getVersion()));
|
getLaterVersionsForModule(group.getId(), plugin, libraryVersion));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<DependencyVersion> allVersions = moduleVersions.values().stream().flatMap(SortedSet::stream).distinct()
|
List<DependencyVersion> allVersions = moduleVersions.values().stream().flatMap(SortedSet::stream).distinct()
|
||||||
.filter((dependencyVersion) -> isPermitted(dependencyVersion, library.getProhibitedVersions()))
|
.filter((dependencyVersion) -> isPermitted(dependencyVersion, library.getProhibitedVersions()))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
if (allVersions.isEmpty()) {
|
if (allVersions.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return allVersions.stream()
|
||||||
|
.map((version) -> new ResolvedVersionOption(version, getMissingModules(moduleVersions, version)))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
List<VersionOption> versionOptions = allVersions.stream()
|
Set<String> versions = new HashSet<>();
|
||||||
.map((version) -> new VersionOption(version, getMissingModules(moduleVersions, version)))
|
for (Group group : library.getGroups()) {
|
||||||
.collect(Collectors.toList());
|
for (Module module : group.getModules()) {
|
||||||
VersionOption current = new VersionOption(library.getVersion(), Collections.emptyList());
|
String version = dependencyVersions.getVersion(group.getId(), module.getName());
|
||||||
VersionOption selected = this.userInputHandler.selectOption(library.getName() + " " + library.getVersion(),
|
if (version != null) {
|
||||||
versionOptions, current);
|
versions.add(version);
|
||||||
return (selected.equals(current)) ? null : new Upgrade(library, selected.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");
|
||||||
|
}
|
||||||
|
String requiredVersion = versions.iterator().next();
|
||||||
|
return new AlignedVersionOption(DependencyVersion.parse(requiredVersion), alignmentLibrary);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isPermitted(DependencyVersion dependencyVersion, List<ProhibitedVersion> prohibitedVersions) {
|
private boolean isPermitted(DependencyVersion dependencyVersion, List<ProhibitedVersion> prohibitedVersions) {
|
||||||
@ -125,23 +183,53 @@ public final class InteractiveUpgradeResolver implements UpgradeResolver {
|
|||||||
return versions;
|
return versions;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class VersionOption {
|
private static class VersionOption {
|
||||||
|
|
||||||
private final DependencyVersion version;
|
private final DependencyVersion version;
|
||||||
|
|
||||||
|
protected VersionOption(DependencyVersion version) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.version.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class AlignedVersionOption extends VersionOption {
|
||||||
|
|
||||||
|
private final Library alignedWith;
|
||||||
|
|
||||||
|
private AlignedVersionOption(DependencyVersion version, Library alignedWith) {
|
||||||
|
super(version);
|
||||||
|
this.alignedWith = alignedWith;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return super.toString() + " (aligned with " + this.alignedWith.getName() + " "
|
||||||
|
+ this.alignedWith.getVersion().getVersion() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ResolvedVersionOption extends VersionOption {
|
||||||
|
|
||||||
private final List<String> missingModules;
|
private final List<String> missingModules;
|
||||||
|
|
||||||
private VersionOption(DependencyVersion version, List<String> missingModules) {
|
private ResolvedVersionOption(DependencyVersion version, List<String> missingModules) {
|
||||||
this.version = version;
|
super(version);
|
||||||
this.missingModules = missingModules;
|
this.missingModules = missingModules;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (this.missingModules.isEmpty()) {
|
if (this.missingModules.isEmpty()) {
|
||||||
return this.version.toString();
|
return super.toString();
|
||||||
}
|
}
|
||||||
return this.version + " (some modules are missing: "
|
return super.toString() + " (some modules are missing: "
|
||||||
+ StringUtils.collectionToDelimitedString(this.missingModules, ", ") + ")";
|
+ StringUtils.collectionToDelimitedString(this.missingModules, ", ") + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,8 +46,14 @@ class UpgradeApplicator {
|
|||||||
Matcher matcher = Pattern.compile("library\\(\"" + upgrade.getLibrary().getName() + "\", \"(.+)\"\\)")
|
Matcher matcher = Pattern.compile("library\\(\"" + upgrade.getLibrary().getName() + "\", \"(.+)\"\\)")
|
||||||
.matcher(buildFileContents);
|
.matcher(buildFileContents);
|
||||||
if (!matcher.find()) {
|
if (!matcher.find()) {
|
||||||
throw new IllegalStateException("Failed to find definition for library '" + upgrade.getLibrary().getName()
|
matcher = Pattern
|
||||||
+ "' in bom '" + this.buildFile + "'");
|
.compile("library\\(\"" + upgrade.getLibrary().getName() + "\"\\) \\{\\s+version\\(\"(.+)\"\\)",
|
||||||
|
Pattern.MULTILINE)
|
||||||
|
.matcher(buildFileContents);
|
||||||
|
if (!matcher.find()) {
|
||||||
|
throw new IllegalStateException("Failed to find definition for library '"
|
||||||
|
+ upgrade.getLibrary().getName() + "' in bom '" + this.buildFile + "'");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
String version = matcher.group(1);
|
String version = matcher.group(1);
|
||||||
if (version.startsWith("${") && version.endsWith("}")) {
|
if (version.startsWith("${") && version.endsWith("}")) {
|
||||||
@ -55,7 +61,7 @@ class UpgradeApplicator {
|
|||||||
return this.gradleProperties;
|
return this.gradleProperties;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
updateBuildFile(upgrade, buildFileContents);
|
updateBuildFile(upgrade, buildFileContents, matcher.start(1), matcher.end(1));
|
||||||
return this.buildFile;
|
return this.buildFile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,15 +69,15 @@ class UpgradeApplicator {
|
|||||||
private void updateGradleProperties(Upgrade upgrade, String version) throws IOException {
|
private void updateGradleProperties(Upgrade upgrade, String version) throws IOException {
|
||||||
String property = version.substring(2, version.length() - 1);
|
String property = version.substring(2, version.length() - 1);
|
||||||
String gradlePropertiesContents = new String(Files.readAllBytes(this.gradleProperties), StandardCharsets.UTF_8);
|
String gradlePropertiesContents = new String(Files.readAllBytes(this.gradleProperties), StandardCharsets.UTF_8);
|
||||||
String modified = gradlePropertiesContents.replace(property + "=" + upgrade.getLibrary().getVersion(),
|
String modified = gradlePropertiesContents.replace(
|
||||||
property + "=" + upgrade.getVersion());
|
property + "=" + upgrade.getLibrary().getVersion().getVersion(), property + "=" + upgrade.getVersion());
|
||||||
overwrite(this.gradleProperties, modified);
|
overwrite(this.gradleProperties, modified);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateBuildFile(Upgrade upgrade, String buildFileContents) throws IOException {
|
private void updateBuildFile(Upgrade upgrade, String buildFileContents, int versionStart, int versionEnd)
|
||||||
String modified = buildFileContents.replace(
|
throws IOException {
|
||||||
"library(\"" + upgrade.getLibrary().getName() + "\", \"" + upgrade.getLibrary().getVersion() + "\")",
|
String modified = buildFileContents.substring(0, versionStart) + upgrade.getVersion()
|
||||||
"library(\"" + upgrade.getLibrary().getName() + "\", \"" + upgrade.getVersion() + "\")");
|
+ buildFileContents.substring(versionEnd);
|
||||||
overwrite(this.buildFile, modified);
|
overwrite(this.buildFile, modified);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,6 +197,34 @@ public class BomPluginIntegrationTests {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Test
|
||||||
|
// void versionAlignmentIsVerified() throws IOException {
|
||||||
|
// try (PrintWriter out = new PrintWriter(new FileWriter(this.buildFile))) {
|
||||||
|
// out.println("plugins {");
|
||||||
|
// out.println(" id 'org.springframework.boot.bom'");
|
||||||
|
// out.println("}");
|
||||||
|
// out.println("bom {");
|
||||||
|
// out.println(" library('OAuth2 OIDC SDK', '8.36.1') {");
|
||||||
|
// out.println(" alignedWith('Spring Security') {");
|
||||||
|
// out.println(
|
||||||
|
// "
|
||||||
|
// source('https://github.com/spring-projects/spring-security/blob/${libraryVersion}/config/gradle/dependency-locks/optional.lockfile')");
|
||||||
|
// out.println(" pattern('com.nimbusds:oauth2-oidc-sdk:(.+)')");
|
||||||
|
// out.println(" }");
|
||||||
|
// out.println(" group('com.nimbusds') {");
|
||||||
|
// out.println(" modules = [");
|
||||||
|
// out.println(" 'oauth2-oidc-sdk'");
|
||||||
|
// out.println(" ]");
|
||||||
|
// out.println(" }");
|
||||||
|
// out.println(" }");
|
||||||
|
// out.println(" library('Spring Security', '5.4.7') {");
|
||||||
|
// out.println(" }");
|
||||||
|
// out.println("}");
|
||||||
|
// }
|
||||||
|
// System.out.println(runGradle(DeployedPlugin.GENERATE_POM_TASK_NAME,
|
||||||
|
// "-s").getOutput());
|
||||||
|
// }
|
||||||
|
|
||||||
private BuildResult runGradle(String... args) {
|
private BuildResult runGradle(String... args) {
|
||||||
return GradleRunner.create().withDebug(true).withProjectDir(this.projectDir).withArguments(args)
|
return GradleRunner.create().withDebug(true).withProjectDir(this.projectDir).withArguments(args)
|
||||||
.withPluginClasspath().build();
|
.withPluginClasspath().build();
|
||||||
|
@ -28,6 +28,7 @@ import org.junit.jupiter.api.Test;
|
|||||||
import org.junit.jupiter.api.io.TempDir;
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
import org.springframework.boot.build.bom.Library;
|
import org.springframework.boot.build.bom.Library;
|
||||||
|
import org.springframework.boot.build.bom.Library.LibraryVersion;
|
||||||
import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
|
import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
|
||||||
import org.springframework.util.FileCopyUtils;
|
import org.springframework.util.FileCopyUtils;
|
||||||
|
|
||||||
@ -51,13 +52,28 @@ class UpgradeApplicatorTests {
|
|||||||
String originalContents = new String(Files.readAllBytes(bom.toPath()), StandardCharsets.UTF_8);
|
String originalContents = new String(Files.readAllBytes(bom.toPath()), StandardCharsets.UTF_8);
|
||||||
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())
|
new UpgradeApplicator(bom.toPath(), gradleProperties.toPath()).apply(new Upgrade(
|
||||||
.apply(new Upgrade(new Library("ActiveMQ", DependencyVersion.parse("5.15.11"), null, null),
|
new Library("ActiveMQ", new LibraryVersion(DependencyVersion.parse("5.15.11"), null), null, null, null),
|
||||||
DependencyVersion.parse("5.16")));
|
DependencyVersion.parse("5.16")));
|
||||||
String bomContents = new String(Files.readAllBytes(bom.toPath()), StandardCharsets.UTF_8);
|
String bomContents = new String(Files.readAllBytes(bom.toPath()), StandardCharsets.UTF_8);
|
||||||
assertThat(bomContents.length()).isEqualTo(originalContents.length() - 3);
|
assertThat(bomContents.length()).isEqualTo(originalContents.length() - 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenUpgradeIsAppliedToLibraryWithAlignedVersionThenBomIsUpdated() throws IOException {
|
||||||
|
File bom = new File(this.temp, "bom.gradle");
|
||||||
|
FileCopyUtils.copy(new File("src/test/resources/bom.gradle"), bom);
|
||||||
|
String originalContents = new String(Files.readAllBytes(bom.toPath()), StandardCharsets.UTF_8);
|
||||||
|
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("OAuth2 OIDC SDK", new LibraryVersion(DependencyVersion.parse("8.36.1"), null),
|
||||||
|
null, null, null), DependencyVersion.parse("8.36.2")));
|
||||||
|
String bomContents = new String(Files.readAllBytes(bom.toPath()), StandardCharsets.UTF_8);
|
||||||
|
assertThat(bomContents.length()).isEqualTo(originalContents.length());
|
||||||
|
assertThat(bomContents).contains("version(\"8.36.2\")");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void whenUpgradeIsAppliedToLibraryWithVersionPropertyThenGradlePropertiesIsUpdated() throws IOException {
|
void whenUpgradeIsAppliedToLibraryWithVersionPropertyThenGradlePropertiesIsUpdated() throws IOException {
|
||||||
File bom = new File(this.temp, "bom.gradle");
|
File bom = new File(this.temp, "bom.gradle");
|
||||||
@ -65,7 +81,8 @@ class UpgradeApplicatorTests {
|
|||||||
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()).apply(new Upgrade(
|
||||||
new Library("Kotlin", DependencyVersion.parse("1.3.70"), null, null), DependencyVersion.parse("1.4")));
|
new Library("Kotlin", new LibraryVersion(DependencyVersion.parse("1.3.70"), null), null, null, null),
|
||||||
|
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);
|
||||||
|
@ -48,4 +48,14 @@ bom {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
library("OAuth2 OIDC SDK") {
|
||||||
|
version("8.36.1") {
|
||||||
|
shouldAlignWithVersionFrom("Spring Security")
|
||||||
|
}
|
||||||
|
group("com.nimbusds") {
|
||||||
|
modules = [
|
||||||
|
"oauth2-oidc-sdk"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user