Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conflict with "force" feature with dependencies #265

Closed
javajon opened this issue Oct 18, 2018 · 8 comments
Closed

Conflict with "force" feature with dependencies #265

javajon opened this issue Oct 18, 2018 · 8 comments

Comments

@javajon
Copy link

javajon commented Oct 18, 2018

For idempotency of our builds, reporting, and problems with transitive dependency hell we like to force the explicit jar versions in our classpath. With failOnVersionConflict() enabled our builds break intentionally when there are discovered version conflicts. It's a very helpful Gradle feature.

The dependencyUpdates task will not report dependency updates when the force command is used. We have an explicit list of forced jars in our Gradle dependencies and we force all versions with this technique:

        resolutionStrategy {
            failOnVersionConflict()
            preferProjectModules()
            libs.each {k, v -> force(v)}
        }

As a result, the gradle-versions-plugin respects this "force" command by not reporting the potential new updates. e.g we forced the guava jar to version 19 with the force it does not report that v27 is available. Removing the force allows us to us to see the updates. However, we need the force on normally.

Would you consider these two feature suggestions:

  1. Have the report state that the version reported has been explicitly forced to avoid misinterpretation of false positives.
  2. Have a property such as respect-force=true / false (default: true) to allow us to change the plugin behavior to ignore the forced version and see if there is a newer one available.
@ben-manes
Copy link
Owner

Hi @javajon,

Since we let Gradle perform the heavy lifting, our flow is to clone the source configuration and rewrite the dependencies to be dynamic, and compare the results. I'm not sure if there is a global flag for us to set in this regard.

Instead, you could follow the tricks in #194. That discusses the options of gradle.startParameter.taskNames or gradle.taskGraph.hasTask(tasks.dependencyUpdates) as conditions to toggle by. Then when running this reporting task you can skip over the restrictions, while still requiring them everywhere else. It's a little ugly, but so far acceptable as this is usually a one-off call for keeping a project healthy.

@javajon
Copy link
Author

javajon commented Oct 19, 2018

Hi @ben-manes,

I was thinking the same that having some conditional around the force may also do the trick. The suggestion in #194 led me to this solution that fixes the issue:

      resolutionStrategy {
            failOnVersionConflict()
            preferProjectModules()

            gradle.taskGraph.whenReady {taskGraph ->
                if (! taskGraph.hasTask(dependencyUpdates)) {
                    libs.each {k, v -> force(v)}
                }
            }
    }

However, others may not realize this is happening and even with force would assume this plugin would still be reaching out to find recent versions. Perhaps at least in the output and/or in the report, there could be a note to the reader that force is being used and therefore may affect the reporting outcome. Relying on Gradle for the heaving lifting is smart and it helps with compatibility as Gradle evolves. Perhaps consider a warning message for those who may not realize how the presence of force has on this plugin's behavior. Perhaps just a note in the readme.

Thanks for the quick help and such a great plugin!

@ben-manes
Copy link
Owner

Sure, do you mind sending a PR with a short suggestion?

@javajon
Copy link
Author

javajon commented Oct 19, 2018

Happy to help. What are your preferences to these considered proposals?

  1. A note in the readme about the behaviors with force turned on would be helpful
  2. A task output message as a warning when the presence of force is detected?
  3. A note in the output (text, xml, json, etc) warning when the presence of force is detected?

Choice 1 is the least invasive and covers 80% of the problem.

@ben-manes
Copy link
Owner

I think (1) is best, too.

Since Gradle is a big API, we'd have to query it to determine upfront if there were restrictions, as I don't believe there is a way to get feedback per dependency. This could mean that restrictions (such as those applied dynamically by other plugins) may not be detectable and it would be a losing battle.
I am pretty sure that we wouldn't be able to detect that restriction to issue a warning.

For completeness (in case we someone disagrees and we do want to explore code changes) the approach would probably be to,

  • Get the Configuration#getResolutionStrategy(). I don't know if the hierarchical configuration aspect means we have to walk the parent's or if its cascades.
  • Use ResolutionStrategy#getForcedModules() to filter the results to see if any forcing was applied (or assume if non-empty it probably was).
  • I don't know how features like dependency locking interact, as it isn't queriable on the resolution strategy. I'm afraid there are likely a few of these types of cases that we can't detect in a reasonable fashion.

@lwasyl
Copy link

lwasyl commented May 13, 2019

@ben-manes How about a note in the documentation, but properly handling dependency version none? Right now I provide all dependencies without the version and override them in custom resolution strategy using useVersion API on resolutionStrategy.eachDependency.

When I disable this behavior when dependencyUpdates task is in the task graph, all the dependencies versions are resolved to none and it doesn't seem to be properly handled by the library.

Is there any way none can be handled as a lowest possible version? I'm happy to contribute, but would appreciate pointing me in the right file(s)

@ben-manes
Copy link
Owner

I'm not sure, but you are welcome to poke around. The main file of interest is Resolver.groovy which determines the dependency status for a given project's configuration. This is called for every configuration in all projects, aggregated, and reported on. If you can obtain the results you want it would be done here.

A local file, e.g. libs/guava.jar would have a null version and we specify none ourselves. This is detected when there are local artifacts attached. Otherwise we use + to allow the resolution to proceed, e.g. if versions are declared implicitly through a BOM. This should work if you don't restrict + in your resolution; see createQueryDependency for these details.

@lwasyl
Copy link

lwasyl commented May 14, 2019

You gave me an idea, and actually if I force version to +, the plugin properly resolves updates. So I just always set + version whenever dependencyUpdates task is ran.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants