From cb74c8bfc827e72953fb5465118ad06d51bd04a9 Mon Sep 17 00:00:00 2001 From: Martin Chalupa Date: Mon, 1 Nov 2021 17:59:25 -0700 Subject: [PATCH 1/3] Improve filtering of paths Some assumptions about conflict resolutions were too aggressive and filtered actually required paths with relevant changes --- .../diff/PathAwareDiffReportGenerator.kt | 123 +++++++++--------- .../PathAwareDependencyDiffSpec.groovy | 116 +++++++++++++++++ 2 files changed, 178 insertions(+), 61 deletions(-) diff --git a/src/main/kotlin/nebula/plugin/dependencylock/diff/PathAwareDiffReportGenerator.kt b/src/main/kotlin/nebula/plugin/dependencylock/diff/PathAwareDiffReportGenerator.kt index 1e279b1..c55dfa2 100644 --- a/src/main/kotlin/nebula/plugin/dependencylock/diff/PathAwareDiffReportGenerator.kt +++ b/src/main/kotlin/nebula/plugin/dependencylock/diff/PathAwareDiffReportGenerator.kt @@ -10,7 +10,6 @@ import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.DefaultV import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.DefaultVersionSelectorScheme import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionParser import java.util.* -import kotlin.collections.HashSet typealias Path = List @@ -24,13 +23,14 @@ class PathAwareDiffReportGenerator : DiffReportGenerator { // is marked with configuration names where those paths belong. override fun generateDiffReport(project: Project, diffsByConfiguration: Map> ): List> { val pathsPerConfiguration: List = diffsByConfiguration.map { (configurationName: String, differences: List) -> - val allPaths: Set = constructShortestPathsToAllDependencies(differences, project, configurationName) - val pathsWithChanges: Set = filterPathsWithSignificantChanges(allPaths) - val pathsFromDirectDependencies: Set = removeNonSignificantPathParts(pathsWithChanges) + val completeDependencyTree: AnnotatedDependencyTree = constructPathsToAllDependencies(differences, project, configurationName) + val removedInsignificantChanges: AnnotatedDependencyTree = filterPathsWithSignificantChanges(completeDependencyTree) + val removeAlreadyVisited: AnnotatedDependencyTree = filterPathsWithDuplicatedElements(removedInsignificantChanges) + val removedInsignificantChangesAfterRemovingAlreadyVisited: AnnotatedDependencyTree = filterPathsWithSignificantChanges(removeAlreadyVisited) val removed: List = differences.filter { it.isRemoved }.map { it.dependency } - ConfigurationPaths(configurationName, Differences(pathsFromDirectDependencies, removed)) + ConfigurationPaths(configurationName, Differences(removedInsignificantChangesAfterRemovingAlreadyVisited, removed)) } val groupedDiffs: Map> = groupConfigurationsWithSameChanges(pathsPerConfiguration) @@ -38,80 +38,85 @@ class PathAwareDiffReportGenerator : DiffReportGenerator { return groupedDiffs.map { (differences: Differences, configurations: List) -> mapOf( "configurations" to configurations, - "differentPaths" to createDiffTree(differences.paths, hashSetOf()), + "differentPaths" to createDiffTree(differences.newAndUpdated.root), "removed" to differences.removed ) } } - //this method constructs shotests paths to all unique dependencies from module root within a configuration - private fun constructShortestPathsToAllDependencies(differences: List, project: Project, configurationName: String): Set { + //this method constructs paths to all unique dependencies from module root within a configuration + private fun constructPathsToAllDependencies(differences: List, project: Project, configurationName: String): AnnotatedDependencyTree { val differencesByDependency: Map = differences.associateBy { it.dependency } //build paths for all dependencies val pathQueue: Queue = LinkedList() - pathQueue.add(DependencyPathElement(project.configurations.getByName(configurationName).incoming.resolutionResult.root, null, null, null)) - val terminatedPaths: MutableSet = HashSet() + val root = DependencyPathElement(project.configurations.getByName(configurationName).incoming.resolutionResult.root, null, null) + pathQueue.add(root) while (!pathQueue.isEmpty()) { val forExploration = pathQueue.poll() - val nextLevel: Set = forExploration.selected.dependencies.filterIsInstance().map { - DependencyPathElement(it.selected, it.requested, differencesByDependency[it.selected.moduleName()], forExploration) - }.toSet() - - if (nextLevel.isNotEmpty()) - nextLevel.forEach { - pathQueue.add(it) - } - else - terminatedPaths.add(forExploration) + forExploration.selected.dependencies.filterIsInstance().forEach { + //attach new element to the tree + val newElement = DependencyPathElement(it.selected, it.requested, differencesByDependency[it.selected.moduleName()]) + forExploration.children.add(newElement) + pathQueue.add(newElement) + } } - //convert path to a list structure and drop root from it - return terminatedPaths.mapTo(mutableSetOf()) { it.toList().drop(1) } + return AnnotatedDependencyTree(root) } // we need to find only paths that have significant changes in them. A significant change is any new version requested by parent // or a rule or force. If change is caused by conflict resolution but the path is not responsible for bringing the winning version, // it is not considered significant change - private fun filterPathsWithSignificantChanges(allPaths: Set): Set { - return allPaths.filterTo(mutableSetOf()) { fullPath -> - val firstChangeOnPath = fullPath.find { it.isChangedInUpdate() } - if (firstChangeOnPath == null) { - false - } else { - !firstChangeOnPath.selected.selectionReason.isConflictResolution || firstChangeOnPath.isWinnerOfConflictResolution() - } - } - } - // dependency paths can have a significant change in a middle but transitive dependencies of the updated dependency // might be the same, we will drop any parts of a path after changed dependency that is unchanged or just changed // by conflict resolution - private fun removeNonSignificantPathParts(pathsWithChanges: Set) = - pathsWithChanges.map { fullPath -> - val pathWithoutUnchangedTail = fullPath.dropLastWhile { !it.isChangedInUpdate()} - val indexOfFirstConflictResolutionLooser = pathWithoutUnchangedTail.indexOfFirst { it.selected.selectionReason.isConflictResolution && !it.isWinnerOfConflictResolution() } + private fun filterPathsWithSignificantChanges(completeDependencyTree: AnnotatedDependencyTree): AnnotatedDependencyTree { + removeInsignificantDependencyPathElements(completeDependencyTree.root) + return completeDependencyTree + } - if (indexOfFirstConflictResolutionLooser > 0) { - pathWithoutUnchangedTail.take(indexOfFirstConflictResolutionLooser) - } else { - pathWithoutUnchangedTail - } - }.toSet() + private fun removeInsignificantDependencyPathElements(element: DependencyPathElement): Boolean { + //we assume that if this node lost conflict resolution there will be another place where this subtree + //was a winner so we don't need to cover it on all places where it was used instead of losers + //the exception are aligned dependencies that could have "no winner" since they are using virtual platform to upgrade to desired version + val selectionReason = element.selected.selectionReason + if (selectionReason.isConflictResolution && !selectionReason.isConstrained && !element.isWinnerOfConflictResolution()) { + return true + } + element.children.removeIf { + removeInsignificantDependencyPathElements(it) + } + return element.children.isEmpty() && !element.isChangedInUpdate() + } + + private fun filterPathsWithDuplicatedElements(completeDependencyTree: AnnotatedDependencyTree): AnnotatedDependencyTree { + removeAlreadyVisited(completeDependencyTree.root, mutableSetOf()) + return completeDependencyTree + } + + private fun removeAlreadyVisited(element: DependencyPathElement, visited: MutableSet) { + visited.add(element.selected.id) + element.children.forEach { + if (visited.contains(it.selected.id)) { + it.alreadyVisited = true + it.children.clear() + } else { + removeAlreadyVisited(it, visited) + } + } + } // some configurations can have the exact same changes we want to avoid duplicating the same information so // configurations with the exact same changes are grouped together. private fun groupConfigurationsWithSameChanges(pathsPerConfiguration: List) = pathsPerConfiguration.groupBy { it.paths }.mapValues { it.value.map { it.configurationName } } - private fun createDiffTree(paths: Set, visited: MutableSet): List> { - val grouped: Map> = currentLevelPathElementsWithChildren(paths) - - return grouped.map { (dependencyPathElement: DependencyPathElement, restOfPaths: Set) -> + private fun createDiffTree(parentElement: DependencyPathElement): List> { + return parentElement.children.map { dependencyPathElement: DependencyPathElement -> val result = mutableMapOf( "dependency" to dependencyPathElement.selected.moduleName(), - if (!visited.contains(dependencyPathElement.selected.id)) { - visited.add(dependencyPathElement.selected.id) - "children" to createDiffTree(restOfPaths, visited) + if (! dependencyPathElement.alreadyVisited) { + "children" to createDiffTree(dependencyPathElement) } else { "repeated" to true }, @@ -148,15 +153,14 @@ class PathAwareDiffReportGenerator : DiffReportGenerator { class ConfigurationPaths(val configurationName: String, val paths: Differences) - data class Differences(val paths: Set, val removed: List) + data class Differences(val newAndUpdated: AnnotatedDependencyTree, val removed: List) - class DependencyPathElement(val selected: ResolvedComponentResult, val requested: ComponentSelector?, val dependencyDiff: DependencyDiff?, val parent: DependencyPathElement?): Comparable { + data class AnnotatedDependencyTree(val root: DependencyPathElement) - fun toList(): MutableList { - val tail = parent?.toList() ?: LinkedList() - tail.add(this) - return tail - } + class DependencyPathElement(val selected: ResolvedComponentResult, val requested: ComponentSelector?, val dependencyDiff: DependencyDiff?): Comparable { + + var alreadyVisited: Boolean = false + val children: MutableSet = sortedSetOf() //return true if the dependency has been somehow updated/added in the graph fun isChangedInUpdate(): Boolean { @@ -225,15 +229,12 @@ class PathAwareDiffReportGenerator : DiffReportGenerator { other as DependencyPathElement if (selected.id != other.selected.id) return false - if (parent != other.parent) return false return true } override fun hashCode(): Int { - var result = selected.id.hashCode() - result = 31 * result + (parent?.hashCode() ?: 0) - return result + return selected.id.hashCode() } } diff --git a/src/test/groovy/nebula/plugin/dependencylock/PathAwareDependencyDiffSpec.groovy b/src/test/groovy/nebula/plugin/dependencylock/PathAwareDependencyDiffSpec.groovy index 714444b..4167fce 100644 --- a/src/test/groovy/nebula/plugin/dependencylock/PathAwareDependencyDiffSpec.groovy +++ b/src/test/groovy/nebula/plugin/dependencylock/PathAwareDependencyDiffSpec.groovy @@ -41,6 +41,10 @@ class PathAwareDependencyDiffSpec extends IntegrationTestKitSpec { 'test.example.alignment:consumer1-library:2.0.0 -> test.example.alignment:core-library:2.0.0', 'test.example.alignment:consumer2-library:1.0.0 -> test.example.alignment:core2-library:1.0.0', 'test.example.alignment:consumer2-library:2.0.0 -> test.example.alignment:core2-library:2.0.0', + 'test.example:consumer-of-aligned-dependency1:1.0.0 -> test.example.alignment:consumer1-library:1.0.0', + 'test.example:consumer-of-aligned-dependency1:2.0.0 -> test.example.alignment:consumer1-library:1.0.0', + 'test.example:consumer-of-aligned-dependency2:1.0.0 -> test.example.alignment:consumer2-library:1.0.0', + 'test.example:consumer-of-aligned-dependency2:2.0.0 -> test.example.alignment:consumer2-library:2.0.0', 'test.example:consumer-of-replaced:1.0.0 -> test.example:replaced:1.0.0', 'test.example:consumer-of-replacer:1.0.0 -> test.example:replacer:1.0.0', 'test.example:replaced:1.0.0', @@ -374,6 +378,118 @@ class PathAwareDependencyDiffSpec extends IntegrationTestKitSpec { consumer2.children[0].change.previousVersion == "2.0.0" } + def 'diff lock with paths with alignment without clear conflict resolution winner'() { + File rulesJsonFile = new File(projectDir, "${moduleName}.json") + rulesJsonFile << '''\ + { + "deny": [], "reject": [], "substitute": [], "replace": [], + "align": [ + { + "name": "testNebula", + "group": "test.example.alignment", + "reason": "Align test.example.alignment dependencies", + "author": "Example Person ", + "date": "2016-03-17T20:21:20.368Z" + } + ] + } + '''.stripIndent() + + new File("${projectDir}/gradle.properties").text = "systemProp.nebula.features.pathAwareDependencyDiff=true" + def dependenciesLock = new File(projectDir, 'dependencies.lock') + dependenciesLock << LockGenerator.duplicateIntoConfigsWhenUsingImplementationConfigurationOnly('''\ + "test.example:consumer-of-aligned-dependency1": { + "locked": "1.0.0" + }, + "test.example:consumer-of-aligned-dependency2": { + "locked": "1.0.0" + }, + "test.example.alignment:consumer1-library": { + "locked": "1.0.0", + "transitive": [ + "test.example:consumer-of-aligned-dependency1" + ] + }, + "test.example.alignment:consumer2-library": { + "locked": "1.0.0", + "transitive": [ + "test.example:consumer-of-aligned-dependency2" + ] + }, + "test.example.alignment:core-library": { + "locked": "1.0.0", + "transitive": [ + "test.example.alignment:consumer1-library" + ] + }, + "test.example.alignment:core2-library": { + "locked": "1.0.0", + "transitive": [ + "test.example.alignment:consumer2-library" + ] + } + '''.stripIndent()) + buildFile << """\ + plugins { + id 'nebula.dependency-lock' + id "nebula.resolution-rules" version "9.0.0" + } + + apply plugin: 'java' + repositories { + maven { url '${repoDir.absolutePath}' } + } + + dependencyLock { + includeTransitives = true + } + + dependencies { + resolutionRules files('$rulesJsonFile') + implementation 'test.example:consumer-of-aligned-dependency1:2.0.0' + implementation 'test.example:consumer-of-aligned-dependency2:2.0.0' + } + """.stripIndent() + + when: + def result = runTasks('generateLock', 'diffLock') + + then: + def lockdiff = new JsonSlurper().parse(new File(projectDir, 'build/dependency-lock/lockdiff.json')) + def allConfigurations = lockdiff.find { it.configurations.contains("compileClasspath")} + def directDependencies = allConfigurations["differentPaths"] + def alignedConsumer1 = directDependencies.find { it.dependency == "test.example:consumer-of-aligned-dependency1"} + alignedConsumer1.version == "2.0.0" + alignedConsumer1.change.description == "requested" + alignedConsumer1.change.type == "UPDATED" + alignedConsumer1.change.previousVersion == "1.0.0" + def consumer1 = alignedConsumer1.children.find { it.dependency == "test.example.alignment:consumer1-library"} + consumer1.version == "2.0.0" + consumer1.change.description == "belongs to platform aligned-platform:diff-lock-with-paths-with-alignment-without-clear-conflict-resolution-winner-0-for-test.example.alignment:2.0.0" + consumer1.change.type == "UPDATED" + consumer1.change.previousVersion == "1.0.0" + consumer1.children[0].dependency == "test.example.alignment:core-library" + consumer1.children[0].version == "2.0.0" + consumer1.children[0].change.description == "belongs to platform aligned-platform:diff-lock-with-paths-with-alignment-without-clear-conflict-resolution-winner-0-for-test.example.alignment:2.0.0" + consumer1.children[0].change.type == "UPDATED" + consumer1.children[0].change.previousVersion == "1.0.0" + def alignedConsumer2 = directDependencies.find { it.dependency == "test.example:consumer-of-aligned-dependency2"} + alignedConsumer2.version == "2.0.0" + alignedConsumer2.change.description == "requested" + alignedConsumer2.change.type == "UPDATED" + alignedConsumer2.change.previousVersion == "1.0.0" + def consumer2 = alignedConsumer2.children.find { it.dependency == "test.example.alignment:consumer2-library"} + consumer2.version == "2.0.0" + consumer2.change.description == "belongs to platform aligned-platform:diff-lock-with-paths-with-alignment-without-clear-conflict-resolution-winner-0-for-test.example.alignment:2.0.0" + consumer2.change.type == "UPDATED" + consumer2.change.previousVersion == "1.0.0" + consumer2.children[0].dependency == "test.example.alignment:core2-library" + consumer2.children[0].version == "2.0.0" + consumer2.children[0].change.description == "belongs to platform aligned-platform:diff-lock-with-paths-with-alignment-without-clear-conflict-resolution-winner-0-for-test.example.alignment:2.0.0" + consumer2.children[0].change.type == "UPDATED" + consumer2.children[0].change.previousVersion == "1.0.0" + } + def 'diff lock with paths with constrained dependency'() { new File("${projectDir}/gradle.properties").text = "systemProp.nebula.features.pathAwareDependencyDiff=true" def dependenciesLock = new File(projectDir, 'dependencies.lock') From 17fa5c919b85b094e62b9b0a7a178f5fb27d1ff4 Mon Sep 17 00:00:00 2001 From: Martin Chalupa Date: Tue, 2 Nov 2021 14:02:41 -0700 Subject: [PATCH 2/3] Improve descriptions with all the reasons participating in dependency selection, messages for conflict resolution are latered to reduce length if too many versions participate --- .../diff/PathAwareDiffReportGenerator.kt | 37 ++++++------------- .../PathAwareDependencyDiffSpec.groovy | 28 +++++++------- 2 files changed, 25 insertions(+), 40 deletions(-) diff --git a/src/main/kotlin/nebula/plugin/dependencylock/diff/PathAwareDiffReportGenerator.kt b/src/main/kotlin/nebula/plugin/dependencylock/diff/PathAwareDiffReportGenerator.kt index c55dfa2..18f191e 100644 --- a/src/main/kotlin/nebula/plugin/dependencylock/diff/PathAwareDiffReportGenerator.kt +++ b/src/main/kotlin/nebula/plugin/dependencylock/diff/PathAwareDiffReportGenerator.kt @@ -174,34 +174,19 @@ class PathAwareDiffReportGenerator : DiffReportGenerator { fun changeDescription(): String { - return if (selected.selectionReason.isSelectedByRule) { - findDescriptionForCause(ComponentSelectionCause.SELECTED_BY_RULE) - } else if (selected.selectionReason.isExpected) { - if (isSubmodule()) { - "new local submodule" - } else { - findDescriptionForCause(ComponentSelectionCause.REQUESTED) - } - } else if (selected.selectionReason.isForced) { - val forcedDescription = findDescriptionForCause(ComponentSelectionCause.FORCED) - //if it is force and also constrained we add message for constraint, it would be typically alignment - forcedDescription + if (selected.selectionReason.isConstrained) { - ", ${findDescriptionForCause(ComponentSelectionCause.CONSTRAINT)}" - } else { - "" - } - } else if (selected.selectionReason.isConstrained) { - findDescriptionForCause(ComponentSelectionCause.CONSTRAINT) - } else if (isWinnerOfConflictResolution()) { - findDescriptionForCause(ComponentSelectionCause.REQUESTED) - } else { - "" + val causesWithDescription = selected.selectionReason.descriptions.associate { it.cause to it.description }.toSortedMap() + if (causesWithDescription.contains(ComponentSelectionCause.REQUESTED) && isSubmodule()) { + causesWithDescription[ComponentSelectionCause.REQUESTED] = "new local submodule" } - } + if (causesWithDescription.contains(ComponentSelectionCause.CONFLICT_RESOLUTION)) { + val message = if (isWinnerOfConflictResolution()) + "this path brought the winner of conflict resolution" + else + "this path participates in conflict resolution, but the winner is from a different path" + causesWithDescription[ComponentSelectionCause.CONFLICT_RESOLUTION] = message - private fun findDescriptionForCause(cause: ComponentSelectionCause): String { - val gradleDescription = selected.selectionReason.descriptions.find { it.cause == cause} - return gradleDescription!!.description + } + return causesWithDescription.values.joinToString("; ") } fun isSubmodule(): Boolean { diff --git a/src/test/groovy/nebula/plugin/dependencylock/PathAwareDependencyDiffSpec.groovy b/src/test/groovy/nebula/plugin/dependencylock/PathAwareDependencyDiffSpec.groovy index 4167fce..f2e750f 100644 --- a/src/test/groovy/nebula/plugin/dependencylock/PathAwareDependencyDiffSpec.groovy +++ b/src/test/groovy/nebula/plugin/dependencylock/PathAwareDependencyDiffSpec.groovy @@ -173,7 +173,7 @@ class PathAwareDependencyDiffSpec extends IntegrationTestKitSpec { def directDependencies = allConfigurations["differentPaths"] def directTransitive = directDependencies.find { it.dependency == "test.example:direct-dependency-updating-transitive"} directTransitive.version == "2.2.0" - directTransitive.change.description == "requested" + directTransitive.change.description == "requested; this path brought the winner of conflict resolution" directTransitive.change.type == "UPDATED" directTransitive.change.previousVersion == "2.0.0" def ruleUpdateConsumer = directDependencies.find { it.dependency == "test.example:updated-by-rule-dependency-consumer"} @@ -181,7 +181,7 @@ class PathAwareDependencyDiffSpec extends IntegrationTestKitSpec { ruleUpdateConsumer.change == null ruleUpdateConsumer.children[0].dependency == "test.example:updated-by-rule-dependency" ruleUpdateConsumer.children[0].version == "2.0.0" - ruleUpdateConsumer.children[0].change.description == "substitue test.example:updated-by-rule-dependency:1.0.0 with 2.0.0 because JIRA-1039" + ruleUpdateConsumer.children[0].change.description == "requested; substitue test.example:updated-by-rule-dependency:1.0.0 with 2.0.0 because JIRA-1039" ruleUpdateConsumer.children[0].change.type == "UPDATED" ruleUpdateConsumer.children[0].change.previousVersion == "1.0.0" def qux = directDependencies.find { it.dependency == "test.example:qux"} @@ -191,12 +191,12 @@ class PathAwareDependencyDiffSpec extends IntegrationTestKitSpec { qux.change.previousVersion == "1.0.0" def foo = qux.children.find { it.dependency == "test.example:foo" } foo.version == "2.0.1" - foo.change.description == "requested" + foo.change.description == "requested; this path brought the winner of conflict resolution" foo.change.type == "UPDATED" foo.change.previousVersion == "1.0.1" foo.children[0].dependency == "test.example:direct-dependency-updated-transitively" foo.children[0].version == "1.1.0" - foo.children[0].change.description == "requested" + foo.children[0].change.description == "requested; this path brought the winner of conflict resolution" foo.children[0].change.type == "UPDATED" foo.children[0].change.previousVersion == "1.0.0" def newDependency = qux.children.find { it.dependency == "test.example:new-dependency" } @@ -277,7 +277,7 @@ class PathAwareDependencyDiffSpec extends IntegrationTestKitSpec { def directDependencies = allConfigurations["differentPaths"] def qux = directDependencies.find { it.dependency == "test.example:qux"} qux.version == "2.0.0" - qux.change.description == "Recommending version 2.0.0 for dependency test.example:qux via conflict resolution recommendation\n\twith reasons: nebula.dependency-recommender uses mavenBom: test.nebula.bom:testbom:pom:1.0.0" + qux.change.description == "requested; Recommending version 2.0.0 for dependency test.example:qux via conflict resolution recommendation\n\twith reasons: nebula.dependency-recommender uses mavenBom: test.nebula.bom:testbom:pom:1.0.0" qux.change.type == "UPDATED" qux.change.previousVersion == "1.0.0" } @@ -358,22 +358,22 @@ class PathAwareDependencyDiffSpec extends IntegrationTestKitSpec { def directDependencies = allConfigurations["differentPaths"] def consumer1 = directDependencies.find { it.dependency == "test.example.alignment:consumer1-library"} consumer1.version == "1.0.0" - consumer1.change.description == "forced, belongs to platform aligned-platform:diff-lock-with-paths-with-forced-alignment-0-for-test.example.alignment:1.0.0" + consumer1.change.description == "requested; forced; belongs to platform aligned-platform:diff-lock-with-paths-with-forced-alignment-0-for-test.example.alignment:1.0.0" consumer1.change.type == "UPDATED" consumer1.change.previousVersion == "2.0.0" consumer1.children[0].dependency == "test.example.alignment:core-library" consumer1.children[0].version == "1.0.0" - consumer1.children[0].change.description == "forced, belongs to platform aligned-platform:diff-lock-with-paths-with-forced-alignment-0-for-test.example.alignment:1.0.0" + consumer1.children[0].change.description == "requested; forced; belongs to platform aligned-platform:diff-lock-with-paths-with-forced-alignment-0-for-test.example.alignment:1.0.0" consumer1.children[0].change.type == "UPDATED" consumer1.children[0].change.previousVersion == "2.0.0" def consumer2 = directDependencies.find { it.dependency == "test.example.alignment:consumer2-library"} consumer2.version == "1.0.0" - consumer2.change.description == "forced, belongs to platform aligned-platform:diff-lock-with-paths-with-forced-alignment-0-for-test.example.alignment:1.0.0" + consumer2.change.description == "requested; forced; belongs to platform aligned-platform:diff-lock-with-paths-with-forced-alignment-0-for-test.example.alignment:1.0.0" consumer2.change.type == "UPDATED" consumer2.change.previousVersion == "2.0.0" consumer2.children[0].dependency == "test.example.alignment:core2-library" consumer2.children[0].version == "1.0.0" - consumer2.children[0].change.description == "forced, belongs to platform aligned-platform:diff-lock-with-paths-with-forced-alignment-0-for-test.example.alignment:1.0.0" + consumer2.children[0].change.description == "requested; forced; belongs to platform aligned-platform:diff-lock-with-paths-with-forced-alignment-0-for-test.example.alignment:1.0.0" consumer2.children[0].change.type == "UPDATED" consumer2.children[0].change.previousVersion == "2.0.0" } @@ -465,12 +465,12 @@ class PathAwareDependencyDiffSpec extends IntegrationTestKitSpec { alignedConsumer1.change.previousVersion == "1.0.0" def consumer1 = alignedConsumer1.children.find { it.dependency == "test.example.alignment:consumer1-library"} consumer1.version == "2.0.0" - consumer1.change.description == "belongs to platform aligned-platform:diff-lock-with-paths-with-alignment-without-clear-conflict-resolution-winner-0-for-test.example.alignment:2.0.0" + consumer1.change.description == "requested; this path participates in conflict resolution, but the winner is from a different path; belongs to platform aligned-platform:diff-lock-with-paths-with-alignment-without-clear-conflict-resolution-winner-0-for-test.example.alignment:2.0.0" consumer1.change.type == "UPDATED" consumer1.change.previousVersion == "1.0.0" consumer1.children[0].dependency == "test.example.alignment:core-library" consumer1.children[0].version == "2.0.0" - consumer1.children[0].change.description == "belongs to platform aligned-platform:diff-lock-with-paths-with-alignment-without-clear-conflict-resolution-winner-0-for-test.example.alignment:2.0.0" + consumer1.children[0].change.description == "requested; belongs to platform aligned-platform:diff-lock-with-paths-with-alignment-without-clear-conflict-resolution-winner-0-for-test.example.alignment:2.0.0" consumer1.children[0].change.type == "UPDATED" consumer1.children[0].change.previousVersion == "1.0.0" def alignedConsumer2 = directDependencies.find { it.dependency == "test.example:consumer-of-aligned-dependency2"} @@ -480,12 +480,12 @@ class PathAwareDependencyDiffSpec extends IntegrationTestKitSpec { alignedConsumer2.change.previousVersion == "1.0.0" def consumer2 = alignedConsumer2.children.find { it.dependency == "test.example.alignment:consumer2-library"} consumer2.version == "2.0.0" - consumer2.change.description == "belongs to platform aligned-platform:diff-lock-with-paths-with-alignment-without-clear-conflict-resolution-winner-0-for-test.example.alignment:2.0.0" + consumer2.change.description == "requested; belongs to platform aligned-platform:diff-lock-with-paths-with-alignment-without-clear-conflict-resolution-winner-0-for-test.example.alignment:2.0.0" consumer2.change.type == "UPDATED" consumer2.change.previousVersion == "1.0.0" consumer2.children[0].dependency == "test.example.alignment:core2-library" consumer2.children[0].version == "2.0.0" - consumer2.children[0].change.description == "belongs to platform aligned-platform:diff-lock-with-paths-with-alignment-without-clear-conflict-resolution-winner-0-for-test.example.alignment:2.0.0" + consumer2.children[0].change.description == "requested; belongs to platform aligned-platform:diff-lock-with-paths-with-alignment-without-clear-conflict-resolution-winner-0-for-test.example.alignment:2.0.0" consumer2.children[0].change.type == "UPDATED" consumer2.children[0].change.previousVersion == "1.0.0" } @@ -545,7 +545,7 @@ class PathAwareDependencyDiffSpec extends IntegrationTestKitSpec { consumer.children[0].version == "1.0.0" consumer.children[0].change.type == "UPDATED" consumer.children[0].change.previousVersion == "2.0.0" - consumer.children[0].change.description == "constraint" + consumer.children[0].change.description == "constraint; by ancestor" } def 'diff lock with paths with repeated dependencies'() { From b2c6600c77509c83f9bc59c440f4c028b99005fc Mon Sep 17 00:00:00 2001 From: Martin Chalupa Date: Tue, 2 Nov 2021 14:26:10 -0700 Subject: [PATCH 3/3] change isSubmodule to submodule flag name to better play with jackson expectations at reading side --- .../dependencylock/diff/PathAwareDiffReportGenerator.kt | 2 +- .../plugin/dependencylock/PathAwareDependencyDiffSpec.groovy | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/nebula/plugin/dependencylock/diff/PathAwareDiffReportGenerator.kt b/src/main/kotlin/nebula/plugin/dependencylock/diff/PathAwareDiffReportGenerator.kt index 18f191e..cc5d2d1 100644 --- a/src/main/kotlin/nebula/plugin/dependencylock/diff/PathAwareDiffReportGenerator.kt +++ b/src/main/kotlin/nebula/plugin/dependencylock/diff/PathAwareDiffReportGenerator.kt @@ -121,7 +121,7 @@ class PathAwareDiffReportGenerator : DiffReportGenerator { "repeated" to true }, if (dependencyPathElement.isSubmodule()) - "isSubmodule" to true + "submodule" to true else "version" to dependencyPathElement.selected.moduleVersion() ) diff --git a/src/test/groovy/nebula/plugin/dependencylock/PathAwareDependencyDiffSpec.groovy b/src/test/groovy/nebula/plugin/dependencylock/PathAwareDependencyDiffSpec.groovy index f2e750f..1114db5 100644 --- a/src/test/groovy/nebula/plugin/dependencylock/PathAwareDependencyDiffSpec.groovy +++ b/src/test/groovy/nebula/plugin/dependencylock/PathAwareDependencyDiffSpec.groovy @@ -794,7 +794,7 @@ class PathAwareDependencyDiffSpec extends IntegrationTestKitSpec { def allConfigurations = lockdiff[0] def directDependencies = allConfigurations["differentPaths"] def common = directDependencies.find { it.dependency == "test:common"} - common.isSubmodule == true + common.submodule == true def foo = common.children.find { it.dependency == "test.example:foo" } foo.version == "2.0.1" foo.change.description == "requested" @@ -845,7 +845,7 @@ class PathAwareDependencyDiffSpec extends IntegrationTestKitSpec { def allConfigurations = lockdiff[0] def directDependencies = allConfigurations["differentPaths"] def common = directDependencies.find { it.dependency == "test:common"} - common.isSubmodule == true + common.submodule == true common.change.description == "new local submodule" common.change.type == "NEW" }