quickenv: An unintrusive environment manager
direnv is a manager for loading/unloading environment
variables per-project. It achieves this by hooking into your shell and
executing a shellscript called .envrc upon cd, loading environment
variables generated by that shellscript into your current shell. It is useful
for automatically activating
virtualenvs, for example.
I have had two problems with direnv in the past:
-
direnvruns the.envrcevery time youcdin and out of a project. This can feel like a drag if the.envrcis slow and you usecda lot. -
direnvoften does not run for me when I want it to, because there is no interactive shell. For example:- When I run
cd ../other-repo/ && pytestfrom a script - or Claude Code runs the same command for me when working on multiple repos
- or my IDE spawns pytest without a shell
- When I run
quickenv is a replacement for direnv. It works with existing .envrcs, and
as such is a drop-in replacement, but how you interact with quickenv and how
it loads environment variables is fundamentally different.
quickenvdoes not hook into your shell. It only requires an addition to yourPATH.quickenvdoes not load environment variables into your shell. Instead it creates global shim binaries that dispatch to the right executable.quickenvdoes not load.envrcwhen changing directories. It loads it whenever you explicitly runquickenv reload, or as part of shimmed commands whenexport QUICKENV_AUTORELOAD=1is set.
quickenv is heavily inspired by volta which achieves
version management for nodejs by also providing "shim" binaries for the most
common commands (yarn, npm, node).
Installation
Install quickenv:
- from Codeberg as a standalone binary
- from AUR for ArchLinux, e.g.
paru -S quickenv - or build from source
Then set it up in your shell:
# Into your bashrc/zshrc. This should be at the front of your PATH, such that
# quickenv can shim/shadow binaries effectively.
# You can remove "direnv hook" from your bashrc/zshrc, but the tool needs to
# stay installed.
Some notes:
-
quickenvcurrently assumesdirenvis in your path, in order to load its "standard library". -
quickenvalso currently does not have pre-built binaries. You need to install Rust and install it using Rust's package manager, Cargo. -
quickenvassumes a POSIX environment.
Building from source
Usage
We're going to check out sentry, because
that's one of the .envrcs I use. Note that Sentry's .envrc only works on
MacOS.
# Execute the .envrc and cache the resulting environment variables in ~/.quickenv/envs/.
# Sentry will prompt you to create a virtualenv, install dependencies via homebrew, etc.
# Re-run this command manually everytime the .envrc changes.
# As part of executing the .envrc, a virtualenv has been created at './.venv/'.
# There are multiple commands available in '.venv/bin/', such as 'pytest' (a test
# runner), or 'sentry' (the main application).
# 'quickenv shim' makes those commands available in your shell.
# These commands will now run with the virtualenv enabled.
# Since .envrc got a new mtime, pytest will reload the .envrc and update the cache.
# This behavior is opt-in using QUICKENV_AUTORELOAD=1, normally one has to run
# `quickenv reload` explicitly.
QUICKENV_AUTORELOAD=1
Advanced usage
# Alternatively you can shim commands explicitly. Be careful: Any command you
# missed (such as 'python' or 'pip') would run outside of the virtualenv!
# You can also run commands within the current .envrc without shimming them.
# Your git hooks don't execute in the virtualenv for some reason? Just replace
# git with a binary that itself loads the virtualenv.
# Actually activate the virtualenv in your current shell. `quickenv vars`
# prints all the extra environment variables with which each shimmed binary runs.
# Or alternatively, substitute your shell with one where your .envrc is loaded
# Or shim 'bash', so that when you open a subshell, the virtualenv is activated.
# Or shim 'make', so your Makefile runs in the virtualenv.
# Curious which binary is actually being executed?
# --> /home/user/.quickenv/bin/make
# --> /usr/bin/make
# --> /usr/bin/make
# Or for general debugging, increase the log level:
QUICKENV_LOG=debug
# [DEBUG quickenv] argv[0] is "make"
# [DEBUG quickenv] attempting to launch shim
# [DEBUG quickenv] abspath of self is /home/user/.quickenv/bin/make
# [DEBUG quickenv] removing own entry from PATH: /home/user/.quickenv/bin
# [DEBUG quickenv] execvp /usr/bin/make
# ...
Command Reference
An unintrusive environment manager
Usage: quickenv <COMMAND>
Commands:
reload Execute .envrc in the current or parent directory, and cache the new variables
vars Dump out cached environment variables
shim Create a new shim binary in ~/.quickenv/bin/
unshim Remove a shim binary from ~/.quickenv/bin/
exec Run a program with .envrc loaded without having to shim it
which Determine which program quickenv's shim would launch under the hood
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
-V, --version Print version
Environment variables:
QUICKENV_LOG=debug to enable debug output (in shim commands as well)
QUICKENV_LOG=error to silence everything but errors
QUICKENV_NO_SHIM=1 to disable loading of .envrc, and effectively disable shims
QUICKENV_SHIM_EXEC=1 to directly exec() shims instead of spawning them as subprocess. This can help with attaching debuggers.
QUICKENV_NO_SHIM_WARNINGS=1 to disable nags about running 'quickenv shim' everytime a new binary is added
QUICKENV_AUTORELOAD=1 to automatically reload .envrc when watched files change (uses DIRENV_WATCHES)
QUICKENV_PRELUDE can be overridden to customize the shell code injected before executing each envrc. By default, quickenv loads ~/.config/direnv/lib/*.sh files and then runs 'eval "$(direnv stdlib)"' to mimic direnv's behavior.
License
Licensed under MIT, see LICENSE.