ben-manes / gradle-versions-plugin

Gradle plugin to discover dependency updates
Apache License 2.0
3.86k stars 199 forks source link

Report has differences when nothing changes #844

Open dalewking opened 7 months ago

dalewking commented 7 months ago

Was trying to upgrade some dependencies and wanted to make sure that I was not getting unexpected transitive dependency updates. What i wanted to do was:

The problem is there were differences in the report even with no changes to dependencies.

It appears for the most of the changes it it that it is somewhat randomly choosing between the old and new version for the project URL

For example, here is part of the JSON report:

   {
    "group": "androidx.compose.ui",
    "name": "ui",
    "version": "1.4.3",
    "projectUrl": "https://developer.android.com/jetpack/androidx/releases/compose-ui#1.6.2",
    "userReason": null,
    "available": {
     "release": null,
     "milestone": "1.6.2",
     "integration": null
    }
   },

compared to this entry with no changes to dependencies:

   {
    "group": "androidx.compose.ui",
    "name": "ui",
    "version": "1.4.3",
    "projectUrl": "https://developer.android.com/jetpack/androidx/releases/compose-ui#1.4.3",
    "userReason": null,
    "available": {
     "release": null,
     "milestone": "1.6.2",
     "integration": null
    }
   },

I also see differences in reason:

   {
    "group": "androidx.test",
    "name": "rules",
    "version": "1.6.0-alpha01",
    "projectUrl": "1.5.0",
    "userReason": null,
    "reason": "org.gradle.internal.resolve.ModuleVersionResolveException: Could not resolve androidx.test:rules:{strictly 1.6.0-alpha01}.\nRequired by:....

vs.

   {
    "group": "androidx.test",
    "name": "rules",
    "version": "1.6.0-alpha01",
    "projectUrl": "1.5.0",
    "userReason": null,
    "reason": "org.gradle.internal.resolve.ModuleVersionResolveException: Could not resolve androidx.test:rules:+.\nRequired by:
ben-manes commented 7 months ago

Since we defer to Gradle to resolve the dependency, maybe it is non-deterministic due to caching and repository ordering? I usually add --refresh-dependencies so I wonder if that would make it more consistent for you, as it forces it to bypass the cache?

dalewking commented 7 months ago

I don't think it is gradle, because it is not a question of versions (at least for the first part). It is getting the correct versions for current and milestone versions, it is the projectUrl

ben-manes commented 7 months ago

We actually get that from Gradle as well,

https://github.com/ben-manes/gradle-versions-plugin/blob/438cbb5cc6ab62c8204c9bb73fbf9c72fa22daf0/gradle-versions-plugin/src/main/kotlin/com/github/benmanes/gradle/versions/updates/Resolver.kt#L376-L410

dalewking commented 7 months ago

Could there be a race condition ambiguity here whether it uses resolvedCoordinate or originalCoordinate

  private fun getStatus(
    coordinates: Map<Coordinate.Key, Coordinate>,
    resolved: Set<ResolvedDependency>,
    unresolved: Set<UnresolvedDependency>,
  ): Set<DependencyStatus> {
    val result = hashSetOf<DependencyStatus>()
    for (dependency in resolved) {
      val resolvedCoordinate = Coordinate.from(dependency.module.id)
      val originalCoordinate = coordinates[resolvedCoordinate.key]
      val coord = originalCoordinate ?: resolvedCoordinate
      val projectUrl = getProjectUrl(dependency.module.id)
      result.add(DependencyStatus(coord, resolvedCoordinate.version, projectUrl))
    }
ben-manes commented 6 months ago

I believe the only shared mutable state in this plugin is the projectUrls cache, which uses a precursor idiom to computeIfAbsent as that was written in pre-Java 8 Groovy. The rest is thread local state, so assuming that the configuration is not modified at runtime it should be stable as computations passed down between methods. That's not always true as much of Gradle is mutable and allowed to change, e.g. #98 takes advantage of that. So your idea makes a lot of sense, but I don't think we could do much at the plugin level?

dalewking commented 6 months ago

All i am asking is to do a little debugging to see where it is happening. For example, putting some printlns to see if which version number is being used to call getProjectUrl and see if that is changing.