diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index a0fa56fff9..26c2898aea 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -39,9 +39,11 @@ dependencies { implementation("org.springframework:spring-core") implementation("org.springframework:spring-web") - testImplementation("org.assertj:assertj-core:3.11.1") testImplementation("org.apache.logging.log4j:log4j-core:2.17.1") + testImplementation("org.assertj:assertj-core:3.11.1") + testImplementation("org.hamcrest:hamcrest:2.2") testImplementation("org.junit.jupiter:junit-jupiter:5.6.0") + testImplementation("org.springframework:spring-test") testRuntimeOnly("org.junit.platform:junit-platform-launcher") } diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/BomExtension.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/BomExtension.java index 0c63ba882a..5291f71909 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/BomExtension.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/BomExtension.java @@ -113,8 +113,8 @@ public class BomExtension { LibraryHandler libraryHandler = objects.newInstance(LibraryHandler.class, (version != null) ? version : ""); action.execute(libraryHandler); LibraryVersion libraryVersion = new LibraryVersion(DependencyVersion.parse(libraryHandler.version)); - addLibrary(new Library(name, libraryVersion, libraryHandler.groups, libraryHandler.prohibitedVersions, - libraryHandler.considerSnapshots)); + addLibrary(new Library(name, libraryHandler.calendarName, libraryVersion, libraryHandler.groups, + libraryHandler.prohibitedVersions, libraryHandler.considerSnapshots)); } public void effectiveBomArtifact() { @@ -218,6 +218,8 @@ public class BomExtension { private String version; + private String calendarName; + @Inject public LibraryHandler(String version) { this.version = version; @@ -231,6 +233,10 @@ public class BomExtension { this.considerSnapshots = true; } + public void setCalendarName(String calendarName) { + this.calendarName = calendarName; + } + public void group(String id, Action action) { GroupHandler groupHandler = new GroupHandler(id); action.execute(groupHandler); diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/Library.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/Library.java index 9ab51896ff..42912bb6df 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/Library.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/Library.java @@ -34,6 +34,8 @@ public class Library { private final String name; + private final String calendarName; + private final LibraryVersion version; private final List groups; @@ -48,14 +50,17 @@ public class Library { * Create a new {@code Library} with the given {@code name}, {@code version}, and * {@code groups}. * @param name name of the library + * @param calendarName name of the library as it appears in the Spring Calendar. May + * be {@code null} in which case the {@code name} is used. * @param version version of the library * @param groups groups in the library * @param prohibitedVersions version of the library that are prohibited * @param considerSnapshots whether to consider snapshots */ - public Library(String name, LibraryVersion version, List groups, List prohibitedVersions, - boolean considerSnapshots) { + public Library(String name, String calendarName, LibraryVersion version, List groups, + List prohibitedVersions, boolean considerSnapshots) { this.name = name; + this.calendarName = (calendarName != null) ? calendarName : name; this.version = version; this.groups = groups; this.versionProperty = "Spring Boot".equals(name) ? null @@ -68,6 +73,10 @@ public class Library { return this.name; } + public String getCalendarName() { + return this.calendarName; + } + public LibraryVersion getVersion() { return this.version; } diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/MoveToSnapshots.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/MoveToSnapshots.java index f0927d866b..b475458393 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/MoveToSnapshots.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/MoveToSnapshots.java @@ -17,13 +17,23 @@ package org.springframework.boot.build.bom.bomr; import java.net.URI; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.function.BiPredicate; import javax.inject.Inject; import org.gradle.api.Task; +import org.gradle.api.tasks.TaskAction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.boot.build.bom.BomExtension; import org.springframework.boot.build.bom.Library; +import org.springframework.boot.build.bom.bomr.ReleaseSchedule.Release; +import org.springframework.boot.build.bom.bomr.github.Milestone; +import org.springframework.boot.build.bom.bomr.version.DependencyVersion; /** * A {@link Task} to move to snapshot dependencies. @@ -32,6 +42,8 @@ import org.springframework.boot.build.bom.Library; */ public abstract class MoveToSnapshots extends UpgradeDependencies { + private static final Logger log = LoggerFactory.getLogger(MoveToSnapshots.class); + private final URI REPOSITORY_URI = URI.create("https://repo.spring.io/snapshot/"); @Inject @@ -40,6 +52,12 @@ public abstract class MoveToSnapshots extends UpgradeDependencies { getRepositoryUris().add(this.REPOSITORY_URI); } + @Override + @TaskAction + void upgradeDependencies() { + super.upgradeDependencies(); + } + @Override protected String issueTitle(Upgrade upgrade) { String snapshotVersion = upgrade.getVersion().toString(); @@ -63,4 +81,28 @@ public abstract class MoveToSnapshots extends UpgradeDependencies { return library.isConsiderSnapshots() && super.eligible(library); } + @Override + protected List> determineUpdatePredicates(Milestone milestone) { + ReleaseSchedule releaseSchedule = new ReleaseSchedule(); + Map> releases = releaseSchedule.releasesBetween(OffsetDateTime.now(), + milestone.getDueOn()); + List> predicates = super.determineUpdatePredicates(milestone); + predicates.add((library, candidate) -> { + List releasesForLibrary = releases.get(library.getCalendarName()); + if (releasesForLibrary != null) { + for (Release release : releasesForLibrary) { + if (candidate.isSnapshotFor(release.getVersion())) { + return true; + } + } + } + if (log.isInfoEnabled()) { + log.info("Ignoring " + candidate + ". No release of " + library.getName() + " scheduled before " + + milestone.getDueOn()); + } + return false; + }); + return predicates; + } + } diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/ReleaseSchedule.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/ReleaseSchedule.java new file mode 100644 index 0000000000..28d827867c --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/ReleaseSchedule.java @@ -0,0 +1,108 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.build.bom.bomr; + +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.springframework.boot.build.bom.bomr.version.DependencyVersion; +import org.springframework.http.ResponseEntity; +import org.springframework.util.LinkedCaseInsensitiveMap; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +/** + * Release schedule for Spring projects, retrieved from + * https://calendar.spring.io. + * + * @author Andy Wilkinson + */ +class ReleaseSchedule { + + private static final Pattern LIBRARY_AND_VERSION = Pattern.compile("([A-Za-z0-9 ]+) ([0-9A-Za-z.-]+)"); + + private final RestOperations rest; + + ReleaseSchedule() { + this(new RestTemplate()); + } + + ReleaseSchedule(RestOperations rest) { + this.rest = rest; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + Map> releasesBetween(OffsetDateTime start, OffsetDateTime end) { + ResponseEntity response = this.rest + .getForEntity("https://calendar.spring.io/releases?start=" + start + "&end=" + end, List.class); + List> body = response.getBody(); + Map> releasesByLibrary = new LinkedCaseInsensitiveMap<>(); + body.stream() + .map(this::asRelease) + .filter(Objects::nonNull) + .forEach((release) -> releasesByLibrary.computeIfAbsent(release.getLibraryName(), (l) -> new ArrayList<>()) + .add(release)); + return releasesByLibrary; + } + + private Release asRelease(Map entry) { + LocalDate due = LocalDate.parse(entry.get("start")); + String title = entry.get("title"); + Matcher matcher = LIBRARY_AND_VERSION.matcher(title); + if (!matcher.matches()) { + return null; + } + String library = matcher.group(1); + String version = matcher.group(2); + return new Release(library, DependencyVersion.parse(version), due); + } + + static class Release { + + private final String libraryName; + + private final DependencyVersion version; + + private final LocalDate dueOn; + + Release(String libraryName, DependencyVersion version, LocalDate dueOn) { + this.libraryName = libraryName; + this.version = version; + this.dueOn = dueOn; + } + + String getLibraryName() { + return this.libraryName; + } + + DependencyVersion getVersion() { + return this.version; + } + + LocalDate getDueOn() { + return this.dueOn; + } + + } + +} diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/StandardLibraryUpdateResolver.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/StandardLibraryUpdateResolver.java index 31d6723931..3e39206fc3 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/StandardLibraryUpdateResolver.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/StandardLibraryUpdateResolver.java @@ -23,16 +23,14 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.SortedSet; +import java.util.function.BiPredicate; -import org.apache.maven.artifact.versioning.DefaultArtifactVersion; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.build.bom.Library; import org.springframework.boot.build.bom.Library.Group; import org.springframework.boot.build.bom.Library.Module; -import org.springframework.boot.build.bom.Library.ProhibitedVersion; -import org.springframework.boot.build.bom.UpgradePolicy; import org.springframework.boot.build.bom.bomr.version.DependencyVersion; /** @@ -46,15 +44,21 @@ class StandardLibraryUpdateResolver implements LibraryUpdateResolver { private final VersionResolver versionResolver; - private final UpgradePolicy upgradePolicy; + private final BiPredicate predicate; - private final boolean movingToSnapshots; - - StandardLibraryUpdateResolver(VersionResolver versionResolver, UpgradePolicy upgradePolicy, - boolean movingToSnapshots) { + StandardLibraryUpdateResolver(VersionResolver versionResolver, + List> predicates) { this.versionResolver = versionResolver; - this.upgradePolicy = upgradePolicy; - this.movingToSnapshots = movingToSnapshots; + BiPredicate predicate = null; + for (BiPredicate p : predicates) { + if (predicate == null) { + predicate = p; + } + else { + predicate = predicate.and(p); + } + } + this.predicate = predicate; } @Override @@ -85,57 +89,29 @@ class StandardLibraryUpdateResolver implements LibraryUpdateResolver { private List determineResolvedVersionOptions(Library library) { Map> moduleVersions = new LinkedHashMap<>(); - DependencyVersion libraryVersion = library.getVersion().getVersion(); for (Group group : library.getGroups()) { for (Module module : group.getModules()) { moduleVersions.put(group.getId() + ":" + module.getName(), - getLaterVersionsForModule(group.getId(), module.getName(), libraryVersion)); + getLaterVersionsForModule(group.getId(), module.getName(), library)); } for (String bom : group.getBoms()) { - moduleVersions.put(group.getId() + ":" + bom, - getLaterVersionsForModule(group.getId(), bom, libraryVersion)); + moduleVersions.put(group.getId() + ":" + bom, getLaterVersionsForModule(group.getId(), bom, library)); } for (String plugin : group.getPlugins()) { moduleVersions.put(group.getId() + ":" + plugin, - getLaterVersionsForModule(group.getId(), plugin, libraryVersion)); + getLaterVersionsForModule(group.getId(), plugin, library)); } } return moduleVersions.values() .stream() .flatMap(SortedSet::stream) .distinct() - .filter((dependencyVersion) -> isPermitted(dependencyVersion, library.getProhibitedVersions())) + .filter((dependencyVersion) -> this.predicate.test(library, dependencyVersion)) .map((version) -> (VersionOption) new VersionOption.ResolvedVersionOption(version, getMissingModules(moduleVersions, version))) .toList(); } - private boolean isPermitted(DependencyVersion dependencyVersion, List prohibitedVersions) { - for (ProhibitedVersion prohibitedVersion : prohibitedVersions) { - String dependencyVersionToString = dependencyVersion.toString(); - if (prohibitedVersion.getRange() != null && prohibitedVersion.getRange() - .containsVersion(new DefaultArtifactVersion(dependencyVersionToString))) { - return false; - } - for (String startsWith : prohibitedVersion.getStartsWith()) { - if (dependencyVersionToString.startsWith(startsWith)) { - return false; - } - } - for (String endsWith : prohibitedVersion.getEndsWith()) { - if (dependencyVersionToString.endsWith(endsWith)) { - return false; - } - } - for (String contains : prohibitedVersion.getContains()) { - if (dependencyVersionToString.contains(contains)) { - return false; - } - } - } - return true; - } - private List getMissingModules(Map> moduleVersions, DependencyVersion version) { List missingModules = new ArrayList<>(); @@ -147,12 +123,8 @@ class StandardLibraryUpdateResolver implements LibraryUpdateResolver { return missingModules; } - private SortedSet getLaterVersionsForModule(String groupId, String artifactId, - DependencyVersion currentVersion) { - SortedSet versions = this.versionResolver.resolveVersions(groupId, artifactId); - versions.removeIf((candidate) -> !this.upgradePolicy.test(candidate, currentVersion)); - versions.removeIf((candidate) -> !currentVersion.isUpgrade(candidate, this.movingToSnapshots)); - return versions; + private SortedSet getLaterVersionsForModule(String groupId, String artifactId, Library library) { + return this.versionResolver.resolveVersions(groupId, artifactId); } } diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/UpgradeDependencies.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/UpgradeDependencies.java index 9375ebe367..2110e762b8 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/UpgradeDependencies.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/UpgradeDependencies.java @@ -27,11 +27,13 @@ import java.util.Arrays; import java.util.List; import java.util.Properties; import java.util.Set; +import java.util.function.BiPredicate; import java.util.function.Predicate; import java.util.regex.Pattern; import javax.inject.Inject; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; import org.gradle.api.DefaultTask; import org.gradle.api.InvalidUserDataException; import org.gradle.api.internal.tasks.userinput.UserInputHandler; @@ -45,10 +47,12 @@ import org.gradle.api.tasks.options.Option; import org.springframework.boot.build.bom.BomExtension; import org.springframework.boot.build.bom.Library; +import org.springframework.boot.build.bom.Library.ProhibitedVersion; import org.springframework.boot.build.bom.bomr.github.GitHub; import org.springframework.boot.build.bom.bomr.github.GitHubRepository; import org.springframework.boot.build.bom.bomr.github.Issue; import org.springframework.boot.build.bom.bomr.github.Milestone; +import org.springframework.boot.build.bom.bomr.version.DependencyVersion; import org.springframework.util.StringUtils; /** @@ -70,8 +74,8 @@ public abstract class UpgradeDependencies extends DefaultTask { protected UpgradeDependencies(BomExtension bom, boolean movingToSnapshots) { this.bom = bom; - this.movingToSnapshots = movingToSnapshots; getThreads().convention(2); + this.movingToSnapshots = movingToSnapshots; } @Input @@ -97,7 +101,7 @@ public abstract class UpgradeDependencies extends DefaultTask { this.bom.getUpgrade().getGitHub().getRepository()); List issueLabels = verifyLabels(repository); Milestone milestone = determineMilestone(repository); - List upgrades = resolveUpgrades(); + List upgrades = resolveUpgrades(milestone); applyUpgrades(repository, issueLabels, milestone, upgrades); } @@ -213,15 +217,52 @@ public abstract class UpgradeDependencies extends DefaultTask { } @SuppressWarnings("deprecation") - private List resolveUpgrades() { + private List resolveUpgrades(Milestone milestone) { List upgrades = new InteractiveUpgradeResolver(getServices().get(UserInputHandler.class), new MultithreadedLibraryUpdateResolver(getThreads().get(), new StandardLibraryUpdateResolver(new MavenMetadataVersionResolver(getRepositoryUris().get()), - this.bom.getUpgrade().getPolicy(), this.movingToSnapshots))) + determineUpdatePredicates(milestone)))) .resolveUpgrades(matchingLibraries(), this.bom.getLibraries()); return upgrades; } + protected List> determineUpdatePredicates(Milestone milestone) { + BiPredicate compilesWithUpgradePolicy = (library, + candidate) -> this.bom.getUpgrade().getPolicy().test(candidate, library.getVersion().getVersion()); + BiPredicate isAnUpgrade = (library, + candidate) -> library.getVersion().getVersion().isUpgrade(candidate, this.movingToSnapshots); + BiPredicate isPermitted = (library, candidate) -> { + for (ProhibitedVersion prohibitedVersion : library.getProhibitedVersions()) { + String candidateString = candidate.toString(); + if (prohibitedVersion.getRange() != null + && prohibitedVersion.getRange().containsVersion(new DefaultArtifactVersion(candidateString))) { + return false; + } + for (String startsWith : prohibitedVersion.getStartsWith()) { + if (candidateString.startsWith(startsWith)) { + return false; + } + } + for (String endsWith : prohibitedVersion.getEndsWith()) { + if (candidateString.endsWith(endsWith)) { + return false; + } + } + for (String contains : prohibitedVersion.getContains()) { + if (candidateString.contains(contains)) { + return false; + } + } + } + return true; + }; + List> updatePredicates = new ArrayList<>(); + updatePredicates.add(compilesWithUpgradePolicy); + updatePredicates.add(isAnUpgrade); + updatePredicates.add(isPermitted); + return updatePredicates; + } + private List matchingLibraries() { List matchingLibraries = this.bom.getLibraries().stream().filter(this::eligible).toList(); if (matchingLibraries.isEmpty()) { diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/Milestone.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/Milestone.java index f50dd28c48..7a1ce8ae26 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/Milestone.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/Milestone.java @@ -16,6 +16,8 @@ package org.springframework.boot.build.bom.bomr.github; +import java.time.OffsetDateTime; + /** * A milestone in a {@link GitHubRepository GitHub repository}. * @@ -27,9 +29,12 @@ public class Milestone { private final int number; - Milestone(String name, int number) { + private final OffsetDateTime dueOn; + + Milestone(String name, int number, OffsetDateTime dueOn) { this.name = name; this.number = number; + this.dueOn = dueOn; } /** @@ -48,6 +53,10 @@ public class Milestone { return this.number; } + public OffsetDateTime getDueOn() { + return this.dueOn; + } + @Override public String toString() { return this.name + " (" + this.number + ")"; diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/StandardGitHubRepository.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/StandardGitHubRepository.java index 28027e2c88..2a5fb9ba52 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/StandardGitHubRepository.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/github/StandardGitHubRepository.java @@ -17,6 +17,7 @@ package org.springframework.boot.build.bom.bomr.github; import java.time.Duration; +import java.time.OffsetDateTime; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -75,8 +76,9 @@ final class StandardGitHubRepository implements GitHubRepository { @Override public List getMilestones() { - return get("milestones?per_page=100", - (milestone) -> new Milestone((String) milestone.get("title"), (Integer) milestone.get("number"))); + return get("milestones?per_page=100", (milestone) -> new Milestone((String) milestone.get("title"), + (Integer) milestone.get("number"), + (milestone.get("due_on") != null) ? OffsetDateTime.parse((String) milestone.get("due_on")) : null)); } @Override diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/ArtifactVersionDependencyVersion.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/ArtifactVersionDependencyVersion.java index e3d29c0c4f..61e1d761e2 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/ArtifactVersionDependencyVersion.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/ArtifactVersionDependencyVersion.java @@ -88,24 +88,40 @@ class ArtifactVersionDependencyVersion extends AbstractDependencyVersion { if (this.artifactVersion.equals(other)) { return false; } - if (this.artifactVersion.getMajorVersion() == other.getMajorVersion() - && this.artifactVersion.getMinorVersion() == other.getMinorVersion() - && this.artifactVersion.getIncrementalVersion() == other.getIncrementalVersion()) { + if (sameMajorMinorIncremental(other)) { if (!StringUtils.hasLength(this.artifactVersion.getQualifier()) || "RELEASE".equals(this.artifactVersion.getQualifier())) { return false; } - if ("SNAPSHOT".equals(this.artifactVersion.getQualifier()) - || "BUILD".equals(this.artifactVersion.getQualifier())) { + if (isSnapshot()) { return true; } - else if ("SNAPSHOT".equals(other.getQualifier()) || "BUILD".equals(other.getQualifier())) { + else if (((ArtifactVersionDependencyVersion) candidate).isSnapshot()) { return movingToSnapshots; } } return super.isUpgrade(candidate, movingToSnapshots); } + private boolean sameMajorMinorIncremental(ArtifactVersion other) { + return this.artifactVersion.getMajorVersion() == other.getMajorVersion() + && this.artifactVersion.getMinorVersion() == other.getMinorVersion() + && this.artifactVersion.getIncrementalVersion() == other.getIncrementalVersion(); + } + + private boolean isSnapshot() { + return "SNAPSHOT".equals(this.artifactVersion.getQualifier()) + || "BUILD".equals(this.artifactVersion.getQualifier()); + } + + @Override + public boolean isSnapshotFor(DependencyVersion candidate) { + if (!isSnapshot() || !(candidate instanceof ArtifactVersionDependencyVersion)) { + return false; + } + return sameMajorMinorIncremental(((ArtifactVersionDependencyVersion) candidate).artifactVersion); + } + @Override public int compareTo(DependencyVersion other) { if (other instanceof ArtifactVersionDependencyVersion otherArtifactDependencyVersion) { diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/DependencyVersion.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/DependencyVersion.java index 11a3a88ff5..f4b9b897a1 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/DependencyVersion.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/DependencyVersion.java @@ -46,13 +46,21 @@ public interface DependencyVersion extends Comparable { /** * Returns whether the given {@code candidate} is an upgrade of this version. - * @param candidate the version the consider + * @param candidate the version to consider * @param movingToSnapshots whether the upgrade is to be considered as part of moving * to snaphots * @return {@code true} if the candidate is an upgrade, otherwise false */ boolean isUpgrade(DependencyVersion candidate, boolean movingToSnapshots); + /** + * Returns whether this version is a snapshot for the given {@code candidate}. + * @param candidate the version to consider + * @return {@code true} if this version is a snapshot for the candidate, otherwise + * false + */ + boolean isSnapshotFor(DependencyVersion candidate); + static DependencyVersion parse(String version) { List> parsers = Arrays.asList(CalendarVersionDependencyVersion::parse, ArtifactVersionDependencyVersion::parse, ReleaseTrainDependencyVersion::parse, diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/ReleaseTrainDependencyVersion.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/ReleaseTrainDependencyVersion.java index 6c35a6c000..c9c79bcdc6 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/ReleaseTrainDependencyVersion.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/ReleaseTrainDependencyVersion.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -72,8 +72,7 @@ final class ReleaseTrainDependencyVersion implements DependencyVersion { if (comparison != 0) { return comparison < 0; } - if (movingToSnapshots && !"BUILD-SNAPSHOT".equals(this.type) - && "BUILD-SNAPSHOT".equals(candidateReleaseTrain.type)) { + if (movingToSnapshots && !isSnapshot() && candidateReleaseTrain.isSnapshot()) { return true; } comparison = this.type.compareTo(candidateReleaseTrain.type); @@ -83,6 +82,19 @@ final class ReleaseTrainDependencyVersion implements DependencyVersion { return Integer.compare(this.version, candidateReleaseTrain.version) < 0; } + private boolean isSnapshot() { + return "BUILD-SNAPSHOT".equals(this.type); + } + + @Override + public boolean isSnapshotFor(DependencyVersion candidate) { + if (!isSnapshot() || !(candidate instanceof ReleaseTrainDependencyVersion)) { + return false; + } + ReleaseTrainDependencyVersion candidateReleaseTrain = (ReleaseTrainDependencyVersion) candidate; + return this.releaseTrain.equals(candidateReleaseTrain.releaseTrain); + } + @Override public boolean isSameMajor(DependencyVersion other) { return isSameReleaseTrain(other); diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/UnstructuredDependencyVersion.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/UnstructuredDependencyVersion.java index e23a6e5ed7..cdd44f9cbc 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/UnstructuredDependencyVersion.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/version/UnstructuredDependencyVersion.java @@ -48,6 +48,11 @@ final class UnstructuredDependencyVersion extends AbstractDependencyVersion impl return this.version; } + @Override + public boolean isSnapshotFor(DependencyVersion candidate) { + return false; + } + static UnstructuredDependencyVersion parse(String version) { return new UnstructuredDependencyVersion(version); } diff --git a/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/ReleaseScheduleTests.java b/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/ReleaseScheduleTests.java new file mode 100644 index 0000000000..3931221ffa --- /dev/null +++ b/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/ReleaseScheduleTests.java @@ -0,0 +1,62 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.build.bom.bomr; + +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.build.bom.bomr.ReleaseSchedule.Release; +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.MediaType; +import org.springframework.test.web.client.MockRestServiceServer; +import org.springframework.web.client.RestTemplate; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; + +/** + * Tests for {@link ReleaseSchedule}. + * + * @author Andy Wilkinson + */ +public class ReleaseScheduleTests { + + private final RestTemplate rest = new RestTemplate(); + + private final ReleaseSchedule releaseSchedule = new ReleaseSchedule(this.rest); + + private final MockRestServiceServer server = MockRestServiceServer.bindTo(this.rest).build(); + + @Test + void releasesBetween() { + this.server + .expect(requestTo("https://calendar.spring.io/releases?start=2023-09-01T00:00Z&end=2023-09-21T23:59Z")) + .andRespond(withSuccess(new ClassPathResource("releases.json"), MediaType.APPLICATION_JSON)); + Map> releases = this.releaseSchedule + .releasesBetween(OffsetDateTime.parse("2023-09-01T00:00Z"), OffsetDateTime.parse("2023-09-21T23:59Z")); + assertThat(releases).hasSize(23); + assertThat(releases.get("Spring Framework")).hasSize(3); + assertThat(releases.get("Spring Boot")).hasSize(4); + assertThat(releases.get("Spring Modulith")).hasSize(1); + assertThat(releases.get("spring graphql")).hasSize(3); + } + +} diff --git a/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/UpgradeApplicatorTests.java b/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/UpgradeApplicatorTests.java index 64235849dc..af184a2be5 100644 --- a/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/UpgradeApplicatorTests.java +++ b/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/UpgradeApplicatorTests.java @@ -51,9 +51,9 @@ class UpgradeApplicatorTests { 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, false), - DependencyVersion.parse("5.16"))); + new UpgradeApplicator(bom.toPath(), gradleProperties.toPath()) + .apply(new Upgrade(new Library("ActiveMQ", null, new LibraryVersion(DependencyVersion.parse("5.15.11")), + null, null, false), DependencyVersion.parse("5.16"))); String bomContents = Files.readString(bom.toPath()); assertThat(bomContents).hasSize(originalContents.length() - 3); } @@ -65,7 +65,7 @@ class UpgradeApplicatorTests { 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("Kotlin", new LibraryVersion(DependencyVersion.parse("1.3.70")), null, null, false), + new Library("Kotlin", null, new LibraryVersion(DependencyVersion.parse("1.3.70")), null, null, false), DependencyVersion.parse("1.4"))); Properties properties = new Properties(); try (InputStream in = new FileInputStream(gradleProperties)) { diff --git a/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/version/ArtifactVersionDependencyVersionTests.java b/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/version/ArtifactVersionDependencyVersionTests.java index 639fb06d49..a50e6ba016 100644 --- a/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/version/ArtifactVersionDependencyVersionTests.java +++ b/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/version/ArtifactVersionDependencyVersionTests.java @@ -62,6 +62,71 @@ class ArtifactVersionDependencyVersionTests { assertThat(version("1.10.2").isSameMinor(version("1.9.1"))).isFalse(); } + @Test + void isSnapshotForWhenSnapshotForReleaseShouldReturnTrue() { + assertThat(version("1.10.2-SNAPSHOT").isSnapshotFor(version("1.10.2"))).isTrue(); + } + + @Test + void isSnapshotForWhenBuildSnapshotForReleaseShouldReturnTrue() { + assertThat(version("1.10.2.BUILD-SNAPSHOT").isSnapshotFor(version("1.10.2.RELEASE"))).isTrue(); + } + + @Test + void isSnapshotForWhenSnapshotForReleaseCandidateShouldReturnTrue() { + assertThat(version("1.10.2-SNAPSHOT").isSnapshotFor(version("1.10.2-RC2"))).isTrue(); + } + + @Test + void isSnapshotForWhenBuildSnapshotForReleaseCandidateShouldReturnTrue() { + assertThat(version("1.10.2.BUILD-SNAPSHOT").isSnapshotFor(version("1.10.2.RC2"))).isTrue(); + } + + @Test + void isSnapshotForWhenSnapshotForMilestoneShouldReturnTrue() { + assertThat(version("1.10.2-SNAPSHOT").isSnapshotFor(version("1.10.2-M1"))).isTrue(); + } + + @Test + void isSnapshotForWhenBuildSnapshotForMilestoneShouldReturnTrue() { + assertThat(version("1.10.2.BUILD-SNAPSHOT").isSnapshotFor(version("1.10.2.M1"))).isTrue(); + } + + @Test + void isSnapshotForWhenSnapshotForDifferentReleaseShouldReturnFalse() { + assertThat(version("1.10.1-SNAPSHOT").isSnapshotFor(version("1.10.2"))).isFalse(); + } + + @Test + void isSnapshotForWhenBuildSnapshotForDifferentReleaseShouldReturnTrue() { + assertThat(version("1.10.1.BUILD-SNAPSHOT").isSnapshotFor(version("1.10.2.RELEASE"))).isFalse(); + } + + @Test + void isSnapshotForWhenSnapshotForDifferentReleaseCandidateShouldReturnTrue() { + assertThat(version("1.10.1-SNAPSHOT").isSnapshotFor(version("1.10.2-RC2"))).isFalse(); + } + + @Test + void isSnapshotForWhenBuildSnapshotForDifferentReleaseCandidateShouldReturnTrue() { + assertThat(version("1.10.1.BUILD-SNAPSHOT").isSnapshotFor(version("1.10.2.RC2"))).isFalse(); + } + + @Test + void isSnapshotForWhenSnapshotForDifferentMilestoneShouldReturnTrue() { + assertThat(version("1.10.1-SNAPSHOT").isSnapshotFor(version("1.10.2-M1"))).isFalse(); + } + + @Test + void isSnapshotForWhenBuildSnapshotForDifferentMilestoneShouldReturnTrue() { + assertThat(version("1.10.1.BUILD-SNAPSHOT").isSnapshotFor(version("1.10.2.M1"))).isFalse(); + } + + @Test + void isSnapshotForWhenNotSnapshotShouldReturnFalse() { + assertThat(version("1.10.1-M1").isSnapshotFor(version("1.10.1"))).isFalse(); + } + private ArtifactVersionDependencyVersion version(String version) { return ArtifactVersionDependencyVersion.parse(version); } diff --git a/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/version/ReleaseTrainDependencyVersionTests.java b/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/version/ReleaseTrainDependencyVersionTests.java index 8aa9b922de..d9c4541c91 100644 --- a/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/version/ReleaseTrainDependencyVersionTests.java +++ b/buildSrc/src/test/java/org/springframework/boot/build/bom/bomr/version/ReleaseTrainDependencyVersionTests.java @@ -67,6 +67,46 @@ class ReleaseTrainDependencyVersionTests { assertThat(version("Kay-SR6").isSameMinor(calendarVersion("2020.0.0"))).isFalse(); } + @Test + void isSnapshotForWhenSnapshotForServiceReleaseShouldReturnTrue() { + assertThat(version("Kay-BUILD-SNAPSHOT").isSnapshotFor(version("Kay-SR2"))).isTrue(); + } + + @Test + void isSnapshotForWhenSnapshotForReleaseShouldReturnTrue() { + assertThat(version("Kay-BUILD-SNAPSHOT").isSnapshotFor(version("Kay-RELEASE"))).isTrue(); + } + + @Test + void isSnapshotForWhenSnapshotForReleaseCandidateShouldReturnTrue() { + assertThat(version("Kay-BUILD-SNAPSHOT").isSnapshotFor(version("Kay-RC1"))).isTrue(); + } + + @Test + void isSnapshotForWhenSnapshotForMilestoneShouldReturnTrue() { + assertThat(version("Kay-BUILD-SNAPSHOT").isSnapshotFor(version("Kay-M2"))).isTrue(); + } + + @Test + void isSnapshotForWhenSnapshotForDifferentReleaseShouldReturnFalse() { + assertThat(version("Kay-BUILD-SNAPSHOT").isSnapshotFor(version("Lovelace-RELEASE"))).isFalse(); + } + + @Test + void isSnapshotForWhenSnapshotForDifferentReleaseCandidateShouldReturnTrue() { + assertThat(version("Kay-BUILD-SNAPSHOT").isSnapshotFor(version("Lovelace-RC2"))).isFalse(); + } + + @Test + void isSnapshotForWhenSnapshotForDifferentMilestoneShouldReturnTrue() { + assertThat(version("Kay-BUILD-SNAPSHOT").isSnapshotFor(version("Lovelace-M1"))).isFalse(); + } + + @Test + void isSnapshotForWhenNotSnapshotShouldReturnFalse() { + assertThat(version("Kay-M1").isSnapshotFor(version("Kay-RELEASE"))).isFalse(); + } + private static ReleaseTrainDependencyVersion version(String input) { return ReleaseTrainDependencyVersion.parse(input); } diff --git a/buildSrc/src/test/resources/releases.json b/buildSrc/src/test/resources/releases.json new file mode 100644 index 0000000000..3c5be29801 --- /dev/null +++ b/buildSrc/src/test/resources/releases.json @@ -0,0 +1,272 @@ +[ + { + "allDay": true, + "start": "2023-09-22", + "title": "Spring Modulith 1.0.1", + "url": "https://github.com/spring-projects/spring-modulith/milestone/15" + }, + { + "allDay": true, + "start": "2023-09-22", + "title": "Spring Modulith 1.1 M1", + "url": "https://github.com/spring-projects/spring-modulith/milestone/16" + }, + { + "allDay": true, + "start": "2023-09-12", + "title": "Reactor 2020.0.36", + "url": "https://github.com/reactor/reactor/milestone/51" + }, + { + "allDay": true, + "start": "2023-09-12", + "title": "Reactor 2022.0.11", + "url": "https://github.com/reactor/reactor/milestone/52" + }, + { + "allDay": true, + "start": "2023-09-12", + "title": "Reactor 2023.0.0-M3", + "url": "https://github.com/reactor/reactor/milestone/53" + }, + { + "allDay": true, + "start": "2023-09-12", + "title": "Reactor Core 3.4.33", + "url": "https://github.com/reactor/reactor-core/milestone/158" + }, + { + "allDay": true, + "start": "2023-09-12", + "title": "Reactor Core 3.5.10", + "url": "https://github.com/reactor/reactor-core/milestone/159" + }, + { + "allDay": true, + "start": "2023-09-12", + "title": "Reactor Core 3.6.0-M3", + "url": "https://github.com/reactor/reactor-core/milestone/160" + }, + { + "allDay": true, + "start": "2023-09-13", + "title": "Sts4 4.20.0.RELEASE", + "url": "https://github.com/spring-projects/sts4/milestone/66" + }, + { + "allDay": true, + "start": "2023-09-20", + "title": "Spring Batch 5.1.0-M3", + "url": "https://github.com/spring-projects/spring-batch/milestone/150" + }, + { + "allDay": true, + "start": "2023-09-19", + "title": "Spring Integration 6.2.0-M3", + "url": "https://github.com/spring-projects/spring-integration/milestone/306" + }, + { + "allDay": true, + "start": "2023-09-19", + "title": "Spring Integration 5.5.19", + "url": "https://github.com/spring-projects/spring-integration/milestone/309" + }, + { + "allDay": true, + "start": "2023-09-19", + "title": "Spring Integration 6.1.3", + "url": "https://github.com/spring-projects/spring-integration/milestone/310" + }, + { + "allDay": true, + "start": "2023-09-15", + "title": "Spring Data Release 2023.1.0-M3", + "url": "https://github.com/spring-projects/spring-data-release/milestone/30" + }, + { + "allDay": true, + "start": "2023-09-15", + "title": "Spring Data Release 2021.2.16", + "url": "https://github.com/spring-projects/spring-data-release/milestone/39" + }, + { + "allDay": true, + "start": "2023-09-15", + "title": "Spring Data Release 2022.0.10", + "url": "https://github.com/spring-projects/spring-data-release/milestone/40" + }, + { + "allDay": true, + "start": "2023-09-15", + "title": "Spring Data Release 2023.0.4", + "url": "https://github.com/spring-projects/spring-data-release/milestone/41" + }, + { + "allDay": true, + "start": "2023-09-19", + "title": "Spring Graphql 1.0.5", + "url": "https://github.com/spring-projects/spring-graphql/milestone/27" + }, + { + "allDay": true, + "start": "2023-09-19", + "title": "Spring Graphql 1.1.6", + "url": "https://github.com/spring-projects/spring-graphql/milestone/33" + }, + { + "allDay": true, + "start": "2023-09-19", + "title": "Spring Graphql 1.2.3", + "url": "https://github.com/spring-projects/spring-graphql/milestone/34" + }, + { + "allDay": true, + "start": "2023-09-19", + "title": "Spring Authorization Server 1.2.0-M1", + "url": "https://github.com/spring-projects/spring-authorization-server/milestone/34" + }, + { + "allDay": true, + "start": "2023-09-18", + "title": "Spring Kafka 3.1.0-M1", + "url": "https://github.com/spring-projects/spring-kafka/milestone/225" + }, + { + "allDay": true, + "start": "2023-09-14", + "title": "Spring Cloud Dataflow 2.11.0", + "url": "https://github.com/spring-cloud/spring-cloud-dataflow/milestone/159" + }, + { + "allDay": true, + "start": "2023-09-11", + "title": "Micrometer 1.9.15", + "url": "https://github.com/micrometer-metrics/micrometer/milestone/217" + }, + { + "allDay": true, + "start": "2023-09-11", + "title": "Micrometer 1.10.11", + "url": "https://github.com/micrometer-metrics/micrometer/milestone/218" + }, + { + "allDay": true, + "start": "2023-09-11", + "title": "Micrometer 1.11.4", + "url": "https://github.com/micrometer-metrics/micrometer/milestone/219" + }, + { + "allDay": true, + "start": "2023-09-11", + "title": "Micrometer 1.12.0-M3", + "url": "https://github.com/micrometer-metrics/micrometer/milestone/220" + }, + { + "allDay": true, + "start": "2023-09-11", + "title": "Tracing 1.0.10", + "url": "https://github.com/micrometer-metrics/tracing/milestone/33" + }, + { + "allDay": true, + "start": "2023-09-11", + "title": "Tracing 1.1.5", + "url": "https://github.com/micrometer-metrics/tracing/milestone/34" + }, + { + "allDay": true, + "start": "2023-09-26", + "title": "Spring Cloud Release 2023.0.0-M2", + "url": "https://github.com/spring-cloud/spring-cloud-release/milestone/134" + }, + { + "allDay": true, + "start": "2023-09-11", + "title": "Context Propagation 1.0.6", + "url": "https://github.com/micrometer-metrics/context-propagation/milestone/19" + }, + { + "allDay": true, + "start": "2023-09-14", + "title": "Spring Ldap 3.2.0-M3", + "url": "https://github.com/spring-projects/spring-ldap/milestone/63" + }, + { + "allDay": true, + "start": "2023-09-21", + "title": "Spring Boot 3.2.0-M3", + "url": "https://github.com/spring-projects/spring-boot/milestone/306" + }, + { + "allDay": true, + "start": "2023-09-21", + "title": "Spring Boot 2.7.16", + "url": "https://github.com/spring-projects/spring-boot/milestone/315" + }, + { + "allDay": true, + "start": "2023-09-21", + "title": "Spring Boot 3.0.11", + "url": "https://github.com/spring-projects/spring-boot/milestone/316" + }, + { + "allDay": true, + "start": "2023-09-21", + "title": "Spring Boot 3.1.4", + "url": "https://github.com/spring-projects/spring-boot/milestone/317" + }, + { + "allDay": true, + "start": "2023-09-14", + "title": "Spring Cloud Deployer 2.9.0", + "url": "https://github.com/spring-cloud/spring-cloud-deployer/milestone/116" + }, + { + "allDay": true, + "start": "2023-09-12", + "title": "Reactor Kafka 1.3.21", + "url": "https://github.com/reactor/reactor-kafka/milestone/38" + }, + { + "allDay": true, + "start": "2023-09-18", + "title": "Spring Security 6.2.0-M3", + "url": "https://github.com/spring-projects/spring-security/milestone/308" + }, + { + "allDay": true, + "start": "2023-09-22", + "title": "Stream Applications 4.0.0", + "url": "https://github.com/spring-cloud/stream-applications/milestone/7" + }, + { + "allDay": true, + "start": "2023-09-12", + "title": "Reactor Netty 1.1.11", + "url": "https://github.com/reactor/reactor-netty/milestone/153" + }, + { + "allDay": true, + "start": "2023-09-12", + "title": "Reactor Netty 1.0.36", + "url": "https://github.com/reactor/reactor-netty/milestone/154" + }, + { + "allDay": true, + "start": "2023-09-14", + "title": "Spring Framework 6.0.12", + "url": "https://github.com/spring-projects/spring-framework/milestone/331" + }, + { + "allDay": true, + "start": "2023-09-14", + "title": "Spring Framework 5.3.30", + "url": "https://github.com/spring-projects/spring-framework/milestone/332" + }, + { + "allDay": true, + "start": "2023-09-14", + "title": "Spring Framework 6.1.0-RC1", + "url": "https://github.com/spring-projects/spring-framework/milestone/333" + } +] \ No newline at end of file diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 9fd61d6a04..25c059f131 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1003,6 +1003,7 @@ bom { } library("Micrometer Tracing", "1.2.0-M2") { considerSnapshots() + calendarName = "Tracing" group("io.micrometer") { imports = [ "micrometer-tracing-bom" @@ -1320,6 +1321,7 @@ bom { } library("Reactor Bom", "2023.0.0-M2") { considerSnapshots() + calendarName = "Reactor" group("io.projectreactor") { imports = [ "reactor-bom" @@ -1508,11 +1510,17 @@ bom { } library("Spring Data Bom", "2023.1.0-M2") { considerSnapshots() + calendarName = "Spring Data Release" + prohibit { + versionRange "[2022.0.0-M1,)" + because "it uses Spring Framework 6" + } group("org.springframework.data") { imports = [ "spring-data-bom" ] } + } library("Spring Framework", "${springFrameworkVersion}") { considerSnapshots()