mirror of
https://github.com/rife2/bld
synced 2026-07-28 02:17:47 +02:00
Warn when declared versions differ from applicable BOMs and only report in download
This commit is contained in:
parent
7d77f48aec
commit
ec185788a1
|
|
@ -178,6 +178,40 @@ public class DependencyScopes extends LinkedHashMap<Scope, DependencySet> {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the declared dependencies whose explicit version differs
|
||||||
|
* from the version that a BOM applying to their scope manages them at.
|
||||||
|
* <p>
|
||||||
|
* The declared version is used for the dependency itself, its
|
||||||
|
* transitive dependencies still resolve to the versions that the BOMs
|
||||||
|
* manage. Dependencies whose version is supplied by a
|
||||||
|
* {@code bld.override} property are not reported. Each difference is
|
||||||
|
* reported once.
|
||||||
|
*
|
||||||
|
* @param properties the properties to use to get artifacts
|
||||||
|
* @param retriever the retriever to use to get artifacts
|
||||||
|
* @param repositories the repositories to use for the BOM resolution
|
||||||
|
* @return the version differences between the declared dependencies
|
||||||
|
* and the applicable BOMs
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
public List<VersionResolution.DeclaredVersionConflict> declaredVersionConflicts(HierarchicalProperties properties, ArtifactRetriever retriever, List<Repository> repositories) {
|
||||||
|
var result = new ArrayList<VersionResolution.DeclaredVersionConflict>();
|
||||||
|
var seen = new HashSet<String>();
|
||||||
|
for (var entry : entrySet()) {
|
||||||
|
var effective_boms = effectiveBoms(entry.getKey());
|
||||||
|
if (effective_boms.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (var conflict : VersionResolution.resolveDeclaredVersionConflicts(properties, retriever, repositories, effective_boms, entry.getValue())) {
|
||||||
|
if (seen.add(conflict.dependency() + conflict.declaredVersion() + conflict.bom() + conflict.bomVersion())) {
|
||||||
|
result.add(conflict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the transitive set of dependencies that would be used for the compile scope in a project.
|
* Returns the transitive set of dependencies that would be used for the compile scope in a project.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -233,6 +233,78 @@ public class VersionResolution {
|
||||||
return conflicts;
|
return conflicts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes a declared dependency whose explicit version differs from
|
||||||
|
* the version that an applicable bill of materials manages it at.
|
||||||
|
* <p>
|
||||||
|
* The declared version is used for the dependency itself, its
|
||||||
|
* transitive dependencies still resolve to the versions that the BOM
|
||||||
|
* manages.
|
||||||
|
*
|
||||||
|
* @param dependency the group and artifact identifiers of the
|
||||||
|
* declared dependency
|
||||||
|
* @param declaredVersion the version the dependency is declared with
|
||||||
|
* @param bom the BOM that manages the dependency, the one
|
||||||
|
* with the highest precedence when several do
|
||||||
|
* @param bomVersion the version the BOM manages the dependency at
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
public record DeclaredVersionConflict(String dependency, Version declaredVersion, String bom, Version bomVersion) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the declared dependencies whose explicit version differs from
|
||||||
|
* the version that the provided bills of materials manage them at.
|
||||||
|
* <p>
|
||||||
|
* Dependencies that are declared without a version or whose version is
|
||||||
|
* supplied by a {@code bld.override} property are not reported.
|
||||||
|
*
|
||||||
|
* @param properties the properties to use to get artifacts
|
||||||
|
* @param retriever the retriever to use to get the BOMs
|
||||||
|
* @param repositories the repositories to resolve the BOMs in
|
||||||
|
* @param boms the BOMs to check, in precedence order
|
||||||
|
* @param declared the declared dependencies to check
|
||||||
|
* @return the version differences between the declared dependencies and the BOMs
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
public static List<DeclaredVersionConflict> resolveDeclaredVersionConflicts(HierarchicalProperties properties, ArtifactRetriever retriever, List<Repository> repositories, Collection<Bom> boms, Collection<Dependency> declared) {
|
||||||
|
var base = new VersionResolution(properties);
|
||||||
|
var managed_versions = new LinkedHashMap<String, Version>();
|
||||||
|
var managed_boms = new LinkedHashMap<String, String>();
|
||||||
|
if (boms != null) {
|
||||||
|
for (var bom : boms) {
|
||||||
|
var pom = new DependencyResolver(base, retriever, repositories, bom).getMavenPom(bom);
|
||||||
|
for (var managed : pom.getManagedDependencies()) {
|
||||||
|
if (managed.version() != null && !managed.version().isBlank()) {
|
||||||
|
var dependency = managed.convertToDependency();
|
||||||
|
var key = managedKey(dependency);
|
||||||
|
// the first BOM that manages a dependency determines
|
||||||
|
// its version, mirroring the resolution precedence
|
||||||
|
if (managed_versions.putIfAbsent(key, dependency.version()) == null) {
|
||||||
|
managed_boms.put(key, bom.toArtifactString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var conflicts = new ArrayList<DeclaredVersionConflict>();
|
||||||
|
if (declared != null) {
|
||||||
|
for (var dependency : declared) {
|
||||||
|
if (VersionNumber.UNKNOWN.equals(dependency.version()) ||
|
||||||
|
base.versionOverrides_.containsKey(dependency.toArtifactString())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var key = managedKey(dependency);
|
||||||
|
var managed_version = managed_versions.get(key);
|
||||||
|
if (managed_version != null && !managed_version.equals(dependency.version())) {
|
||||||
|
conflicts.add(new DeclaredVersionConflict(dependency.toArtifactString(), dependency.version(), managed_boms.get(key), managed_version));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return conflicts;
|
||||||
|
}
|
||||||
|
|
||||||
// builds the identity that dependency management entries are matched
|
// builds the identity that dependency management entries are matched
|
||||||
// on, mirroring Maven this includes the type and the classifier, the
|
// on, mirroring Maven this includes the type and the classifier, the
|
||||||
// modular and forced-classpath JAR types match the plain jar entries
|
// modular and forced-classpath JAR types match the plain jar entries
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,9 @@ public class DownloadOperation extends AbstractOperation<DownloadOperation> {
|
||||||
for (var conflict : dependencies().bomVersionConflicts(properties(), artifactRetriever(), repositories())) {
|
for (var conflict : dependencies().bomVersionConflicts(properties(), artifactRetriever(), repositories())) {
|
||||||
System.out.println(formatBomVersionConflict(conflict));
|
System.out.println(formatBomVersionConflict(conflict));
|
||||||
}
|
}
|
||||||
|
for (var conflict : dependencies().declaredVersionConflicts(properties(), artifactRetriever(), repositories())) {
|
||||||
|
System.out.println(formatDeclaredVersionConflict(conflict));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -110,6 +113,20 @@ public class DownloadOperation extends AbstractOperation<DownloadOperation> {
|
||||||
return message.toString();
|
return message.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a warning message for a declared dependency whose version
|
||||||
|
* differs from the version that a BOM manages it at.
|
||||||
|
*
|
||||||
|
* @param conflict the declared version conflict to format
|
||||||
|
* @return the formatted warning message
|
||||||
|
* @since 2.4.0
|
||||||
|
*/
|
||||||
|
protected static String formatDeclaredVersionConflict(rife.bld.dependencies.VersionResolution.DeclaredVersionConflict conflict) {
|
||||||
|
return "Warning: '" + conflict.dependency() + "' is declared with version " + conflict.declaredVersion() +
|
||||||
|
" while BOM '" + conflict.bom() + "' manages it at " + conflict.bomVersion() +
|
||||||
|
", the declared version is used but transitive dependencies still follow the BOM";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Part of the {@link #execute} operation, download the {@code compile} scope artifacts.
|
* Part of the {@link #execute} operation, download the {@code compile} scope artifacts.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,6 @@ public class PurgeOperation extends AbstractOperation<PurgeOperation> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
executeReportUncoveredDependencies();
|
|
||||||
executePurgeCompileDependencies();
|
executePurgeCompileDependencies();
|
||||||
executePurgeProvidedDependencies();
|
executePurgeProvidedDependencies();
|
||||||
executePurgeRuntimeDependencies();
|
executePurgeRuntimeDependencies();
|
||||||
|
|
@ -65,24 +64,6 @@ public class PurgeOperation extends AbstractOperation<PurgeOperation> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Part of the {@link #execute} operation, warns about version-less
|
|
||||||
* dependencies that are not covered by a BOM in their scope.
|
|
||||||
*
|
|
||||||
* @since 2.4.0
|
|
||||||
*/
|
|
||||||
protected void executeReportUncoveredDependencies() {
|
|
||||||
if (silent()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (var dependency : dependencies().versionlessDependenciesWithoutBom(properties(), artifactRetriever(), repositories())) {
|
|
||||||
System.out.println("Warning: '" + dependency.toArtifactString() + "' isn't covered by a BOM, its latest version will be used");
|
|
||||||
}
|
|
||||||
for (var conflict : dependencies().bomVersionConflicts(properties(), artifactRetriever(), repositories())) {
|
|
||||||
System.out.println(DownloadOperation.formatBomVersionConflict(conflict));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Part of the {@link #execute} operation, purge the {@code compile} scope artifacts.
|
* Part of the {@link #execute} operation, purge the {@code compile} scope artifacts.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -506,6 +506,48 @@ public class TestBom {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDeclaredVersionConflictsAreDetected() throws Exception {
|
||||||
|
var server = createArtifactServer(Map.of(
|
||||||
|
"bom1:1.0.0", bomPom("bom1", "1.0.0", managed("a", "1.4.0") + managed("b", "3.0.0"))),
|
||||||
|
Map.of());
|
||||||
|
server.start();
|
||||||
|
try {
|
||||||
|
var retriever = ArtifactRetriever.cachingInstance();
|
||||||
|
var repositories = serverRepositories(server);
|
||||||
|
var scopes = new DependencyScopes();
|
||||||
|
scopes.scope(compile)
|
||||||
|
.include(new Bom("com.example", "bom1", new VersionNumber(1, 0, 0)))
|
||||||
|
// declared at a different version than the BOM manages
|
||||||
|
.include(new Dependency("com.example", "a", new VersionNumber(2, 2, 0)))
|
||||||
|
// declared at the same version as the BOM manages
|
||||||
|
.include(new Dependency("com.example", "b", new VersionNumber(3, 0, 0)));
|
||||||
|
scopes.scope(Scope.test)
|
||||||
|
// the same difference through scope composition is
|
||||||
|
// reported once
|
||||||
|
.include(new Dependency("com.example", "a", new VersionNumber(2, 2, 0)))
|
||||||
|
// a version-less dependency takes the BOM version and
|
||||||
|
// isn't a difference
|
||||||
|
.include(new Dependency("com.example", "b"));
|
||||||
|
|
||||||
|
var conflicts = scopes.declaredVersionConflicts(new HierarchicalProperties(), retriever, repositories);
|
||||||
|
assertEquals(1, conflicts.size());
|
||||||
|
var conflict = conflicts.get(0);
|
||||||
|
assertEquals("com.example:a", conflict.dependency());
|
||||||
|
assertEquals(Version.parse("2.2.0"), conflict.declaredVersion());
|
||||||
|
assertEquals("com.example:bom1", conflict.bom());
|
||||||
|
assertEquals(Version.parse("1.4.0"), conflict.bomVersion());
|
||||||
|
|
||||||
|
// a bld.override that supplies the version prevents the report
|
||||||
|
var override_properties = new HierarchicalProperties();
|
||||||
|
override_properties.put(VersionResolution.PROPERTY_OVERRIDE_PREFIX, "com.example:a:2.2.0");
|
||||||
|
assertEquals(List.of(),
|
||||||
|
scopes.declaredVersionConflicts(override_properties, retriever, repositories));
|
||||||
|
} finally {
|
||||||
|
server.stop(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testEffectiveBomsComposition() {
|
void testEffectiveBomsComposition() {
|
||||||
var compile_bom = new Bom("com.example", "compile-bom", new VersionNumber(1, 0, 0));
|
var compile_bom = new Bom("com.example", "compile-bom", new VersionNumber(1, 0, 0));
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue