Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save ProdPreva1l/cbcf2beb0893038fc9f2be6fc664fef7 to your computer and use it in GitHub Desktop.

Select an option

Save ProdPreva1l/cbcf2beb0893038fc9f2be6fc664fef7 to your computer and use it in GitHub Desktop.
Resolve repositories for dependencies in a configuration. (Gradle [Kotlin DSL])
package info.preva1l.trashcan.tasks
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ResolvedDependency
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
import org.gradle.api.artifacts.result.ResolvedArtifactResult
import org.gradle.api.tasks.InputFiles
import java.net.URI
/**
* Created on 10/06/2025
*
* @author Preva1l
*/
abstract class ResolveDependencyRepositoriesTask : DefaultTask() {
init {
group = "trashcan"
description = "Resolves the repository which each dependency of a configuration can be downloaded from."
}
@get:InputFiles
abstract var configuration: Configuration
private val repositories = (project.rootProject.repositories + project.repositories)
.filterIsInstance<MavenArtifactRepository>()
@TaskAction
fun resolveAndPrint() {
if (repositories.isEmpty()) {
logger.warn("No Maven repositories found to search")
return
}
val firstLevelDeps = configuration.resolvedConfiguration.firstLevelModuleDependencies
.associateBy { "${it.moduleGroup}:${it.moduleName}" }
logger.info("Resolving repository URLs for ${configuration.incoming.artifacts.artifacts.size} artifacts...")
configuration.incoming.artifacts.artifacts.forEach { artifact ->
resolveArtifactRepository(artifact, repositories, firstLevelDeps)
}
}
/**
* Resolves the repository URL for a single artifact
*/
private fun resolveArtifactRepository(
artifact: ResolvedArtifactResult,
repositories: List<MavenArtifactRepository>,
firstLevelDeps: Map<String, ResolvedDependency>
) {
val componentId = parseComponentId(artifact.id.componentIdentifier.displayName)
?: return
val depKey = "${componentId.group}:${componentId.name}"
if (!firstLevelDeps.containsKey(depKey)) {
return
}
val jarFileName = "${componentId.name}-${componentId.fullVersion}.jar"
val artifactPath = "${componentId.groupPath}/${componentId.name}/${componentId.version}/$jarFileName"
repositories.forEach { repo ->
val candidateUrl = "${repo.url.toString().trimEnd('/')}/$artifactPath"
if (checkUrlExists(candidateUrl)) {
println("✓ Resolved: ${componentId.group}:${componentId.name}:${componentId.version} -> $candidateUrl")
return
}
}
logger.warn("✗ Could not resolve repository for: ${componentId.group}:${componentId.name}:${componentId.version}")
}
/**
* Data class to hold parsed component information
*/
data class ComponentId(
val group: String,
val name: String,
val version: String,
val classifier: String? = null
) {
val groupPath: String = group.replace(".", "/")
val fullVersion: String = version.removeSuffix("-SNAPSHOT") +
if (classifier != null) "-$classifier" else ""
}
/**
* Parses component identifier string into structured data
*/
private fun parseComponentId(displayName: String): ComponentId? {
val parts = displayName.split(":")
return when {
parts.size >= 3 -> ComponentId(
group = parts[0],
name = parts[1],
version = parts[2],
classifier = if (parts.size >= 4) parts[3] else null
)
else -> {
logger.warn("Invalid component identifier format: $displayName")
null
}
}
}
/**
* Checks if a URL exists by attempting to open a connection
*/
private fun checkUrlExists(url: String): Boolean {
return try {
URI.create(url).toURL().openStream().use { stream ->
stream != null
}
} catch (e: Exception) {
false
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment