Skip to content

Commit 68164b8

Browse files
authored
Avoid resolving configurations that are deprecated for resolving
Certain configurations can be reported as "Deprecated for resolving", which is detectable via the`DeprecatableConfiguration.canSafelyBeResolved` internal API. Resolving these configurations during dependency graph generationg can be problematic, particularly in the case of strict Dependency Locking, since no lockfile is likely to be generated for these configurations. With this fix, reflection is used to attempt to determine if a Configuration can be safely resolved, avoiding resolution of deprecated configurations. Fixes #128
1 parent dc07592 commit 68164b8

File tree

5 files changed

+145
-28
lines changed

5 files changed

+145
-28
lines changed

.github/workflows/gradle.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
matrix:
3434
# Test earliest and latest supported version of 5.x, 6.x and 7.x, as well as all patched minor versions of 8.x
3535
# Latest 8.x is tested in 'quick-check' job using the wrapper
36-
gradle-version: [ "5.2.1", "5.6.4", "6.0.1", "6.9.4", "7.1.1", "7.6.3", "8.0.2", "8.1.1", "8.2.1", "8.3", "8.4", "8.5"]
36+
gradle-version: [ "5.2.1", "5.6.4", "6.0.1", "6.9.4", "7.1.1", "7.6.4", "8.0.2", "8.1.1", "8.2.1", "8.3", "8.4", "8.5", "8.6"]
3737
runs-on: ubuntu-latest
3838
env:
3939
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package org.gradle.github.dependencygraph
2+
3+
import org.gradle.test.fixtures.PluginPublisher
4+
import org.gradle.test.fixtures.maven.MavenModule
5+
import org.gradle.util.GradleVersion
6+
import spock.lang.IgnoreIf
7+
8+
class DependencyLockingDependencyExtractorTest extends BaseExtractorTest {
9+
private MavenModule foo
10+
private MavenModule bar
11+
private MavenModule baz
12+
private File settingsFile
13+
private File buildFile
14+
15+
def setup() {
16+
establishEnvironmentVariables()
17+
18+
foo = mavenRepo.module("org.test", "foo", "1.0").publish()
19+
20+
settingsFile = file("settings.gradle") << """
21+
rootProject.name = 'a'
22+
"""
23+
24+
buildFile = file("build.gradle") << """
25+
apply plugin: 'java'
26+
27+
repositories {
28+
maven { url "${mavenRepo.uri}" }
29+
}
30+
"""
31+
}
32+
33+
def "extracts dependencies when dependency locking is enabled"() {
34+
given:
35+
buildFile << """
36+
dependencies {
37+
implementation "org.test:foo:+"
38+
}
39+
40+
dependencyLocking {
41+
lockAllConfigurations()
42+
}
43+
"""
44+
45+
// Write dependency lock file
46+
run("dependencies", "--write-locks")
47+
mavenRepo.module("org.test", "foo", "1.1").publish()
48+
49+
when:
50+
applyDependencyGraphPlugin()
51+
run()
52+
53+
then:
54+
def manifest = gitHubManifest()
55+
manifest.sourceFile == "settings.gradle"
56+
57+
manifest.assertResolved([
58+
"org.test:foo:1.0": [
59+
package_url: purlFor(foo)
60+
]
61+
])
62+
}
63+
64+
@IgnoreIf({
65+
// `LockMode.STRICT` was introduced in Gradle 6.1
66+
GradleVersion.version(testGradleVersion) < GradleVersion.version("6.1")
67+
})
68+
def "extracts dependencies when Strict dependency locking is enabled"() {
69+
given:
70+
buildFile << """
71+
dependencies {
72+
implementation "org.test:foo:+"
73+
}
74+
75+
dependencyLocking {
76+
lockAllConfigurations()
77+
lockMode = LockMode.STRICT
78+
}
79+
"""
80+
81+
// Write dependency lock file
82+
run("dependencies", "--write-locks")
83+
mavenRepo.module("org.test", "foo", "1.1").publish()
84+
85+
when:
86+
applyDependencyGraphPlugin()
87+
run()
88+
89+
then:
90+
def manifest = gitHubManifest()
91+
manifest.sourceFile == "settings.gradle"
92+
93+
manifest.assertResolved([
94+
"org.test:foo:1.0": [
95+
package_url: purlFor(foo)
96+
]
97+
])
98+
}
99+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package org.gradle.forceresolve
2+
3+
import org.gradle.api.DefaultTask
4+
import org.gradle.api.artifacts.Configuration
5+
import org.gradle.api.tasks.Internal
6+
import org.gradle.dependencygraph.extractor.ResolvedConfigurationFilter
7+
import org.gradle.work.DisableCachingByDefault
8+
import java.lang.reflect.Method
9+
10+
@DisableCachingByDefault(because = "Not worth caching")
11+
abstract class AbstractResolveProjectDependenciesTask : DefaultTask() {
12+
private val canSafelyBeResolvedMethod: Method? = getCanSafelyBeResolvedMethod()
13+
14+
@Internal
15+
var configurationFilter: ResolvedConfigurationFilter? = null
16+
17+
@Internal
18+
protected fun getReportableConfigurations(): List<Configuration> {
19+
return project.configurations.filter {
20+
canSafelyBeResolved(it) && configurationFilter!!.include(project.path, it.name)
21+
}
22+
}
23+
24+
/**
25+
* If `DeprecatableConfiguration.canSafelyBeResolve()` is available, use it.
26+
* Else fall back to `Configuration.canBeResolved`.
27+
*/
28+
private fun canSafelyBeResolved(configuration: Configuration): Boolean {
29+
if (canSafelyBeResolvedMethod != null) {
30+
return canSafelyBeResolvedMethod.invoke(configuration) as Boolean
31+
}
32+
return configuration.isCanBeResolved
33+
}
34+
35+
private fun getCanSafelyBeResolvedMethod(): Method? {
36+
return try {
37+
val dc = Class.forName("org.gradle.internal.deprecation.DeprecatableConfiguration")
38+
dc.getMethod("canSafelyBeResolved")
39+
} catch (e: ReflectiveOperationException) {
40+
null
41+
}
42+
}
43+
}

plugin/src/main/kotlin/org/gradle/forceresolve/LegacyResolveProjectDependenciesTask.kt

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,10 @@
11
package org.gradle.forceresolve
22

3-
import org.gradle.api.DefaultTask
4-
import org.gradle.api.artifacts.Configuration
5-
import org.gradle.api.tasks.Internal
63
import org.gradle.api.tasks.TaskAction
7-
import org.gradle.dependencygraph.extractor.ResolvedConfigurationFilter
84
import org.gradle.work.DisableCachingByDefault
95

106
@DisableCachingByDefault(because = "Not worth caching")
11-
abstract class LegacyResolveProjectDependenciesTask: DefaultTask() {
12-
@Internal
13-
var configurationFilter: ResolvedConfigurationFilter? = null
14-
15-
private fun getReportableConfigurations(): List<Configuration> {
16-
return project.configurations.filter {
17-
it.isCanBeResolved && configurationFilter!!.include(project.path, it.name)
18-
}
19-
}
7+
abstract class LegacyResolveProjectDependenciesTask: AbstractResolveProjectDependenciesTask() {
208

219
@TaskAction
2210
fun action() {

plugin/src/main/kotlin/org/gradle/forceresolve/ResolveProjectDependenciesTask.kt

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,21 @@
11
package org.gradle.forceresolve
22

3-
import org.gradle.api.DefaultTask
4-
import org.gradle.api.artifacts.Configuration
53
import org.gradle.api.artifacts.result.ResolvedComponentResult
64
import org.gradle.api.provider.Provider
7-
import org.gradle.api.tasks.Internal
85
import org.gradle.api.tasks.TaskAction
9-
import org.gradle.dependencygraph.extractor.ResolvedConfigurationFilter
106
import org.gradle.internal.serialization.Cached
117
import org.gradle.work.DisableCachingByDefault
128

139
@DisableCachingByDefault(because = "Not worth caching")
14-
abstract class ResolveProjectDependenciesTask: DefaultTask() {
10+
abstract class ResolveProjectDependenciesTask: AbstractResolveProjectDependenciesTask() {
1511
private val configurationResolvers = Cached.of { createConfigurationResolvers() }
1612

17-
@Internal
18-
var configurationFilter: ResolvedConfigurationFilter? = null
19-
2013
private fun createConfigurationResolvers(): List<Provider<ResolvedComponentResult>> {
2114
return getReportableConfigurations().map {
2215
it.incoming.resolutionResult.rootComponent
2316
}
2417
}
2518

26-
private fun getReportableConfigurations(): List<Configuration> {
27-
return project.configurations.filter {
28-
it.isCanBeResolved && configurationFilter!!.include(project.path, it.name)
29-
}
30-
}
31-
3219
@TaskAction
3320
fun action() {
3421
for (configuration in configurationResolvers.get()) {

0 commit comments

Comments
 (0)