commit
c1ca05d99d
|
@ -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.
|
||||
|
@ -16,32 +16,15 @@
|
|||
|
||||
package org.springframework.boot.build.bom.bomr;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
|
||||
import org.gradle.api.InvalidUserDataException;
|
||||
import org.gradle.api.internal.tasks.userinput.UserInputHandler;
|
||||
|
||||
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.Module;
|
||||
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.bomr.version.DependencyVersion;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Interactive {@link UpgradeResolver} that uses command line input to choose the upgrades
|
||||
|
@ -51,17 +34,13 @@ import org.springframework.util.StringUtils;
|
|||
*/
|
||||
public final class InteractiveUpgradeResolver implements UpgradeResolver {
|
||||
|
||||
private final VersionResolver versionResolver;
|
||||
|
||||
private final UpgradePolicy upgradePolicy;
|
||||
|
||||
private final UserInputHandler userInputHandler;
|
||||
|
||||
InteractiveUpgradeResolver(VersionResolver versionResolver, UpgradePolicy upgradePolicy,
|
||||
UserInputHandler userInputHandler) {
|
||||
this.versionResolver = versionResolver;
|
||||
this.upgradePolicy = upgradePolicy;
|
||||
private final LibraryUpdateResolver libraryUpdateResolver;
|
||||
|
||||
InteractiveUpgradeResolver(UserInputHandler userInputHandler, LibraryUpdateResolver libraryUpdateResolver) {
|
||||
this.userInputHandler = userInputHandler;
|
||||
this.libraryUpdateResolver = libraryUpdateResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -70,195 +49,22 @@ public final class InteractiveUpgradeResolver implements UpgradeResolver {
|
|||
for (Library library : libraries) {
|
||||
librariesByName.put(library.getName(), library);
|
||||
}
|
||||
return librariesToUpgrade.stream().filter((library) -> !library.getName().equals("Spring Boot"))
|
||||
.map((library) -> resolveUpgrade(library, librariesByName)).filter(Objects::nonNull).toList();
|
||||
List<LibraryWithVersionOptions> libraryUpdates = this.libraryUpdateResolver
|
||||
.findLibraryUpdates(librariesToUpgrade, librariesByName);
|
||||
return libraryUpdates.stream().map(this::resolveUpgrade).filter(Objects::nonNull).toList();
|
||||
}
|
||||
|
||||
private Upgrade resolveUpgrade(Library library, Map<String, Library> libraries) {
|
||||
List<VersionOption> versionOptions = getVersionOptions(library, libraries);
|
||||
if (versionOptions.isEmpty()) {
|
||||
private Upgrade resolveUpgrade(LibraryWithVersionOptions libraryWithVersionOptions) {
|
||||
if (libraryWithVersionOptions.getVersionOptions().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) {
|
||||
return determineAlignedVersionOption(library, libraries);
|
||||
}
|
||||
return determineResolvedVersionOptions(library);
|
||||
}
|
||||
|
||||
private List<VersionOption> determineResolvedVersionOptions(Library library) {
|
||||
Map<String, SortedSet<DependencyVersion>> 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));
|
||||
}
|
||||
for (String bom : group.getBoms()) {
|
||||
moduleVersions.put(group.getId() + ":" + bom,
|
||||
getLaterVersionsForModule(group.getId(), bom, libraryVersion));
|
||||
}
|
||||
for (String plugin : group.getPlugins()) {
|
||||
moduleVersions.put(group.getId() + ":" + plugin,
|
||||
getLaterVersionsForModule(group.getId(), plugin, libraryVersion));
|
||||
}
|
||||
}
|
||||
List<DependencyVersion> allVersions = moduleVersions.values().stream().flatMap(SortedSet::stream).distinct()
|
||||
.filter((dependencyVersion) -> isPermitted(dependencyVersion, library.getProhibitedVersions()))
|
||||
.toList();
|
||||
if (allVersions.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Stream<VersionOption> resolvedVersionOptions = allVersions.stream()
|
||||
.map((version) -> new ResolvedVersionOption(version, getMissingModules(moduleVersions, version)));
|
||||
return resolvedVersionOptions.toList();
|
||||
}
|
||||
|
||||
private List<VersionOption> determineAlignedVersionOption(Library library, Map<String, Library> libraries) {
|
||||
VersionOption alignedVersionOption = alignedVersionOption(library, libraries);
|
||||
if (alignedVersionOption == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (!isPermitted(alignedVersionOption.version, library.getProhibitedVersions())) {
|
||||
throw new InvalidUserDataException("Version alignment failed. Version " + alignedVersionOption.version
|
||||
+ " from " + library.getName() + " is prohibited");
|
||||
}
|
||||
return Collections.singletonList(alignedVersionOption);
|
||||
}
|
||||
|
||||
private VersionOption alignedVersionOption(Library library, Map<String, Library> libraries) {
|
||||
VersionAlignment versionAlignment = library.getVersion().getVersionAlignment();
|
||||
Library alignmentLibrary = libraries.get(versionAlignment.getLibraryName());
|
||||
DependencyVersions dependencyVersions = alignmentLibrary.getDependencyVersions();
|
||||
if (dependencyVersions == null) {
|
||||
throw new InvalidUserDataException("Cannot align with library '" + versionAlignment.getLibraryName()
|
||||
+ "' as it does not define any dependency versions");
|
||||
}
|
||||
if (!dependencyVersions.available()) {
|
||||
return null;
|
||||
}
|
||||
Set<String> versions = new HashSet<>();
|
||||
for (Group group : library.getGroups()) {
|
||||
for (Module module : group.getModules()) {
|
||||
String version = dependencyVersions.getVersion(group.getId(), module.getName());
|
||||
if (version != null) {
|
||||
versions.add(version);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (versions.isEmpty()) {
|
||||
throw new InvalidUserDataException("Cannot align with library '" + versionAlignment.getLibraryName()
|
||||
+ "' as its dependency versions do not include any of this library's modules");
|
||||
}
|
||||
if (versions.size() > 1) {
|
||||
throw new InvalidUserDataException("Cannot align with library '" + versionAlignment.getLibraryName()
|
||||
+ "' as it uses multiple different versions of this library's modules");
|
||||
}
|
||||
DependencyVersion version = DependencyVersion.parse(versions.iterator().next());
|
||||
return library.getVersion().getVersion().equals(version) ? null
|
||||
: new AlignedVersionOption(version, alignmentLibrary);
|
||||
}
|
||||
|
||||
private boolean isPermitted(DependencyVersion dependencyVersion, List<ProhibitedVersion> 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<String> getMissingModules(Map<String, SortedSet<DependencyVersion>> moduleVersions,
|
||||
DependencyVersion version) {
|
||||
List<String> missingModules = new ArrayList<>();
|
||||
moduleVersions.forEach((name, versions) -> {
|
||||
if (!versions.contains(version)) {
|
||||
missingModules.add(name);
|
||||
}
|
||||
});
|
||||
return missingModules;
|
||||
}
|
||||
|
||||
private SortedSet<DependencyVersion> getLaterVersionsForModule(String groupId, String artifactId,
|
||||
DependencyVersion currentVersion) {
|
||||
SortedSet<DependencyVersion> versions = this.versionResolver.resolveVersions(groupId, artifactId);
|
||||
versions.removeIf((candidate) -> !this.upgradePolicy.test(candidate, currentVersion));
|
||||
return versions;
|
||||
}
|
||||
|
||||
private static class VersionOption {
|
||||
|
||||
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 ResolvedVersionOption(DependencyVersion version, List<String> missingModules) {
|
||||
super(version);
|
||||
this.missingModules = missingModules;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (this.missingModules.isEmpty()) {
|
||||
return super.toString();
|
||||
}
|
||||
return super.toString() + " (some modules are missing: "
|
||||
+ StringUtils.collectionToDelimitedString(this.missingModules, ", ") + ")";
|
||||
}
|
||||
|
||||
VersionOption current = new VersionOption(libraryWithVersionOptions.getLibrary().getVersion().getVersion());
|
||||
VersionOption selected = this.userInputHandler.selectOption(
|
||||
libraryWithVersionOptions.getLibrary().getName() + " "
|
||||
+ libraryWithVersionOptions.getLibrary().getVersion().getVersion(),
|
||||
libraryWithVersionOptions.getVersionOptions(), current);
|
||||
return (selected.equals(current)) ? null
|
||||
: new Upgrade(libraryWithVersionOptions.getLibrary(), selected.getVersion());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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.
|
||||
* 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.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.build.bom.Library;
|
||||
|
||||
/**
|
||||
* Resolves library updates.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
public interface LibraryUpdateResolver {
|
||||
|
||||
/**
|
||||
* Finds library updates.
|
||||
* @param librariesToUpgrade libraries to update
|
||||
* @param librariesByName libraries indexed by name
|
||||
* @return library which have updates
|
||||
*/
|
||||
List<LibraryWithVersionOptions> findLibraryUpdates(Collection<Library> librariesToUpgrade,
|
||||
Map<String, Library> librariesByName);
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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.
|
||||
* 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.util.List;
|
||||
|
||||
import org.springframework.boot.build.bom.Library;
|
||||
|
||||
class LibraryWithVersionOptions {
|
||||
|
||||
private final Library library;
|
||||
|
||||
private final List<VersionOption> versionOptions;
|
||||
|
||||
LibraryWithVersionOptions(Library library, List<VersionOption> versionOptions) {
|
||||
this.library = library;
|
||||
this.versionOptions = versionOptions;
|
||||
}
|
||||
|
||||
Library getLibrary() {
|
||||
return this.library;
|
||||
}
|
||||
|
||||
List<VersionOption> getVersionOptions() {
|
||||
return this.versionOptions;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* 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.
|
||||
* 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.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.boot.build.bom.Library;
|
||||
import org.springframework.boot.build.bom.UpgradePolicy;
|
||||
|
||||
/**
|
||||
* Uses multiple threads to find library updates.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
class MultithreadedLibraryUpdateResolver extends StandardLibraryUpdateResolver {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(MultithreadedLibraryUpdateResolver.class);
|
||||
|
||||
private final int threads;
|
||||
|
||||
MultithreadedLibraryUpdateResolver(VersionResolver versionResolver, UpgradePolicy upgradePolicy, int threads) {
|
||||
super(versionResolver, upgradePolicy);
|
||||
this.threads = threads;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LibraryWithVersionOptions> findLibraryUpdates(Collection<Library> librariesToUpgrade,
|
||||
Map<String, Library> librariesByName) {
|
||||
LOGGER.info("Looking for updates using {} threads", this.threads);
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(this.threads);
|
||||
try {
|
||||
List<Future<LibraryWithVersionOptions>> jobs = new ArrayList<>();
|
||||
for (Library library : librariesToUpgrade) {
|
||||
if (isLibraryExcluded(library)) {
|
||||
continue;
|
||||
}
|
||||
jobs.add(executorService.submit(() -> {
|
||||
LOGGER.info("Looking for updates for {}", library.getName());
|
||||
long start = System.nanoTime();
|
||||
List<VersionOption> versionOptions = getVersionOptions(library, librariesByName);
|
||||
LOGGER.info("Found {} updates for {}, took {}", versionOptions.size(), library.getName(),
|
||||
Duration.ofNanos(System.nanoTime() - start));
|
||||
return new LibraryWithVersionOptions(library, versionOptions);
|
||||
}));
|
||||
}
|
||||
List<LibraryWithVersionOptions> result = new ArrayList<>();
|
||||
for (Future<LibraryWithVersionOptions> job : jobs) {
|
||||
try {
|
||||
result.add(job.get());
|
||||
}
|
||||
catch (InterruptedException | ExecutionException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
finally {
|
||||
executorService.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
* 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.
|
||||
* 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.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
|
||||
import org.gradle.api.InvalidUserDataException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
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.Module;
|
||||
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.bomr.version.DependencyVersion;
|
||||
|
||||
/**
|
||||
* Standard implementation for {@link LibraryUpdateResolver}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class StandardLibraryUpdateResolver implements LibraryUpdateResolver {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(StandardLibraryUpdateResolver.class);
|
||||
|
||||
private final VersionResolver versionResolver;
|
||||
|
||||
private final UpgradePolicy upgradePolicy;
|
||||
|
||||
StandardLibraryUpdateResolver(VersionResolver versionResolver, UpgradePolicy upgradePolicy) {
|
||||
this.versionResolver = versionResolver;
|
||||
this.upgradePolicy = upgradePolicy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LibraryWithVersionOptions> findLibraryUpdates(Collection<Library> librariesToUpgrade,
|
||||
Map<String, Library> librariesByName) {
|
||||
List<LibraryWithVersionOptions> result = new ArrayList<>();
|
||||
for (Library library : librariesToUpgrade) {
|
||||
if (isLibraryExcluded(library)) {
|
||||
continue;
|
||||
}
|
||||
LOGGER.info("Looking for updates for {}", library.getName());
|
||||
long start = System.nanoTime();
|
||||
List<VersionOption> versionOptions = getVersionOptions(library, librariesByName);
|
||||
result.add(new LibraryWithVersionOptions(library, versionOptions));
|
||||
LOGGER.info("Found {} updates for {}, took {}", versionOptions.size(), library.getName(),
|
||||
Duration.ofNanos(System.nanoTime() - start));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected boolean isLibraryExcluded(Library library) {
|
||||
return library.getName().equals("Spring Boot");
|
||||
}
|
||||
|
||||
protected List<VersionOption> getVersionOptions(Library library, Map<String, Library> libraries) {
|
||||
if (library.getVersion().getVersionAlignment() != null) {
|
||||
return determineAlignedVersionOption(library, libraries);
|
||||
}
|
||||
return determineResolvedVersionOptions(library);
|
||||
}
|
||||
|
||||
private List<VersionOption> determineResolvedVersionOptions(Library library) {
|
||||
Map<String, SortedSet<DependencyVersion>> 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));
|
||||
}
|
||||
for (String bom : group.getBoms()) {
|
||||
moduleVersions.put(group.getId() + ":" + bom,
|
||||
getLaterVersionsForModule(group.getId(), bom, libraryVersion));
|
||||
}
|
||||
for (String plugin : group.getPlugins()) {
|
||||
moduleVersions.put(group.getId() + ":" + plugin,
|
||||
getLaterVersionsForModule(group.getId(), plugin, libraryVersion));
|
||||
}
|
||||
}
|
||||
List<DependencyVersion> allVersions = moduleVersions.values().stream().flatMap(SortedSet::stream).distinct()
|
||||
.filter((dependencyVersion) -> isPermitted(dependencyVersion, library.getProhibitedVersions()))
|
||||
.collect(Collectors.toList());
|
||||
if (allVersions.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return allVersions.stream().map((version) -> new VersionOption.ResolvedVersionOption(version,
|
||||
getMissingModules(moduleVersions, version))).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<VersionOption> determineAlignedVersionOption(Library library, Map<String, Library> libraries) {
|
||||
VersionOption alignedVersionOption = alignedVersionOption(library, libraries);
|
||||
if (alignedVersionOption == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (!isPermitted(alignedVersionOption.getVersion(), library.getProhibitedVersions())) {
|
||||
throw new InvalidUserDataException("Version alignment failed. Version " + alignedVersionOption.getVersion()
|
||||
+ " from " + library.getName() + " is prohibited");
|
||||
}
|
||||
return Collections.singletonList(alignedVersionOption);
|
||||
}
|
||||
|
||||
private VersionOption alignedVersionOption(Library library, Map<String, Library> libraries) {
|
||||
VersionAlignment versionAlignment = library.getVersion().getVersionAlignment();
|
||||
Library alignmentLibrary = libraries.get(versionAlignment.getLibraryName());
|
||||
DependencyVersions dependencyVersions = alignmentLibrary.getDependencyVersions();
|
||||
if (dependencyVersions == null) {
|
||||
throw new InvalidUserDataException("Cannot align with library '" + versionAlignment.getLibraryName()
|
||||
+ "' as it does not define any dependency versions");
|
||||
}
|
||||
if (!dependencyVersions.available()) {
|
||||
return null;
|
||||
}
|
||||
Set<String> versions = new HashSet<>();
|
||||
for (Group group : library.getGroups()) {
|
||||
for (Module module : group.getModules()) {
|
||||
String version = dependencyVersions.getVersion(group.getId(), module.getName());
|
||||
if (version != null) {
|
||||
versions.add(version);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (versions.isEmpty()) {
|
||||
throw new InvalidUserDataException("Cannot align with library '" + versionAlignment.getLibraryName()
|
||||
+ "' as its dependency versions do not include any of this library's modules");
|
||||
}
|
||||
if (versions.size() > 1) {
|
||||
throw new InvalidUserDataException("Cannot align with library '" + versionAlignment.getLibraryName()
|
||||
+ "' as it uses multiple different versions of this library's modules");
|
||||
}
|
||||
DependencyVersion version = DependencyVersion.parse(versions.iterator().next());
|
||||
return library.getVersion().getVersion().equals(version) ? null
|
||||
: new VersionOption.AlignedVersionOption(version, alignmentLibrary);
|
||||
}
|
||||
|
||||
private boolean isPermitted(DependencyVersion dependencyVersion, List<ProhibitedVersion> prohibitedVersions) {
|
||||
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<String> getMissingModules(Map<String, SortedSet<DependencyVersion>> moduleVersions,
|
||||
DependencyVersion version) {
|
||||
List<String> missingModules = new ArrayList<>();
|
||||
moduleVersions.forEach((name, versions) -> {
|
||||
if (!versions.contains(version)) {
|
||||
missingModules.add(name);
|
||||
}
|
||||
});
|
||||
return missingModules;
|
||||
}
|
||||
|
||||
private SortedSet<DependencyVersion> getLaterVersionsForModule(String groupId, String artifactId,
|
||||
DependencyVersion currentVersion) {
|
||||
SortedSet<DependencyVersion> versions = this.versionResolver.resolveVersions(groupId, artifactId);
|
||||
versions.removeIf((candidate) -> !this.upgradePolicy.test(candidate, currentVersion));
|
||||
return versions;
|
||||
}
|
||||
|
||||
}
|
|
@ -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.
|
||||
|
@ -55,10 +55,11 @@ import org.springframework.util.StringUtils;
|
|||
* {@link Task} to upgrade the libraries managed by a bom.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
public class UpgradeBom extends DefaultTask {
|
||||
|
||||
private final Set<String> repositoryUrls;
|
||||
private final Set<String> repositoryUrls = new LinkedHashSet<>();
|
||||
|
||||
private final BomExtension bom;
|
||||
|
||||
|
@ -66,10 +67,11 @@ public class UpgradeBom extends DefaultTask {
|
|||
|
||||
private String libraries;
|
||||
|
||||
private int threads = 8;
|
||||
|
||||
@Inject
|
||||
public UpgradeBom(BomExtension bom) {
|
||||
this.bom = bom;
|
||||
this.repositoryUrls = new LinkedHashSet<>();
|
||||
getProject().getRepositories().withType(MavenArtifactRepository.class, (repository) -> {
|
||||
String repositoryUrl = repository.getUrl().toString();
|
||||
if (!repositoryUrl.endsWith("snapshot")) {
|
||||
|
@ -83,6 +85,11 @@ public class UpgradeBom extends DefaultTask {
|
|||
this.milestone = milestone;
|
||||
}
|
||||
|
||||
@Option(option = "threads", description = "Number of Threads to use for update resolution")
|
||||
public void setThreads(String threads) {
|
||||
this.threads = Integer.parseInt(threads);
|
||||
}
|
||||
|
||||
@Input
|
||||
public String getMilestone() {
|
||||
return this.milestone;
|
||||
|
@ -104,7 +111,7 @@ public class UpgradeBom extends DefaultTask {
|
|||
void upgradeDependencies() {
|
||||
GitHubRepository repository = createGitHub().getRepository(this.bom.getUpgrade().getGitHub().getOrganization(),
|
||||
this.bom.getUpgrade().getGitHub().getRepository());
|
||||
List<String> availableLabels = repository.getLabels();
|
||||
Set<String> availableLabels = repository.getLabels();
|
||||
List<String> issueLabels = this.bom.getUpgrade().getGitHub().getIssueLabels();
|
||||
if (!availableLabels.containsAll(issueLabels)) {
|
||||
List<String> unknownLabels = new ArrayList<>(issueLabels);
|
||||
|
@ -114,9 +121,10 @@ public class UpgradeBom extends DefaultTask {
|
|||
}
|
||||
Milestone milestone = determineMilestone(repository);
|
||||
List<Issue> existingUpgradeIssues = repository.findIssues(issueLabels, milestone);
|
||||
List<Upgrade> upgrades = new InteractiveUpgradeResolver(new MavenMetadataVersionResolver(this.repositoryUrls),
|
||||
this.bom.getUpgrade().getPolicy(), getServices().get(UserInputHandler.class))
|
||||
.resolveUpgrades(matchingLibraries(this.libraries), this.bom.getLibraries());
|
||||
List<Upgrade> upgrades = new InteractiveUpgradeResolver(getServices().get(UserInputHandler.class),
|
||||
new MultithreadedLibraryUpdateResolver(new MavenMetadataVersionResolver(this.repositoryUrls),
|
||||
this.bom.getUpgrade().getPolicy(), this.threads))
|
||||
.resolveUpgrades(matchingLibraries(this.libraries), this.bom.getLibraries());
|
||||
Path buildFile = getProject().getBuildFile().toPath();
|
||||
Path gradleProperties = new File(getProject().getRootProject().getProjectDir(), "gradle.properties").toPath();
|
||||
UpgradeApplicator upgradeApplicator = new UpgradeApplicator(buildFile, gradleProperties);
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* 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.
|
||||
* 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.util.List;
|
||||
|
||||
import org.springframework.boot.build.bom.Library;
|
||||
import org.springframework.boot.build.bom.bomr.version.DependencyVersion;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* An option for a library update.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class VersionOption {
|
||||
|
||||
private final DependencyVersion version;
|
||||
|
||||
VersionOption(DependencyVersion version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
DependencyVersion getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.version.toString();
|
||||
}
|
||||
|
||||
static final class AlignedVersionOption extends VersionOption {
|
||||
|
||||
private final Library alignedWith;
|
||||
|
||||
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() + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static final class ResolvedVersionOption extends VersionOption {
|
||||
|
||||
private final List<String> missingModules;
|
||||
|
||||
ResolvedVersionOption(DependencyVersion version, List<String> missingModules) {
|
||||
super(version);
|
||||
this.missingModules = missingModules;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (this.missingModules.isEmpty()) {
|
||||
return super.toString();
|
||||
}
|
||||
return super.toString() + " (some modules are missing: "
|
||||
+ StringUtils.collectionToDelimitedString(this.missingModules, ", ") + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2021 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.
|
||||
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.boot.build.bom.bomr.github;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Minimal API for interacting with a GitHub repository.
|
||||
|
@ -40,7 +41,7 @@ public interface GitHubRepository {
|
|||
* Returns the labels in the repository.
|
||||
* @return the labels
|
||||
*/
|
||||
List<String> getLabels();
|
||||
Set<String> getLabels();
|
||||
|
||||
/**
|
||||
* Returns the milestones in the repository.
|
||||
|
|
|
@ -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.
|
||||
|
@ -17,8 +17,10 @@
|
|||
package org.springframework.boot.build.bom.bomr.github;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
@ -70,8 +72,8 @@ final class StandardGitHubRepository implements GitHubRepository {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<String> getLabels() {
|
||||
return get("labels?per_page=100", (label) -> (String) label.get("name"));
|
||||
public Set<String> getLabels() {
|
||||
return new HashSet<>(get("labels?per_page=100", (label) -> (String) label.get("name")));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Loading…
Reference in New Issue