forked from ben-manes/gradle-versions-plugin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
DependencyUpdates.kt
169 lines (154 loc) · 6.1 KB
/
DependencyUpdates.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
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 org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.UnresolvedDependency
/**
* An evaluator for reporting of which dependencies have later versions.
* <p>
* The <tt>revision</tt> property controls the resolution strategy:
* <ul>
* <li>release: selects the latest release
* <li>milestone: select the latest version being either a milestone or a release (default)
* <li>integration: selects the latest revision of the dependency module (such as SNAPSHOT)
* </ul>
*/
class DependencyUpdates @JvmOverloads constructor(
val project: Project,
val resolutionStrategy: Action<in ResolutionStrategyWithCurrent>?,
val revision: String,
private val outputFormatterArgument: OutputFormatterArgument,
val outputDir: String,
val reportfileName: String?,
val checkForGradleUpdate: Boolean,
val gradleReleaseChannel: String,
val checkConstraints: Boolean = false,
val checkBuildEnvironmentConstraints: Boolean = false,
) {
/**
* Evaluates the project dependencies and then the buildScript dependencies to apply different
* task options and returns a reporter for the results.
*/
fun run(): DependencyUpdatesReporter {
val projectConfigs = project.allprojects
.associateBy({ it }, { it.configurations.toLinkedHashSet() })
val status: Set<DependencyStatus> = resolveProjects(projectConfigs, checkConstraints)
val buildscriptProjectConfigs = project.allprojects
.associateBy({ it }, { it.buildscript.configurations.toLinkedHashSet() })
val buildscriptStatus: Set<DependencyStatus> = resolveProjects(
buildscriptProjectConfigs, checkBuildEnvironmentConstraints
)
val statuses = status + buildscriptStatus
val versions = VersionMapping(project, statuses)
val unresolved = statuses
.filter { it.unresolved != null }
.map { it.unresolved }
.toSet() as Set<UnresolvedDependency>
val projectUrls = statuses
.filter { !it.projectUrl.isNullOrEmpty() }
.associateBy(
{ mapOf("group" to it.coordinate.groupId, "name" to it.coordinate.artifactId) },
{ it.projectUrl.toString() }
)
return createReporter(versions, unresolved, projectUrls)
}
private fun resolveProjects(
projectConfigs: Map<Project, Set<Configuration>>,
checkConstraints: Boolean,
): Set<DependencyStatus> {
val resultStatus = hashSetOf<DependencyStatus>()
projectConfigs.forEach { (currentProject, currentConfigurations) ->
val resolver = Resolver(currentProject, resolutionStrategy, checkConstraints)
for (currentConfiguration in currentConfigurations) {
for (newStatus in resolve(resolver, currentProject, currentConfiguration)) {
addValidatedDependencyStatus(resultStatus, newStatus)
}
}
}
return resultStatus
}
private fun resolve(
resolver: Resolver,
project: Project,
config: Configuration,
): Set<DependencyStatus> {
return try {
resolver.resolve(config, revision)
} catch (e: Exception) {
project.logger.info("Skipping configuration ${project.path}:${config.name}", e)
emptySet()
}
}
private fun createReporter(
versions: VersionMapping,
unresolved: Set<UnresolvedDependency>,
projectUrls: Map<Map<String, String>, String>,
): DependencyUpdatesReporter {
val currentVersions = versions.current
.associateBy({ mapOf("group" to it.groupId, "name" to it.artifactId) }, { it })
val latestVersions = versions.latest
.associateBy({ mapOf("group" to it.groupId, "name" to it.artifactId) }, { it })
val upToDateVersions = versions.upToDate
.associateBy({ mapOf("group" to it.groupId, "name" to it.artifactId) }, { it })
val downgradeVersions = toMap(versions.downgrade)
val upgradeVersions = toMap(versions.upgrade)
// Check for Gradle updates.
val gradleUpdateChecker = GradleUpdateChecker(checkForGradleUpdate)
return DependencyUpdatesReporter(
project, revision, outputFormatterArgument, outputDir,
reportfileName, currentVersions, latestVersions, upToDateVersions, downgradeVersions,
upgradeVersions, versions.undeclared, unresolved, projectUrls, gradleUpdateChecker,
gradleReleaseChannel
)
}
companion object {
/**
* A new status will be added if either,
* <ol>
* <li>[Coordinate.Key] of new status is not yet present in status collection
* <li>new status has concrete version (not `none`); the old status will then be removed
* if its coordinate is `none` versioned</li>
* </ol>
*/
private fun addValidatedDependencyStatus(
statusCollection: HashSet<DependencyStatus>,
status: DependencyStatus,
) {
val statusWithSameCoordinateKey = statusCollection.find {
it.coordinate.key == status.coordinate.key
}
if (statusWithSameCoordinateKey == null) {
statusCollection.add(status)
} else if (status.coordinate.version != "none") {
statusCollection.add(status)
if (statusWithSameCoordinateKey.coordinate.version == "none") {
statusCollection.remove(statusWithSameCoordinateKey)
}
}
}
private fun toMap(coordinates: Set<Coordinate>): Map<Map<String, String>, Coordinate> {
val map = HashMap<Map<String, String>, Coordinate>()
for (coordinate in coordinates) {
var i = 0
while (true) {
val artifactId = coordinate.artifactId + if (i == 0) "" else "[${i + 1}]"
val keyMap = linkedMapOf<String, String>().apply {
put("group", coordinate.groupId)
put("name", artifactId)
}
if (!map.containsKey(keyMap)) {
map[keyMap] = coordinate
break
}
++i
}
}
return map
}
}
}
private fun <T> Collection<T>.toLinkedHashSet(): LinkedHashSet<T> {
return toCollection(LinkedHashSet<T>(this.size))
}