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
rtx activate incompatible with direnv
#8
Comments
|
I may have to think about this one a bit. direnv and rtx are likely conflicting with each other. They both maintain a list of "diffs" between what the env was before it ran on directory change as well as after. So what is likely happening is rtx says "here is the state before", then direnv makes some changes and rtx later overwrites them to put things back the way they were. If that makes sense. I am pretty sure this is a problem with both rtx and direnv since if you put them in opposite order then direnv would overwrite what rtx is doing. I think in the short term I'd be careful to not manage the same environment variable with both (chiefly |
|
I just stumbled on the same problem, but with My flow is to choose Python version via Workaround with switching order does not really work for me, since I have installed Maybe solution might be Python virtualenv specific, not I am happy to provide additional info if needed regarding my setup. |
|
@delicb you've said a couple of things that concern me so I want to be very clear:
In terms of direnv generally, the solution appears to skip using I don't think any of this has to do with virualenv in particular but I suppose we'll see. Short term, you can try disabling That seems to work for me in basic testing. |
|
@jdxcode While approach suggested in Do you have a suggestion for making tools defined in Thanks. |
|
I've been thinking about this but haven't come up with a great idea so far. Here are some ideas I've considered:
However here is what I think would be best but I haven't tried it myself: First, call What that will do is set some global runtimes for you. You can put it into For project runtimes you can use the rtx direnv integration. You'll still need to always have a envrc file with It's not the greatest solution but it should work. |
|
To expand on my If I modify that bookkeeping—which is a little hairy because it's like a base64 encoded, compressed json blob, I can trick direnv into thinking that some new environment variables were in place before it so it won't clobber things back. That's relatively straightforward with ad-hoc env vars like |
|
One other idea is that we could use shims. Of course if you've read the README you know I don't like them, but the overhead of calling them with rtx is negligible—probably only 2-3ms. You can do the following: Now in any directory rtx will have that shim point to the correct node runtime. |
|
Ok one more crazy idea—this one really is bonkers. In theory 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 You could also do a hybrid approach and create an 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. |
|
Thanks for the quick reply and explanation. I did try using I trust you when you say that this would complicate things. I am not Rust dev so I haven't looked at source code, but I did read I did set up project to use Adding For using shims - I am not a fan either and I totally understand your logic behind not using them. I would not go down that road unless absolutely necessary. It did cross my mind to use the same approach you mentioned (I even created a small script to generate these shims for all files in Let me use Thanks again for the response, help and awesome tool :). |
|
Hah, I was typing during at the same time while you were sending your last comment. I did not thing of using It is definitely interesting idea, I will give it a try one in the next following days and write up findings. |
|
So in regards to local .tool-version files, it's not that rtx only looks at ones in the same directory as the envrc file, it will actually look at all the parents of it as well. The problem is that I have no way of knowing where children of the envrc file will be and I have to tell direnv up front which .tool-version files to look out for and cause a change to be triggered. Remember when you use the direnv integration it's direnv that requests new env vars from rtx, we have no good way to tell direnv to expect one. This is possibly fixable. Perhaps we could have a special shell extension that we just use for communicating with direnv and telling it to trigger a change. Or maybe we could cache recent locations of all of your tool-version files or something. |
|
The thing about asdf-direnv and asdf-exec is I really don't think a good solution is to bring some kind of addon to asdf. Now you've got 3 different tools to maintain. I used asdf-direnv for a bit but it did behave very strangely a time or two and I couldn't figure out if the problem was with asdf, direnv, or with asdf-direnv. Maybe what I need to do is make rtx a complete direnv replacement. It already is capable of loading environment variables in directories like direnv does. I don't think I could ever get to the level of scripting functionality that direnv provides in The hard part about this is we'd need a new file since .tool-versions isn't a good place for environment variables. I have a TOML format but it needs work. |
|
Yet another idea (this one more practical): you could do your configuration for rtx in Practically everything should work the same, this just gives you 1 instead of 2 files to keep updated. |
|
I'm going to keep this task open in the hope that someone might notice it and have a good idea to share. |
|
Thanks for all the thoughts you put into solving the rtx/direnv conflict. Doing some more testing, I can confirm, that my initial work-around with the "correct" zsh hook order is only working in the directory containing .envrc and .tools-version. As soon as I continue to traverse a directory further down in the project, rtx will overwrite the changes of direnv. ❯ ls .envrc .tool-versions
.envrc .tool-versions
# rtx and direnv updated the path, as expected
❯ echo $PATH
/Users/user/Projects/x/node_modules/.bin:/Users/user/Projects/x/.direnv/python-3.9.12/bin:/Users/user/.local/share/rtx/installs/python/3.9.12/bin:...
# on directory change, direnv changes are overwritten
❯ cd apps
❯ echo $PATH
/Users/user/.local/share/rtx/installs/python/3.9.12/bin:...I think for me, the way you described above is feasible and I will give it a try. So far looks good. Unfortunately, I found an issue with GOROOT and RUBYLIB environment variables. As the issue is within direnv, but also when used without direnv, I opened a new issue #47 |
|
I had an idea here. I was thinking rtx is in a better position in regards to updating PATH because it knows that the only things it adds to PATH are prefixed with ~/.local/share/rtx/installs. I'm already dropping those paths explicitly every time rtx starts. That alone means that I can not worry about replacing PATH to what it was before the hook is run, just leave everything as it was but remove rtx directories. This means that rtx wouldn't override anything set by the user or direnv. However there is still the inverse: direnv will overwrite what rtx sets. For that we will need to go with my original idea of parsing DIRENV_DIFF and taking it to think the new rtx paths were in place before direnv ran. Or perhaps if we simply always ran direnv before rtx that might be enough. Hard to think through exactly how this would behave. This works for PATH but not if you're setting GOROOT and stuff with direnv and rtx. That feels much less important though. |
|
This is probably the most important issue concerning me at the moment. I think the idea I sketched out above should work though. It'll take me a bit to implement it but when I'm able to get around to it I hope rtx will be fully compatible without the use_rtx function. |
|
I think, your idea of 'in-place' updates would work in combination with direnv. And I hope, you also don't need to care about DIRENV_DIFF, when rtx and direnv run in the 'right' order. Based on what I tested with the current rtx integration (not using If the rtx hook would run after the direnv hook, direnv would see the PATH binaries pointing to the global .tool-versions binaries, while running the .envrc content, and i.e. functions like And I think with this order, also the GOROOT settings could work. But it's possible, if direnv modified i.e. GOROOT, that they get overwritten by rtx, when switching to a subdirectory. Perhaps here, you would need to read DIRENV_DIFF? But I agree, that solving the issue with the PATH is more relevant. I hope, you could follow my thoughts. And can evaluate, if they made sense or not. It's totally possible, I missed something or have a wrong understanding of hooks/rtx/direnv. |
|
1.2.9 is out with direnv updates, including patching DIRENV_DIFF. This means you should be able to use For that reason I'm leaving in the guidance in the README to say not to use it, but if you wouldn't mind trying to test it I would really appreciate it. I'm really hoping that bug was just a one-off from development or something. I realized that it doesn't matter what the order is because you can have directories with envrc files or with tool-versions files so ultimately direnv and rtx are going to run at different times regardless. So they have to be able to execute in either order. (Perhaps not with I'm hoping we're at least on the right track for getting direnv and rtx to play nice. |
rtx activate incompatible with direnv
|
I tested this a bit this morning with two sample projects and it really improved a lot. For my use-cases of direnv and rtx the order of the hooks is still relevant though. Given the
layout python3
PATH_add "foo"
python 3.8.10It is working as expected for me with
eval "$(direnv hook zsh)"
eval "$(rtx activate --quiet -s zsh)"Resulting in the PATH: ❯ cd rtx-env-variables-direnv
direnv: loading ~/Projects/tmp/rtx-env-variables-direnv/.envrc
direnv: export +VIRTUAL_ENV ~PATH
❯ echo $PATH
/Users/user/Projects/tmp/rtx-env-variables-direnv/foo:/Users/user/Projects/tmp/rtx-env-variables-direnv/.direnv/python-3.8.10/bin:/Users/user/.local/share/rtx/installs/python/3.8.10/bin:...
❯ python --version
Python 3.8.10
❯ which python
/Users/user/Projects/tmp/rtx-env-variables-direnv/.direnv/python-3.8.10/bin/python
❯ echo $VIRTUAL_ENV
/Users/user/Projects/tmp/rtx-env-variables-direnv/.direnv/python-3.8.10If I switch the order of the both hooks, the result is:
eval "$(rtx activate --quiet -s zsh)"
eval "$(direnv hook zsh)" cd rtx-env-variables-direnv
direnv: loading ~/Projects/tmp/rtx-env-variables-direnv/.envrc
direnv: export +VIRTUAL_ENV ~PATH
# PATH order not as I would expect
❯ echo $PATH
/Users/user/.local/share/rtx/installs/python/3.8.10/bin:...:/Users/user/Projects/tmp/rtx-env-variables-direnv/foo:/Users/user/Projects/tmp/rtx-env-variables-direnv/.direnv/python-3.10.9/bin:...
❯ python --version
Python 3.8.10
❯ which python
/Users/user/.local/share/rtx/installs/python/3.8.10/bin/python
# VIRTUAL_ENV set by layout python3 used global python version. Not the local one
❯ echo $VIRTUAL_ENV
/Users/user/Projects/tmp/rtx-env-variables-direnv/.direnv/python-3.10.9As you can see the order of the added paths in PATH differs, but more importantly the content as well. I only did a limited test of course. So it is possible, there are use cases for both orders. Just wanted to mention it.
|
|
Unfortunately, I must correct myself. I ran also in the issue with the cleared out PATH. It occurred for me, when directly switching from one project using only direnv Steps to reproduce: Note: Set
eval "$(direnv hook zsh)"
eval "$(rtx activate --quiet -s zsh)"❯ cd ~/dev/tmp/rtx-direnv-break-path-source
direnv: loading ~/Projects/tmp/rtx-direnv-break-path-source/.envrc
direnv: export +FOO
❯ ll
.rw-r--r--@ 17 user staff 1 Feb 11:34 .envrc
❯ cat .envrc
export FOO='foo'
❯ ll ../rtx-direnv-break-path-target
.rw-r--r--@ 14 user staff 1 Feb 11:43 .tool-versions
❯ cat ../rtx-direnv-break-path-target/.tool-versions
python 3.8.10
❯ cd ~/dev/tmp/rtx-direnv-break-path-target
direnv: unloading
__zoxide_hook:2: command not found: zoxide
direnv: error can't find bash: exec: "bash": executable file not found in $PATH
❯ echo $PATH
/Users/user/.local/share/rtx/installs/python/3.8.10/bin:
direnv: error can't find bash: exec: "bash": executable file not found in $PATH |
|
you're very good at QA @amoosbr :) I also was able to reproduce this on a different computer once but again wasn't able to retrace my steps. One thing that would be helpful is if I added Now let me see if I can repro what you've put here. EDIT: nice! this does reproduce my issue! Nice work! |
|
thanks for the E2E test, I think it's fine for now to keep everything consolidated in this issue. I think the But yeah, rtx adds things to PATH and DIRENV_DIFF, when you navigate away, it removes it from PATH but not from DIRENV_DIFF so direnv is adding back things that should have been removed. This is a relatively easy fix, I just need to remove the installs from DIRENV_DIFF too and I already have a library that does that, I just need to call it. I think, based on my knowledge of the internals, there is another potential bug people could be surprised by. If you manually set I think it makes sense to make both changes at the same time since otherwise I'd have to put some naive logic in for just DIRENV_DIFF and replace it later. Thanks again for your help with this @delicb @amoosbr. I think we're getting close here. |
|
here's the next steps I think:
in more plain english, what this change will do is track the entries in PATH that were added when rtx was loaded. Then the next time that rtx loads it first removes these entries from PATH and DIRENV_DIFF. That was it is starting from a state as if rtx hadn't run to begin with. It's basically a rollback to get the environment variables back into the state they were before it was run. Hopefully I can get around to this soon. |
|
1.5.0 is out which makes the incremental changes to PATH and DIRENV_DIFF. That e2e test is passing now. I'd like to close this but I also anticipate that one of you will uncover more behavior that still isn't quite right so I'll wait. |
|
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 eval "$(direnv hook zsh)"
eval "$(rtx activate zsh)" For zsh, you can verify hook order via ❯ echo $chpwd_functions
_rtx_hook _direnv_hookPerhaps 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.
layout node
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:...
When traversing to subproject with new # 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:...
This will anyway be complicated to automate in combination with direnv, as direnv doesn't watch .tool-versions file. ❯ 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:... |
|
added more information around this to the readme in bb61bc5. I'm probably going to focus on other things (like I think we've sorted out the major issues with |
|
I was thinking about all of this this morning and I feel like the fact that you're doing Because rtx is capable of modifying environment variables for all commands, not just in shims, I wonder if there could be an rtx python plugin that could do more here so that it wouldn't be necessary to also use direnv for configuring the virtualenv. If someone knows a lot more about Python than I do, perhaps you could offer a suggestion on what might be possible here. One of my first designs of rtx was that it was actually a mix of direnv and asdf and it used a different config file than |
I kind of agree with this: I would always recommend creating a venv for a project and that means the rtx supplied python will actually be overwritten by the one from the venv. The only time you need one is when you have to create the venv. For that I now create my venvs like that: To make this nicer in direnv, I will probably build something like UPDATED: |
|
what about for some runtimes this might be more challenging since they have more than just |
Yes, python should work that way. For others, the |
|
I'd like to see some use cases about specific languages. I feel this isn't likely to even come up since nothing else really has virtualenv-like behavior. (Though I'm happy to be corrected on this point, I don't know every language) |
|
Rust sets two more env variables: Basically every asdf plugin with an exec-env would need more than PATH. -> It's not a virtualenv scenario, but |
|
What I'm not convinced of is that people would need to run I get that it would be somewhat of a pain to setup just rust by itself but I'm not sure anyone would have a reason for wanting to do that in the first place. If you just want to configure a single runtime, then why are there other runtimes in BTW I'm not happy with rtx/asdf's integration with rust anyways but that's a separate discussion. I think rtx should integrate with rustup directly rather than managing rust compilers itself. |
|
I'm not sure, I could follow the discussion. But related to
I don't see rtx and direnv both managing the actual python version. With the right hook order, the python version is managed by PreRequirement:
Correct Sequence with the hooks running in the correct order:
In this scenario, if the Sequence with hooks running in the "wrong" order:
Not sure, if the discussion was about the hook order requirement or my usage of But the hook order is not only related just to python. Also valid for every other direnv use case modifying the PATH, where the PATH order is relevant. That's why I used a nodejs sample in this comment. |
|
What I don't like is that the virtualenv contains a python binary that gets added to PATH ahead of rtx. That means the order matters, and it makes the potential for conflicts possible. I'm not sure how feasible this would be, but I wonder if rtx could set the virtualenv up so direnv wouldn't have to. That way we wouldn't need to prescribe a specific order.
The thing is that should be a pretty esoteric use-case: managing the path to |
|
Thanks for clarifying your point. Now I understood your thoughts better. But I don't have a good solution. Personally, I'm not convinced yet, that rtx should start handling additional use cases like the python virtualenv setup. I like that rtx (or asdf) manage 'just' the correct tool version and other tools like direnv, poetry, ... can use the provided tool versions and can focus on the setup of the development environment. But I can from a usability perspective understand, that the requirement on a correct order of hooks, is not perfect and might lead to complains with users not reading the documentation in detail. But for these use cases, I think But perhaps you or someone else has a great idea to solve this. |
|
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. |
|
I think we can safely close this now as I'm not hearing reports about any more issues with direnv. Happy to reopen if people have any. I've also added experimental shims support in #213 which provides an alternative way to use rtx. |
|
for people coming across this: you can use direnv with rtx in 2 ways: with The first is what I would recommend. If you need the rtx runtimes loaded in direnv (such as when using There is also The downside of |
|
think there is a typo: you're recommending the second |
|
I did notice a bit of strange behavior just now: if you run However at least in the case of python I don't know if this would have practical implications, the same python version is used, it's just referenced from a different PATH. I figured I would mention it though. If your setup is sensitive to this sort of thing, use |
|
changed my recommendation to add |
|
for I think this will work better than using I'm no python expert though, so let me know if it works ok. I looked at the direnv source and |
|
@jdxcode I quickly checked
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.
I can open ticket in https://github.com/jdxcode/rtx-python if you wish to track it there, but I think replacing |
Python version is the same, but environment might not be. E.g. I keep global python environment (in I understand what you meant - for Ruby, Go - it does not matter. But for Python it does, since all dependencies are within virtualenv. |
|
I've got a fix for most of these, but the "stickiness" I haven't quite figured out yet. I only looked at it for a few minutes last night though. I think I have a idea if generally why it might happen though. I'll see if I can fix it later today. This might involve some back and forth just because I'm not a great tester but I am pretty sure we can get there. |
I mean, now that rtx has support for venv's directly this might be a moot point, but I still think this shouldn't be a problem. With what I proposed: In other words, you might encounter the following: I haven't tested this, but wouldn't this still cause pip to install dependencies to ~/src/myproj/.venv and keep ~/.rtx/installs/python/3.11 clean? |
I started playing with
rtxtoday to replace my asdf-direnv setup.When using
layout python3from the direnv stdlib, it used my global rtx python3 version and not the rtx local one. In the shell itself, thevenvof direnv wasn't used, as rtx added the configured path sections to the fron of PATH.Updated:
My fix was to moveThe work-around is only working in the folder containing .envrc and .tool-versions, but not in subfolders (see comment below):eval "$(rtx activate -s zsh)"after my direnv one in.zshrcIs 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:.zshrc~/dev/tmp/rtx-direnv/.envrc:~/dev/tmp/rtx-direnv/.tool-versions:Steps:
With updated
.zshrc:Steps:
The text was updated successfully, but these errors were encountered: