Skip to content

Commit

Permalink
Emit warning when updates are available for libraries w/ version cond…
Browse files Browse the repository at this point in the history
…itions

Fixes #17
  • Loading branch information
hvisser committed Jan 30, 2022
1 parent 626e273 commit c5c4b70
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 11 deletions.
11 changes: 11 additions & 0 deletions catalog/src/main/kotlin/nl/littlerobots/vcu/model/HasVersion.kt
Expand Up @@ -18,3 +18,14 @@ package nl.littlerobots.vcu.model
interface HasVersion {
val version: VersionDefinition
}

fun HasVersion.resolvedVersion(catalog: VersionCatalog): VersionDefinition {
return when (val version = version) {
is VersionDefinition.Condition,
is VersionDefinition.Unspecified,
is VersionDefinition.Simple -> version
is VersionDefinition.Reference -> checkNotNull(catalog.versions[version.ref]) {
"Version reference ${version.ref} is missing in the catalog"
}
}
}
4 changes: 2 additions & 2 deletions catalog/src/main/kotlin/nl/littlerobots/vcu/model/Library.kt
Expand Up @@ -33,9 +33,9 @@ data class Library(
}
}

fun Library.resolveSimpleVersionReference(versionCatalog: VersionCatalog): String? {
fun Library.resolveSimpleVersionReference(versionCatalog: VersionCatalog): VersionDefinition? {
return when (version) {
is VersionDefinition.Simple -> version.version
is VersionDefinition.Simple -> version
is VersionDefinition.Reference -> {
versionCatalog.versions[version.ref]
}
Expand Down
Expand Up @@ -83,12 +83,17 @@ class VersionReportParser {
)
}

return VersionReportResult(report.exceeded.dependencies.toSet(), VersionCatalog(emptyMap(), libraries, emptyMap(), plugins))
return VersionReportResult(
report.exceeded.dependencies.toSet(),
report.outdated.dependencies.toSet(),
VersionCatalog(emptyMap(), libraries, emptyMap(), plugins)
)
}
}

data class VersionReportResult(
val exceeded: Set<Dependency>,
val outdated: Set<Dependency>,
val catalog: VersionCatalog
)

Expand Down
Expand Up @@ -21,6 +21,7 @@ import nl.littlerobots.vcu.model.VersionCatalog
import nl.littlerobots.vcu.model.VersionDefinition
import nl.littlerobots.vcu.model.mapPlugins
import nl.littlerobots.vcu.model.resolveSimpleVersionReference
import nl.littlerobots.vcu.model.resolvedVersion
import nl.littlerobots.vcu.model.sortKeys
import nl.littlerobots.vcu.model.updateFrom
import nl.littlerobots.vcu.versions.VersionReportParser
Expand Down Expand Up @@ -113,6 +114,37 @@ abstract class VersionCatalogUpdateTask : DefaultTask() {
if (versionsReportResult.exceeded.isNotEmpty() && !createCatalog) {
emitExceededWarning(versionsReportResult.exceeded, currentCatalog)
}

checkForUpdatesForLibrariesWithVersionCondition(updatedCatalog, versionsReportResult.outdated)
}

private fun checkForUpdatesForLibrariesWithVersionCondition(catalog: VersionCatalog, outdated: Set<Dependency>) {
val librariesWithVersionCondition = (catalog.libraries.values).filter {
it.resolvedVersion(catalog) is VersionDefinition.Condition
}.mapNotNull { library ->
outdated.firstOrNull {
it.group == library.group && it.name == library.name
}?.let {
library to it
}
}.toMap()

if (librariesWithVersionCondition.isNotEmpty()) {
project.logger.warn("There are libraries using a version condition that could be updated:")
for (library in librariesWithVersionCondition) {
val key = catalog.libraries.entries.first {
it.value == library.key
}.key

val versionRef = when (val version = library.key.version) {
is VersionDefinition.Reference -> " ref:${version.ref}"
else -> ""
}
project.logger.warn(
" - ${library.key.module} ($key$versionRef) -> ${library.value.latestVersion}"
)
}
}
}

private fun emitExceededWarning(dependencies: Set<Dependency>, catalog: VersionCatalog) {
Expand All @@ -128,17 +160,24 @@ abstract class VersionCatalogUpdateTask : DefaultTask() {
if (!didOutputPreamble) {
project.logger.warn(
"Some libraries declared in the version catalog did not match the resolved version used this project.\n" +
"This mismatch can occur when you declare a version that does not exist, or when a dependency is referenced by a transitive dependency that requires a different version.\n" +
"This mismatch can occur when a version is declared that does not exist, or when a dependency is referenced by a transitive dependency that requires a different version.\n" +
"The version in the version catalog has been updated to the actual version. If this is not what you want, consider using a strict version definition.\n\n" +
"The affected libraries are:"
)
didOutputPreamble = true
}
val versionRef = when (val version = it.value.version) {
is VersionDefinition.Reference -> " (${version.ref}) "
is VersionDefinition.Reference -> " (${version.ref})"
else -> ""
}
project.logger.warn("\t${dependency.group}:${dependency.name} (libs.${declaredCatalogEntry.key.replace('-','.')})\n\t\trequested version: ${dependency.currentVersion}$versionRef --> ${dependency.latestVersion}")
project.logger.warn(
" - ${dependency.group}:${dependency.name} (libs.${
declaredCatalogEntry.key.replace(
'-',
'.'
)
})\n requested: ${dependency.currentVersion}$versionRef, resolved: ${dependency.latestVersion}"
)
}
}
}
Expand Down
Expand Up @@ -416,7 +416,6 @@ class VersionCatalogUpdatePluginTest {
""".trimIndent()
)

// empty report
reportJson.writeText(
"""
{
Expand Down Expand Up @@ -467,17 +466,96 @@ class VersionCatalogUpdatePluginTest {
> Task :versionCatalogUpdate
Some libraries declared in the version catalog did not match the resolved version used this project.
This mismatch can occur when you declare a version that does not exist, or when a dependency is referenced by a transitive dependency that requires a different version.
This mismatch can occur when a version is declared that does not exist, or when a dependency is referenced by a transitive dependency that requires a different version.
The version in the version catalog has been updated to the actual version. If this is not what you want, consider using a strict version definition.
The affected libraries are:
androidx.compose.ui:ui-test-junit4 (libs.androidx.test.junit4)
requested version: 1.1.0-rc02 --> 1.1.0-rc01
- androidx.compose.ui:ui-test-junit4 (libs.androidx.test.junit4)
requested: 1.1.0-rc02, resolved: 1.1.0-rc01
BUILD SUCCESSFUL in 4s
1 actionable task: 1 executed
""".trimIndent(),
""".trimIndent(),
buildResult.output
)
}

@Test
fun `available update for version condition emits warning`() {
val reportJson = tempDir.newFile()

buildFile.writeText(
"""
plugins {
id "nl.littlerobots.version-catalog-update"
}
tasks.named("versionCatalogUpdate").configure {
it.reportJson = file("${reportJson.name}")
}
""".trimIndent()
)

reportJson.writeText(
"""
{
"outdated": {
"dependencies": [
{
"group": "io.coil-kt",
"available": {
"release": null,
"milestone": "2.0.0-alpha06",
"integration": null
},
"userReason": null,
"version": "2.0.0-alpha05",
"projectUrl": "https://github.com/coil-kt/coil",
"name": "coil-compose"
}
]
} }
""".trimIndent()
)

val toml = """
[libraries]
coil = { module = "io.coil-kt:coil-compose", version = {strictly = "1.0.0"}}
""".trimIndent()

File(tempDir.root, "gradle").mkdir()
File(tempDir.root, "gradle/libs.versions.toml").writeText(toml)

val buildResult = GradleRunner.create()
.withProjectDir(tempDir.root)
.withArguments("versionCatalogUpdate")
.withPluginClasspath()
.build()

val libs = File(tempDir.root, "gradle/libs.versions.toml").readText()

assertEquals(
"""
[libraries]
coil = { module = "io.coil-kt:coil-compose", version = { strictly = "1.0.0" } }
""".trimIndent(),
libs
)
assertEquals(
"""
Type-safe dependency accessors is an incubating feature.
> Task :versionCatalogUpdate
There are libraries using a version condition that could be updated:
- io.coil-kt:coil-compose (coil) -> 2.0.0-alpha06
BUILD SUCCESSFUL in 4s
1 actionable task: 1 executed
""".trimIndent(),
buildResult.output
)
}
Expand Down

0 comments on commit c5c4b70

Please sign in to comment.