Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Guard constraint resolution by a property since it isn't really possi… #353

Merged
merged 1 commit into from
Oct 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
}