Introduce a `pub upgrade --(no)-allow-prereleases` flag.
It is somewhat surprising that the solver will use pre-releases for dependencies. See eg. https://github.com/dart-lang/pub/issues/2446
Ideally this would be off by default - but that might be a breaking change.
If solving without prereleases fails we should probably report that a solution exists with prereleases and mention the flag.
One question we have not answered: Probably want to allow explicitly chosen pre-releases from the root pubspec.
dependencies:
foo: 1.0.0-dev # just get me that prerelease!
But it is not clear how to generalize this. (Should we allow that also transitively - what does ^1.0.0-dev mean...)
The use case I feel the most strongly about is the one where a pubspec.yaml lists a package and a stable version (single or range), and the solver ends up resolving that to a pre-release. Consider this example:
- package
foois published in one version:foo 1.0.0which depends onbar: ^1.0.0
- package
baris published in one version:bar 1.0.0with no dependencies
The developer starts with this pubspec.yaml:
foo: ^1.0.0
bar: ^1.0.0
Running pub get, the developers gets locked on foo 1.0.0 and bar 1.0.0 as expected.
Updates are now published on pub.dev:
- package
foois published in two versions:foo 1.0.0which depends onbar: ^1.0.0foo 2.0.0which depends onbar: ^1.1.0-dev
- package
baris published in two versions:bar 1.0.0with no dependenciesbar 1.1.0-dev-1.0with no dependencies
The developer now hears about foo 2.0.0 and it has something they need, so they update their pubspec.yaml to:
foo: ^2.0.0
bar: ^1.0.0
The developer runs pub get with an intention of picking up foo 2.0.0. They are not aware that foo itself depends on a pre-release bar, and they generally have a policy to only use stable versions. So when they later debug a bug in their app, and realize it's coming from bar and that they had been locked on foo 2.0.0 and bar 1.1.0-dev-1.0 they get angry.
I suggest that a pub get/upgrade without an --allow-pre-release flag should have failed and reported something like:
Could not resolve, foo 2.0.0 requires bar >=1.1.0-dev and your pubspec allows only stable versions of bar >=1.0.0 <2.0.0. Update your dependency on bar to allow a pre-release, or opt-in to selecting pre-release by passing the flag --allow-pre-releases.
They can then either use the flag, or change pubspec.yaml to:
foo: ^2.0.0
bar: ^1.1.0-dev-1.0
Probably want to allow explicitly chosen pre-releases from the root pubspec.
I agree that this is probably the most desirable behavior, with a small tweak which is that we allow explicit pre-release dependencies in pre-release packages always. This way an explicit pre-release dependency in the root package is allowed to itself have pre-release deps without any warnings.
Reasoning
- Transitive prerelease dependencies are surprising and should always be flagged whether they are explicit or not.
- Unless they are the result of an explicit root package pre-release dep, then it isn't surprising.
- Ultimately we don't want people to release stable versions of a package if they have any immediate prerelease dependencies anyways, so this just encourages that.
- Assuming everybody follows this policy there can be no explicit prerelease dep (even transitively) without a prerelease dep in the root pubspec.
Ultimately we don't want people to release stable versions of a package if they have any immediate prerelease dependencies anyways, so this just encourages that.
@sigurdm recently did a warning to discourage this too.
Assuming everybody follows this policy there can be no explicit prerelease dep (even transitively) without a prerelease dep in the root pubspec.
We could enforce this, but I'm not sure we need to be this strict.
If solving without prereleases fails we should probably report that a solution exists with prereleases and mention the flag.
That, or we could print a warning if we use pre-releases in the solution.
So 1st try to solve without pre-releases (except those allowed by root pubspec.yaml, or transitively from there), then try to solve with pre-releases if that doesn't work. And print a warning if we had to do a second resolution.
If we go with the solution suggested by @jakemac53 two more questions came up while @jonasfj and i discussed this a bit:
-
If
- the root package depends on
foo: 1.0.0andbar: 1.0.0-devand foo1.0.0 depends on baz: ^1.0.0bar1.0.0-dev depends on baz: ^1.1.0-devbazexists in 1.0.0 and 1.1.0-dev Should this resolve?
- the root package depends on
-
Another interesting case:
- A stable package
foo: 1.0.0 depends onbar: 1.0.0-dev. - The root package depends on
foo. Should this resolve?
- A stable package
My suggestion: Without the -allow-pre-release flag, we simple disallow / filter away all pre-release versions. We could allow-list any packages in the pubspec.yaml listed with pre-release versions. That seems like a simple to understand solution?
Are we still interested in this?