Shane Xu's Home

Life is too short for so much sorrow.

direnv

2014年的苹果WWDC上有个口号:

Write the Code. Change the World.

然而我觉得 direnv 的作者则是:

Write the Code. Save the World.

这几年杂七杂八的东西学的太多了,几乎每一门语言都有自身版本和三方库版本的问题,比如java里面有maven这样的构建工具,来控制三方库的版本。更有甚者,如ruby的rvm,node的nvm,Python的virtualenv,go的gvm,groovy的GVM,haskell的stack,haskell的cabal。这些工具基本都肩负着三个使命,一是控制语言的版本,二是控制三方库的set,三是设置环境变量。于是每次到某个工程下面,就要执行 rvm use xxxnvm use xxx ,这些命令来切换环境。然后偶尔一次忘记,就会各种神奇的错误。

然而这几天,正好在测试同一个项目,用不同版本的语言编译的效果,结果在不同的环境切换下,竟然切晕了。虽然用 tmux 的分屏,多窗口等方法,勉强能凑活,但是还是偶尔几次忘记自己当前的环境。所以我想有没有能够,在不同目录下自动切换环境变量的工具。于是用google搜索 directory environment ,还真被我找到了。

安装

git clone https://github.com/direnv/direnv
cd direnv
make install

如果是以下系统的话,还可以用这些系统的包管理工具来安装,着实方便啊。

There’s package definitions on Homebrew, Arch’s AUR, Gentoo go-overlay overlay and NixOS’s nixpkgs.

设置

ZSH

~/.zshrc 中添加:

eval "$(direnv hook zsh)"

BASH

~/.bashrc 中添加:

eval "$(direnv hook bash)"

FISH

~/.config/fish/config.fish 中添加:

eval (direnv hook fish)

TCSH

~/.cshrc 中添加:

eval `direnv hook tcsh`

使用

说到用法的话,差不多只要拿出首页的例子就可以了。

$ cd ~/my_project
$ echo ${FOO-nope}
nope
$ echo export FOO=foo > .envrc
.envrc is not allowed
$ direnv allow .
direnv: reloading
direnv: loading .envrc
direnv export: +FOO
$ echo ${FOO-nope}
foo
$ cd ..
direnv: unloading
direnv export: ~PATH
$ echo ${FOO-nope}
nope

在想要自动切换环境的目录下面新建一个 .envrc 文件,文件的内容就是设置环境的命令。但是要主要的是 .envrc 只支持 bash 语法,因为这个文件最终也是在bash中执行,然后再设置到对应的shell中的。另外值得一提的是 ~/.direnvrc 中可以写入全局配置,比如加一些函数进去。direnv还提供了一个 stdlib ,通过 direnv stdlib 可以查看。

实战

比如我现在有个工程要用Python环境,于是只要在工程的根目录下面新建 .envrc 文件并 写入如下内容:

layout python

然后在首次进入这个目录的时候,就会在 .direnv 这个目录下建立,整套Python的virtualenv。然而PyCharm似乎不是很满意。PyCharm能够自动识别工程根目录下的 venv 目录。但是现在用direnv之后virtualenv的目录改变了。解决方案有很多。比如不用direnv的 layout python ,用virtualenv生成的active脚本,在 .envrc 中添加内容:

source venv/bin/active

或者修改 direnv stdlib 中的 python_layout 方法:

 1: layout_python() {
 2:   local python=${1:-python}
 3:   local old_env=$PWD/virtualenv
 4:   unset PYTHONHOME
 5:   if [[ -d $old_env && $python = python ]]; then
 6:     export VIRTUAL_ENV=$old_env
 7:   else
 8:     local python_version
 9:     python_version=$("$python" -c "import platform as p;print(p.python_version())")
10:     if [[ -z $python_version ]]; then
11:       log_error "Could not find python's version"
12:       return 1
13:     fi
14: 
15:     export VIRTUAL_ENV=$PWD/venv
16:     if [[ ! -d $VIRTUAL_ENV ]]; then
17:       virtualenv "--python=$python" "$VIRTUAL_ENV"
18:     fi
19:   fi
20:   PATH_add "$VIRTUAL_ENV/bin"
21: }

Comments

comments powered by Disqus