いまさら direnv の解説

AWS とかを頻繁に弄るようになってきた今、[direnv](https://github.com/direnv/direnv) 無しでは生きられない体になってきました。

が、いままで漠然と使ってきていたので direnv について改めて調べました。

## インストール

下記から適当なバイナリをダウンロードし、パスの通ったところに保存して実行属性を付けます。

- https://github.com/direnv/direnv/releases

またはソースからビルドします。

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

あるいは `go get` でも大丈夫だと思います（ビルドできる環境があるなら）。

```sh
go get github.com/direnv/direnv
```

インストールできたら Bash のシェルのフックを設定します。

```sh
vim ~/.bashrc
```

次のように追記します。

```sh
# ~/.bashrc
eval "$(direnv hook bash)"
```

反映します。

```sh
exec bash --login
```

## 使ってみる

適当なディレクトリで `direnv edit .` するとエディタが起動します。

```sh
direnv edit .
```

以下のように環境変数のエクスポートを記述します。

```sh
export AWS_DEFAULT_PROFILE=oreore
export AWS_PROFILE=$AWS_DEFAULT_PROFILE
```

エディタを終了すると次のように表示されます。

```
direnv: loading .envrc
direnv: export +AWS_DEFAULT_PROFILE +AWS_PROFILE
```

env で環境変数を確認してみると、設定されていることがわかります。

```sh
env | grep ^AWS
#=> AWS_PROFILE=oreore
#=> AWS_DEFAULT_PROFILE=oreore
```

別のディレクトリに移動します。

```sh
cd ../other
```

すると次のように表示されます。

```sh
direnv: unloading
```

環境変数がクリアされています。

```sh
env | grep ^AWS
#=>
```

## direnv: error .envrc is blocked. Run `direnv allow` to approve its content.

`direnv edit` で編集されるファイルはカレントディレクトリの `.envrc` なので直接編集することも可能です。

```
vim .envrc
```

編集すると次の警告が表示されます。

```
direnv: error .envrc is blocked. Run `direnv allow` to approve its content.
```

安全のために、`.envrc` が変更された後は `direnv allow` するまで無効になっています。
例えば、誰かが `/tmp/.envrc` を `rm -fr /` みたいな内容で作成した状況を想像してみてください。

`direnv allow` すると有効になります。

```
direnv allow
```

`direnv edit` で編集した場合は自動的に `direnv allow` されるので、`direnv allow` する必要はありません。

`direnv allow` の情報は `~/.config/direnv/allow/` に保存されています。

```sh
ll ~/.config/direnv/allow/
#=> -rw-r--r--  1 ngyuki  staff  0 10 22 01:22 221e5718...
#=> -rw-r--r--  1 ngyuki  staff  0 10 22 01:15 6d84e3c8...
#=> -rw-r--r--  1 ngyuki  staff  0 10 22 01:23 dc9609ff...
```

sha256 っぽいファイルが沢山作成されています。ファイル名＋内容を sha256 したもののようです。

```
{ realpath .envrc; cat .envrc; } | sha256sum
```

詳細は以下のとおりです。

- [direnv/rc.go#L186](https://github.com/direnv/direnv/blob/9da7b29d02631f3588568ff03cb7fec3660e28b6/rc.go#L186)

## stdlib

`.envrc` ファイルの中ではいくつか便利なユーティリティ関数が使えます。特に使いそうなやつを説明します。

### expand_path

```
expand_path <rel_path> [relative_to]
```

- 指定された相対パスを絶対パスに変換して出力します
- 2番目の引数が基準となるディレクトリです
- 省略するとカレントディレクトリが基準になります

```sh
cd /path/to/ore
expand_path ../are
#=> /path/to/are
```

### find_up

```
find_up [filename]
```

- ディレクトリを遡って、直近のファイル名が `filename` であるファイルのパスを出力します
- ファイル名を省略すると `.envrc` が検索されます

```sh
cd /path/to/ore
touch dore
cd are/sore
pwd
#=> /path/to/ore/are/sore
find_up dore
#=> /path/to/ore/dore
```

### PATH_add

```
PATH_add <path>
```

- 環境変数 `PATH` に指定したパスを追加します
- 相対パスを指定した場合でも絶対パスに変換されてから追加されます

```sh
echo $PATH
#=> /usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin
cd /path/to/ore
PATH_add bin
echo $PATH
#=> /path/to/ore/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin
```

### path_add

```
path_add <varname> <path>
```

- 環境変数の名前を指定できる意外は `PATH_add` と同じです

```sh
export GOPATH=/path/to/golang
echo $GOPATH
#=> /path/to/golang
cd /path/to/ore
path_add GOPATH go
echo $GOPATH
#=> /path/to/ore/go:/path/to/golang
```

----

プログラミング言語に特有の設定とかもあったりします。詳細はマニュアルを参照してください。

- [direnv-stdlib(1)](http://direnv.net/#man/direnv-stdlib.1)

## .direnvrc

`~/.direnvrc` が存在する場合、`.envrc` のロード前にロードされます。

例えば、独自のユーティリティ関数を作ったりできます。

```sh
# ~/.direnvrc
aws_profile(){
    export AWS_PROFILE=$1
    export AWS_DEFAULT_PROFILE=$AWS_PROFILE
}
```

この関数は `.envrc` で使用することができます。

```sh
# .envrc
aws_profile oreore
```

また、stdlib で `use` という関数が定義されており、`use foo bar` の形式で呼び出すと `use_foo bar` という関数が呼ばれるようになっています。なので、`~/.direnvrc` で次のような関数を作成しておくと、

```sh
# ~/.direnvrc
use_aws(){
    export AWS_PROFILE=$1
    export AWS_DEFAULT_PROFILE=$AWS_PROFILE
}
```

`.envrc` で次のように使用することができます。

```sh
# .envrc
use aws oreore
```

## Loading layered .envrc

次のように複数の階層に `.envrc` が存在していた場合。

- `/a/.envrc`
- `/a/b/.envrc`

`/a/b/` に `cd` したときは `/a/b/.envrc` が有効になります。

`/a/b/.envrc` で `source_env` を使うと、上の階層の `.envrc` もロードすることができます。

```sh
# /a/b/.envrc
source_env ..
```

もしくは `source_up` を使えば、階層を遡って直近の `.envrc` が自動でロードされます。

```sh
# /a/b/.envrc
source_up
```

## dotenv とのコンパチ

次のように書いておくと、dotenv の `.env` を direnv の `.envrc` からロードすることができます。

```sh
# .envrc
dotenv .env
```

dotenv の `.env` は次のような形式です。

```sh
# .env
AWS_ACCESS_KEY_ID="ore-no-key"
AWS_SECRET_ACCESS_KEY="ore-no-secret"
```

これは RHEL の `/etc/sysconfig/` とかに置かれるファイルの形式（＝systemd の `EnvironmentFile`）と同じです。

例えば、次のようなケースで使えます。

- ローカルでなにかデーモンを開発している
- デーモンに必要な環境変数は `.env` に書いて `.envrc` からロード
- 開発にしか必要ない環境変数は `.envrc` にそのまま書く
- systemd の `EnvironmentFile` で `.env` を指定する

# direnv exec

exec サブコマンドを使えば、指定したディレクトリの `.envrc` が反映された状態でコマンドを実行できます。

```sh
direnv exec [DIR] COMMAND [...ARGS]
```

`DIR` を省略すると `COMMAND` で指定したファイルのあるディレクトリから `.envrc` がロードされます。

例えば、インタプリタとスクリプトを指定する場合は次のように呼び出します。

```sh
direnv exec /path/to/project php /path/to/project/hoge.php
```

スクリプトがそのまま実行可能なら次のように呼び出します。

```sh
direnv exec /path/to/project/vendor/bin/hoge
```

（そういう使い方はしたことありませんが）デーモンとかバッチとかで起動するときにはこのサブコマンドを使うと良いと思います。