Skip to content

Commit 363de4c

Browse files
committed
Add dependency lock and constraint version alignment to Bomr
Closes gh-27044
1 parent d0f1239 commit 363de4c

File tree

9 files changed

+421
-42
lines changed

9 files changed

+421
-42
lines changed

buildSrc/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ dependencies {
2929
testImplementation("org.assertj:assertj-core:3.11.1")
3030
testImplementation("org.apache.logging.log4j:log4j-core:2.12.1")
3131
testImplementation("org.junit.jupiter:junit-jupiter:5.6.0")
32+
testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.6.3")
3233
}
3334

3435
checkstyle {

buildSrc/src/main/java/org/springframework/boot/build/bom/BomExtension.java

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,15 @@
5656
import org.w3c.dom.NodeList;
5757

5858
import org.springframework.boot.build.DeployedPlugin;
59+
import org.springframework.boot.build.bom.Library.DependencyConstraintsDependencyVersions;
60+
import org.springframework.boot.build.bom.Library.DependencyLockDependencyVersions;
61+
import org.springframework.boot.build.bom.Library.DependencyVersions;
5962
import org.springframework.boot.build.bom.Library.Exclusion;
6063
import org.springframework.boot.build.bom.Library.Group;
64+
import org.springframework.boot.build.bom.Library.LibraryVersion;
6165
import org.springframework.boot.build.bom.Library.Module;
6266
import org.springframework.boot.build.bom.Library.ProhibitedVersion;
67+
import org.springframework.boot.build.bom.Library.VersionAlignment;
6368
import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
6469
import org.springframework.boot.build.mavenplugin.MavenExec;
6570
import org.springframework.util.FileCopyUtils;
@@ -101,11 +106,17 @@ public Upgrade getUpgrade() {
101106
this.upgradeHandler.gitHub.repository, this.upgradeHandler.gitHub.issueLabels));
102107
}
103108

109+
public void library(String name, Closure<?> closure) {
110+
this.library(name, null, closure);
111+
}
112+
104113
public void library(String name, String version, Closure<?> closure) {
105-
LibraryHandler libraryHandler = new LibraryHandler();
114+
LibraryHandler libraryHandler = new LibraryHandler(version);
106115
ConfigureUtil.configure(closure, libraryHandler);
107-
addLibrary(new Library(name, DependencyVersion.parse(version), libraryHandler.groups,
108-
libraryHandler.prohibitedVersions));
116+
LibraryVersion libraryVersion = new LibraryVersion(DependencyVersion.parse(libraryHandler.version),
117+
libraryHandler.versionAlignment);
118+
addLibrary(new Library(name, libraryVersion, libraryHandler.groups, libraryHandler.prohibitedVersions,
119+
libraryHandler.dependencyVersions));
109120
}
110121

111122
public void effectiveBomArtifact() {
@@ -171,17 +182,18 @@ private void addLibrary(Library library) {
171182
this.libraries.add(library);
172183
String versionProperty = library.getVersionProperty();
173184
if (versionProperty != null) {
174-
this.properties.put(versionProperty, library.getVersion());
185+
this.properties.put(versionProperty, library.getVersion().getVersion());
175186
}
176187
for (Group group : library.getGroups()) {
177188
for (Module module : group.getModules()) {
178189
putArtifactVersionProperty(group.getId(), module.getName(), versionProperty);
179190
this.dependencyHandler.getConstraints().add(JavaPlatformPlugin.API_CONFIGURATION_NAME,
180-
createDependencyNotation(group.getId(), module.getName(), library.getVersion()));
191+
createDependencyNotation(group.getId(), module.getName(), library.getVersion().getVersion()));
181192
}
182193
for (String bomImport : group.getBoms()) {
183194
putArtifactVersionProperty(group.getId(), bomImport, versionProperty);
184-
String bomDependency = createDependencyNotation(group.getId(), bomImport, library.getVersion());
195+
String bomDependency = createDependencyNotation(group.getId(), bomImport,
196+
library.getVersion().getVersion());
185197
this.dependencyHandler.add(JavaPlatformPlugin.API_CONFIGURATION_NAME,
186198
this.dependencyHandler.platform(bomDependency));
187199
this.dependencyHandler.add(BomPlugin.API_ENFORCED_CONFIGURATION_NAME,
@@ -196,6 +208,23 @@ public static class LibraryHandler {
196208

197209
private final List<ProhibitedVersion> prohibitedVersions = new ArrayList<>();
198210

211+
private String version;
212+
213+
private VersionAlignment versionAlignment;
214+
215+
private DependencyVersions dependencyVersions;
216+
217+
public LibraryHandler(String version) {
218+
this.version = version;
219+
}
220+
221+
public void version(String version, Closure<?> closure) {
222+
this.version = version;
223+
VersionHandler versionHandler = new VersionHandler();
224+
ConfigureUtil.configure(closure, versionHandler);
225+
this.versionAlignment = new VersionAlignment(versionHandler.libraryName);
226+
}
227+
199228
public void group(String id, Closure<?> closure) {
200229
GroupHandler groupHandler = new GroupHandler(id);
201230
ConfigureUtil.configure(closure, groupHandler);
@@ -215,6 +244,21 @@ public void prohibit(String range, Closure<?> closure) {
215244
}
216245
}
217246

247+
public void dependencyVersions(Closure<?> closure) {
248+
DependencyVersionsHandler dependencyVersionsHandler = new DependencyVersionsHandler();
249+
ConfigureUtil.configure(closure, dependencyVersionsHandler);
250+
}
251+
252+
public static class VersionHandler {
253+
254+
private String libraryName;
255+
256+
public void shouldAlignWithVersionFrom(String libraryName) {
257+
this.libraryName = libraryName;
258+
}
259+
260+
}
261+
218262
public static class ProhibitedVersionHandler {
219263

220264
private String reason;
@@ -277,6 +321,29 @@ public void exclude(Map<String, String> exclusion) {
277321

278322
}
279323

324+
public class DependencyVersionsHandler {
325+
326+
public void extractFrom(Closure<?> closure) {
327+
ExtractFromHandler extractFromHandler = new ExtractFromHandler();
328+
ConfigureUtil.configure(closure, extractFromHandler);
329+
}
330+
331+
public class ExtractFromHandler {
332+
333+
public void dependencyLock(String location) {
334+
LibraryHandler.this.dependencyVersions = new DependencyLockDependencyVersions(location,
335+
LibraryHandler.this.version);
336+
}
337+
338+
public void dependencyConstraints(String location) {
339+
LibraryHandler.this.dependencyVersions = new DependencyConstraintsDependencyVersions(location,
340+
LibraryHandler.this.version);
341+
}
342+
343+
}
344+
345+
}
346+
280347
}
281348

282349
public static class UpgradeHandler {

buildSrc/src/main/java/org/springframework/boot/build/bom/CheckBom.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ void checkBom() {
5050
for (Group group : library.getGroups()) {
5151
for (Module module : group.getModules()) {
5252
if (!module.getExclusions().isEmpty()) {
53-
checkExclusions(group.getId(), module, library.getVersion());
53+
checkExclusions(group.getId(), module, library.getVersion().getVersion());
5454
}
5555
}
5656
}

buildSrc/src/main/java/org/springframework/boot/build/bom/Library.java

Lines changed: 166 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,20 @@
1616

1717
package org.springframework.boot.build.bom;
1818

19+
import java.io.BufferedReader;
20+
import java.io.IOException;
21+
import java.io.InputStreamReader;
22+
import java.net.URI;
1923
import java.util.Collections;
24+
import java.util.HashMap;
2025
import java.util.List;
2126
import java.util.Locale;
27+
import java.util.Map;
28+
import java.util.regex.Matcher;
29+
import java.util.regex.Pattern;
2230

2331
import org.apache.maven.artifact.versioning.VersionRange;
32+
import org.gradle.api.GradleException;
2433

2534
import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
2635

@@ -34,37 +43,41 @@ public class Library {
3443

3544
private final String name;
3645

37-
private final DependencyVersion version;
46+
private final LibraryVersion version;
3847

3948
private final List<Group> groups;
4049

4150
private final String versionProperty;
4251

4352
private final List<ProhibitedVersion> prohibitedVersions;
4453

54+
private final DependencyVersions dependencyVersions;
55+
4556
/**
4657
* Create a new {@code Library} with the given {@code name}, {@code version}, and
4758
* {@code groups}.
4859
* @param name name of the library
4960
* @param version version of the library
5061
* @param groups groups in the library
5162
* @param prohibitedVersions version of the library that are prohibited
63+
* @param dependencyVersions the library's dependency versions
5264
*/
53-
public Library(String name, DependencyVersion version, List<Group> groups,
54-
List<ProhibitedVersion> prohibitedVersions) {
65+
public Library(String name, LibraryVersion version, List<Group> groups, List<ProhibitedVersion> prohibitedVersions,
66+
DependencyVersions dependencyVersions) {
5567
this.name = name;
5668
this.version = version;
5769
this.groups = groups;
5870
this.versionProperty = "Spring Boot".equals(name) ? null
5971
: name.toLowerCase(Locale.ENGLISH).replace(' ', '-') + ".version";
6072
this.prohibitedVersions = prohibitedVersions;
73+
this.dependencyVersions = dependencyVersions;
6174
}
6275

6376
public String getName() {
6477
return this.name;
6578
}
6679

67-
public DependencyVersion getVersion() {
80+
public LibraryVersion getVersion() {
6881
return this.version;
6982
}
7083

@@ -80,6 +93,10 @@ public List<ProhibitedVersion> getProhibitedVersions() {
8093
return this.prohibitedVersions;
8194
}
8295

96+
public DependencyVersions getDependencyVersions() {
97+
return this.dependencyVersions;
98+
}
99+
83100
/**
84101
* A version or range of versions that are prohibited from being used in a bom.
85102
*/
@@ -104,6 +121,27 @@ public String getReason() {
104121

105122
}
106123

124+
public static class LibraryVersion {
125+
126+
private final DependencyVersion version;
127+
128+
private final VersionAlignment versionAlignment;
129+
130+
public LibraryVersion(DependencyVersion version, VersionAlignment versionAlignment) {
131+
this.version = version;
132+
this.versionAlignment = versionAlignment;
133+
}
134+
135+
public DependencyVersion getVersion() {
136+
return this.version;
137+
}
138+
139+
public VersionAlignment getVersionAlignment() {
140+
return this.versionAlignment;
141+
}
142+
143+
}
144+
107145
/**
108146
* A collection of modules, Maven plugins, and Maven boms with the same group ID.
109147
*/
@@ -194,4 +232,128 @@ public String getArtifactId() {
194232

195233
}
196234

235+
public interface DependencyVersions {
236+
237+
String getVersion(String groupId, String artifactId);
238+
239+
default boolean available() {
240+
return true;
241+
}
242+
243+
}
244+
245+
public static class DependencyLockDependencyVersions implements DependencyVersions {
246+
247+
private final Map<String, Map<String, String>> dependencyVersions = new HashMap<>();
248+
249+
private final String sourceTemplate;
250+
251+
private final String libraryVersion;
252+
253+
public DependencyLockDependencyVersions(String sourceTemplate, String libraryVersion) {
254+
this.sourceTemplate = sourceTemplate;
255+
this.libraryVersion = libraryVersion;
256+
}
257+
258+
@Override
259+
public boolean available() {
260+
return !this.libraryVersion.contains("-SNAPSHOT");
261+
}
262+
263+
@Override
264+
public String getVersion(String groupId, String artifactId) {
265+
if (this.dependencyVersions.isEmpty()) {
266+
loadVersions();
267+
}
268+
return this.dependencyVersions.computeIfAbsent(groupId, (key) -> Collections.emptyMap()).get(artifactId);
269+
}
270+
271+
private void loadVersions() {
272+
String source = this.sourceTemplate.replace("<libraryVersion>", this.libraryVersion);
273+
try {
274+
try (BufferedReader reader = new BufferedReader(
275+
new InputStreamReader(URI.create(source).toURL().openStream()))) {
276+
String line;
277+
while ((line = reader.readLine()) != null) {
278+
if (!line.startsWith("#")) {
279+
String[] components = line.split(":");
280+
Map<String, String> groupDependencies = this.dependencyVersions
281+
.computeIfAbsent(components[0], (key) -> new HashMap<>());
282+
groupDependencies.put(components[1], components[2]);
283+
}
284+
}
285+
}
286+
}
287+
catch (IOException ex) {
288+
throw new GradleException("Failed to load versions from dependency lock file '" + source + "'", ex);
289+
}
290+
}
291+
292+
}
293+
294+
public static class DependencyConstraintsDependencyVersions implements DependencyVersions {
295+
296+
private static final Pattern CONSTRAINT_PATTERN = Pattern.compile("api \"(.+):(.+):(.+)\"");
297+
298+
private final Map<String, Map<String, String>> dependencyVersions = new HashMap<>();
299+
300+
private final String sourceTemplate;
301+
302+
private final String libraryVersion;
303+
304+
public DependencyConstraintsDependencyVersions(String sourceTemplate, String libraryVersion) {
305+
this.sourceTemplate = sourceTemplate;
306+
this.libraryVersion = libraryVersion;
307+
}
308+
309+
@Override
310+
public String getVersion(String groupId, String artifactId) {
311+
if (this.dependencyVersions.isEmpty()) {
312+
loadVersions();
313+
}
314+
return this.dependencyVersions.computeIfAbsent(groupId, (key) -> Collections.emptyMap()).get(artifactId);
315+
}
316+
317+
private void loadVersions() {
318+
String version = this.libraryVersion;
319+
if (version.endsWith("-SNAPSHOT")) {
320+
version = version.substring(0, version.lastIndexOf('.')) + ".x";
321+
}
322+
String source = this.sourceTemplate.replace("<libraryVersion>", version);
323+
try {
324+
try (BufferedReader reader = new BufferedReader(
325+
new InputStreamReader(URI.create(source).toURL().openStream()))) {
326+
String line;
327+
while ((line = reader.readLine()) != null) {
328+
Matcher matcher = CONSTRAINT_PATTERN.matcher(line.trim());
329+
if (matcher.matches()) {
330+
Map<String, String> groupDependencies = this.dependencyVersions
331+
.computeIfAbsent(matcher.group(1), (key) -> new HashMap<>());
332+
groupDependencies.put(matcher.group(2), matcher.group(3));
333+
}
334+
}
335+
}
336+
}
337+
catch (IOException ex) {
338+
throw new GradleException(
339+
"Failed to load versions from dependency constraints declared in '" + source + "'", ex);
340+
}
341+
}
342+
343+
}
344+
345+
public static class VersionAlignment {
346+
347+
private final String libraryName;
348+
349+
public VersionAlignment(String libraryName) {
350+
this.libraryName = libraryName;
351+
}
352+
353+
public String getLibraryName() {
354+
return this.libraryName;
355+
}
356+
357+
}
358+
197359
}

0 commit comments

Comments
 (0)