mirror of
https://github.com/rife2/bld
synced 2026-07-26 01: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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -233,6 +233,78 @@ public class VersionResolution {
|
|||
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
|
||||
// on, mirroring Maven this includes the type and the classifier, the
|
||||
// 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())) {
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -54,7 +54,6 @@ public class PurgeOperation extends AbstractOperation<PurgeOperation> {
|
|||
return;
|
||||
}
|
||||
|
||||
executeReportUncoveredDependencies();
|
||||
executePurgeCompileDependencies();
|
||||
executePurgeProvidedDependencies();
|
||||
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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
void testEffectiveBomsComposition() {
|
||||
var compile_bom = new Bom("com.example", "compile-bom", new VersionNumber(1, 0, 0));
|
||||
|
|
|
|||
Loading…
Reference in a new issue