asdf-vm / asdf

Extendable version manager with support for Ruby, Node.js, Elixir, Erlang & more
https://asdf-vm.com/
MIT License
21.62k stars 769 forks source link

Non-language plugins #367

Open excid3 opened 5 years ago

excid3 commented 5 years ago

Rbenv allows you to install plugins that hook into more functionality of the version manager. For example, rbenv-vars allows you to set project specific environment variables and are automatically loaded when Ruby is executed.

I've replicated this for asdf called asdf-vars, but right now I have to inject into the asdf-exec script to add this functionality. The downside is that this is going to cause a conflict when updating asdf in the future.

There's a list of other interesting plugins here: https://github.com/rbenv/rbenv/wiki/Plugins

These are the hooks that they support for plugins: https://github.com/rbenv/rbenv/wiki/Authoring-plugins#rbenv-hooks

Would adding hooks to support non-language plugins to asdf be something that would get accepted as a PR or is already on the roadmap?

Stratus3D commented 5 years ago

It depends on those hooks are implemented. I do think there is a need for something like this, but I want to make sure the solution we come up with is generic enough to satisfy all the possible use cases. We've already got a need for something like asdf-vars in several other plugins. For example, the Erlang plugin reads certain environment variables during installation, and Erlang itself also reads other environment variables at runtime. There is definitely a need for a more standardized way of making sure environment variables are set before compilation or execution of a program.

So to answer your question, yes, we definitely want something like this. But we want to make sure it's the right solution so we don't have to revisit this in the future.

For asdf-vars, I think we could potentially bake that functionality right into asdf. Imagine an optional .asdf-ruby-compile-env and .asdf-ruby-runtime-env files that get sourced before compilation and before runtime. asdf would just look for files matching the pattern .asdf-<plugin name>-*-env and it would work across the board for all plugins with no changes necessary to any of the plugins.

For more generalized "before exec" hooks we'd want to make it easy for the user to plug in anything they wanted without having to jump through too many hoops. We'd also want to limit the scope of the "before exec" hook. The hook shouldn't be responsible for directory traversal, version manipulation, or need to duplicate any of the existing functionality of asdf.

excid3 commented 5 years ago

I want to clarify too, my use case for asdf-vars is actually more for setting environment variables for apps separately.

For example, I can set DATABASE_URL=whatever in .asdf-vars in two different folders which allows my apps to run on the same machine without conflicts.

This could be useful for other things, but I don't know of enough of the use cases where you'd want something like -compile-env and -runtime-env etc. A generic version might actually cover most things, but I'd be curious to hear about other use cases.

Jwashton commented 5 years ago

I fear there will be a tension here between environment variables that are part of your personal setup (e.g. the local DB port/URL as you suggest, or the local path to a sibling repository) and environment variables that you want set consistently for everyone working on the project (e.g. compilation flags for Erlang). I could even imagine someone wanting a different environment variable file for production.

I very much appreciate this idea for both use cases presented so far, but already there seems to be a conflict at the level of "do I want to track this file in version control?" What happens when you want both?

excid3 commented 5 years ago

Agreed. And one thing that asdf-vars / rbenv-vars does is recursively look up the folder stack for vars files. That way you could keep shared variables in version control and production can have it's own, untracked vars in a parent folder. The merging allows you some flexibility there.

Stratus3D commented 5 years ago

@excid3 yes, we could have to traverse your directory structure looking for a .asdf-ruby-vars file until it finds one. Placing one in your project would set env vars just for that project, placing one in parent directory would set env vars for that dir and all sub dirs.

@Jwashton yes that is a concern. Check personal settings into VC could be a problem. Of course we could tell people not to check the env files in, or to only check in sample env files that would be renamed by the user. I'm on the fence about this feature. I can see benefits to having it, but it would have the potential to cause a lot of problems.

This is my mindset when figuring out how proposed features should be implemented:

I'm not really sure where this feature falls.

neerfri commented 5 years ago

I believe there's a great tool for the use case of per-project environment variables already: https://direnv.net/

In the unix spirit it does one thing and does it well. It also has a feature to "authorize" a .envrc file to ensure you are not exposed to risky env vars from projects you clone and cd into.

excid3 commented 5 years ago

I think the one downside of direnv is that it installs itself by prepending it self to your prompt so it gets triggered every time.

Correct me if I'm wrong, but this won't work if you're over SSH using a non-login, non-interactive shell, which is what I'm considering using asdf with.

_direnv_hook() {
  local previous_exit_status=$?;
  eval "$(direnv export bash)";
  return $previous_exit_status;
};
if ! [[ "$PROMPT_COMMAND" =~ _direnv_hook ]]; then
  PROMPT_COMMAND="_direnv_hook;$PROMPT_COMMAND";
fi
Stratus3D commented 5 years ago

I'm not familiar with direnv, but I do know it's far more intrusive than asdf. All asdf really does is stick a few things on your PATH.

mwean commented 5 years ago

I just came across a situation where I'd like to create some directories after redis is installed, which would be a perfect use for a post-install hook. It would be great for setting up config files too.

bitwalker commented 5 years ago

I've been putting together a PR that adds support for intrusive plugins, in two main ways:

So given my proposal about aliases, a possible approach would be:

  1. Implement porcelain for alias-add, alias-remove, alias-list
  2. Implement a post-processing hook that inspects the command being invoked, and if the command takes a version, try to resolve that version as an alias - if an alias is found, then modify the argument list so that the version is swapped with the resolved ("real") version mapped to the alias.

I looked into adding hooks to various points in the utils/command scripts, but the reality is that there is no sane way to do so without making large portions of the internals effectively a public API. The advantage of the approach above is that plugins can modify any public env vars, perform arbitrary work at initialization, and can transparently modify the command/arguments asdf will dispatch on - and that's a powerful tool set, but ultimately the burden is on the plugin author, not the asdf maintainers. By allowing dispatch to plugin porcelain in the case of unresolved commands, plugins can feel like a natural part of the CLI, and again without imposing any maintenance burden on the asdf maintainers.

I haven't had time to experiment further, but figured I'd leave my notes here for feedback.

jakepearson commented 12 months ago

Hi, I was wondering if there was an update on this issue. I'm interested in using asdf to add env vars like asdf works for other tools. Getting everyone on my team to follow the install steps at the top of this issue would be suboptimal. I also found this plugin that might be a little easier to setup: https://github.com/asdf-community/asdf-direnv

excid3 commented 12 months ago

For anyone curious, I forked asdf and added asdf vars that loads .asdf-vars files and evaluates them during exec.

https://github.com/asdf-vm/asdf/compare/master...excid3:asdf:master

jakepearson commented 12 months ago

For anyone curious, I forked asdf and added asdf vars that loads .asdf-vars files and evaluates them during exec.

master...excid3:asdf:master

Cool, will this get merged to main asdf?