Shadowenv
Shadowenv provides a way to perform a set of manipulations to the process environment upon entering a directory in a shell. These manipulations are reversed when leaving the directory, and there is some limited ability to make the manipulations dynamic.
In order to use shadowenv, add a line to your shell profile (.zshrc
, .bash_profile
, or
config.fish
) reading:
eval "$(shadowenv init bash)" # for bash
eval "$(shadowenv init zsh)" # for zsh
shadowenv init fish | source # for fish
With this code loaded, upon entering a directory containing a .shadowenv.d
directory,
any *.lisp
files in that directory will be executed and you will see "activated shadowenv." in your
shell.
The syntax for the .shadowenv.d/*.lisp
files is Shadowlisp,
a minimal Scheme-like language.
Unlike other tools like direnv, this has the interesting property of allowing
us to do things like simulate chruby reset
upon entry into a directory without the user having
chruby
installed (and undo these changes to the environment when cd
'ing back out):
(provide "ruby")
(when-let ((ruby-root (env/get "RUBY_ROOT")))
(env/remove-from-pathlist "PATH" (path-concat ruby-root "bin"))
(when-let ((gem-root (env/get "GEM_ROOT")))
(env/remove-from-pathlist "PATH" (path-concat gem-root "bin"))
(env/remove-from-pathlist "GEM_PATH" gem-root))
(when-let ((gem-home (env/get "GEM_HOME")))
(env/remove-from-pathlist "PATH" (path-concat gem-home "bin"))
(env/remove-from-pathlist "GEM_PATH" gem-home)))
The intention isn't really for users to write these files directly, nor to commit them to repositories , but for other tool authors to generate configuration on the user's machine. Here's an example of a generated Shadowlisp file for activating ruby 2.7.1:
(provide "ruby" "2.7.1")
(when-let ((ruby-root (env/get "RUBY_ROOT")))
(env/remove-from-pathlist "PATH" (path-concat ruby-root "bin"))
(when-let ((gem-root (env/get "GEM_ROOT")))
(env/remove-from-pathlist "PATH" (path-concat gem-root "bin")))
(when-let ((gem-home (env/get "GEM_HOME")))
(env/remove-from-pathlist "PATH" (path-concat gem-home "bin"))))
(env/set "GEM_PATH" ())
(env/set "GEM_HOME" ())
(env/set "RUBYOPT" ())
(env/set "RUBY_ROOT" "/opt/rubies/2.7.1")
(env/prepend-to-pathlist "PATH" "/opt/rubies/2.7.1/bin")
(env/set "RUBY_ENGINE" "ruby")
(env/set "RUBY_VERSION" "2.7.1")
(env/set "GEM_ROOT" "/opt/rubies/2.7.1/lib/ruby/gems/2.7.0")
(when-let ((gem-root (env/get "GEM_ROOT")))
(env/prepend-to-pathlist "GEM_PATH" gem-root)
(env/prepend-to-pathlist "PATH" (path-concat gem-root "bin")))
(let ((gem-home
(path-concat (env/get "HOME") ".gem" (env/get "RUBY_ENGINE") (env/get "RUBY_VERSION"))))
(do
(env/set "GEM_HOME" gem-home)
(env/prepend-to-pathlist "GEM_PATH" gem-home)
(env/prepend-to-pathlist "PATH" (path-concat gem-home "bin"))))
.shadowenv.d
The .shadowenv.d
directory will generally exist at the root of your repository (in the same
directory as .git
.
We strongly recommend creating gitignore
'ing everything under .shadowenv.d
(echo '*' > .shadowenv.d/.gitignore
).
A .shadowenv.d
will contain any number of *.lisp
files. These are evaluated in the order in which
the OS returns when reading the directory: generally alphabetically. We strongly recommend using
a prefix like 090_something.lisp
to make it easy to maintain ordering.
.shadowenv.d
will also contain a .trust-<fingerprint>
file if it has been marked as trusted. (see
the trust section).
Language
See https://shopify.github.io/shadowenv/ for Shadowlisp documentation.
Integrations
Shadowenv has plugins for multiple editors and/or IDEs:
Trust
If you cd
into a directory containing .shadowenv.d/*.lisp
files, they will not be run and you
will see a message indicating so. This is for security reasons: we don't want to enable random
stuff downloaded from the internet to modify your PATH
, for example.
You can run shadowenv trust
to mark a directory as trusted.
Technically, running shadowenv trust
will create a file at .shadowenv.d/.trust-<fingerprint>
,
indicating that it's okay for shadowenv
to run this code. The .shadowenv.d/.trust-*
file contains
a cryptographic signature of the directory path. The key is generated the first time shadowenv
is
run, and the fingerprint is an identifier for the key.