Asdf: Use best-matching version

Created on 11 Sep 2018  路  13Comments  路  Source: asdf-vm/asdf

Steps to reproduce

$ asdf list ruby
> 2.3.1
  2.3.7
$ asdf local ruby 2.3

Expected behavior

Use version 2.3.7, since it is the highest version that matches the given value.

Actual behavior

version 2.3 is not installed for ruby

Environment

OS: macOS 10.13.6

asdf version: 0.5.1

Similar to #226, I'd love to be able to be less specific about versions and get the highest matching installed version. For something like postgres, I might just say 9 or 10, for ruby, I might use major and minor. I think this would make the tool a lot more useable.

If this sounds good, I could probably put together a PR, although I'm not sure how much overlap there will be in the logic for installing a highest-matching version.

enhancement help wanted proposal

Most helpful comment

Hm, currently asdf is very dumb about versions, they can actually be anything. I mean, not only semantic versions, but things like "system", "ref:master", even "foobar" is valid (I have things like "java intellij" or "android-tools brew" used with my asdf-link plugin)

My primary use for asdf is keeping a single .tool-versions in my projects and let any developer have the same exact versions for tools (compilers, etc). I'm using my .tool-versions file just like a Gem.lock or any-other-tool-locks-file to share the exact version to use on development, testing and production environments.

So I'd say implementing this would cause more problems (not allowing you to not be really sure what was the exact version you are using just by looking at the source code of your project).

I'd consider this issue as something not desirable. But if you really like to have the behaviour you want, you could easily do:

ln -s ~/.asdf/installs/ruby/2.3.7 ~/.asdf/installs/ruby/2.3

Yes, you'd have to update that symlink as you see fit. But again it's only on your development environment and not for all people in the world ^^.

All 13 comments

Hm, currently asdf is very dumb about versions, they can actually be anything. I mean, not only semantic versions, but things like "system", "ref:master", even "foobar" is valid (I have things like "java intellij" or "android-tools brew" used with my asdf-link plugin)

My primary use for asdf is keeping a single .tool-versions in my projects and let any developer have the same exact versions for tools (compilers, etc). I'm using my .tool-versions file just like a Gem.lock or any-other-tool-locks-file to share the exact version to use on development, testing and production environments.

So I'd say implementing this would cause more problems (not allowing you to not be really sure what was the exact version you are using just by looking at the source code of your project).

I'd consider this issue as something not desirable. But if you really like to have the behaviour you want, you could easily do:

ln -s ~/.asdf/installs/ruby/2.3.7 ~/.asdf/installs/ruby/2.3

Yes, you'd have to update that symlink as you see fit. But again it's only on your development environment and not for all people in the world ^^.

My primary use for asdf is keeping a single .tool-versions in my projects and let any developer have the same exact versions for tools

That's definitely one of my use cases, but I also use it for side projects, scripts, etc. where I don't care about the exact version.

This change wouldn't impact any existing users. If you specify 2.3.7, it will use that exact version. If you just specify 2.3, it will find the best versions available. In my mind, it just adds more flexibility and doesn't take anything away.

Perhaps something better would be allowing some custom hooks for pre-post tool installation (so you can create the symlinks I mentioned, IIRC other people have asked for executing some commands like reshiming, or installing essential packages after tool installation. Can't remember right now if there's an issue for that).

Again, I guess I understand your use case (and yeah, for things like postgres, I think it'd make sense not to care about the minor version), but I'm still not convinced asdf should do anything of this kind by itself. If it did, you'd also have to have a way to ask asdf what it understands for 2.3 given your local installations. I'd certainly go for for creating the symlinks, either via a hook or a tiny script that just fixes the symlink for the tool.

Actually you could create a gist of such a script as a prove of concept, and it doesn't even have to be in asdf core, it can source all the asdf functions to get the list of installed versions and stuff, and just create the symlink.

I'd like to know what other people think on this matter. I'm just in favor of not adding unnecessary complexity (best code is deleted-or-never-written-code) and keeping asdf as agnostic as possible (keep in mind that every tool can have its own versioning semantics or none at all).

Yeah, I think hooks could support what I want to do. I can imagine post-install being useful for a bunch of things

https://github.com/asdf-vm/asdf/issues/216 https://github.com/asdf-vm/asdf/issues/226 are two issues related to this. I agree with @vic that this is the sort of thing that doesn't belong in asdf core. Every tool can have it's own versioning quirks. For example, the Erlang plugin has to deal with two version schemes since the way Erlang is versioned changed. There is also no guarantee to that tools will continue to use semantic versioning. In the future new version schemes could become popular. To do this properly we'd have to account for all of this, and handle every scenario correctly. In my experience just trying to sort semantic versions in Bash is hard enough to make me want to avoid this entirely in asdf core (we've even got stuff in the plugin guide about this).

Now there are other solutions to this problem that wouldn't necessarily require this to be implemented in asdf. As @vic said a simple script to create symlinks could achieve what you need. You could also create some sort of "resolver" Bash script that would actually generate the .tool-versions or set the appropriate env variables for your projects based on the state of the project and the tool versions you have installed. Another option is to figure out how to implement this behavior in asdf plugins, without having to make extensive changes to asdf core. Then plugins (like asdf-nodejs) could implement this best-matching version behavior, and others could not, without affecting core.

Another option is to figure out how to implement this behavior in asdf plugins, without having to make extensive changes to asdf core. Then plugins (like asdf-nodejs) could implement this best-matching version behavior, and others could not, without affecting core.

This would be great. Are there plans for doing it? I think it would be especially useful for people that come from nvm, where this is supported.

@mwean current branch has pre/post install hooks support (#434) . so you can try and use them to create symlinks as mentioned in previous comments.

Awesome! I'll give it a try

Another option is to figure out how to implement this behavior in asdf plugins, without having to make extensive changes to asdf core. Then plugins (like asdf-nodejs) could implement this best-matching version behavior, and others could not, without affecting core.

This would be great. Are there plans for doing it? I think it would be especially useful for people that come from nvm, where this is supported.

+1 for the feature coming from rvm (ruby). It is even more unexpected when legacy_version_file is set to yes, and .ruby-version contains something like 2.6 which worked for rvm but is not working for asdf.

Pre/post install scripts won't fix the issue if no suitable version was installed beforehand. On the other hand, introducing fuzzy matching between version specified in .tool-versions and installed/used version can be unexpected for people who use .tool-versions only with specific versions. Perhaps it is worth adding additional config flag that allows plugins to use this fuzzy behaviour?

Great tool btw, it feels nice to drop rvm/kiex/nvm and use a single version manager :+1:.

UPD: actually, right now plugins that support legacy_version_file can introduce some logic that will always pick latest available version when parsing version from legacy file. Downside would be the obligation to update your local version whenever newer patch version is released. @Stratus3D would you be interested in me trying to implement such logic for asdf-ruby?

New user of asdf, and enjoying it. I'd also be pleased to see this. I'd suggest that .tool-versions follow something like is commonly done for "Semantic Versioning" schemes to allow pinning to a specific version, or following minor / patch releases. Naturally it's up to the system owner to install a given version of a tool, but at least one chore can be reduced in a variety of cases.

Use Case

I have a number of small, non-public, nodejs projects that follow minor and patch releases along with an LTS release. They each have docker containers that already does the same. Before a production deploy, or during regular development, I will check the current LTS version, install it, and test. In these cases the specific version isn't terribly important, so long as it is an LTS version. It's very rare that I'd need to pin to a specific version. Using common semver syntax, for example, ^10.13.0 would be sufficient to follow along with the nodejs LTS releases.

Implementation

The semantic versioning "standard" doesn't specify the syntax, only the precedence rules which I would suggest honoring due to common use. For example, RubyGems and npm both have similar syntax, but differ in a few ways, so I don't suggest any particular syntax. Even a comparatively lightweight implementation like azhi references above would be convenient.

Edit: After reading Stratus3D's comment above, if this can be done in the individual plugins, that would be fantastic.

Could this perhaps be something that plugins could opt in to doing. I wouldn't want to assume that every language has the same versioning semantics. However, the Ruby plugin could know that 2.5.3 is semantically OB for 2.5 and in the absence of more specific qualifier, is a better choice than 2.5.1.

In other words, the Ruby plugin is best positioned to decide whether it should add a 2.5 symlink to the highest 2.5.x version it is currently managing, because it is the most appropriate component to interpres the semantics of the versions. It would also know not to conflate jruby, REE, rubinius, truffleruby, etc versions.

  • asdf install ruby 2.5.1 - creates 2.5.1 install dir, 2.5 -> 2.5.1 symlink, maybe 2 -> 2.5[.1] symlink
  • asdf install ruby 2.5.3 - creates 2.5.3 install dir, updates 2.5 and possibly 2 symlink to point to it.

This would be consistent with chruby and perhaps others like RVM/rbenv, without opting in all languages to such behaviour.

FWIW, I use a tactic like the following for a few langs to build my $HOME/.tool-versions so that by default it just picks the highest one available:

$ asdf list all ruby | 
    grep -vE 'truffle|rbx|ree|jruby|preview|-rc[0-9]|mruby|maglev|-dev' |
    sort -r |
    xargs echo ruby |
    tee -a ~/.tool-versions
ruby 2.7.0 2.6.5 2.6.4 2.6.3 2.6.2 2.6.1 2.6.0 2.5.7 2.5.6 2.5.5 2.5.4 2.5.3 2.5.2 2.5.1 2.5.0 2.4.9 2.4.8 2.4.7 2.4.6 2
.4.5 2.4.4 2.4.3 2.4.2 2.4.1 2.4.0 2.3.8 2.3.7 2.3.6 2.3.5 2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.9 2.2.8 2.2.7 2.2.6 2.2.5 2
.2.4 2.2.3 2.2.2 2.2.10 2.2.1 2.2.0 2.1.9 2.1.8 2.1.7 2.1.6 2.1.5 2.1.4 2.1.3 2.1.2 2.1.10 2.1.1 2.1.0 2.0.0-p648 2.0.0-
p647 2.0.0-p645 2.0.0-p643 2.0.0-p598 2.0.0-p594 2.0.0-p576 2.0.0-p481 2.0.0-p451 2.0.0-p353 2.0.0-p247 2.0.0-p195 2.0.0
-p0 1.9.3-p551 1.9.3-p550 1.9.3-p547 1.9.3-p545 1.9.3-p484 1.9.3-p448 1.9.3-p429 1.9.3-p426 1.9.3-p392 1.9.3-p385 1.9.3-
p374 1.9.3-p362 1.9.3-p327 1.9.3-p286 1.9.3-p194 1.9.3-p125 1.9.3-p105 1.9.3-p0 1.9.2-p330 1.9.2-p326 1.9.2-p320 1.9.2-p
318 1.9.2-p290 1.9.2-p180 1.9.2-p136 1.9.2-p0 1.9.1-p431 1.9.1-p430 1.9.1-p429 1.9.1-p378 1.9.1-p376 1.9.1-p243 1.9.1-p1
29 1.9.1-p0 1.9.0-5 1.9.0-4 1.9.0-3 1.9.0-2 1.9.0-1 1.9.0-0 1.8.7-p72 1.8.7-p71 1.8.7-p375 1.8.7-p374 1.8.7-p373 1.8.7-p
371 1.8.7-p370 1.8.7-p358 1.8.7-p357 1.8.7-p352 1.8.7-p334 1.8.7-p330 1.8.7-p302 1.8.7-p301 1.8.7-p299 1.8.7-p249 1.8.7-
p248 1.8.7-p22 1.8.7-p174 1.8.7-p173 1.8.7-p17 1.8.7-p160 1.8.7 1.8.6-p420 1.8.6-p399 1.8.6-p398 1.8.6-p388 1.8.6-p383 1
.8.6-p369 1.8.6-p368 1.8.6-p36 1.8.6-p287 1.8.6-p286 1.8.6-p230 1.8.6-p114 1.8.6-p111 1.8.6-p110 1.8.6 1.8.5-p52 1.8.5-p
231 1.8.5-p115 1.8.5-p114 1.8.5-p113

Maybe plug asdf current into a plugin script (maybe bin/resolve-version?) then each plugin provides some version resolution of its own. I hit a similar problem in https://github.com/asdf-vm/asdf-nodejs/pull/176 for implementing compatibility with nvm lts aliases will not work in current asdf because they have a / in the text and current asdf versions need to be acceptable directory names. With a custom resolve-query script I could just replace the / with a - and call it a day. Also implement node ranges and what not

Was this page helpful?
0 / 5 - 0 ratings