Updated the `updateDependencies` task, so that it queries the repository servers directly, instead of whatever weird stuff `com.github.ben-manes:gradle-versions-plugin` was doing to take 16 minutes to **parse** the project dependency hierarchy.

master
Robinson 2021-04-11 01:47:32 +02:00
parent 52ef7fa9a8
commit ee09959bdd
9 changed files with 379 additions and 67 deletions

17
LICENSE
View File

@ -13,15 +13,20 @@
Kotlin Compiler, Test Data+Libraries, and Tools repository contain third-party code, to which different licenses may apply
See: https://github.com/JetBrains/kotlin/blob/master/license/README.md
- Gradle Versions Plugin - This plugin provides a task to determine which dependencies have updates
[The Apache Software License, Version 2.0]
https://github.com/ben-manes/gradle-versions-plugin
Copyright 2021
Ben Manes
- JSON in Java - A light-weight language independent data interchange format.
[The JSON License]
https://github.com/stleary/JSON-java
https://www.json.org/json-en.html
Copyright 2021
JSON.org
- Version - Java Semantic Versioning with exceptions. Minor/Patch number optional and build-after-final-dot (minor/patch) permitted.
[MIT License]
https://git.dorkbox.com/dorkbox/Version
Copyright 2020
Dorkbox LLC
G. Richard Bellamy
Kenduck
Larry Bordowitz <lbordowitz@yahoo-inc.com>
Martin Rüegg <martin.rueegg@bristolpound.org> <martin.rueegg@metaworx.ch>
Zafar Khaja <zafarkhaja@gmail.com>

21
LICENSE.MIT Normal file
View File

@ -0,0 +1,21 @@
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018 dorkbox, llc
* Copyright 2021 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -22,11 +22,11 @@ plugins {
java
`java-gradle-plugin`
id("com.gradle.plugin-publish") version "0.12.0"
id("com.gradle.plugin-publish") version "0.14.0"
id("com.dorkbox.Licensing") version "2.5.4"
id("com.dorkbox.VersionUpdate") version "2.1"
id("com.dorkbox.GradleUtils") version "1.12"
id("com.dorkbox.Licensing") version "2.5.5"
id("com.dorkbox.VersionUpdate") version "2.3"
id("com.dorkbox.GradleUtils") version "2.0"
kotlin("jvm") version "1.4.31"
}
@ -35,7 +35,7 @@ object Extras {
// set for the project
const val description = "Gradle Plugin to manage various Gradle tasks, such as updating gradle and dependencies"
const val group = "com.dorkbox"
const val version = "1.17"
const val version = "2.0"
// set as project.ext
const val name = "Gradle Utils"
@ -43,6 +43,7 @@ object Extras {
const val vendor = "Dorkbox LLC"
const val url = "https://git.dorkbox.com/dorkbox/GradleUtils"
val tags = listOf("gradle", "update", "dependencies", "dependency management")
val buildDate = Instant.now().toString()
}
@ -74,22 +75,22 @@ sourceSets {
}
repositories {
jcenter()
maven {
url = uri("https://plugins.gradle.org/m2/")
}
mavenLocal()
mavenCentral()
gradlePluginPortal()
}
dependencies {
// compile only, so we dont force kotlin version info into dependencies
// compile only, so we dont force kotlin/dsl version info into dependencies
// the kotlin version is taken from the plugin, so it is not necessary to set it here
compileOnly("org.jetbrains.kotlin:kotlin-gradle-plugin")
// setup checking for the latest version of a plugin or dependency
implementation("com.github.ben-manes:gradle-versions-plugin:0.38.0")
// for parsing JSON
implementation("org.json:json:20210307")
// for parsing version information from maven
implementation("com.dorkbox:Version:2.4")
}
tasks.jar.get().apply {

View File

@ -1,3 +1,18 @@
/*
* Copyright 2021 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.gradle
import org.gradle.api.Project
@ -10,25 +25,30 @@ object DependencyScanner {
/**
* THIS MUST BE IN "afterEvaluate" or run from a specific task.
*/
fun scan(project: Project,
configurationName: String,
projectDependencies: MutableList<Dependency>,
existingNames: MutableSet<String>): MutableList<Dependency> {
fun scan(
project: Project,
configurationName: String,
projectDependencies: MutableList<Dependency>,
existingNames: MutableSet<String>,
) {
project.configurations.getByName(configurationName).resolvedConfiguration.firstLevelModuleDependencies.forEach { dep ->
val config = project.configurations.getByName(configurationName)
if (!config.isCanBeResolved) {
return
}
config.resolvedConfiguration.lenientConfiguration.getFirstLevelModuleDependencies(org.gradle.api.specs.Specs.SATISFIES_ALL).forEach { dep ->
// we know the FIRST series will exist
val makeDepTree = makeDepTree(dep, existingNames)
if (makeDepTree != null) {
// it's only null if we've ALREADY scanned it
projectDependencies.add(makeDepTree)
if (!projectDependencies.contains(makeDepTree)) {
projectDependencies.add(makeDepTree)
}
}
}
return projectDependencies
}
// how to resolve dependencies
// NOTE: it is possible, when we have a project DEPEND on an older version of that project (ie: bootstrapped from an older version)
// we can have infinite recursion.
@ -78,11 +98,13 @@ object DependencyScanner {
}
}
data class Dependency(val group: String,
val name: String,
val version: String,
val artifacts: List<DependencyInfo>,
val children: List<Dependency>) {
data class Dependency(
val group: String,
val name: String,
val version: String,
val artifacts: List<DependencyInfo>,
val children: List<Dependency>
) {
fun mavenId(): String {
return "$group:$name:$version"
@ -99,4 +121,11 @@ object DependencyScanner {
return "$group:$name:$version"
}
}
data class MavenData(val group: String, val name: String, val version: String) {
val id: String
get() {
return "$group:$name:$version"
}
}
}

View File

@ -0,0 +1,162 @@
/*
* Copyright 2021 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.gradle
import com.dorkbox.version.Version
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.wrapper.Wrapper
import org.gradle.util.GradleVersion
import org.json.JSONObject
import java.io.InputStreamReader
import java.net.URL
open class
GetVersionInfoTask : DefaultTask() {
companion object {
private val versionMatcher = """^.*(<release>)(.*)(<\/release>)""".toRegex()
private fun getLatestVersionInfo(repositories: List<String>, metadataUrl: String): Version? {
var largestReleaseVersion: Version? = null
repositories.forEach { repoUrl ->
try {
val url = URL(repoUrl + metadataUrl)
// println("Trying: $url")
with(url.openConnection() as java.net.HttpURLConnection) {
val lastVersion = InputStreamReader(inputStream).readLines().lastOrNull { line ->
// only care about <release>!
// <release>1.0</release>
line.matches(versionMatcher)
}
val matchResult = versionMatcher.find(lastVersion ?: "")
if (matchResult != null) {
val (_, ver, _) = matchResult.destructured
val releaseVer = Version.from(ver)
if (largestReleaseVersion == null) {
largestReleaseVersion = releaseVer
} else if (releaseVer.greaterThan(largestReleaseVersion)) {
largestReleaseVersion = releaseVer
}
}
}
} catch (e: Exception) {
}
}
return largestReleaseVersion
}
}
@TaskAction
fun run() {
val staticMethodsAndTools = StaticMethodsAndTools(project)
val repositories = staticMethodsAndTools.getProjectRepositoryUrls(project)
val latestVersionInfo = mutableListOf<dorkbox.gradle.DependencyScanner.MavenData>()
val oldVersionInfo = mutableListOf<Pair<dorkbox.gradle.DependencyScanner.MavenData, Version>>()
val unknownVersionInfo = mutableListOf<dorkbox.gradle.DependencyScanner.MavenData>()
staticMethodsAndTools.resolveBuildScriptDependencies(project).forEach { dep ->
val latestVersion = getLatestVersionInfo(repositories, "${dep.group.replace(".", "/")}/${dep.name}/maven-metadata.xml")
if (latestVersion != null) {
if (Version.from(dep.version) == latestVersion) {
latestVersionInfo.add(dep)
} else {
oldVersionInfo.add(Pair(dep, latestVersion))
}
} else {
unknownVersionInfo.add(dep)
}
}
// BUILD SCRIPT DEPS HAVE FUNNY NOTATION!
println("------------------------------------------------------------")
println("The following build script dependencies are using the latest release version:")
latestVersionInfo.forEach { dep ->
println("\t - ${dep.group}:${dep.version}")
}
if (oldVersionInfo.isNotEmpty()) {
println()
println("The following build script dependencies need updates:")
oldVersionInfo.forEach { (dep, ver) ->
println("\t - ${dep.group} [${dep.version} -> $ver]")
}
}
if (unknownVersionInfo.isNotEmpty()) {
println()
println("The following build script dependencies have unknown updates:")
unknownVersionInfo.forEach { dep ->
println("\t - ${dep.group}:${dep.version}")
}
}
println()
println()
println()
// now for project dependencies!
latestVersionInfo.clear()
oldVersionInfo.clear()
unknownVersionInfo.clear()
staticMethodsAndTools.resolveAllDependencies(project).forEach { dep ->
val latestVersion = getLatestVersionInfo(repositories, "${dep.group.replace(".", "/")}/${dep.name}/maven-metadata.xml")
val data = DependencyScanner.MavenData(dep.group, dep.name, dep.version)
if (latestVersion != null) {
if (Version.from(dep.version) == latestVersion) {
latestVersionInfo.add(data)
} else {
oldVersionInfo.add(Pair(data, latestVersion))
}
} else {
unknownVersionInfo.add(data)
}
}
println("------------------------------------------------------------")
println("The following project dependencies are using the latest release version:")
latestVersionInfo.forEach { dep ->
println("\t - ${dep.group}:${dep.name}:${dep.version}")
}
if (oldVersionInfo.isNotEmpty()) {
println()
println("The following project dependencies need updates:")
oldVersionInfo.forEach { (dep, ver) ->
println("\t - ${dep.group}:${dep.name} [${dep.version} -> $ver]")
}
}
if (unknownVersionInfo.isNotEmpty()) {
println()
println("The following project dependencies have unknown updates:")
unknownVersionInfo.forEach { dep ->
println("\t - ${dep.group}:${dep.name}:${dep.version}")
}
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018 dorkbox, llc
* Copyright 2021 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -27,12 +27,6 @@ import java.net.URL
open class
GradleUpdateTask : DefaultTask() {
init {
outputs.upToDateWhen { false }
outputs.cacheIf { false }
description = "Automatically update GRADLE to the latest version"
}
@TaskAction
fun run() {
val releaseText = URL("https://services.gradle.org/versions/current").readText()

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018 dorkbox, llc
* Copyright 2021 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -15,13 +15,11 @@
*/
package dorkbox.gradle
import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask
import org.gradle.api.JavaVersion
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.compile.AbstractCompile
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import java.util.*
@ -42,31 +40,38 @@ class GradleUtils : Plugin<Project> {
project.tasks.create("updateGradleWrapper", GradleUpdateTask::class.java).apply {
group = "gradle"
outputs.upToDateWhen { false }
outputs.cacheIf { false }
description = "Automatically update GRADLE to the latest version"
}
project.tasks.create("updateDependencies", DependencyUpdatesTask::class.java).apply {
project.tasks.create("updateDependencies", GetVersionInfoTask::class.java).apply {
group = "gradle"
outputs.upToDateWhen { false }
outputs.cacheIf { false }
resolutionStrategy { strategy ->
strategy.componentSelection { rules ->
rules.all { component ->
val rejected = listOf("alpha", "beta", "rc", "cr", "m", "preview")
.map { qualifier -> Regex("(?i).*[.-]$qualifier[.\\d-]*") }
.any { regex -> regex.matches(component.candidate.version) }
if (rejected) {
component.reject("Release candidate")
}
}
}
}
// optional parameters
checkForGradleUpdate = false
description = "Fetch the latest version information for project dependencies"
}
}
@Synchronized
fun getVersion(): String? {
var version: String? = null
// try to load from maven properties first
try {
val p = Properties()
val `is` = javaClass.getResourceAsStream("/META-INF/maven/com.my.group/my-artefact/pom.properties")
if (`is` != null) {
p.load(`is`)
version = p.getProperty("version", "")
}
} catch (e: Exception) {
// ignore
}
return version
}
}
// Fix defaults for gradle, since it's mildly retarded when it comes to kotlin, so we can have sane sourceset/configuration options

View File

@ -1,3 +1,18 @@
/*
* Copyright 2021 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.gradle
import org.gradle.api.GradleException
@ -6,10 +21,10 @@ import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.file.DuplicatesStrategy
import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.specs.Specs
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.jvm.tasks.Jar
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
import org.jetbrains.kotlin.gradle.plugin.KotlinBasePluginWrapper
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import java.io.File
import java.util.*
@ -125,12 +140,77 @@ open class StaticMethodsAndTools(private val project: Project) {
}
}
/**
* Gets all of the Maven-style Repository URLs for the specified project (or for the root project if not specified).
*
* @param project which project to get the repository root URLs for
* @param onlyRemote true to ONLY get the remote repositories (ie: don't include mavenLocal)
*/
fun getProjectRepositoryUrls(project: Project = this.project, onlyRemote: Boolean = true): List<String> {
val repositories = mutableListOf<String>()
project.repositories.filterIsInstance<org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository>().forEach { repo ->
val resolver = repo.createResolver()
if (resolver is org.gradle.api.internal.artifacts.repositories.resolver.MavenResolver) {
// println("searching ${resolver.name}")
// println(resolver.root)
// all maven patterns are the same!
// https://plugins.gradle.org/m2/com/dorkbox/Utilities/maven-metadata.xml
// https://repo1.maven.org/maven2/com/dorkbox/Utilities/maven-metadata.xml
// https://repo.maven.apache.org/com/dorkbox/Utilities/maven-metadata.xml
if ((onlyRemote && !resolver.isLocal) || !onlyRemote) {
try {
val toURL = resolver.root.toASCIIString()
if (toURL.endsWith('/')) {
repositories.add(toURL)
} else {
// the root doesn't always end with a '/', and we must guarantee that
repositories.add("$toURL/")
}
} catch (e: Exception) {
}
}
}
}
return repositories
}
/**
* Resolves all dependencies of the project buildscript
*
* THIS MUST BE IN "afterEvaluate" or run from a specific task.
*/
fun resolveBuildScriptDependencies(project: Project = this.project): List<DependencyScanner.MavenData> {
val existingNames = mutableSetOf<String>()
return project.buildscript.configurations.flatMap { config ->
config.resolvedConfiguration
.lenientConfiguration
.getFirstLevelModuleDependencies(Specs.SATISFIES_ALL)
.mapNotNull { dep ->
val module = dep.module.id
val group = module.group
val name = module.name
val version = module.version
val moduleName = "$group:$name"
if (!existingNames.contains(moduleName)) {
existingNames.add(moduleName)
DependencyScanner.MavenData(group, name, version)
} else {
null
}
}
}
}
/**
* Resolves all child dependencies of the project
*
* THIS MUST BE IN "afterEvaluate" or run from a specific task.
*/
fun resolveDependencies(): List<DependencyScanner.Dependency> {
fun resolveAllDependencies(project: Project = this.project): List<DependencyScanner.Dependency> {
val projectDependencies = mutableListOf<DependencyScanner.Dependency>()
val existingNames = mutableSetOf<String>()
@ -145,7 +225,7 @@ open class StaticMethodsAndTools(private val project: Project) {
*
* THIS MUST BE IN "afterEvaluate" or run from a specific task.
*/
fun resolveCompileDependencies(): List<DependencyScanner.Dependency> {
fun resolveCompileDependencies(project: Project = this.project): List<DependencyScanner.Dependency> {
val projectDependencies = mutableListOf<DependencyScanner.Dependency>()
val existingNames = mutableSetOf<String>()
@ -159,7 +239,7 @@ open class StaticMethodsAndTools(private val project: Project) {
*
* THIS MUST BE IN "afterEvaluate" or run from a specific task.
*/
fun resolveRuntimeDependencies(): List<DependencyScanner.Dependency> {
fun resolveRuntimeDependencies(project: Project = this.project): List<DependencyScanner.Dependency> {
val projectDependencies = mutableListOf<DependencyScanner.Dependency>()
val existingNames = mutableSetOf<String>()

View File

@ -1,3 +1,18 @@
/*
* Copyright 2021 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.gradle
/**