Skip to content

Commit 7b59d2f

Browse files
committed
Merge branch '3.0.x' into 3.1.x
Closes spring-projectsgh-37224
2 parents 768dfb2 + cce3c9d commit 7b59d2f

19 files changed

+753
-74
lines changed

buildSrc/build.gradle

+3-1
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,11 @@ dependencies {
4444
implementation("org.springframework:spring-core")
4545
implementation("org.springframework:spring-web")
4646

47-
testImplementation("org.assertj:assertj-core:3.11.1")
4847
testImplementation("org.apache.logging.log4j:log4j-core:2.17.1")
48+
testImplementation("org.assertj:assertj-core:3.11.1")
49+
testImplementation("org.hamcrest:hamcrest:2.2")
4950
testImplementation("org.junit.jupiter:junit-jupiter:5.6.0")
51+
testImplementation("org.springframework:spring-test")
5052

5153
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
5254
}

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

+8-2
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@ public void library(String name, String version, Action<LibraryHandler> action)
113113
LibraryHandler libraryHandler = objects.newInstance(LibraryHandler.class, (version != null) ? version : "");
114114
action.execute(libraryHandler);
115115
LibraryVersion libraryVersion = new LibraryVersion(DependencyVersion.parse(libraryHandler.version));
116-
addLibrary(new Library(name, libraryVersion, libraryHandler.groups, libraryHandler.prohibitedVersions,
117-
libraryHandler.considerSnapshots));
116+
addLibrary(new Library(name, libraryHandler.calendarName, libraryVersion, libraryHandler.groups,
117+
libraryHandler.prohibitedVersions, libraryHandler.considerSnapshots));
118118
}
119119

120120
public void effectiveBomArtifact() {
@@ -218,6 +218,8 @@ public static class LibraryHandler {
218218

219219
private String version;
220220

221+
private String calendarName;
222+
221223
@Inject
222224
public LibraryHandler(String version) {
223225
this.version = version;
@@ -231,6 +233,10 @@ public void considerSnapshots() {
231233
this.considerSnapshots = true;
232234
}
233235

236+
public void setCalendarName(String calendarName) {
237+
this.calendarName = calendarName;
238+
}
239+
234240
public void group(String id, Action<GroupHandler> action) {
235241
GroupHandler groupHandler = new GroupHandler(id);
236242
action.execute(groupHandler);

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

+11-2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public class Library {
3434

3535
private final String name;
3636

37+
private final String calendarName;
38+
3739
private final LibraryVersion version;
3840

3941
private final List<Group> groups;
@@ -48,14 +50,17 @@ public class Library {
4850
* Create a new {@code Library} with the given {@code name}, {@code version}, and
4951
* {@code groups}.
5052
* @param name name of the library
53+
* @param calendarName name of the library as it appears in the Spring Calendar. May
54+
* be {@code null} in which case the {@code name} is used.
5155
* @param version version of the library
5256
* @param groups groups in the library
5357
* @param prohibitedVersions version of the library that are prohibited
5458
* @param considerSnapshots whether to consider snapshots
5559
*/
56-
public Library(String name, LibraryVersion version, List<Group> groups, List<ProhibitedVersion> prohibitedVersions,
57-
boolean considerSnapshots) {
60+
public Library(String name, String calendarName, LibraryVersion version, List<Group> groups,
61+
List<ProhibitedVersion> prohibitedVersions, boolean considerSnapshots) {
5862
this.name = name;
63+
this.calendarName = (calendarName != null) ? calendarName : name;
5964
this.version = version;
6065
this.groups = groups;
6166
this.versionProperty = "Spring Boot".equals(name) ? null
@@ -68,6 +73,10 @@ public String getName() {
6873
return this.name;
6974
}
7075

76+
public String getCalendarName() {
77+
return this.calendarName;
78+
}
79+
7180
public LibraryVersion getVersion() {
7281
return this.version;
7382
}

buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/MoveToSnapshots.java

+42
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,23 @@
1717
package org.springframework.boot.build.bom.bomr;
1818

1919
import java.net.URI;
20+
import java.time.OffsetDateTime;
21+
import java.util.List;
22+
import java.util.Map;
23+
import java.util.function.BiPredicate;
2024

2125
import javax.inject.Inject;
2226

2327
import org.gradle.api.Task;
28+
import org.gradle.api.tasks.TaskAction;
29+
import org.slf4j.Logger;
30+
import org.slf4j.LoggerFactory;
2431

2532
import org.springframework.boot.build.bom.BomExtension;
2633
import org.springframework.boot.build.bom.Library;
34+
import org.springframework.boot.build.bom.bomr.ReleaseSchedule.Release;
35+
import org.springframework.boot.build.bom.bomr.github.Milestone;
36+
import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
2737

2838
/**
2939
* A {@link Task} to move to snapshot dependencies.
@@ -32,6 +42,8 @@
3242
*/
3343
public abstract class MoveToSnapshots extends UpgradeDependencies {
3444

45+
private static final Logger log = LoggerFactory.getLogger(MoveToSnapshots.class);
46+
3547
private final URI REPOSITORY_URI = URI.create("https://repo.spring.io/snapshot/");
3648

3749
@Inject
@@ -40,6 +52,12 @@ public MoveToSnapshots(BomExtension bom) {
4052
getRepositoryUris().add(this.REPOSITORY_URI);
4153
}
4254

55+
@Override
56+
@TaskAction
57+
void upgradeDependencies() {
58+
super.upgradeDependencies();
59+
}
60+
4361
@Override
4462
protected String issueTitle(Upgrade upgrade) {
4563
String snapshotVersion = upgrade.getVersion().toString();
@@ -63,4 +81,28 @@ protected boolean eligible(Library library) {
6381
return library.isConsiderSnapshots() && super.eligible(library);
6482
}
6583

84+
@Override
85+
protected List<BiPredicate<Library, DependencyVersion>> determineUpdatePredicates(Milestone milestone) {
86+
ReleaseSchedule releaseSchedule = new ReleaseSchedule();
87+
Map<String, List<Release>> releases = releaseSchedule.releasesBetween(OffsetDateTime.now(),
88+
milestone.getDueOn());
89+
List<BiPredicate<Library, DependencyVersion>> predicates = super.determineUpdatePredicates(milestone);
90+
predicates.add((library, candidate) -> {
91+
List<Release> releasesForLibrary = releases.get(library.getCalendarName());
92+
if (releasesForLibrary != null) {
93+
for (Release release : releasesForLibrary) {
94+
if (candidate.isSnapshotFor(release.getVersion())) {
95+
return true;
96+
}
97+
}
98+
}
99+
if (log.isInfoEnabled()) {
100+
log.info("Ignoring " + candidate + ". No release of " + library.getName() + " scheduled before "
101+
+ milestone.getDueOn());
102+
}
103+
return false;
104+
});
105+
return predicates;
106+
}
107+
66108
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright 2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.build.bom.bomr;
18+
19+
import java.time.LocalDate;
20+
import java.time.OffsetDateTime;
21+
import java.util.ArrayList;
22+
import java.util.List;
23+
import java.util.Map;
24+
import java.util.Objects;
25+
import java.util.regex.Matcher;
26+
import java.util.regex.Pattern;
27+
28+
import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
29+
import org.springframework.http.ResponseEntity;
30+
import org.springframework.util.LinkedCaseInsensitiveMap;
31+
import org.springframework.web.client.RestOperations;
32+
import org.springframework.web.client.RestTemplate;
33+
34+
/**
35+
* Release schedule for Spring projects, retrieved from
36+
* <a href="https://calendar.spring.io">https://calendar.spring.io</a>.
37+
*
38+
* @author Andy Wilkinson
39+
*/
40+
class ReleaseSchedule {
41+
42+
private static final Pattern LIBRARY_AND_VERSION = Pattern.compile("([A-Za-z0-9 ]+) ([0-9A-Za-z.-]+)");
43+
44+
private final RestOperations rest;
45+
46+
ReleaseSchedule() {
47+
this(new RestTemplate());
48+
}
49+
50+
ReleaseSchedule(RestOperations rest) {
51+
this.rest = rest;
52+
}
53+
54+
@SuppressWarnings({ "unchecked", "rawtypes" })
55+
Map<String, List<Release>> releasesBetween(OffsetDateTime start, OffsetDateTime end) {
56+
ResponseEntity<List> response = this.rest
57+
.getForEntity("https://calendar.spring.io/releases?start=" + start + "&end=" + end, List.class);
58+
List<Map<String, String>> body = response.getBody();
59+
Map<String, List<Release>> releasesByLibrary = new LinkedCaseInsensitiveMap<>();
60+
body.stream()
61+
.map(this::asRelease)
62+
.filter(Objects::nonNull)
63+
.forEach((release) -> releasesByLibrary.computeIfAbsent(release.getLibraryName(), (l) -> new ArrayList<>())
64+
.add(release));
65+
return releasesByLibrary;
66+
}
67+
68+
private Release asRelease(Map<String, String> entry) {
69+
LocalDate due = LocalDate.parse(entry.get("start"));
70+
String title = entry.get("title");
71+
Matcher matcher = LIBRARY_AND_VERSION.matcher(title);
72+
if (!matcher.matches()) {
73+
return null;
74+
}
75+
String library = matcher.group(1);
76+
String version = matcher.group(2);
77+
return new Release(library, DependencyVersion.parse(version), due);
78+
}
79+
80+
static class Release {
81+
82+
private final String libraryName;
83+
84+
private final DependencyVersion version;
85+
86+
private final LocalDate dueOn;
87+
88+
Release(String libraryName, DependencyVersion version, LocalDate dueOn) {
89+
this.libraryName = libraryName;
90+
this.version = version;
91+
this.dueOn = dueOn;
92+
}
93+
94+
String getLibraryName() {
95+
return this.libraryName;
96+
}
97+
98+
DependencyVersion getVersion() {
99+
return this.version;
100+
}
101+
102+
LocalDate getDueOn() {
103+
return this.dueOn;
104+
}
105+
106+
}
107+
108+
}

buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/StandardLibraryUpdateResolver.java

+20-48
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,15 @@
2424
import java.util.List;
2525
import java.util.Map;
2626
import java.util.SortedSet;
27+
import java.util.function.BiPredicate;
2728
import java.util.stream.Collectors;
2829

29-
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
3030
import org.slf4j.Logger;
3131
import org.slf4j.LoggerFactory;
3232

3333
import org.springframework.boot.build.bom.Library;
3434
import org.springframework.boot.build.bom.Library.Group;
3535
import org.springframework.boot.build.bom.Library.Module;
36-
import org.springframework.boot.build.bom.Library.ProhibitedVersion;
37-
import org.springframework.boot.build.bom.UpgradePolicy;
3836
import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
3937

4038
/**
@@ -48,15 +46,21 @@ class StandardLibraryUpdateResolver implements LibraryUpdateResolver {
4846

4947
private final VersionResolver versionResolver;
5048

51-
private final UpgradePolicy upgradePolicy;
49+
private final BiPredicate<Library, DependencyVersion> predicate;
5250

53-
private final boolean movingToSnapshots;
54-
55-
StandardLibraryUpdateResolver(VersionResolver versionResolver, UpgradePolicy upgradePolicy,
56-
boolean movingToSnapshots) {
51+
StandardLibraryUpdateResolver(VersionResolver versionResolver,
52+
List<BiPredicate<Library, DependencyVersion>> predicates) {
5753
this.versionResolver = versionResolver;
58-
this.upgradePolicy = upgradePolicy;
59-
this.movingToSnapshots = movingToSnapshots;
54+
BiPredicate<Library, DependencyVersion> predicate = null;
55+
for (BiPredicate<Library, DependencyVersion> p : predicates) {
56+
if (predicate == null) {
57+
predicate = p;
58+
}
59+
else {
60+
predicate = predicate.and(p);
61+
}
62+
}
63+
this.predicate = predicate;
6064
}
6165

6266
@Override
@@ -87,26 +91,24 @@ protected List<VersionOption> getVersionOptions(Library library, Map<String, Lib
8791

8892
private List<VersionOption> determineResolvedVersionOptions(Library library) {
8993
Map<String, SortedSet<DependencyVersion>> moduleVersions = new LinkedHashMap<>();
90-
DependencyVersion libraryVersion = library.getVersion().getVersion();
9194
for (Group group : library.getGroups()) {
9295
for (Module module : group.getModules()) {
9396
moduleVersions.put(group.getId() + ":" + module.getName(),
94-
getLaterVersionsForModule(group.getId(), module.getName(), libraryVersion));
97+
getLaterVersionsForModule(group.getId(), module.getName(), library));
9598
}
9699
for (String bom : group.getBoms()) {
97-
moduleVersions.put(group.getId() + ":" + bom,
98-
getLaterVersionsForModule(group.getId(), bom, libraryVersion));
100+
moduleVersions.put(group.getId() + ":" + bom, getLaterVersionsForModule(group.getId(), bom, library));
99101
}
100102
for (String plugin : group.getPlugins()) {
101103
moduleVersions.put(group.getId() + ":" + plugin,
102-
getLaterVersionsForModule(group.getId(), plugin, libraryVersion));
104+
getLaterVersionsForModule(group.getId(), plugin, library));
103105
}
104106
}
105107
List<DependencyVersion> allVersions = moduleVersions.values()
106108
.stream()
107109
.flatMap(SortedSet::stream)
108110
.distinct()
109-
.filter((dependencyVersion) -> isPermitted(dependencyVersion, library.getProhibitedVersions()))
111+
.filter((dependencyVersion) -> this.predicate.test(library, dependencyVersion))
110112
.toList();
111113
if (allVersions.isEmpty()) {
112114
return Collections.emptyList();
@@ -117,32 +119,6 @@ private List<VersionOption> determineResolvedVersionOptions(Library library) {
117119
.collect(Collectors.toList());
118120
}
119121

120-
private boolean isPermitted(DependencyVersion dependencyVersion, List<ProhibitedVersion> prohibitedVersions) {
121-
for (ProhibitedVersion prohibitedVersion : prohibitedVersions) {
122-
String dependencyVersionToString = dependencyVersion.toString();
123-
if (prohibitedVersion.getRange() != null && prohibitedVersion.getRange()
124-
.containsVersion(new DefaultArtifactVersion(dependencyVersionToString))) {
125-
return false;
126-
}
127-
for (String startsWith : prohibitedVersion.getStartsWith()) {
128-
if (dependencyVersionToString.startsWith(startsWith)) {
129-
return false;
130-
}
131-
}
132-
for (String endsWith : prohibitedVersion.getEndsWith()) {
133-
if (dependencyVersionToString.endsWith(endsWith)) {
134-
return false;
135-
}
136-
}
137-
for (String contains : prohibitedVersion.getContains()) {
138-
if (dependencyVersionToString.contains(contains)) {
139-
return false;
140-
}
141-
}
142-
}
143-
return true;
144-
}
145-
146122
private List<String> getMissingModules(Map<String, SortedSet<DependencyVersion>> moduleVersions,
147123
DependencyVersion version) {
148124
List<String> missingModules = new ArrayList<>();
@@ -154,12 +130,8 @@ private List<String> getMissingModules(Map<String, SortedSet<DependencyVersion>>
154130
return missingModules;
155131
}
156132

157-
private SortedSet<DependencyVersion> getLaterVersionsForModule(String groupId, String artifactId,
158-
DependencyVersion currentVersion) {
159-
SortedSet<DependencyVersion> versions = this.versionResolver.resolveVersions(groupId, artifactId);
160-
versions.removeIf((candidate) -> !this.upgradePolicy.test(candidate, currentVersion));
161-
versions.removeIf((candidate) -> !currentVersion.isUpgrade(candidate, this.movingToSnapshots));
162-
return versions;
133+
private SortedSet<DependencyVersion> getLaterVersionsForModule(String groupId, String artifactId, Library library) {
134+
return this.versionResolver.resolveVersions(groupId, artifactId);
163135
}
164136

165137
}

0 commit comments

Comments
 (0)