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

Feature/current version in component selection #327

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
23 changes: 14 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,17 +86,22 @@ The strategy can be specified either on the task or as a system property for ad
gradle dependencyUpdates -Drevision=release
```

The latest versions can be further filtered using [Component Selection Rules][component_selection_rules].
For example, to disallow release candidates as upgradable versions a selection rule could be defined as:
The latest versions can be further filtered using [Component Selection Rules][component_selection_rules].
The current version of a component can be retrieved with `currentVersion` property. For example, to
disallow release candidates as upgradable versions from stable versions, a selection rule could be
defined as:

```groovy
dependencyUpdates.resolutionStrategy {
componentSelection { rules ->
rules.all { ComponentSelection selection ->
boolean rejected = ['alpha', 'beta', 'rc', 'cr', 'm', 'preview', 'b', 'ea'].any { qualifier ->
selection.candidate.version ==~ /(?i).*[.-]$qualifier[.\d-+]*/
rules.all {
def isNonStable = { String version ->
['alpha', 'beta', 'rc', 'cr', 'm', 'preview', 'b', 'ea'].any { qualifier ->
version ==~ /(?i).*[.-]\$qualifier[.\\d-+]*/
}
}
if (rejected) {

if (isNonStable(candidate.version) && !isNonStable(currentVersion)) {
selection.reject('Release candidate')
}
}
Expand All @@ -113,10 +118,10 @@ tasks.named<DependencyUpdatesTask>("dependencyUpdates") {
resolutionStrategy {
componentSelection {
all {
val rejected = listOf("alpha", "beta", "rc", "cr", "m", "preview", "b", "ea").any { qualifier ->
candidate.version.matches(Regex("(?i).*[.-]$qualifier[.\\d-+]*"))
fun isNonStable(version: String) = listOf("alpha", "beta", "rc", "cr", "m", "preview", "b", "ea").any { qualifier ->
version.matches(Regex("(?i).*[.-]\$qualifier[.\\d-+]*"))
}
if (rejected) {
if (isNonStable(candidate.version) && !isNonStable(currentVersion)) {
reject("Release candidate")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import groovy.transform.TypeChecked
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ModuleVersionIdentifier
import org.gradle.api.artifacts.ModuleVersionSelector
import org.gradle.api.artifacts.component.ModuleComponentIdentifier

/**
* The dependency's coordinate.
Expand Down Expand Up @@ -64,6 +65,10 @@ class Coordinate implements Comparable<Coordinate> {
return new Coordinate(identifier.group, identifier.name, identifier.version)
}

static Coordinate from(ModuleComponentIdentifier identifier) {
return new Coordinate(identifier.group, identifier.module, identifier.version)
}

@EqualsAndHashCode
static class Key implements Comparable<Key> {
final String groupId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@
package com.github.benmanes.gradle.versions.updates

import com.github.benmanes.gradle.versions.updates.gradle.GradleUpdateChecker
import com.github.benmanes.gradle.versions.updates.resolutionstrategy.ResolutionStrategyWithCurrent
import groovy.transform.TupleConstructor
import groovy.transform.TypeChecked
import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.UnresolvedDependency

import static groovy.transform.TypeCheckingMode.SKIP

/**
* An evaluator for reporting of which dependencies have later versions.
* <p>
Expand All @@ -38,7 +38,7 @@ import static groovy.transform.TypeCheckingMode.SKIP
@TupleConstructor
class DependencyUpdates {
Project project
Closure resolutionStrategy
Action<? super ResolutionStrategyWithCurrent> resolutionStrategy
String revision
Object outputFormatter
String outputDir
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
*/
package com.github.benmanes.gradle.versions.updates

import com.github.benmanes.gradle.versions.updates.resolutionstrategy.ResolutionStrategyWithCurrent
import groovy.transform.TypeChecked
import org.gradle.api.Action
import org.gradle.api.DefaultTask
import org.gradle.api.artifacts.ResolutionStrategy
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.TaskAction
Expand Down Expand Up @@ -52,8 +52,8 @@ class DependencyUpdatesTask extends DefaultTask {
@Input
boolean checkForGradleUpdate = true

Object outputFormatter = 'plain';
Closure resolutionStrategy = null;
Object outputFormatter = 'plain'
Action<? super ResolutionStrategyWithCurrent> resolutionStrategyAction = null

DependencyUpdatesTask() {
description = 'Displays the dependency updates for the project.'
Expand All @@ -66,7 +66,7 @@ class DependencyUpdatesTask extends DefaultTask {
def dependencyUpdates() {
project.evaluationDependsOnChildren()

def evaluator = new DependencyUpdates(project, resolutionStrategy, revisionLevel(),
def evaluator = new DependencyUpdates(project, resolutionStrategyAction, revisionLevel(),
outputFormatterProp(), outputDirectory(), getReportfileName(), checkForGradleUpdate, gradleReleaseChannelLevel())
DependencyUpdatesReporter reporter = evaluator.run()
reporter?.write()
Expand All @@ -76,10 +76,8 @@ class DependencyUpdatesTask extends DefaultTask {
* Sets the {@link #resolutionStrategy} to the provided strategy.
* @param resolutionStrategy the resolution strategy
*/
void resolutionStrategy(final Action<? super ResolutionStrategy> resolutionStrategy) {
// The delegate of the Closure body is ResolutionStrategy:
// https://docs.gradle.org/current/javadoc/org/gradle/api/artifacts/Configuration.html#resolutionStrategy-groovy.lang.Closure-
this.resolutionStrategy = { resolutionStrategy.execute(delegate as ResolutionStrategy) }
void resolutionStrategy(final Action<? super ResolutionStrategyWithCurrent> resolutionStrategy) {
this.resolutionStrategyAction = resolutionStrategy
}

/** Returns the resolution revision level. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
*/
package com.github.benmanes.gradle.versions.updates

import com.github.benmanes.gradle.versions.updates.resolutionstrategy.ResolutionStrategyWithCurrent
import groovy.transform.TypeChecked
import java.util.concurrent.ConcurrentMap
import java.util.concurrent.ConcurrentHashMap
import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.artifacts.ComponentMetadata
import org.gradle.api.artifacts.ComponentSelection
Expand Down Expand Up @@ -51,12 +53,12 @@ import static org.gradle.api.specs.Specs.SATISFIES_ALL
@TypeChecked
class Resolver {
final Project project
final Closure resolutionStrategy
final Action<? super ResolutionStrategyWithCurrent> resolutionStrategy
final boolean useSelectionRules
final boolean collectProjectUrls
final ConcurrentMap<ModuleVersionIdentifier, ProjectUrl> projectUrls

Resolver(Project project, Closure resolutionStrategy) {
Resolver(Project project, Action<? super ResolutionStrategyWithCurrent> resolutionStrategy) {
this.projectUrls = new ConcurrentHashMap<>()
this.resolutionStrategy = resolutionStrategy
this.project = project
Expand All @@ -72,7 +74,8 @@ class Resolver {
/** Returns the version status of the configuration's dependencies at the given revision. */
public Set<DependencyStatus> resolve(Configuration configuration, String revision) {
Map<Coordinate.Key, Coordinate> coordinates = getCurrentCoordinates(configuration)
Configuration latestConfiguration = createLatestConfiguration(configuration, revision)
Configuration latestConfiguration = createLatestConfiguration(configuration, revision,
coordinates)

LenientConfiguration lenient = latestConfiguration.resolvedConfiguration.lenientConfiguration
Set<ResolvedDependency> resolved = lenient.getFirstLevelModuleDependencies(SATISFIES_ALL)
Expand Down Expand Up @@ -106,7 +109,8 @@ class Resolver {
}

/** Returns a copy of the configuration where dependencies will be resolved up to the revision. */
private Configuration createLatestConfiguration(Configuration configuration, String revision) {
private Configuration createLatestConfiguration(Configuration configuration, String revision,
Map<Coordinate.Key, Coordinate> currentCoordinates) {
List<Dependency> latest = configuration.dependencies.findAll { dependency ->
dependency instanceof ExternalDependency
}.collect { dependency ->
Expand All @@ -124,7 +128,7 @@ class Resolver {

if (useSelectionRules) {
addRevisionFilter(copy, revision)
addCustomResolutionStrategy(copy)
addCustomResolutionStrategy(copy, currentCoordinates)
}
return copy
}
Expand Down Expand Up @@ -165,9 +169,16 @@ class Resolver {
}

/** Adds a custom resolution strategy only applicable for the dependency updates task. */
private void addCustomResolutionStrategy(Configuration configuration) {
private void addCustomResolutionStrategy(Configuration configuration,
Map<Coordinate.Key, Coordinate> currentCoordinates) {
if (resolutionStrategy != null) {
configuration.resolutionStrategy(resolutionStrategy)
configuration.resolutionStrategy(new Action<ResolutionStrategy>() {
@java.lang.Override
void execute(ResolutionStrategy inner) {
resolutionStrategy.execute(new ResolutionStrategyWithCurrent(inner as ResolutionStrategy,
currentCoordinates))
}
})
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package com.github.benmanes.gradle.versions.updates.resolutionstrategy

import com.github.benmanes.gradle.versions.updates.Coordinate
import groovy.transform.TupleConstructor
import org.gradle.api.Action
import org.gradle.api.artifacts.ComponentSelection
import org.gradle.api.artifacts.ComponentSelectionRules
import org.gradle.internal.rules.RuleAction
import org.gradle.internal.rules.RuleSourceBackedRuleAction
import org.gradle.model.internal.type.ModelType

@TupleConstructor(includeFields=true)
class ComponentSelectionRulesWithCurrent {

private final ComponentSelectionRules delegate
private final Map<Coordinate.Key, Coordinate> currentCoordinates

ComponentSelectionRulesWithCurrent all(
Action<? super ComponentSelectionWithCurrent> selectionAction) {
delegate.all(new Action<ComponentSelection>() {
void execute(ComponentSelection inner) {
selectionAction.execute(wrapComponentSelection(inner))
}
})
return this
}

ComponentSelectionRulesWithCurrent all(Closure<?> closure) {
delegate.all(new Action<ComponentSelection>() {
void execute(ComponentSelection inner) {
ComponentSelectionWithCurrent wrapped = wrapComponentSelection(inner)
closure.delegate = wrapped
closure(wrapped)
}
})
return this
}

ComponentSelectionRulesWithCurrent all(Object ruleSource) {
RuleAction<ComponentSelectionWithCurrent> ruleAction = RuleSourceBackedRuleAction.create(
ModelType.of(ComponentSelectionWithCurrent), ruleSource)
delegate.all(new Action<ComponentSelection>() {
void execute(ComponentSelection inner) {
ruleAction.execute(wrapComponentSelection(inner), [])
}
})
return this
}

ComponentSelectionRulesWithCurrent withModule(Object id,
Action<? super ComponentSelectionWithCurrent> selectionAction) {
delegate.withModule(id, new Action<ComponentSelection>() {
void execute(ComponentSelection inner) {
selectionAction.execute(wrapComponentSelection(inner))
}
})
return this
}

ComponentSelectionRulesWithCurrent withModule(Object id, Closure<?> closure) {
delegate.withModule(id, new Action<ComponentSelection>() {
void execute(ComponentSelection inner) {
ComponentSelectionWithCurrent wrapped = wrapComponentSelection(inner)
closure.delegate = wrapped
closure(wrapped)
}
})
return this
}

ComponentSelectionRulesWithCurrent withModule(Object id, Object ruleSource) {
RuleAction<ComponentSelectionWithCurrent> ruleAction = RuleSourceBackedRuleAction.create(
ModelType.of(ComponentSelectionWithCurrent), ruleSource)
delegate.withModule(id, new Action<ComponentSelection>() {
void execute(ComponentSelection inner) {
ruleAction.execute(wrapComponentSelection(inner), [])
}
})
return this
}

private ComponentSelectionWithCurrent wrapComponentSelection(ComponentSelection inner) {
Coordinate candidateCoordinate = Coordinate.from(inner.candidate)
Coordinate current = currentCoordinates.get(candidateCoordinate.key)
ComponentSelectionWithCurrent wrapped = new ComponentSelectionWithCurrent(current.version,
inner)
return wrapped
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.github.benmanes.gradle.versions.updates.resolutionstrategy

import groovy.transform.TupleConstructor
import org.gradle.api.artifacts.ComponentSelection

@TupleConstructor(includeFields=true)
class ComponentSelectionWithCurrent {

final String currentVersion

@Delegate
private final ComponentSelection delegate
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.github.benmanes.gradle.versions.updates.resolutionstrategy

import com.github.benmanes.gradle.versions.updates.Coordinate
import groovy.transform.TupleConstructor
import org.gradle.api.Action
import org.gradle.api.artifacts.DependencyResolveDetails
import org.gradle.api.artifacts.DependencySubstitutions
import org.gradle.api.artifacts.ResolutionStrategy

@TupleConstructor(includeFields = true)
class ResolutionStrategyWithCurrent {

@Delegate(interfaces = false, excludes = ['componentSelection', 'getComponentSelection'])
private ResolutionStrategy delegate

private Map<Coordinate.Key, Coordinate> currentCoordinates

ResolutionStrategyWithCurrent failOnVersionConflict() {
delegate.failOnVersionConflict()
return this
}

ResolutionStrategyWithCurrent activateDependencyLocking() {
delegate.activateDependencyLocking()
return this
}

ResolutionStrategyWithCurrent force(Object... moduleVersionSelectorNotations) {
delegate.force(moduleVersionSelectorNotations)
return this
}

ResolutionStrategyWithCurrent setForcedModules(Object... moduleVersionSelectorNotations) {
delegate.setForcedModules(moduleVersionSelectorNotations)
return this
}

ResolutionStrategyWithCurrent eachDependency(Action<? super DependencyResolveDetails> rule) {
delegate.eachDependency(rule)
return this
}

ComponentSelectionRulesWithCurrent getComponentSelection() {
return new ComponentSelectionRulesWithCurrent(delegate.getComponentSelection(),
currentCoordinates)
}

ResolutionStrategyWithCurrent componentSelection(
Action<? super ComponentSelectionRulesWithCurrent> action) {
action.execute(getComponentSelection())
return this
}

ResolutionStrategyWithCurrent componentSelection(Closure<?> closure) {
return componentSelection(new Action<ComponentSelectionRulesWithCurrent>() {
@java.lang.Override
void execute(ComponentSelectionRulesWithCurrent componentSelectionRulesWithCurrent) {
closure.delegate = componentSelectionRulesWithCurrent
closure(componentSelectionRulesWithCurrent)
}
})
}

ResolutionStrategyWithCurrent dependencySubstitution(
Action<? super DependencySubstitutions> action) {
delegate.dependencySubstitution(action)
return this
}
}
Loading