Skip to content

Commit

Permalink
Guard constraint resolution by a property since it isn't really possi…
Browse files Browse the repository at this point in the history
…ble to know if a constraint is added by a plugin and out of user control or not.
  • Loading branch information
anuraaga committed Oct 16, 2019
1 parent d97e7f3 commit 7dba9c4
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 44 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,21 @@ tasks.withType<DependencyUpdatesTask> {

</details>

#### Constraints

If you use constraints, for example to define a BOM using the [`java-platform`](https://docs.gradle.org/current/userguide/java_platform_plugin.html)
plugin or to [manage](https://docs.gradle.org/current/userguide/managing_transitive_dependencies.html#sec:dependency_constraints)
transitive dependency versions, you can enable checking of constraints by specifying the `checkConstraints`
attribute of the `dependencyUpdates` task.

```groovy
tasks {
dependencyUpdates {
checkConstraints = true
}
}
```

#### Kotlin DSL

If using Gradle's [kotlin-dsl][kotlin_dsl], you could configure the `dependencyUpdates` like this:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class DependencyUpdates {
String reportfileName
boolean checkForGradleUpdate
String gradleReleaseChannel
boolean checkConstraints

/** Evaluates the dependencies and returns a reporter. */
DependencyUpdatesReporter run() {
Expand All @@ -65,7 +66,7 @@ class DependencyUpdates {
private Set<DependencyStatus> resolveProjects(Map<Project, Set<Configuration>> projectConfigs) {
projectConfigs.keySet().collect { proj ->
Set<Configuration> configurations = projectConfigs.get(proj)
Resolver resolver = new Resolver(proj, resolutionStrategy)
Resolver resolver = new Resolver(proj, resolutionStrategy, checkConstraints)
configurations.collect { Configuration config ->
resolve(resolver, proj, config)
}.flatten() as Set<DependencyStatus>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ class DependencyUpdatesTask extends DefaultTask {
@Input
boolean checkForGradleUpdate = true

@Input
boolean checkConstraints = false

Object outputFormatter = 'plain'

Closure resolutionStrategy = null;
Expand All @@ -80,7 +83,8 @@ class DependencyUpdatesTask extends DefaultTask {
}

def evaluator = new DependencyUpdates(project, resolutionStrategyAction, revisionLevel(),
outputFormatterProp(), outputDirectory(), getReportfileName(), checkForGradleUpdate, gradleReleaseChannelLevel())
outputFormatterProp(), outputDirectory(), getReportfileName(), checkForGradleUpdate, gradleReleaseChannelLevel(),
checkConstraints)
DependencyUpdatesReporter reporter = evaluator.run()
reporter?.write()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,15 @@ class Resolver {
final Action<? super ResolutionStrategyWithCurrent> resolutionStrategy
final boolean useSelectionRules
final boolean collectProjectUrls
final boolean checkConstraints
final ConcurrentMap<ModuleVersionIdentifier, ProjectUrl> projectUrls

Resolver(Project project, Action<? super ResolutionStrategyWithCurrent> resolutionStrategy) {
Resolver(Project project, Action<? super ResolutionStrategyWithCurrent> resolutionStrategy,
boolean checkConstraints) {
this.projectUrls = new ConcurrentHashMap<>()
this.resolutionStrategy = resolutionStrategy
this.project = project
this.checkConstraints = checkConstraints;

useSelectionRules = new VersionComparator(project)
.compare(project.gradle.gradleVersion, '2.2') >= 0
Expand Down Expand Up @@ -118,7 +121,8 @@ class Resolver {
createQueryDependency(dependency, revision)
}

// Common use case for dependency constraints is a java-platform BOM project.
// Common use case for dependency constraints is a java-platform BOM project or to control
// version of transitive dependency.
if (supportsConstraints(configuration)) {
configuration.dependencyConstraints.each { dependency ->
latest.add(createQueryDependency(dependency, revision))
Expand Down Expand Up @@ -240,7 +244,11 @@ class Resolver {
if (supportsConstraints(copy)) {
for (DependencyConstraint constraint : copy.dependencyConstraints) {
Coordinate coordinate = Coordinate.from(constraint)
coordinates.put(coordinate.key, declared.get(coordinate.key))
// Only add a constraint to the report if there is no dependency matching it, this means it
// is targeting a transitive dependency or is part of a platform.
if (!coordinates.containsKey(coordinate.key)) {
coordinates.put(coordinate.key, declared.get(coordinate.key))
}
}
}

Expand Down Expand Up @@ -360,11 +368,11 @@ class Resolver {
return null
}

private static boolean supportsConstraints(Configuration configuration) {
return configuration.metaClass.respondsTo(configuration, "getDependencyConstraints");
private boolean supportsConstraints(Configuration configuration) {
return checkConstraints && configuration.metaClass.respondsTo(configuration, "getDependencyConstraints");
}

private static List<Coordinate> getResolvableDependencies(Configuration configuration) {
private List<Coordinate> getResolvableDependencies(Configuration configuration) {
List<Coordinate> coordinates = configuration.dependencies.findAll { dependency ->
dependency instanceof ExternalDependency
}.collect { dependency ->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package com.github.benmanes.gradle.versions

import org.gradle.testkit.runner.GradleRunner
import org.junit.Rule
import org.junit.rules.TemporaryFolder
import spock.lang.Specification

import static org.gradle.testkit.runner.TaskOutcome.SUCCESS

final class ConstraintsSpec extends Specification {

@Rule TemporaryFolder testProjectDir = new TemporaryFolder()
private File buildFile

def "Show updates for an api dependency constraint"() {
given:
def mavenRepoUrl = getClass().getResource('/maven/').toURI()
buildFile = testProjectDir.newFile('build.gradle')
buildFile <<
"""
plugins {
id 'java-library'
id 'com.github.ben-manes.versions'
}
tasks.dependencyUpdates {
checkConstraints = true
}
repositories {
maven {
url '${mavenRepoUrl}'
}
}
dependencies {
constraints {
api 'com.google.inject:guice:2.0'
}
}
""".stripIndent()

when:
def result = GradleRunner.create()
.withProjectDir(testProjectDir.root)
.withArguments('dependencyUpdates')
.withPluginClasspath()
.build()

then:
result.output.contains('com.google.inject:guice [2.0 -> 3.1]')
result.task(':dependencyUpdates').outcome == SUCCESS
}

def "Does not override explicit dependency with constraint"() {
given:
def mavenRepoUrl = getClass().getResource('/maven/').toURI()
buildFile = testProjectDir.newFile('build.gradle')
buildFile <<
"""
plugins {
id 'java-library'
id 'com.github.ben-manes.versions'
}
tasks.dependencyUpdates {
checkConstraints = true
}
repositories {
maven {
url '${mavenRepoUrl}'
}
}
dependencies {
api 'com.google.inject:guice:3.0'
constraints {
api 'com.google.inject:guice:2.0'
}
}
""".stripIndent()

when:
def result = GradleRunner.create()
.withProjectDir(testProjectDir.root)
.withArguments('dependencyUpdates')
.withPluginClasspath()
.build()

then:
result.output.contains('com.google.inject:guice [3.0 -> 3.1]')
result.task(':dependencyUpdates').outcome == SUCCESS
}

def "Does not show updates for an api dependency constraint when disabled"() {
given:
def mavenRepoUrl = getClass().getResource('/maven/').toURI()
buildFile = testProjectDir.newFile('build.gradle')
buildFile <<
"""
plugins {
id 'java-library'
id 'com.github.ben-manes.versions'
}
repositories {
maven {
url '${mavenRepoUrl}'
}
}
dependencies {
constraints {
api 'com.google.inject:guice:2.0'
}
}
""".stripIndent()

when:
def result = GradleRunner.create()
.withProjectDir(testProjectDir.root)
.withArguments('dependencyUpdates')
.withPluginClasspath()
.build()

then:
result.output.contains('No dependencies found.')
result.task(':dependencyUpdates').outcome == SUCCESS
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,40 +46,4 @@ final class JavaLibrarySpec extends Specification {
result.output.contains('com.google.inject:guice [2.0 -> 3.1]')
result.task(':dependencyUpdates').outcome == SUCCESS
}

def "Show updates for an api dependency constraint in a java-library project"() {
given:
def mavenRepoUrl = getClass().getResource('/maven/').toURI()
buildFile = testProjectDir.newFile('build.gradle')
buildFile <<
"""
plugins {
id 'java-library'
id 'com.github.ben-manes.versions'
}
repositories {
maven {
url '${mavenRepoUrl}'
}
}
dependencies {
constraints {
api 'com.google.inject:guice:2.0'
}
}
""".stripIndent()

when:
def result = GradleRunner.create()
.withProjectDir(testProjectDir.root)
.withArguments('dependencyUpdates')
.withPluginClasspath()
.build()

then:
result.output.contains('com.google.inject:guice [2.0 -> 3.1]')
result.task(':dependencyUpdates').outcome == SUCCESS
}
}

0 comments on commit 7dba9c4

Please sign in to comment.