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

Straight seems to miss updates or installation of dependencies #1083

Open
minad opened this issue May 2, 2023 · 36 comments
Open

Straight seems to miss updates or installation of dependencies #1083

minad opened this issue May 2, 2023 · 36 comments
Labels

Comments

@minad
Copy link

minad commented May 2, 2023

There have been numerous issues reported to packages using the Compat library as dependency. Straight fails to update or install Compat properly.

All these issues required manual intervention, basically manual installation of Compat and reinstallation of the affected package. Is this related to #964? Can the dependency handling of Straight be made more robust please?

@minad minad added the bug label May 2, 2023
@progfolio
Copy link
Contributor

progfolio commented May 2, 2023

Are we sure about compat failing to install in any of these cases?
The following tests all show compat.el being installed as a dependency of each package:

Installing Consult
(straight-bug-report
  :user-dir "straight.consult"
  :post-bootstrap 
  (straight-use-package 'consult))
  • Test run at: 2023-05-02 14:09:17
  • system-type: gnu/linux
  • straight-version: prerelease (HEAD -> master, origin/master, origin/develop, origin/HEAD, develop) 039e5c9 2023-03-12
  • emacs-version: GNU Emacs 30.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.37, cairo version 1.17.8) of 2023-04-14
Output
Bootstrapping straight.el...
Bootstrapping straight.el...done
Looking for gnu-elpa-mirror recipe → Cloning melpa...
Looking for gnu-elpa-mirror recipe → Cloning melpa...done
Looking for nongnu-elpa recipe → Cloning gnu-elpa-mirror...
Looking for nongnu-elpa recipe → Cloning gnu-elpa-mirror...done
Looking for emacsmirror-mirror recipe → Cloning nongnu-elpa...
Looking for emacsmirror-mirror recipe → Cloning nongnu-elpa...done
Looking for emacsmirror-mirror recipe → Cloning el-get...
Looking for emacsmirror-mirror recipe → Cloning el-get...done
Looking for straight recipe → Cloning emacsmirror-mirror...
Looking for straight recipe → Cloning emacsmirror-mirror...done
Building straight...
Building straight...done

Test run with version: prerelease (HEAD -> develop, origin/master, origin/develop, origin/HEAD) 039e5c9 2023-03-12
Cloning consult...
Cloning consult...done
Building consult...
Building consult → Cloning compat...
Building consult → Cloning compat...done
Building consult → Building compat...
Building consult → Building compat...done
Building consult...
Building consult...done

Packages:
"straight"                n/a                  develop 039e5c9 2023-03-12
"org-elpa"                n/a                  n/a
"melpa"                   n/a                  master 86ca8d06 2023-05-02
"gnu-elpa-mirror"         n/a                  master eda4c96 2023-05-01
"nongnu-elpa"             n/a                  main 0120f3d 2023-03-14
"el-get"                  melpa                master 22c83206 2023-03-19
"emacsmirror-mirror"      n/a                  master 606c3dc 2023-05-02
"consult"                 melpa                main 625edc0 2023-05-02
"compat"                  gnu-elpa-mirror      master cd28402 2023-04-24

Installing Embark
(straight-bug-report
  :user-dir "straight.embark"
  :post-bootstrap 
  (straight-use-package 'embark))
  • Test run at: 2023-05-02 14:12:48
  • system-type: gnu/linux
  • straight-version: prerelease (HEAD -> master, origin/master, origin/develop, origin/HEAD, develop) 039e5c9 2023-03-12
  • emacs-version: GNU Emacs 30.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.37, cairo version 1.17.8) of 2023-04-14
Output
Bootstrapping straight.el...
Bootstrapping straight.el...done
Looking for gnu-elpa-mirror recipe → Cloning melpa...
Looking for gnu-elpa-mirror recipe → Cloning melpa...done
Looking for nongnu-elpa recipe → Cloning gnu-elpa-mirror...
Looking for nongnu-elpa recipe → Cloning gnu-elpa-mirror...done
Looking for emacsmirror-mirror recipe → Cloning nongnu-elpa...
Looking for emacsmirror-mirror recipe → Cloning nongnu-elpa...done
Looking for emacsmirror-mirror recipe → Cloning el-get...
Looking for emacsmirror-mirror recipe → Cloning el-get...done
Looking for straight recipe → Cloning emacsmirror-mirror...
Looking for straight recipe → Cloning emacsmirror-mirror...done
Building straight...
Building straight...done

Test run with version: prerelease (HEAD -> develop, origin/master, origin/develop, origin/HEAD) 039e5c9 2023-03-12
Cloning embark...
Cloning embark...done
Building embark...
Building embark → Cloning compat...
Building embark → Cloning compat...done
Building embark → Building compat...
Building embark → Building compat...done
Building embark...
Building embark...done

Packages:
"straight"                n/a                  develop 039e5c9 2023-03-12
"org-elpa"                n/a                  n/a
"melpa"                   n/a                  master 86ca8d06 2023-05-02
"gnu-elpa-mirror"         n/a                  master eda4c96 2023-05-01
"nongnu-elpa"             n/a                  main 0120f3d 2023-03-14
"el-get"                  melpa                master 22c83206 2023-03-19
"emacsmirror-mirror"      n/a                  master 606c3dc 2023-05-02
"embark"                  melpa                master 1586905 2023-05-01
"compat"                  gnu-elpa-mirror      master cd28402 2023-04-24

Installing Marginalia
(straight-bug-report
  :user-dir "straight.marginalia"
  :post-bootstrap 
  (straight-use-package 'marginalia))
  • Test run at: 2023-05-02 14:10:32
  • system-type: gnu/linux
  • straight-version: prerelease (HEAD -> master, origin/master, origin/develop, origin/HEAD, develop) 039e5c9 2023-03-12
  • emacs-version: GNU Emacs 30.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.37, cairo version 1.17.8) of 2023-04-14
Output
Bootstrapping straight.el...
Bootstrapping straight.el...done
Looking for gnu-elpa-mirror recipe → Cloning melpa...
Looking for gnu-elpa-mirror recipe → Cloning melpa...done
Looking for nongnu-elpa recipe → Cloning gnu-elpa-mirror...
Looking for nongnu-elpa recipe → Cloning gnu-elpa-mirror...done
Looking for emacsmirror-mirror recipe → Cloning nongnu-elpa...
Looking for emacsmirror-mirror recipe → Cloning nongnu-elpa...done
Looking for emacsmirror-mirror recipe → Cloning el-get...
Looking for emacsmirror-mirror recipe → Cloning el-get...done
Looking for straight recipe → Cloning emacsmirror-mirror...
Looking for straight recipe → Cloning emacsmirror-mirror...done
Building straight...
Building straight...done

Test run with version: prerelease (HEAD -> develop, origin/master, origin/develop, origin/HEAD) 039e5c9 2023-03-12
Cloning marginalia...
Cloning marginalia...done
Building marginalia...
Building marginalia → Cloning compat...
Building marginalia → Cloning compat...done
Building marginalia → Building compat...
Building marginalia → Building compat...done
Building marginalia...
Building marginalia...done

Packages:
"straight"                n/a                  develop 039e5c9 2023-03-12
"org-elpa"                n/a                  n/a
"melpa"                   n/a                  master 86ca8d06 2023-05-02
"gnu-elpa-mirror"         n/a                  master eda4c96 2023-05-01
"nongnu-elpa"             n/a                  main 0120f3d 2023-03-14
"el-get"                  melpa                master 22c83206 2023-03-19
"emacsmirror-mirror"      n/a                  master 606c3dc 2023-05-02
"marginalia"              melpa                main 3ddd2b7 2023-04-23
"compat"                  gnu-elpa-mirror      master cd28402 2023-04-24

Installing Magit
(straight-bug-report
  :user-dir "straight.magit"
  :post-bootstrap 
  (straight-use-package 'magit))
  • Test run at: 2023-05-02 14:13:55
  • system-type: gnu/linux
  • straight-version: prerelease (HEAD -> master, origin/master, origin/develop, origin/HEAD, develop) 039e5c9 2023-03-12
  • emacs-version: GNU Emacs 30.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.37, cairo version 1.17.8) of 2023-04-14
Output
Bootstrapping straight.el...
Bootstrapping straight.el...done
Looking for gnu-elpa-mirror recipe → Cloning melpa...
Looking for gnu-elpa-mirror recipe → Cloning melpa...done
Looking for nongnu-elpa recipe → Cloning gnu-elpa-mirror...
Looking for nongnu-elpa recipe → Cloning gnu-elpa-mirror...done
Looking for emacsmirror-mirror recipe → Cloning nongnu-elpa...
Looking for emacsmirror-mirror recipe → Cloning nongnu-elpa...done
Looking for emacsmirror-mirror recipe → Cloning el-get...
Looking for emacsmirror-mirror recipe → Cloning el-get...done
Looking for straight recipe → Cloning emacsmirror-mirror...
Looking for straight recipe → Cloning emacsmirror-mirror...done
Building straight...
Building straight...done

Test run with version: prerelease (HEAD -> develop, origin/master, origin/develop, origin/HEAD) 039e5c9 2023-03-12
Cloning magit...
Cloning magit...done
Building magit...
Building magit → Cloning compat...
Building magit → Cloning compat...done
Building magit → Building compat...
Building magit → Building compat...done
Building magit → Cloning dash.el...
Building magit → Cloning dash.el...done
Building magit → Building dash...
Building magit → Building dash...done
Building magit → Building git-commit...
Building magit → Building git-commit → Cloning transient...
Building magit → Building git-commit → Cloning transient...done
Building magit → Building git-commit → Building transient...
Building magit → Building git-commit → Building transient...done
Building magit → Building git-commit → Cloning with-editor...
Building magit → Building git-commit → Cloning with-editor...done
Building magit → Building git-commit → Building with-editor...
Building magit → Building git-commit → Building with-editor...done
Building magit → Building git-commit...
Building magit → Building git-commit...done
Building magit → Building magit-section...
Building magit → Building magit-section...done
Building magit...
Building magit...done

Packages:
"straight"                n/a                  develop 039e5c9 2023-03-12
"org-elpa"                n/a                  n/a
"melpa"                   n/a                  master 86ca8d06 2023-05-02
"gnu-elpa-mirror"         n/a                  master eda4c96 2023-05-01
"nongnu-elpa"             n/a                  main 0120f3d 2023-03-14
"el-get"                  melpa                master 22c83206 2023-03-19
"emacsmirror-mirror"      n/a                  master 606c3dc 2023-05-02
"magit"                   melpa                main 6067f92c 2023-05-02
"compat"                  gnu-elpa-mirror      master cd28402 2023-04-24
"dash"                    melpa                master b6eef1a 2023-04-16
"git-commit"              melpa                main 6067f92c 2023-05-02
"transient"               melpa                main af7fe42 2023-05-01
"with-editor"             melpa                main df74385 2023-04-12
"magit-section"           melpa                main 6067f92c 2023-05-02
 
Installing Vertico
(straight-bug-report
  :user-dir "straight.vertico"
  :post-bootstrap 
  (straight-use-package 'vertico))
  • Test run at: 2023-05-02 14:12:00
  • system-type: gnu/linux
  • straight-version: prerelease (HEAD -> master, origin/master, origin/develop, origin/HEAD, develop) 039e5c9 2023-03-12
  • emacs-version: GNU Emacs 30.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.37, cairo version 1.17.8) of 2023-04-14
Output
Bootstrapping straight.el...
Bootstrapping straight.el...done
Looking for gnu-elpa-mirror recipe → Cloning melpa...
Looking for gnu-elpa-mirror recipe → Cloning melpa...done
Looking for nongnu-elpa recipe → Cloning gnu-elpa-mirror...
Looking for nongnu-elpa recipe → Cloning gnu-elpa-mirror...done
Looking for emacsmirror-mirror recipe → Cloning nongnu-elpa...
Looking for emacsmirror-mirror recipe → Cloning nongnu-elpa...done
Looking for emacsmirror-mirror recipe → Cloning el-get...
Looking for emacsmirror-mirror recipe → Cloning el-get...done
Looking for straight recipe → Cloning emacsmirror-mirror...
Looking for straight recipe → Cloning emacsmirror-mirror...done
Building straight...
Building straight...done

Test run with version: prerelease (HEAD -> develop, origin/master, origin/develop, origin/HEAD) 039e5c9 2023-03-12
Cloning vertico...
Cloning vertico...done
Building vertico...
Building vertico → Cloning compat...
Building vertico → Cloning compat...done
Building vertico → Building compat...
Building vertico → Building compat...done
Building vertico...
Building vertico...done

Packages:
"straight"                n/a                  develop 039e5c9 2023-03-12
"org-elpa"                n/a                  n/a
"melpa"                   n/a                  master 86ca8d06 2023-05-02
"gnu-elpa-mirror"         n/a                  master eda4c96 2023-05-01
"nongnu-elpa"             n/a                  main 0120f3d 2023-03-14
"el-get"                  melpa                master 22c83206 2023-03-19
"emacsmirror-mirror"      n/a                  master 606c3dc 2023-05-02
"vertico"                 gnu-elpa-mirror      master dd8eb3a 2023-04-26
"compat"                  gnu-elpa-mirror      master cd28402 2023-04-24

The issue, as I understand it, is that updating a package which depends on compat does not automatically update compat to meet the dependent's declared compat version. It's a matter of deciding what to do in these cases.

Automatically updating a dependencies doesn't seem like the best solution to me. The update may fix one package and break others. A warning could be signaled to let users know that a dependent declares version X.Y.Z, but a lower version is installed. This would still require intervention on the part of the user (decide whether to upgrade the dependency, or downgrade the dependent), but it seems like the best way to handle this to me.

Also related: #822

@raxod502: Thoughts?

@oantolin
Copy link

oantolin commented May 3, 2023

The issue, as I understand it, is that updating a package which depends on compat does not automatically update compat to meet the dependent's declared compat version.

Is this specific to compat or does straight just ignore versions of dependencies in general, only ensuring they are installed but not caring what version is installed?

I think the right thing is to update dependencies, like package.el does. I always thought the version numbers in dependency requirements where minimum versions, so if updating a dependency to a newer version broke one of my packages I'd consider it a bug in my package (albeit a bug that isn't my fault, for once!) and fix it.

@minad
Copy link
Author

minad commented May 3, 2023

Is this specific to compat or does straight just ignore versions of dependencies in general, only ensuring they are installed but not caring what version is installed?

It seems so. The particular problem with Compat is that it has seen multiple updates recently. Since Compat is used as a library, packages depending on newly added APIs bumped the version of the Compat dependency.

I think the right thing is to update dependencies, like package.el does.

Yes, it is. Straight could at least print a prominent warning if dependency version requirements are not met.

@progfolio
Copy link
Contributor

@oantolin:

Is this specific to compat or does straight just ignore versions of dependencies in general, only ensuring they are installed but not caring what version is installed?

It's not compat specifically.
Straight does not check dependency version numbers.
See: #822 (comment)
It's not a problem that crops up often.
It's a combination of compat being a "young" library and seeing a fair bit of early adoption.
Also, the nature of a compatibility library means it will tend toward the latest version being requested by dependents (if not the package you're developing, someone else will want to develop a package using the latest version).

I think the right thing is to update dependencies, like package.el does. I always thought the version numbers in dependency requirements where minimum versions, so if updating a dependency to a newer version broke one of my packages I'd consider it a bug in my package (albeit a bug that isn't my fault, for once!) and fix it.

Let's say, for example, a dependency makes some breaking changes.
Package "A" bumps their min dependency version from 1.0 to 2.0.
Packages "B", "C", "D", and "E" still depend on 1.0.
Should we just update to 2.0 and have the user report bugs to the other packages?
Perhaps, given the choice, the user would rather just wait to update "A".
(Unfortunately, Emacs/elisp can't load multiple versions of the same library simultaneously...)
How does package.el handle conflicts of this nature?

@minad:

Straight could at least print a prominent warning if dependency version requirements are not met.

This seems like the simplest solution, and should prevent most of the bug reports in the issue description.

A more complex solution would be:

Automatically bump a dependency when a dependent requests a higher version and:

  • the dependency only has one dependent.

or

  • the major version requested does not exceed any other dependent's requested major version.

Otherwise, warn the user about the conflict. e.g.

dependency conflict: "A" requires compat 2.0, "B", "C", "D", "E" require compat 1.0

@oantolin
Copy link

oantolin commented May 3, 2023

Should we just update to 2.0 and have the user report bugs to the other packages?

I believe this is the only reasonable strategy. As far as I know, there is no way in Package-Requires to specify a maximum version of a library, only a minimum version. So, whether we like it or not (probably not), package maintainers are implicitly promising that their package works with any version of the dependencies with version greater than or equal to the listed one. Is this a problematic system Emacs has adopted? Yes! But it is what we have.

dependency conflict: "A" requires compat 2.0, "B", "C", "D", "E" require compat 1.0

This message would be arguably wrong, according to the documentation, Package-Requires doesn't mean "I require this specific version" it means "I require this version or any later version".

@progfolio
Copy link
Contributor

Yes, it would be worded more carefully, the example was just that.

@oantolin
Copy link

oantolin commented May 3, 2023

OK, how about this wording?

dependency conflict: "A" requires compat 2.0, "B", "C", "D", "E" claim to work with compat >=1.0, but do you believe them?

😛

@oantolin
Copy link

oantolin commented May 3, 2023

I'm only half-joking. I'd personally like such a tongue-in-cheek error message.

@progfolio
Copy link
Contributor

progfolio commented May 3, 2023

any later version

This is a gap in the documentation or design, if that's so.
I think it'd be better to consider a major version change a boundary.

@oantolin
Copy link

oantolin commented May 3, 2023

I think it'd be better to consider a major version change a boundary.

This is also very sensible.

@minad
Copy link
Author

minad commented May 3, 2023

@progfolio

It's not compat specifically.
It's not a problem that crops up often.
It's a combination of compat being a "young" library and seeing a fair bit of early adoption.
Also, the nature of a compatibility library means it will tend toward the latest version being requested by dependents (if not the package you're developing, someone else will want to develop a package using the latest version).

For me the problem is critical and it cropped up quite often already. The crucial point here is that Compat is a library which is hard to debug. Users have difficulties locating the problem since Compat functions are not prefixed with a package name. For this reason, Compat is developed in a defensive style and tested extensively, to make absolutely sure that there are no regressions due to it. Also the versions can be relied on. Straight users generally seem well-versed with Emacs and Elisp and yet they have a hard time with Straight+Compat. For other libraries, users may usually figure out themselves that they have to update another package, e.g., if the new API s-studlify is missing.

This is a gap in the documentation or design, if that's so. I think it'd be better to consider a major version change a boundary.

It seems you are discussing version bounds. Note that Compat uses a special versioning scheme of the form 29.1.major.minor. The semantic versioning happens in the last two components. This means 29.1.2.1 can have additional APIs in comparison to 29.1.1.1. The minor version 29.1.1.2 won't have major additions, only bugfixes. I am not sure if I followed this versioning scheme rigorously, but I can do so in the future if you only want to bump on major revisions. However Compat would need some special casing anyway if you want to detect major versions since the first three components are major, so the better approach would be to always require Compat version bounds to be strictly met.

@progfolio
Copy link
Contributor

Why does compat need to invent its own versioning scheme instead of using semver?

@minad
Copy link
Author

minad commented May 3, 2023

Why does compat need to invent its own versioning scheme instead of using semver?

Obviously because Compat is tied to the Emacs versions, e.g., compat-28.1, compat-29.1 etc. This cannot be changed and is ingrained in the design of the package.

Also note that semver is not a commonly agreed standard for Emacs packages. It would be nice if there were some formal rules, but in reality, there are none. Therefore it would be better to not rely on such assumptions for such a Straight version check.

@progfolio
Copy link
Contributor

Obviously

That's not obvious to me (or perhaps anyone else who isn't developing compat) why the Emacs version would need to be expressed in compat's versioning.

Also note that semver is not a commonly agreed standard for Emacs packages. It would be nice if there were some formal rules, but in reality, there are none. Therefore it would be better to not rely on such assumptions for such a Straight version check.

That sounds like an Emacs/package.el bug.
If there is no consensus on a versioning scheme, I would lean toward just printing a warning when dependencies are in conflict and letting the user decide what to do. Otherwise, it becomes a game of special casing packages (which I don't think is a good idea) or trying to translate between multiple versioning schemes.

@progfolio
Copy link
Contributor

progfolio commented May 3, 2023

If anything, I would've expected the emacs major/minor version to be encoded after compats major version. e.g.

compat 1.28.1

@minad
Copy link
Author

minad commented May 3, 2023

That's not obvious to me (or perhaps anyone else who isn't developing compat) why the Emacs version would need to be expressed in compat's versioning.

Maybe I should elaborate a bit more on the versioning problem for Compat. Users of Compat specify an "API level", which is the Emacs version. This makes sense since Emacs APIs are generally introduced with new Emacs versions. This means the Compat version carries meaning and is updated in lock-step with Emacs releases itself.

As soon as an Emacs version is frozen, e.g., 29.1, we could release a version of Compat 29.1.1, where only the minor version could be bumped. However it can then happen that we backport additional APIs. Compat is not frozen like the Emacs version itself. Some APIs are hard to back port and can be made available only a while later when we figured out a reasonable approach.

I also suggest that you take a look at the Compat manual.

That sounds like an Emacs/package.el bug. If there is no consensus on a versioning scheme, I would lean toward just printing a warning when dependencies are in conflict and letting the user decide what to do.

A warning is fine with me, if it is sufficiently visible and clear. The warning should suggest that a package dependency must be updated too.

@minad
Copy link
Author

minad commented May 3, 2023

Also note how Compat Package-Requires usually look like:

;; Package-Requires: ((emacs "27.1") (compat "29.1.4.1"))

We require Emacs 27.1, API level 29.1, and more precisely Compat version 29.1.4.1. If you wouldn't care about the Compat details you could also specify:

;; Package-Requires: ((emacs "27.1") (compat "29.1"))

But then the problem is that you may miss additional Compat updates, which were added after the initial release of 29.1.0.0, the very issue we are discussing here.

@progfolio
Copy link
Contributor

progfolio commented May 3, 2023

Does package.el have special casing baked in to account for this versioning scheme? How does it handle the case where package "A" requires compat "28.x.y" and package "B" requires "29.x.y"? Indiscriminately update?

This would work for compat, but cause breakage for any package adhering to semantic versioning.

@oantolin
Copy link

oantolin commented May 3, 2023

As I mentioned above, there is no way at all to say you require version exactly 28.x.y, only a way to say you require at least 28.x.y, therefore package.el just updates the dependency.

@minad
Copy link
Author

minad commented May 3, 2023

Yes, it would indiscriminately update and nothing will break since Compat will always stay backward compatible. Note that package.el doesn't know upper bounds for package versions. Newer versions are always considered as "better".

@progfolio
Copy link
Contributor

progfolio commented May 3, 2023

As I mentioned above, there is no way at all to say you require version exactly 28.x.y, only a way to say you require at least 28.x.y, therefore package.el just updates the dependency.

Yes, it would indiscriminately update and nothing will break since Compat will always stay backward compatible. Note that package.el doesn't know upper bounds for package versions. Newer versions are always considered as "better".

I see. Thanks for the explanations.
Personally, I see that as a bug in package.el/Emacs, and I'm betting they'll have to reconcile that design decision at some point.
The bug happens to work for compat.el (which I would consider a corner case).

So it sounds to me like a warning is the best option for straight.el.
I'll give @raxod502 a chance to chime in as well.

@minad
Copy link
Author

minad commented May 3, 2023

@progfolio

Personally, I see that as a bug in package.el/Emacs, and I'm betting they'll have to reconcile that design decision at some point.

I find it hard to tell. A version bound system like in Haskell or Ruby seems like a good idea. Otoh upper version bounds are also a huge pita since they can cause updates being stuck for a long time.

A better approach may be to avoid upper version bounds as is currently the case and develop libraries in a strictly backward compatible way, or at least with long deprecation periods. For Compat this works well since Emacs APIs are usually backward compatible. For other libraries the story may be different.

Imo it also makes a big difference if we talk about libraries vs user packages. User packages can be developed much less rigoursly since they usually won't be depended on.

@progfolio
Copy link
Contributor

I find it hard to tell. A version bound system like in Haskell or Ruby seems like a good idea. OTOH upper version bounds are also a huge pita since they can cause updates being stuck for a long time.

Further development in elisp name spacing (or, allegedly, read-symbol-shorthands, though I haven't looked into how) would help here because it would be possible to load multiple versions of a library without having them interfere. Of course, that would need a lot of work in many areas of Emacs.

A better approach may be to avoid upper version bounds as is currently the case and develop libraries in a strictly backward compatible way, or at least with long deprecation periods.

Authors still need a mechanism to communicate "the deprecation period is over".
Preferably in a way that's easy to deal with programmatically.
Semantic versioning is a simple way to do that.

IMO it also makes a big difference if we talk about libraries vs user packages. User packages can be developed much less rigoursly since they usually won't be depended on.

The more dependents a package has, the higher the chance its breaking changes will affect some of those dependents.
Emacs's customization-culture makes almost every published package a "library" to be built on.
I agree that popular packages should be developed with that in mind.

@minad
Copy link
Author

minad commented May 3, 2023

Further development in elisp name spacing (or, allegedly, read-symbol-shorthands, though I haven't looked into how) would help here because it would be possible to load multiple versions of a library without having them interfere. Of course, that would need a lot of work in many areas of Emacs.

Indeed. But I am not sure if this is even desirable. I'd say time is better spent if we update packages which depend on old libraries.

Authors still need a mechanism to communicate "the deprecation period is over".

Yes, but introducing upper version bounds is a steep price to pay and it is not without downsides.

The more dependents a package has, the higher the chance its breaking changes will affect some of those dependents. Emacs's customization-culture makes almost every published package a "library" to be built on. I agree that popular packages should be developed with that in mind.

Agree. In my packages I make a distinction between private and public APIs, which makes it clear that depending packages move into dangerous territory. I've often seen that packages make too much functionality public right away such that the stable API boundary is unclear.

@raxod502
Copy link
Member

raxod502 commented May 6, 2023

The decision to ignore version bounds in straight.el was made early on and it's generally proven to be a pretty pragmatic one - lack of consistent use of version constraints across different packages means that introducing support for them tends to introduce new problems, as well as resolve existing ones.

I think signaling a warning when a version bound is not satisfied, but not automatically incorporating the information into the dependency resolution algorithm, is a great compromise to resolve the biggest issues here.

@progfolio
Copy link
Contributor

According to: purcell/flycheck-package#11 we can't even rely on the "Version" header metadata to reliably get a package's declared version. To summarize, package-lint declares a "version" of 0 as a place holder in the main source file. It's assumed that an ELPA will dynamically generate a package-lint-pkg.el file with the real version info.
I'm told this isn't a corner case, either, and that the pattern is common (Which goes against my experience, but that is what I'm told).

Good luck to whoever takes on this feature.

@minad
Copy link
Author

minad commented May 8, 2023

@progfolio That doesn't seem very relevant for this issue. We are talking about packages, in particular libraries, which care about versioning and which specify proper versions in their version header. Packages depending on those packages can specify these versions and can reasonably except that the version constraints are satisfied. Packages which don't specify dependencies or versions could be excluded from the check.

@minad
Copy link
Author

minad commented May 8, 2023

As a data point, libraries like dash, s, f, ht and compat specify version headers. I'd say it is a win if these were checked. Btw I wonder how well package-lint works if straight doesn't update version headers during installation? In my installation I get warnings when depending on unavailable package versions.

@progfolio
Copy link
Contributor

progfolio commented May 15, 2023

Packages which don't specify dependencies or versions could be excluded from the check.

It's not that they don't declare a version, its that the proper declaration is not available outside of an ELPA-dependent tarball build. Those versions don't exist from Straight's perspective, and we have no way of easily getting them. So, even in order to do the simplest warning based solution, we'll have to decide on rules for which package Versions (as declared in the package's source repository) to ignore completely

Personally I'm in favor of ignoring a Version: "0".

@minad
Copy link
Author

minad commented May 17, 2023

I've also noticed that there are issues where users have pinned Compat to a specific commit, which is incompatible with other unpinned packages. It would be great if Straight could also check these. It should generally warn about every version constraint, which is not met, for pinned and unpinned packages. This will help users to unpin packages or to update the pinned commit where needed.

@raxod502
Copy link
Member

Pinned? Did we add a feature to support pinning a specific package to a specific commit? I wasn't aware this was possible with straight.el. #246 is still open.

@progfolio
Copy link
Contributor

Did we add a feature to support pinning a specific package to a specific commit?

No. Perhaps they meant a lockfile is in use?

@holocronweaver
Copy link

holocronweaver commented Nov 30, 2023

My case is the same as magit/magit#4913 (comment) which @minad linked above.

However this was not an issue of Compat not being updated along with other libraries - it was updated to the latest along with Magit. Instead straight-pull-* was not cleaning up previously compiled files which resulted in erroneous Symbol’s function definition is void: \(setf\ compat-alist-get\) errors in Magit, but I cannot figure out where those compiled files are.

Deleting eln-cache, straight/build and offending libraries (Magit and Compat) from straight/repos and restarting Emacs to redownload + build both libs was not sufficient. I had to do a git clean -xdf to completely wipe all untracked files from my Emacs config repo, then download and rebuild every repo from scratch.

Besides deleting straight/build, straight/repos and eln-cache, what files did I miss that would have remedied this without a full git repo clean?

And why didn't straight-pull-all clean these compiled files up to avoid the issue?

@progfolio
Copy link
Contributor

My case is the same as magit/magit#4913 (comment) which @minad linked above.
Deleting eln-cache, straight/build and offending libraries (Magit and Compat) from straight/repos and restarting Emacs to redownload + build both libs was not sufficient.

While the symptom may have been the same, the cause may have been different.
If it were the same issue, deleting all those files would have done the trick.

I had to do a git clean -xdf to completely wipe all untracked files from my Emacs config repo, then download and rebuild every repo from scratch.
Besides deleting straight/build, straight/repos and eln-cache, what files did I miss that would have remedied this without a full git repo clean?

My hunch is that deleting everything wasn't necessary, but had the side effect of updating straight.el itself, which bumped you to a version where a couple byte-compilation bugs have been fixed.

What version of straight.el were you running before?
What's the output of M-x straight-version now?

And why didn't straight-pull-all clean these compiled files up to avoid the issue?

straight-pull-all only fetches upstream changes and merges them.
Recompiling files is the responsibility of straight-rebuild-package or straight-rebuild-all.

@progfolio
Copy link
Contributor

progfolio commented Dec 29, 2023

@minad, @oantolin, @raxod502:

I've made some progress on this with Elpaca.
The basic approach is installing a new build step, elpaca--check-version, run just after a dependent's dependencies are finished building.

elpaca--check-version compares a package's min dependency requirements to each dependency's version considering:

Dependency Type Versioning Scheme Compare Against
built-in (including the emacs version requirement) semver package--builtin-versions
built-in MELPA date "core" date alist e.g., (("27.1" . 20200804) ("27.2" . 20210319) ...)
external semver -pkg.el file or main elisp file "Version:" header. Latest git version tag as fallback.
external MELPA date commit date of HEAD converted to YYYYYMMDD

This handles most cases, but still required the addition of a :version recipe keyword which allows the user to provide their own method for reporting a package's version.
For example, Auctex uses Make to bake its version into a variable in tex-site.el (instead of into the "Version:" header of auctex.el).
Users can add :version (lambda (_) (require 'tex-site) AUCTeX-version) to their auctex recipe in that case.
That should be generally flexible enough.

I also made the decision to fail package orders which do not have all their dependency versions satisfied.
What to do (whether that's upgrading Emacs, upgrading the dependency, downgrading the dependent, etc) is left to the decision of the user.
That should convert all those weird, hard-to-track bugs various package authors are receiving into less-frequent, straightforward support requests.

If anyone is interested in implementing something similar for straight.el, here is the relevant Elpaca code:

https://github.com/progfolio/elpaca/blob/60d92eee8ddc2c4ae4f91c9ec00f891ec4f16e29/elpaca.el#L1105-L1169

@minad
Copy link
Author

minad commented Dec 29, 2023

@progfolio Thank you, this sounds really good. I hope that Straight also gets adapted accordingly.

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

No branches or pull requests

5 participants