Skip to content

Commit 08769b1

Browse files
Merge pull request #95 from nebula-plugins/align_and_substitute_using_ruleMatches
AlignedPlatformRule honors align rule excludes and includes
2 parents 246f467 + c0a8c4a commit 08769b1

File tree

2 files changed

+144
-24
lines changed

2 files changed

+144
-24
lines changed

src/integTest/groovy/nebula/plugin/resolutionrules/AlignedPlatformRulesSpec.groovy

Lines changed: 119 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ class AlignedPlatformRulesSpec extends IntegrationTestKitSpec {
6464
.addModule(new ModuleBuilder('test.nebula:f:1.0.3').addDependency('test.nebula:a:1.0.3').build())
6565
.addModule(new ModuleBuilder('test.nebula:f:1.1.0').addDependency('test.nebula:a:1.1.0').build())
6666

67+
.addModule('test.nebula:g:1.0.0')
68+
.addModule('test.nebula:g:1.0.1')
69+
.addModule('test.nebula:g:1.0.2')
70+
.addModule('test.nebula:g:1.0.3')
71+
.addModule('test.nebula:g:1.1.0')
72+
6773
.build()
6874
mavenrepo = new GradleDependencyGenerator(graph, "${projectDir}/testrepogen").generateTestMavenRepo()
6975

@@ -483,7 +489,6 @@ class AlignedPlatformRulesSpec extends IntegrationTestKitSpec {
483489

484490
where:
485491
definedVersionType | definedVersion | subVersionType | subFromVersionAndModule | subToVersionAndModule | subUpOrDown | coreAlignment | ABResultingVersion | CResultingVersion
486-
// missing cases: statically defined dependency: core alignment fails to align when lower versions are missing | core alignment: #coreAlignment
487492
"static version" | "1.0.1" | "range" | "b:[1.0.0,1.1.0)" | "b:0.5.0" | "lower" | false | "1.0.1" | "1.0.1"
488493
"static version" | "1.0.1" | "range" | "b:[1.0.0,1.1.0)" | "b:0.5.0" | "lower" | true | "0.5.0" | "FAILED"
489494
}
@@ -855,14 +860,8 @@ class AlignedPlatformRulesSpec extends IntegrationTestKitSpec {
855860
dependencyInsightContains(result.output, "com.google.inject:guice", resultingVersion)
856861
857862
if (coreAlignment) {
858-
// for direct dependency
859863
assert result.output.contains("com.google.inject:guice:{require 4.1.0; reject [4.2.0,)}")
860864
861-
// for transitive dependencies
862-
assert result.output.contains("com.google.inject.extensions:guice-assistedinject:{require 4.1.0; reject [4.2.0,)}")
863-
assert result.output.contains("com.google.inject.extensions:guice-multibindings:{require 4.1.0; reject [4.2.0,)}")
864-
assert result.output.contains("com.google.inject.extensions:guice-grapher:{require 4.1.0; reject [4.2.0,)}")
865-
866865
assert result.output.contains("belongs to platform aligned-platform:rules-0:$resultingVersion")
867866
}
868867
@@ -898,14 +897,8 @@ class AlignedPlatformRulesSpec extends IntegrationTestKitSpec {
898897
}
899898

900899
if (coreAlignment) {
901-
// for direct dependency
902900
assert result.output.contains("com.google.inject:guice:{require 4.1.0; reject [4.2.0,)}")
903901

904-
// for transitive dependencies
905-
assert result.output.contains("com.google.inject.extensions:guice-assistedinject:{require 4.1.0; reject [4.2.0,)}")
906-
assert result.output.contains("com.google.inject.extensions:guice-multibindings:{require 4.1.0; reject [4.2.0,)}")
907-
assert result.output.contains("com.google.inject.extensions:guice-grapher:{require 4.1.0; reject [4.2.0,)}")
908-
909902
assert result.output.contains("belongs to platform aligned-platform:rules-0:$resultingAlignedVersion")
910903
}
911904

@@ -950,6 +943,119 @@ class AlignedPlatformRulesSpec extends IntegrationTestKitSpec {
950943
coreAlignment << [false, true]
951944
}
952945

946+
@Unroll
947+
def 'substitution rule excludes are honored | core alignment #coreAlignment'() {
948+
given:
949+
def module = "test.nebula:a:[1.0.0,1.0.3)"
950+
def with = "test.nebula:a:1.0.3"
951+
rulesJsonFile << """
952+
{
953+
"substitute": [
954+
{
955+
"module" : "$module",
956+
"with" : "$with",
957+
"reason" : "$reason",
958+
"author" : "Test user <test@example.com>",
959+
"date" : "2015-10-07T20:21:20.368Z"
960+
}
961+
],
962+
"align": [
963+
{
964+
"group": "(test.nebula|test.nebula.ext)",
965+
"reason": "Align test.nebula dependencies",
966+
"author": "Example Person <person@example.org>",
967+
"includes": [],
968+
"excludes": ["(b|c)"],
969+
"date": "2016-03-17T20:21:20.368Z"
970+
}
971+
]
972+
}
973+
""".stripIndent()
974+
975+
buildFile << """
976+
dependencies {
977+
compile 'test.nebula:a:1.0.1'
978+
compile 'test.nebula:b:1.0.1'
979+
compile 'test.nebula:c:1.0.2'
980+
compile 'test.nebula:g:1.0.1'
981+
}
982+
""".stripIndent()
983+
984+
when:
985+
def result = runTasks(*tasks(coreAlignment))
986+
987+
then:
988+
def alignedResultingVersion = "1.0.3"
989+
dependencyInsightContains(result.output, "test.nebula:a", alignedResultingVersion)
990+
dependencyInsightContains(result.output, "test.nebula:g", alignedResultingVersion)
991+
992+
dependencyInsightContains(result.output, "test.nebula:b", '1.0.1')
993+
dependencyInsightContains(result.output, "test.nebula:c", '1.0.2')
994+
995+
if (coreAlignment) {
996+
assert result.output.contains("belongs to platform aligned-platform:rules-0:$alignedResultingVersion")
997+
}
998+
999+
where:
1000+
coreAlignment << [false, true]
1001+
}
1002+
1003+
@Unroll
1004+
def 'substitution rule includes are honored | core alignment #coreAlignment'() {
1005+
given:
1006+
def module = "test.nebula:a:[1.0.0,1.0.3)"
1007+
def with = "test.nebula:a:1.0.3"
1008+
rulesJsonFile << """
1009+
{
1010+
"substitute": [
1011+
{
1012+
"module" : "$module",
1013+
"with" : "$with",
1014+
"reason" : "$reason",
1015+
"author" : "Test user <test@example.com>",
1016+
"date" : "2015-10-07T20:21:20.368Z"
1017+
}
1018+
],
1019+
"align": [
1020+
{
1021+
"group": "(test.nebula|test.nebula.ext)",
1022+
"reason": "Align test.nebula dependencies",
1023+
"author": "Example Person <person@example.org>",
1024+
"includes": ["(a|g)"],
1025+
"excludes": [],
1026+
"date": "2016-03-17T20:21:20.368Z"
1027+
}
1028+
]
1029+
}
1030+
""".stripIndent()
1031+
1032+
buildFile << """
1033+
dependencies {
1034+
compile 'test.nebula:a:1.0.1'
1035+
compile 'test.nebula:b:1.0.1'
1036+
compile 'test.nebula:c:1.0.2'
1037+
compile 'test.nebula:g:1.0.1'
1038+
}
1039+
""".stripIndent()
1040+
1041+
when:
1042+
def result = runTasks(*tasks(coreAlignment))
1043+
1044+
then:
1045+
def alignedResultingVersion = "1.0.3"
1046+
dependencyInsightContains(result.output, "test.nebula:a", alignedResultingVersion)
1047+
dependencyInsightContains(result.output, "test.nebula:g", alignedResultingVersion)
1048+
1049+
dependencyInsightContains(result.output, "test.nebula:b", '1.0.1')
1050+
dependencyInsightContains(result.output, "test.nebula:c", '1.0.2')
1051+
1052+
if (coreAlignment) {
1053+
assert result.output.contains("belongs to platform aligned-platform:rules-0:$alignedResultingVersion")
1054+
}
1055+
1056+
where:
1057+
coreAlignment << [false, true]
1058+
}
9531059

9541060
@Unroll
9551061
def 'recs with core bom support disabled: core alignment should substitute and align from bom version to lower static version | core alignment #coreAlignment'() {

src/main/kotlin/nebula/plugin/resolutionrules/rules.kt

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ data class RuleSet(
7777

7878
class AlignedPlatformRule(alignRule: AlignRule, substituteRules: MutableList<SubstituteRule>) : Serializable {
7979
var substituteRules: List<SubstituteRule> = substituteRules
80+
var alignRule: AlignRule = alignRule
8081

8182
fun apply(project: Project, configuration: Configuration, resolutionStrategy: ResolutionStrategy, extension: NebulaResolutionRulesExtension, reasons: MutableSet<String>) {
8283
substituteRules.forEach {
@@ -93,24 +94,35 @@ class AlignedPlatformRule(alignRule: AlignRule, substituteRules: MutableList<Sub
9394
if (substitutedModule != null && shouldApplyRule(configuration, substitutedModule)) {
9495
firstLevelDependenciesRejectTheSubstitutedVersions(project, configuration, substitutedModule, withSelector, resolutionStrategy)
9596
transitiveDependenciesRejectTheSubstitutedVersions(project, substitutedModule, withSelector)
96-
it.applyForAlignedGroup(project, configuration, configuration.resolutionStrategy, extension, reasons)
97+
it.applyForAlignedGroup(project, configuration, configuration.resolutionStrategy, extension, reasons, alignRule)
9798
}
9899
}
99100
}
100101

101102
private fun shouldApplyRule(configuration: Configuration, substitutedModule: ModuleComponentSelector): Boolean {
102103
var shouldApplyRule = false
103-
configuration.incoming.dependencies.forEach { dep ->
104+
var substitutedDependencyIsInDependencyGraph = false
105+
106+
val incomingDependencies = configuration.incoming.dependencies
107+
108+
incomingDependencies.forEach { dep ->
104109
if (dep.group == substitutedModule.group && dep.name == substitutedModule.module) {
105-
shouldApplyRule = true // should apply only if substituted model is in the dependency graph
110+
substitutedDependencyIsInDependencyGraph = true
111+
}
112+
}
113+
if (substitutedDependencyIsInDependencyGraph) {
114+
incomingDependencies.forEach { dep ->
115+
if (alignRule.ruleMatches(dep.group ?: "", dep.name)) {
116+
shouldApplyRule = true
117+
}
106118
}
107119
}
108120
return shouldApplyRule
109121
}
110122

111123
private fun transitiveDependenciesRejectTheSubstitutedVersions(project: Project, substitutedModule: ModuleComponentSelector, withSelector: ModuleComponentSelector) {
112124
project.dependencies.components.all(TransitiveDependenciesSubstitutionMetadataRule::class.java) {
113-
it.params(substitutedModule.group, substitutedModule.module, substitutedModule.version, withSelector.version)
125+
it.params(alignRule, substitutedModule.group, substitutedModule.module, substitutedModule.version, withSelector.version)
114126
}
115127
}
116128

@@ -120,7 +132,7 @@ class AlignedPlatformRule(alignRule: AlignRule, substituteRules: MutableList<Sub
120132
configuration.incoming.beforeResolve { resolvableDependencies ->
121133
resolvableDependencies.dependencies.forEach { dep ->
122134
if (dep is ExternalModuleDependency) {
123-
if (dep.group!!.startsWith(substitutedModule.group)) {
135+
if (alignRule.ruleMatches(dep.group ?: "", dep.name)) {
124136
val usingDependencyRecommendation = dep.version.isNullOrEmpty()
125137

126138
if (usingDependencyRecommendation) {
@@ -245,7 +257,8 @@ data class SubstituteRule(val module: String, val with: String, override var rul
245257
}
246258
}
247259

248-
fun applyForAlignedGroup(project: Project, configuration: Configuration, resolutionStrategy: ResolutionStrategy, extension: NebulaResolutionRulesExtension, reasons: MutableSet<String>) {
260+
fun applyForAlignedGroup(project: Project, configuration: Configuration, resolutionStrategy: ResolutionStrategy,
261+
extension: NebulaResolutionRulesExtension, reasons: MutableSet<String>, alignRule: AlignRule) {
249262
val substitution = resolutionStrategy.dependencySubstitution
250263
val selector = substitution.module(module)
251264
val withModuleId = ModuleVersionIdentifier.valueOf(with)
@@ -254,16 +267,15 @@ data class SubstituteRule(val module: String, val with: String, override var rul
254267
}
255268
val withSelector = substitution.module(withModuleId.toString()) as ModuleComponentSelector
256269

257-
// TODO: only do this if the alignment rule applies to this one via includes and excludes
258270
if (selector is ModuleComponentSelector) {
259271
resolutionStrategy.dependencySubstitution.all(action {
260272
if (requested is ModuleComponentSelector) {
261273
val requestedSelector = requested as ModuleComponentSelector
262274
val requestedWithSubstitutedVersionFromAlignedModule = ModuleVersionIdentifier.valueOf("${requestedSelector.group}:${requestedSelector.module}:${withSelector.version}")
263275

264-
val hasSameGroup = requestedSelector.group == selector.group
276+
val matches = alignRule.ruleMatches(requestedSelector.group ?: "", requestedSelector.module)
265277
val notTheOriginatingDependency = requestedSelector.module != selector.module
266-
if (hasSameGroup && notTheOriginatingDependency) {
278+
if (matches && notTheOriginatingDependency) {
267279
val versionSelector = VersionWithSelector(selector.version).asSelector()
268280
if (versionSelector.accept(requestedSelector.version)) {
269281
val message = "substitution from aligned dependency '$selector' to '$withSelector' because '$reason'\n" +
@@ -282,13 +294,15 @@ data class SubstituteRule(val module: String, val with: String, override var rul
282294

283295
class TransitiveDependenciesSubstitutionMetadataRule : ComponentMetadataRule, Serializable {
284296
private val logger: Logger = Logging.getLogger(TransitiveDependenciesSubstitutionMetadataRule::class.java)
297+
val alignRule: AlignRule
285298
val substitutionGroup: String
286299
val substitutionModuleName: String
287300
val substitutionVersion: String
288301
val withSelectorVersion: String
289302

290303
@Inject
291-
constructor(substitutionGroup: String, substitutionModuleName: String, substitutionVersion: String, withSelectorVersion: String) {
304+
constructor(alignRule: AlignRule, substitutionGroup: String, substitutionModuleName: String, substitutionVersion: String, withSelectorVersion: String) {
305+
this.alignRule = alignRule
292306
this.substitutionGroup = substitutionGroup
293307
this.substitutionModuleName = substitutionModuleName
294308
this.substitutionVersion = substitutionVersion
@@ -304,7 +318,7 @@ class TransitiveDependenciesSubstitutionMetadataRule : ComponentMetadataRule, Se
304318
details.allVariants {
305319
it.withDependencies { deps ->
306320
deps.forEach { dep ->
307-
if (dep.group.startsWith(substitutionGroup)) {
321+
if (alignRule.ruleMatches(dep.group ?: "", dep.name)) {
308322
dep.version {
309323
it.reject(substitutionVersion)
310324
logger.debug("Rejection of transitive dependency ${dep.group}:${dep.name} version(s) '$substitutionVersion' from aligned dependency '$substitutionGroup:$substitutionModuleName'")

0 commit comments

Comments
 (0)