zwilias / elm-json

Install, upgrade and uninstall Elm dependencies
MIT License
182 stars 9 forks source link

Provide mode to tighten version ranges on packages. #12

Closed rupertlssmith closed 5 years ago

rupertlssmith commented 5 years ago

There is an issue on elm-test, where it fails to resolve versions when the declared version range in a package is wider than it should be:

https://github.com/rtfeldman/node-test-runner/issues/277

The suggested fix there is simply for elm-test to provide a better error message to diagnose the issue, rather than for it to resolve the ranges itself and therefore mask an issue.

It is not incorrect for a package elm.json to declare a wider version range than its transitive dependencies really need, but it is good practice for it declare the correct range.

It would seem that an option could be added to elm-json to perform this automatically? For example:

elm-json tighten

Run the solver then update the declared versions in the dependencies to the actual correct ranges that the package will build against.

zwilias commented 5 years ago

I'm 95% sure that using elm-json solve in node-test-runner (see https://github.com/rtfeldman/node-test-runner/pull/356) would resolve this issue as well, without requiring any extra action by the user.

I have been thinking about some additional diagnostics, and one thing it can already do, is "solve while choosing lower bounds", which gives a very reasonable lower bound for all dependencies. By comparing that to the lower-bounds on the input, I could suggest changing those bounds, but I'm a little bit hesitant about doing so: I can't think of use-cases where this would help, provided that whatever ends up using the version graph is actually using a solver.

rupertlssmith commented 5 years ago

rtfeldman/node-test-runner#277

Problem is Richard doesn't want to automatically fix it in node-test-runner, as he thinks its better to show it up as an error needing fixed...

zwilias commented 5 years ago

Let's say there are 2 dependencies. a and b and both exist at 2 version, 1.0.0 and 1.0.1. Either through transitive dependencies or even by direct exclusion, it is possible that version 1.0.0 of both cannot be installed at the same time, so given ranges like so:

{ "dependencies" :
  { "a" : "1.0.0 <= v < 2.0.0"
  , "b" : "1.0.0 <= v < 2.0.0"
  }
}

we end up with the following combinations:

a b Allowed?
1.0.0 1.0.0 ❌ no
1.0.0 1.0.1 ✅ yes
1.0.1 1.0.0 ✅ yes
1.0.1 1.0.1 ✅ yes

A constraint solver will happen across one of those solutions. Most of the time, you'd end up with maximal versions that fit the constraints, but if we were solving for minimal bounds, both combinations would be equally likely to be the outcome and just a matter of implementation details.

At this point, there are 3 options:

Option A: pick a solution at random

So, let's say we find a = 1.0.0, b = 1.0.1 first. We set the lower bounds in package.json, and... cool, everything works. However, our package cannot be installed by anyone who happens to have b = 1.0.0 installed, while technically it would work. So, this does not seem optimal.

Option B: exhaustively search all solutions and keep track of the minimal bounds

Computationally, this is quite a bit more costly, but perhaps not problematically so given the low number of packages that currently exist in the ecosystem. In this specific case, our resulting lower bounds would be a = 1.0.0 and b = 1.0.0.

As long as the ranges are solved by a version constraint solver rather than by blindly picking the lower bound (which is what elm-test does, and rather badly), this would work perfectly.

Option C: don't bother

This is the current reality: we trust the author's intentions, while using a solver when actually installing packages.


To me, option A seems like worst option.

Option B is computationally quite expensive for very little gain:

However, there are possible improvements that seem more interesting to me, for any of the mentioned options: