jdx / mise

dev tools, env vars, task runner
https://mise.jdx.dev
MIT License
10.37k stars 296 forks source link

Investigate preexec hook for bash/zsh #1294

Open jdx opened 11 months ago

jdx commented 11 months ago

It might be possible to use rtx activate in scripts if I used preexec instead of precmd in zsh. It looks like this package provides preexec for bash too: https://github.com/rcaloras/bash-preexec

I think I might be able to get rid of the "not_found" handler with this too. rtx could perform the bin check before the command is executed.

Fish probably has a similar hook too

jdx commented 11 months ago

It's unclear to me if preexec actually runs in non-interactive sessions or not, I'll need to experiment. If not, chpwd could work for zsh.

Unfortunately most people use bash for scripts so I'm not sure what solution could be used there.

jdx commented 11 months ago

Possible the biggest benefit here would be not being able to run "scripts" but just for zshrc to be able to use tools. Of course zshrc is a "script" but it's a special kind since it prepares the interactive session and I think users would expect it to work.

joshuataylor commented 11 months ago

I ran into this tonight, there is a slight difference to the way zsh and bash hooks seem to work currently with rtx.

2023.12.40 linux-x64 (460e547 2023-12-28)

zsh has much better support for chpwd, but bash is clunky.

Here's a good example when using bash:

Let's say we have a project at ~/foo/bar:

  1. Create a simple Python project with virtualenv, version of Python doesn't really matter:

.rtx.toml

[tools]
python = {version='3.12', virtualenv='.venv'}
  1. Change trace+debug.
    export RTX_DEBUG=1
    export RTX_LOG_LEVEL=trace

zsh

In zsh (5.9 for me, but should work with whatever), this works correctly:

  1. First, change to the directory so .venv etc is created.
cd ~/foo/bar
  1. Now, in a single line, cd to the parent directory, then delete the .venv directory, then cd back into the bar directory, then echo "hello".

In theory, this should first setup the virtualenv then echo hello.

cd ~/foo && rm -rf bar/.venv && cd bar && echo "hello"

Output (correct):

[DEBUG] ARGS: rtx hook-env -s zsh
[DEBUG] Config {
    Config Files: [
        "~/.config/rtx/config.toml",
    ],
    Installed Plugins: [
        "poetry",
    ],
}
[DEBUG] Toolset:
[DEBUG] ARGS: rtx hook-env -s zsh
[DEBUG] Config {
    Config Files: [
        "~/foo/bar/.rtx.toml",
        "~/.config/rtx/config.toml",
    ],
    Installed Plugins: [
        "poetry",
    ],
}
[DEBUG] Toolset: python@3.11
[INFO] rtx setting up virtualenv at: /home/josh/foo/bar/.venv
[DEBUG] $ ~/.local/share/rtx/installs/python/3.11/bin/python -m venv /home/josh/foo/bar/.venv
hello
[DEBUG] ARGS: rtx hook-env -s zsh
[DEBUG] Config {
    Config Files: [
        "~/foo/bar/.rtx.toml",
        "~/.config/rtx/config.toml",
    ],
    Installed Plugins: [
        "poetry",
    ],
}

bash

  1. But in bash, it seems this is never triggered:
    hello
    [DEBUG] ARGS: rtx hook-env -s bash
    [DEBUG] Config {
    Config Files: [
        "~/foo/bar/.rtx.toml",
        "~/.config/rtx/config.toml",
    ],
    Installed Plugins: [
        "poetry",
    ],
    }
    [DEBUG] Toolset: python@3.11
    [INFO] rtx setting up virtualenv at: /home/josh/foo/bar/.venv
    [DEBUG] $ ~/.local/share/rtx/installs/python/3.11/bin/python -m venv /home/josh/foo/bar/.venv
jdx commented 11 months ago

first, thanks for the writeup, it's helpful to get these kind of user stories. I wonder if chpwd only gets fired on a new prompt display? I would expect that line to work like you did. I'll have to do some experimentation here

https://github.com/jdx/rtx/blob/main/src/shell/zsh.rs#L49-L56

joshuataylor commented 11 months ago

I'll have a play with it, Bash isn't my primary shell but agreed, looks like it only fires on prompt as bash doesn't have that hook.

IMHO, it's outside of the scope of rtx to fix if it's a bash issue, the current way is fine for most usecases.

I'll have more of a dig though.

jdx commented 1 day ago

sounds like this was a valiant experiment but ultimately fruitless

jdx commented 1 day ago

crap maybe I read it wrong, it sounds like it works in zsh but not bash? idk why I never considered this for zsh

joshuataylor commented 1 day ago

Yeah there is a noticeable delay with prompt rendering, it's because it's the only sane way to do it unless you're some kind of arcane wizard. I spent a while on this and couldn't get it to work reliably.

There are various things this would solve, as there are actions that could be fixed that can't be done with the current approach.

jdx commented 1 day ago

looks like fish has this: https://github.com/fish-shell/fish-shell/issues/583

I'm annoyed though. While this does fix issues and I get a lot of questions about hook-env in zshrc it creates a lot of new ones. I think I almost have to implement this but here are at least some of the issues I realized that will need to be addressed:

It's the last one that makes me super concerned. This means I probably need to track some state or something to know if it ran on cd and if so then don't run on prompt display the prompt once. This means a lot more logic will need to be added. State management often has bugs. zsh code often has bugs. This is going to be buggy.

I think what I should do is gate this, not even behind MISE_EXPERIMENTAL but behind its own setting so intrepid people can iron the issues out with me. I suspect this will be a long road but needs to happen.

joshuataylor commented 1 day ago

Agreed around how very tricky/unexpected to change because direnv does it the same way with prompts. I feel like mise works exceptionally well and this sort of change is kinda big.