dart-lang / pub-dev

The pub.dev website
https://pub.dev
BSD 3-Clause "New" or "Revised" License
782 stars 147 forks source link

Allow tracking Flutter version constraints at the package level #5587

Open stuartmorgan opened 2 years ago

stuartmorgan commented 2 years ago

(Filing for discussion, since I'm not sure if this is actually a worthwhile idea.)

Because Flutter doesn't use semver (and we therefore don't allow upper bounds on the SDK constraint), there's no way to prevent the case of someone updating Flutter and then getting mysterious compilation failures in packages. And for many users, when that does happen it's not clear what the problem is. As an example, when platform recently needed an update to working with a Flutter update (https://github.com/google/platform.dart/pull/38), we saw a lot of confusion: many bugs filed against Flutter where people didn't know what was happening or what to do, as well as people filing issues and PRs against other packages to update their minimum platform version (indicating a lack of understanding of how to manage transitive dependencies in cases like this).

Ideally we would want to be able to:

While we don't have the information necessary to do that in the package snapshots themselves, the maintainer of a package often knows before most people would hit the issue. For instance, a common flow would be:

  1. Someone on master or beta hits the issue. (For Flutter's 1P packages, this is "our CI alerts us as soon as master starts failing".)
  2. They report it to the package maintainer.
  3. The maintainer fixes the package and releases an updated version that works with the new version of Flutter.

At that last step, the maintainer is aware that their new version X is the first version that supports Flutter Y, and thus any previous versions of their package are not actually compatible with Flutter Y+, despite what their pubspec versions say. My thought was that perhaps we could provide a simple way for people to submit that information to pub.dev, and we could track it. Possible uses:

The main downside I see here is that it requires extra action by maintainers. But it's a small amount of extra action, and there would be some potential incentive for people to do it in the form of getting fewer issue reports.

jonasfj commented 2 years ago

It sounds allowing package authors to publish additional incompatibilities after they've published their package.

It's not unprecedented, in fact package version retraction is the same as publishing an incompatibility that says a given version is incompatible with everything.

Allowing a more fine grained model of this would be cool, but I fear that:

This could be a very powerful feature, and one upside to all these risks is that bad incompatibilities could be undone (same as retraction can be undone).

stuartmorgan commented 2 years ago
  • users would find it opaque, because it's suddenly harder to see why things don't resolve anymore (when they used to resolve),

Yes, it could definitely be confusing if used incorrectly. My thought was that if used correctly it shouldn't be all that confusing because the alternative to "things don't resolve" is "things fail to compile with an error in code they don't know anything about", which is not really any better.

  • package authors would misunderstand when it's appropriate to use this.

Maybe? I couldn't come up with any particularly plausible cases of someone doing the wrong thing with it though.

  • general confusion.

Definitely possible :)

Personally I think the biggest potential downside is that it might not actually get used, so would add complexity for nothing. That seems hard to predict though.

Hixie commented 2 years ago

I was just talking to @stuartmorgan about a related topic on Discord and expressed that I think if I were designing the versioning system from scratch, I would probably use a system with a single version number (not semver), and have dependencies always specify a minimum but no maximum, but then have an out-of-band mechanism (similar to what this issue describes) to specify maximum bounds retroactively based on community feedback. So e.g. I release my "foobar v1" package and it depends on "baz v8+". When baz v10 has a breaking change that affects foobar, I retroactively go into pub and tell it foobar v1's maximum for baz is v9.

This is based on the observation that (a) every change is potentially a breaking change anyway*, and (b) breaking changes are often only breaking to a small subset of dependencies.

Probably there would be some mechanism for non-package-owners to propose these maximum version changes as well, not sure exactly how that would work or be moderated.

* because for example someone could depend on the git hash.

isoos commented 2 years ago

users would find it opaque, because it's suddenly harder to see why things don't resolve anymore (when they used to resolve),

I'd value reliable (reproducible) builds much more than out-of-band annotation of potential incompatibilities. Unless a serious and exceptional action was taken (e.g. a package or version has been removed from the repository), once it was working for a given setup on a given SDK, it should do indefinitely with the same configuration.

When baz v10 has a breaking change that affects foobar, I retroactively go into pub and tell it foobar v1's maximum for baz is v9.

But then again, it could affect only a subset of foobar, which may not be relevant for my app, so I would just ignore it. But if the tool forces the out-of-band version constraint, I'd be stuck with it, even after I got it working...

stuartmorgan commented 2 years ago

Unless a serious and exceptional action was taken (e.g. a package or version has been removed from the repository)

I'm not sure how what I'm proposing is less serious/exceptional than withdrawing a package. The use case is similar: you learn post-publishing that there is a serious problem with your package—in the cases I'm thinking of, the problem has always been "does not compile at with a given version of Flutter" (usually with an extremely opaque error message—and thus take a specific action to mitigate that.

But if the tool forces the out-of-band version constraint, I'd be stuck with it, even after I got it working...

Hasn't this already been solved in the context of package withdrawal? If it's in your lock file already, you still get it, we just change new resolutions. So if you have it working, nothing changes for you.

isoos commented 2 years ago

Hasn't this already been solved in the context of package withdrawal? If it's in your lock file already, you still get it, we just change new resolutions. So if you have it working, nothing changes for you.

Version retraction is limited by time: you may retract a package version up to 7 days after publication. After that, the version stays, and it can be considered immutable. I would expect a successful version resolution, even if it is not in the pubspec.lock. (think in the context of a tutorial: for that version of SDK and pubspec.yaml, people should be able to get the same result).

The scenario that you've described is well-intended, and I'm sure that it will be used with great caution. But mistakes may be made, and that would affect other people's build retroactively. I'm also sure that people will try and will misuse it for different purposes, e.g. to force their users to upgrade, even if it is not really necessary. It is not entirely clear to me that this is a power we want to yield.

Hixie commented 2 years ago

I'd value reliable (reproducible) builds much more than out-of-band annotation of potential incompatibilities.

As far as I can tell we don't have any more reproducible builds today, since someone publishing a package can change what packages get resolved and then cause a failure when the new package is incompatible in some way.

stuartmorgan commented 2 years ago

think in the context of a tutorial: for that version of SDK and pubspec.yaml, people should be able to get the same result

How often do tutorials show pinning an exact version of a dependency, which is what is required for this to be true?

And if they do, one possible approach is to make pinning an exact version in pubspec.yaml override any retroactive changes (retraction, this proposal) so that anyone who values exact reproducibility can express that clearly in their pubspec.yaml, with resorting to adjusting pubspec.lock.

I'm also sure that people will try and will misuse it for different purposes, e.g. to force their users to upgrade, even if it is not really necessary.

Someone who wants to abuse the package system could already do this with a poison-pill update (e.g., put the bare string "This_code_no_longer_compiles_so_get_the_latest_version" at the top of the main Dart file) released as a bugfix version change. See also the NPM faker fiasco.