Skip to content
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

Unload hook #129

Open
nyarly opened this issue Jun 20, 2014 · 32 comments
Open

Unload hook #129

nyarly opened this issue Jun 20, 2014 · 32 comments
Labels

Comments

@nyarly
Copy link
Contributor

nyarly commented Jun 20, 2014

When leaving an .envrc directory, it'd be nice to be able to run a script to undo changes that happened when entering the dir.

For instance, I'm using chruby for use_ruby - so long as I switch from ruby project to ruby project, it's fine, but leaving direnv dirs, I'm left with "whichever ruby I had last" - being able to chruby 2.0 whenever direnv unloads would be handy.

@nyarly
Copy link
Contributor Author

nyarly commented Jun 20, 2014

I guess I could .envrc a chruby in my homedir, huh?

@zimbatm
Copy link
Member

zimbatm commented Jun 20, 2014

How does your use_ruby look like ?

I think that chruby sets environment variables to select the ruby version so direnv should properly revert those changes when going out of the directory. If you're using chruby's auto-switching feature there might be a conflict between direnv and chruby.

@nyarly
Copy link
Contributor Author

nyarly commented Jun 20, 2014

use_ruby(){
  local version=$1
  if [ -z $version ]; then
    version=".ruby-version"
  fi
  if [ -f $version ]; then
    version=$(cat $version)
  fi

  chruby $version
  layout ruby
}

The important part is the chruby - which looks for a matching installed ruby and sets up symlinks for it. I'm specifically not using the chruby/auto.sh script, so my running ruby version doesn't depend on an env var.

Quite reasonably, the answer could be "that's not what direnv is for"...

@zimbatm
Copy link
Member

zimbatm commented Jun 21, 2014

Hey nyarly,

I think you also need to source that chruby.sh file at the start of the use_ruby function. direnv starts a bash sub-shell to evaluate the .envrc and the .bashrc is not supposed to be loaded in that context.
According to https://github.com/postmodern/chruby/blob/master/share/chruby/chruby.sh#L32 chruby is also using environment variables so it should be fine.
Alternatively you can also use load_prefix ~/.rubies/$version instead of chruby.

@nyarly
Copy link
Contributor Author

nyarly commented Jun 21, 2014

load_prefix is an interesting idea.

I think I figured out what happened: I used chruby at the commandline to test something in a different ruby and then left the directory. So direnv didn't change back to the original value of e.g. PATH because PATH had been changed while I was in the dir.

@sknop
Copy link

sknop commented Nov 12, 2014

I have another use case for an unload hook.
I update my shell title when I enter a directory under direnv control:

echo -n -e "\033];In DirEnv\007"

It would be great if I could unset this when I leaving the directory again.

@zimbatm zimbatm added the Feature label Jul 4, 2015
@sknop
Copy link

sknop commented Sep 29, 2015

Resetting the shell title is exactly the use I am looking for.

@zimbatm
Copy link
Member

zimbatm commented Sep 29, 2015

Hey @sknop, I think you can achieve what you want by changing the PS1 no ?

@zimbatm
Copy link
Member

zimbatm commented Sep 29, 2015

not tested but something like that:

in_direnv() {
  if [ -n "$DIRENV_DIR" ]; then
    echo -n -e "\033];In DirEnv\007"
  else
    echo -n -e "\033];Out of DirEnv\007"
  fi
}
PS1='$(in_direnv) > '

@sknop
Copy link

sknop commented Sep 29, 2015

Ah, did not think of using the DIRENV_DIR variable, just discovered that. Not a bad idea, I'll try that.

Thanks

@zimbatm
Copy link
Member

zimbatm commented Sep 29, 2015

Just fixed the script, it's working now

@mattford63
Copy link

I have many dirs each has a code base for a different customer. I can launch the appropriate VPN/sshuttle session on entering the dir. But I can't unload it again :-(

@zimbatm
Copy link
Member

zimbatm commented Sep 14, 2016

Hi @mattford63, what would happen if you had two terminals that cd into the same customer folder?

My main issue with implementing the unload hook is that it supposes some sort of system-wide state. The second issue is, what would happen if the terminal is closed? I think this can be fixed but it would be fairly easy to leave the system in an inconsistent state.

@PanisSupraOmnia
Copy link

I've got a case where this would be useful as well. In order to work on a Rails project that I'm part of, I need to run bundle exec rake sunspot:solr:start: when I enter the folder. However, once I leave that folder I'd like to bundle exec rake sunspot:solr:stop, because I don't need to leave the solr server running in the background taking up resources. Of course I could simply try to remember to stop it when I'm done with it, but that would kind of defeat the whole point of using direnv for this in the first place.

@zimbatm
Copy link
Member

zimbatm commented Jan 5, 2017 via email

@nyarly
Copy link
Contributor Author

nyarly commented Aug 10, 2017

I just stumbled into another potential use case for this: I have a directory for presentation slides. In there I've got a script called "presenting.sh" that e.g. xset s noblank (and a bunch of other "don't blank the screen during a presentation" stuff.)

It'd be nice to have that happen automatically and go away when I was done. Related elsewhere is the problem that closing the terminal would mean that those settings wouldn't be reverted, though.

@zimbatm
Copy link
Member

zimbatm commented Aug 12, 2017

Interesting, thanks for sharing. In this case I would say that it's the presentation software's responsibility to prevent the screen going to sleep, just like when a video player is in full screen.

@theladyjaye
Copy link

theladyjaye commented Feb 18, 2018

Ah, I didn't consider the multiple shells entering the folder problem, aka using something like tmux would present this problem. I often run my unit tests from another shell session/pane and keep my git commands in another. That would definitely be multiple shells, and I see how the issue arises now.

definitely a tricky thing to think about unless state is maintained somewhere.. blah... If the terminal crashes, the state file would effectively be inconsistent.. MORE BLAH!

Tough problem.

What does smartcd do I wonder. They have enter/exit hooks, curious how they handle the same problem or if they even bother and just leave it up to the user to sort out.

@zimbatm
Copy link
Member

zimbatm commented Feb 18, 2018

Most use-cases here would be solved with a process manager. direnv doesn't maintain a running process so it would have to be a new project (dirsvc?).

I think the realization is that we have system-level services, per-user services and now we want per-project services. In systemd, per-user services are started when the first user sessions opens and stopped when the last session closes. Similarly we want the per-project services to start when the first shell opens the project, and stop when the last shell leaves it.

Similarly to direnv it would have a shell hook. It would report directory enter and leaves to the daemon, which would then in turn start and stop the services if a specific file is found (.svcrc?).

$ dirsvc status
/home/user/src/project1 (shells: 3, services: 4)
/home/user/src/project2 (shells: 1, services: 1)
$ dirsvc stop /home/user/src/project2 # forcefully close a session if the shell has disappeared
$ cd /home/user/src/project1 # shell hook adds this shell to the session
$ dirctl status # show the running processes for this folder
* mysql: running
* redis: running
$ dirctl logs mysql
...
$ dirctl restart mysql
...

Potentially the hook could also be implemented in tmux which would give a better reporting facility.

The service definition format is unspecified here. It might make sense to adopt something existing like the systemd units.

A last note: the progression between per-system, per-user and per-project is also a concern for build and runtime dependencies. This is already solved by Nix.

@nyarly
Copy link
Contributor Author

nyarly commented Feb 21, 2018 via email

@zimbatm
Copy link
Member

zimbatm commented Feb 21, 2018

Yeah makes sense, it would be easier to watch the lifetime of each shell that way. I guess tmux fits that bill already.

@skogsbrus
Copy link

I notice that this issue is very old, so maybe there is some solution to this but I haven't found it.

I'd also like there to be an unload hook. My use case is is to run aws-vault exec my-profile on load, and aws-vault clear my-profile on unload.

@kbd
Copy link

kbd commented Feb 16, 2022

This feature seems like it should be possible, given that direnv knows when it's unloading:

direnv: unloading

My use case: I want to set my kitty tab color when I enter a direnv and reset it when I leave.

@steinybot
Copy link

Is there anyway for direnv to start a new subshell? That way we could use traps to perform cleanup.

@archcorsair
Copy link

I set some specific terminal decorations based on the directory I'm in, I'd like to be able to unload them on the way out. Would love to have an unload hook, very useful in general.

@richtong
Copy link

richtong commented Feb 6, 2023

yes an unload hook would be great. For instance if you have Poetry for managing projects, then you want to be able to enter so .direnv has poetry shell in it and this works,

But when you want to cd out, you want do exit that shell. As someone said, you could do this with direnv exit where you just do a exit first, but the shell is then a different one. Hard problem surprisingly hard.

@zimbatm
Copy link
Member

zimbatm commented Feb 7, 2023

The "spawning of a subshell" approach is also a good design. It would make unloading much more straightforward since the tool doesn't have to revert the changes. It also means that aliases and functions can be supported.

I cannot convert direnv to this approach, as it would fundamentally change the tools but I can see an alternate universe where direnv would have taken that approach. Or a new tool could be written to handle this.

@tom-ricci
Copy link

I have a usecase for this: I want to start the docker daemon when I enter a project that requires docker, and stop it when I leave. Currently direnv lets me start the daemon when I enter the project, but there's no way to stop it when I leave.

Being able to run commands on unload would be great for this.

@tmeckel
Copy link

tmeckel commented Mar 8, 2024

Another use case: running pulumi login on entering a directory. On "unload" I want to do a pulumi logout. Would be great to have such a feature.

@skogsbrus
Copy link

Given the age of this issue without anyone having been able to implement it, maybe it doesn't have to be a perfect solution that solves all of the problems in this thread.

My main issue with implementing the unload hook is that it supposes some sort of system-wide state. The second issue is, what would happen if the terminal is closed? I think this can be fixed but it would be fairly easy to leave the system in an inconsistent state.

Would it be acceptable if the feature is defined as "allow users to execute a side effect on unload", full stop? i.e. direnv takes no responsibility for any states or conflicts between multiple shells - as an MVP the feature would just be "dumb" and put that burden on users with clear documentation.

@anpol
Copy link

anpol commented Apr 1, 2024

"allow users to execute a side effect on unload"

If you are using ZSH, try this:

  1. Download script from https://gist.github.com/anpol/7a2706c2766eff454e047b7513fa511b
  2. From ZSH prompt, source ./direnv-hook-preload-postunload.zsh
  3. cd to a directory containing any .envrc file. Observe the result:
direnv: postload from to /path/to/envrc/directory
direnv: postload_func to /path/to/envrc/directory
  1. cd to another directory, not containing an .envrc file. Observe the result:
direnv: preunload from /path/to/envrc/directory to
direnv: preunload_func /path/to/envrc/directory to

That was a demonstration that you definitely can write your own ZSH functions to handle direnv load/unload event.


The documentation looks like:

  1. Edit your .zshrc and do the following:
  2. Paste ./direnv-hook-preload-postunload.zsh without examples.
  3. Write your own function for handling an unload event.
  4. Add that function to direnv_preunload_functions array.

@aae42
Copy link

aae42 commented Apr 17, 2024

Would like to use it to clean up containers in a shared container runtime

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

No branches or pull requests