mise: `rtx activate` incompatible with direnv

I started playing with rtx today to replace my asdf-direnv setup.

When using layout python3 from the direnv stdlib, it used my global rtx python3 version and not the rtx local one. In the shell itself, the venv of direnv wasn’t used, as rtx added the configured path sections to the fron of PATH.

Updated: My fix was to move eval "$(rtx activate -s zsh)" after my direnv one in .zshrc The work-around is only working in the folder containing .envrc and .tool-versions, but not in subfolders (see comment below):

eval "$(direnv hook zsh)"
eval "$(rtx activate -s zsh)"

Is this something for a known-issues/documentation section or can the zsh call sequence somehow be influenced?

Thank you for this initial release of rtx. It’s a great start.

Reproduce:

~/.tool-versions:

python 3.10.9

.zshrc

eval "$(rtx activate -s zsh)"
eval "$(direnv hook zsh)"

~/dev/tmp/rtx-direnv/.envrc:

layout python3

~/dev/tmp/rtx-direnv/.tool-versions:

python 3.8.10

Steps:

❯ echo $PATH
/Users/user/.local/share/rtx/installs/python/3.10.9/bin:/opt/homebrew/opt/mysql-client/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin:/Users/user/.rd/bin:/opt/homebrew/opt/fzf/bin
❯ cd ~/dev/tmp/rtx-direnv
direnv: error /Users/user/Projects/tmp/rtx-direnv/.envrc is blocked. Run `direnv allow` to approve its content

# after the initial `direnv allow` it will seem to work, as the path is already adjusted by rtx
❯ direnv allow
direnv: loading ~/Projects/tmp/rtx-direnv/.envrc
direnv: export +VIRTUAL_ENV ~PATH
❯ cd ..
direnv: unloading
❯ cd ~/dev/tmp/rtx-direnv
direnv: loading ~/Projects/tmp/rtx-direnv/.envrc
direnv: export +VIRTUAL_ENV ~PATH

# direnv venv path overwritten and venv pointing to wrong python version
❯ echo $PATH
/Users/user/.local/share/rtx/installs/python/3.8.10/bin:/Users/user/Projects/tmp/rtx-direnv/.direnv/python-3.10.9/bin:/opt/homebrew/bin:/Users/user/bin:/Users/user/go/bin:/Users/user/.local/bin:/Users/user/.cargo/bin:/opt/homebrew/opt/mysql-client/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin:/Users/user/.rd/bin:/opt/homebrew/opt/fzf/bin

With updated .zshrc:

eval "$(direnv hook zsh)"
eval "$(rtx activate -s zsh)"

Steps:

# open new shell
❯ cd ~/dev/tmp/rtx-direnv
direnv: loading ~/Projects/tmp/rtx-direnv/.envrc
direnv: export +VIRTUAL_ENV ~PATH
❯ echo $PATH
/Users/user/Projects/tmp/rtx-direnv/.direnv/python-3.8.10/bin:/Users/user/.local/share/rtx/installs/python/3.8.10/bin:/opt/homebrew/bin:/Users/user/bin:/Users/user/go/bin:/Users/user/.local/bin:/Users/user/.cargo/bin:/opt/homebrew/opt/mysql-client/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 2
  • Comments: 59 (56 by maintainers)

Commits related to this issue

Most upvoted comments

@jdxcode I quickly checked rtx-python plugin fork. I found two problems:

  1. It does create virtualenv, but it does not deactivate it when directory is changed. Actually, it is very sticky and navigating to other directories and even reloading shell kept created virtualenv first in path. Eventually, I managed to deactivate env with:
$ rtx deactivate
$ set -e __RTX_DIFF
$ set -e __RTX_WATCH
$ rtx cache clean
$ rtx activate fish | source

It might be that only subset of these is needed, but I tried couple of combinations and virtualenv kept being activated until I executed all of these, but since I am not sure why this happens, I am not sure if all of these are required.

  1. Virtualenv is created in subdirectory if it is cd-ed directly. E.g. if I have ~/myproj that contains file .rtx.toml and directory subdir and I do cd myproj/subdir, it creates virtualenv in subdir, when it should be in project root.

I can open ticket in https://github.com/jdxcode/rtx-python if you wish to track it there, but I think replacing layout python will be more involved than current implementation of exec-env. I would love to use virtualenv parameter in .rtx.toml, but for example I have override for direnv_layout_dir to create virtualenvs in $XDG_CACHE_HOME/direnv/layouts/<project-name>, which direnv supports out of the box. There are probably more such features which would complicate python asdf plugin.

think there is a typo: you’re recommending the second rtx activate, not the first rtx direnv activate

agreed on all points. I think a shim mode may make sense, it wouldn’t be that hard to do. I wouldn’t make it the default of course.

added more information around this to the readme in bb61bc5. I’m probably going to focus on other things (like rtx where) when I can focus on rtx development for now. If people are having issues like with what @amoosbr has outlined above (or other issues) go ahead and comment on this ticket.

I think we’ve sorted out the major issues with rtx activate and direnv hook for now.

In my initial test this morning the issues, I wasn’t able to reproduce any of the issues mentioned before.

I just add here a few comments for usage reference:

Hook order

In my experience, its still relevant and for zsh should look like: .zshrc:

eval "$(direnv hook zsh)"
eval "$(rtx activate zsh)"  

For zsh, you can verify hook order via echo $chpwd_functions and _rtx_hook should be listed before _direnv_hook.

❯ echo $chpwd_functions
_rtx_hook _direnv_hook

Perhaps in a documentation update, when this issue gets close?

PATH order gets modified and can break custom dev workflows

I have no idea, how common these scenarios are and if you @jdxcode think, they are working as expected, are to worked on within this issue or should be in their own new issue to collect feedback. I can also try to create an e2e test case, if the order should be kept. I think keeping the PATH order (what was added before rtx paths and after rtx) would avoid scenarios 1. and 2. If it’s how rtx should behave. Example 3. is something, I think rtx can not do much about.

.envrc used in samples:

layout node
  1. Install of new tool, changes PATH order

A bit interesting for a user to predict. It’s probably related to rtx not keeping the PATH order (before/after rtx) mentioned in this comment and not related just to direnv.

-> workaround change out of the direnv managed directory and back in

# add local node_modules bin to front of PATH, to use project binary versions instead of global ones
❯ echo $PATH
/Users/user/Projects/tmp/rtx-direnv-path-order/node_modules/.bin:/Users/user/.local/share/rtx/installs/nodejs/18.0.0/bin:...

# install some new tool versions (not used in the local project)
❯ rtx i nodejs@18.13.0
✓ Runtime nodejs@18.13.0 installed
rtx: nodejs@18.0.0

# path order changed. Now using global node package binaries first
❯ echo $PATH
/Users/user/.local/share/rtx/installs/nodejs/18.0.0/bin:/Users/user/Projects/tmp/rtx-direnv-path-order/node_modules/.bin:...
  1. Similar behavior, when changing to a new directory with new .tool-versions file and back to the previous directory

When traversing to subproject with new .tool-versions file. Which tool added, is irrelevant. -> workaround, where needed: add .envrc with i.e. source_up_if_exists or change out of the direnv managed directory and back in

# add local node_modules bin to front of PATH, to use project binary versions instead of global ones
❯ echo $PATH
/Users/user/Projects/tmp/rtx-direnv-path-order/node_modules/.bin:/Users/user/.local/share/rtx/installs/nodejs/18.0.0/bin:...

# path order changed. Now using global node package binaries first
❯ cd test-with-new-tool-versions
rtx: python@3.8.10 nodejs@18.0.0
❯ echo $PATH
/Users/user/.local/share/rtx/installs/python/3.8.10/bin:/Users/user/.local/share/rtx/installs/nodejs/18.0.0/bin:/Users/user/Projects/tmp/rtx-direnv-path-order/node_modules/.bin:...

# switching back to initial directory keeps the PATH order. Still using global node package binaries first
❯ cd ..
rtx: python@3.10.9 nodejs@18.0.0
❯ echo $PATH
/Users/user/.local/share/rtx/installs/python/3.10.9/bin:/Users/user/.local/share/rtx/installs/nodejs/18.0.0/bin:/Users/user/Projects/tmp/rtx-direnv-path-order/node_modules/.bin:...

  1. On update local tool version

This will anyway be complicated to automate in combination with direnv, as direnv doesn’t watch .tool-versions file. -> If common, add watch_file to local .envrc file or useuse rtx) -> Corner case, that a user can decide, how to handle

❯ echo $PATH
/Users/user/Projects/tmp/rtx-direnv-path-order/node_modules/.bin:/Users/user/.local/share/rtx/installs/nodejs/18.0.0/bin:...
❯ rtx local nodejs@18.13.0
nodejs 18.13.0
rtx: nodejs@18.13.0
❯ echo $PATH
/Users/user/.local/share/rtx/installs/nodejs/18.13.0/bin:/Users/user/Projects/tmp/rtx-direnv-path-order/node_modules/.bin:...

@amoosbr how are you controlling which version of python you want to use for project? Via local .tool-versions? I have both 3.10 and 3.11 installed and use different versions for different project, so this is my way to explicitly set right version so that layout python can pick it up. I guess .tool-versions would work as well.

Edit: I just tried using rtx local python@3.10 which creates .tool-versions locally and it seem to work just like my approach with use python. Still not sure which one I prefer better, will decide over time, both seems to be working.

@amoosbr Thanks, you are right, order is relevant. However, for me, order of hooks that works is different:

rtx activate -s fish | source
direnv hook fish | source

Works, while if I set it the other way around, I always get python from $HOME/.local/share/rtx/installs first in path.

Maybe it has something to do with the shell (I see you are using zsh and I am using fish).

@jdxcode thanks for clarification. What i did was to uninstall direnv installed via homebrew and download binary directly, so that I can control the moment hooks are initiated (since order matters).

I have created $HOME/.config/direnv/lib/use_python.sh with following content (based on your suggestion and similar to other tools direnv supports with use function):

use_python() {
    local version=${1:-}
    echo "using python ${version}"
    PATH_add $(rtx where python@${version})/bin
}

So my .envrc now looks like:

use python 3.10
layout python

and as far as I can tell, this are working as expected now.

Thanks for all the time you spent on this issue 🙌

Ok one more crazy idea—this one really is bonkers.

In theory rtx can be named whatever you want. The CLI is a single file and whatever its name is is its name. That means you can name it asdf. If you did mv ~/bin/rtx ~/bin/asdf then you could install asdf shims with rtx because they would be calling asdf exec but that would actually be launching rtx.

However the commands between the 2 are not identical. They might be close enough though. I’d be happy to find out places were there are miscompatibilities and see how we could get them to align, even if we had to use some sort of “compat” mode. We might need this anyways since plugins commonly call asdf.

You could also do a hybrid approach and create an asdf binary that is your own shim. If it is called with asdf exec, then it dispatches to rtx for speed, if it’s anything else it dispatches to asdf. You would probably want to set RTX_DATA_HOME=$HOME/.asdf—which may or may not function very well.

Anyways I realize this is probably way too much information but as you can tell I’m still trying to think of how best to handle this situation. If you have some time to experiment with any of these ideas please let me know how it goes. I wish this would’ve been as simple as me just patching rtx to not conflict with direnv but I think that might be challenging.

I suppose one other idea is for me to reach out to direnv and see if they have any ideas.

@jdxcode Thanks for your response.

@amoosbr mentioned switching order worked for him, that is what I was referring to. I am not planning using asdf-direnv plugin with rtx, I just mentioned it as mental exercise 😃

Thanks for the snippet, I will give it a try.