Skip to content

Commit

Permalink
Allow the user to filter dependencies
Browse files Browse the repository at this point in the history
* After dependencies are read from a configuration, but before they are
  processed, overridden, skipped, and output, a user-configurable filter
  is applied. This allows dependencies to be white-listed in addition to
  being black-listed.
  • Loading branch information
David Cowden committed Feb 23, 2015
1 parent 3660bdf commit 8390b36
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 4 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ or

* lockFile - This field takes a String. The default is `dependencies.lock`. This filename will be what is generated by `generateLock` and read when locking dependencies.
* configurationNames - This field takes a List<String>. Defaults to the `testRuntime` conf which will include `compile`, `runtime`, and `testCompile`. These will be the configurations that are read when locking.
* dependencyFilter - This field can be assigned a Closure that is used to filter the set of top-level dependencies as they are retrieved from the configurations. This happens before overrides are applied and before any dependencies are skipped. The Closure must accept the dependency's `group`, `name`, and `version` as its 3 parameters. The default implementation returns `true`, meaning all dependencies are used.
* skippedDependencies - This field takes a List<String>. Defaults to empty. This list is used to list dependencies as ones that will never be locked. Strings should be of the format `<group>:<artifact>`
* includeTransitives - This field is a boolean. Defaults to false. False will only lock direct dependencies. True will lock the entire transitive graph.

Expand All @@ -75,6 +76,7 @@ Use the extension if you wish to configure. Each project where gradle-dependency
dependencyLock {
lockFile = 'dependencies.lock'
configurationNames = ['testRuntime']
dependencyFilter = { String group, String name, String version -> true }
skippedDependencies = []
includeTransitives = false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package nebula.plugin.dependencylock
class DependencyLockExtension {
String lockFile = 'dependencies.lock'
Set<String> configurationNames = ['testRuntime'] as Set
Closure dependencyFilter = { String group, String name, String version -> true }
Set<String> skippedDependencies = [] as Set
boolean includeTransitives = false
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class DependencyLockPlugin implements Plugin<Project> {
new File(project.buildDir, clLockFileName ?: extension.lockFile)
}
configurationNames = { extension.configurationNames }
filter = { extension.dependencyFilter }
skippedDependencies = { extension.skippedDependencies }
includeTransitives = { project.hasProperty('dependencyLock.includeTransitives') ? Boolean.parseBoolean(project['dependencyLock.includeTransitives']) : extension.includeTransitives }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ import groovy.transform.EqualsAndHashCode
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ExternalDependency
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.artifacts.ResolvedDependency
import org.gradle.api.tasks.TaskAction

class GenerateLockTask extends AbstractLockTask {
String description = 'Create a lock file in build/<configured name>'
Closure filter = { group, name, version -> true }
Set<String> configurationNames
Set<String> skippedDependencies = []
File dependenciesLock
Expand All @@ -41,18 +41,27 @@ class GenerateLockTask extends AbstractLockTask {
def deps = [:].withDefault { [transitive: [] as Set, firstLevelTransitive: [] as Set, childrenVisited: false] }
def confs = getConfigurationNames().collect { project.configurations.getByName(it) }

// Peers are all the projects in the build to which this plugin is applied.
// Peers are all the projects in the build to which this plugin has been applied.
def peers = project.rootProject.allprojects.collect { new LockKey(group: it.group, artifact: it.name) }

confs.each { Configuration configuration ->

// Capture the version of each dependency as requested in the build script for reference.
configuration.allDependencies.withType(ExternalDependency).each { Dependency dependency ->
def externalDependencies = configuration.allDependencies.withType(ExternalDependency)
def filteredExternalDependencies = externalDependencies.findAll { Dependency dependency ->
filter(dependency.group, dependency.name, dependency.version)
}
filteredExternalDependencies.each { Dependency dependency ->
def key = new LockKey(group: dependency.group, artifact: dependency.name)
deps[key].requested = dependency.version
}

// Lock the version of each dependency specified in the build script as resolved by Gradle.
configuration.resolvedConfiguration.firstLevelModuleDependencies.each { ResolvedDependency resolved ->
def resolvedDependencies = configuration.resolvedConfiguration.firstLevelModuleDependencies
def filteredResolvedDependencies = resolvedDependencies.findAll { ResolvedDependency resolved ->
filter(resolved.moduleGroup, resolved.moduleName, resolved.moduleVersion)
}
filteredResolvedDependencies.each { ResolvedDependency resolved ->
def key = new LockKey(group: resolved.moduleGroup, artifact: resolved.moduleName)

// If this dependency does not exist in our list of peers, it is a standard dependency. Otherwise, it is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,4 +391,34 @@ class GenerateLockTaskSpec extends ProjectSpec {
'''.stripIndent()
task.dependenciesLock.text == lockText
}

def 'filter is applied'() {
project.apply plugin: 'java'

project.repositories { maven { url Fixture.repo } }
project.dependencies {
compile 'test.example:foo:2.+'
testCompile 'test.example:baz:1.+'
}

GenerateLockTask task = project.tasks.create(taskName, GenerateLockTask)
task.dependenciesLock = new File(project.buildDir, 'dependencies.lock')
task.configurationNames = [ 'testRuntime' ]
task.filter = filter as Closure

when:
task.execute()

then:
task.dependenciesLock.text == lockText

where:
filter || lockText
{ group, artifact, version -> false } || '{\n\n}\n'
{ group, artifact, version -> artifact == 'foo' } || '''\
{
"test.example:foo": { "locked": "2.0.1", "requested": "2.+" }
}
'''.stripIndent()
}
}

0 comments on commit 8390b36

Please sign in to comment.