HDE BLOG

コードもサーバも、雲の上

改めて、direnvを使いましょう!

HDE クラウドプロダクト開発部の小本です。

私のお気に入りツールdirenvを紹介します。 direnvとは・・・

  • シェルのcdをフックし、ディレクトリごとに環境変数を自動設定するツール
  • pyenvなどを置き換えることができるツール
  • Pythonistaも、Rubyistも、Gopherも、みんな使って損はないツール

direnv以前の世界(pyenvはawesomeだった)

Pythonには、プロジェクトそれぞれに専用の環境を作ってPythonのバージョンやライブラリを切り替えることができるツールがあります。

  • 専用環境を作れる virtualenv
  • 専用環境をコマンドで簡単に切り替えられる virtualenvwrappper

と進化し、その最終形態がpyenvです。

pyenvはあらかじめディレクトリに環境を紐付けておくと、ディレクトリにcdしたとき、勝手に環境を切り替えてくれます。

$ # ~/foo ディレクトリでは、python 3.4.1を使う
$ cd ~/foo
$ pyenv local 3.4.1

$ # 移動すると、自動でpythonが切り替わる
$ cd ~/foo
$ python --version
Python 3.4.1

これで「小1時間悩んだ挙句、別のPythonで実行していた事に気づき(ry」ということは無くなります。はっきり言って、革命的です、life-changingです。

乱立する ***env

我々が使う言語はPythonだけではありません。しかし、名前の通り pyenv はPython専用です。

rbenv phpenv nodenv jenv denv luaenvなどがあるのですが *1 、どれもやることはほとんど同じです。

もはや、***env を管理すること事態がコストです。

***envを一元的に管理するためのanyenvというツールもありますが、 エレガントとは言えません。

direnv = エレガントな回答

そこでdirenvです

pyenvが実際に行っているのは主に「環境変数PATHを変更し pythonコマンドの指す先を変える」ということです。

そこで、direnvはディレクトリごとに環境変数を切り替えることで、***env群の機能を1つのツールで提供できるのです。

direnvのインストール

direnvはGoで書かれていて、単一のバイナリファイルとして提供されています。

# Linuxの場合
curl https://github.com/zimbatm/direnv/releases/download/v2.6.0/direnv.linux-amd64 > ~/bin/direnv
chmod +x ~/bin/direnv

その後、フックを追加します。

echo 'eval "$(direnv hook bash)"' >> ~/.bashrc
echo 'eval "$(direnv hook zsh)"' >> ~/.zshrc
echo 'eval (direnv hook fish)' >> ~/.config/fish/config.fish
echo 'eval `direnv hook tcsh`' >> ~/.cshrc

基本的な使い方

direnvの基本的な動作は2つです。

  1. ディレクトリにcdしたとき、現在の環境変数を保存した上で、.envrcファイルを読み込む
  2. ディレクトリを離れるとき、以前の環境変数を元に戻す

ディレクトリ固有の環境変数を指定するのが、.envrcファイルです。

とりあえず.envrcを編集するeditだけ覚えておけば十分でしょう。

$ cd ~/my-project

$ direnv edit .  # ~/my-project/.envrc をエディタで開く

$ direnv edit # ~/.envrc が存在すれば . は省略可能

direnvは環境変数以外も扱える

実は.envrcには任意のシェルスクリプトを書くことが出来ます。

例えば以下のようにすれば、ディレクトリに入るたびにSLが走り抜けます。

# ~/foo-project/.envrc

sudo apt-get update -y && sudo apt-get install -y sl
sl

direnvは他にも追加のコマンドを用意しています。

  • layout program_name :その言語用の開発環境をセットアップする
  • PATH_add path: 環境変数PATHpathを追加
  • path_add envname path: 環境変数envnamepathを追加

詳しくはdirenv-stdlib.1.mdをご覧ください。

また、~/.direnvrcに自作コマンドを書くこともできます。 後述のuse pythonなどは~/.direnvrcへの追記が必須です。

direnvの使用例

テスト用環境変数のセット(汎用)

AWSのアクセスキーなどを.envrcで読み込むようにしておくと、 exportする手間が省ける上、 本番環境を誤って破壊したりするのも防げて便利です。

# ~/aws-project/.envrc

export AWS_ACCESS_KEY=XXXXXXXXXXXXXXXXX
export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY

export AWS_SECRET_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_KEY

bundle exec を省略する(Ruby向け)

layout rubybundle exec commandcommandで実行できるようになります(bundle execを省略できるようになります)。

# ~/ruby-project/.envrc

layout ruby # bundle execを省略可能にする。

pyenvの代わりにする(Python向け)

layout pythonで、virtualenvを使った専用環境を作れます。

# ~/python-project/.envrc

use python 2.7.9 # インタープリタのバージョンを指定
layout python    # 専用環境を作成

use python でバージョンを指定する機能もあるのですが、 指定バージョンのインタープリタを検索するコードは自分で~/.direnvrcに書かなくてはなりません。・

以下は pytohnzを使う場合のコードです。

# ~/.direnvrc
use_python() {
  local version=$1
  local found=0
  while read line; do
    name=$(echo $line | cut -d ' ' -f 1)
    path=$(echo $line | cut -d ' ' -f 2-)
    if [ "${name}" = "${version}" \
      -o "${name}" = "CPython-${version}" \
      -o "${name}" = "CPython-${version}.0" \
    ]; then
      PATH_add "${path}/bin"
      found=1
      break
    fi
  done < <(pythonz list -p | sed -e '1d')

  if [ "$found" -eq 0 ]; then
    echo "python '${version}' is not installed." >&2
    return 1
  fi
  return 0
}

パッケージのインストール先を分ける(外部パッケージを隔離する)(Go向け)

入門記事などでは~/.bashrc~/.zshrcの中で、

export GOPATH=~/gocode

などとすることが多いようです。

しかし、この方法だと~/gocodeの中に全プロジェクトのパッケージが混ざるので、

  1. 同じパッケージの異なるバージョンを同時にインストールできない
  2. 変なことをすると、全プロジェクトが巻き添えになる
  3. どれが不要なパッケージか分からなくなる

などの問題があります。

layout goで、カレントディレクトリをGOPATHにすることができます。

# ~/go-project/.envrc

layout go

さらに、path_add, PATH_addを使って、GOPATHに複数のディレクトリを追加します。 こうすると、

  • プロジェクト本体のソース → src/
  • 依存パッケージのソース → .direnv/go/src

と、分けて配置できるので、rm -rf .direnv/go/srcで依存パッケージを簡単におじゃんにできます。

unset GOPATH
layout go
path_add GOPATH .direnv/go
PATH_add .direnv/go/bin
PATH_add bin

追記:ファイルの使い分けなど

このへんで「~/.bashrc.envrcをどう使い分ければいいんだっけ?」と、 たまに混乱してくるので、表にまとめます:

~/.bashrc, ~/.profile等 プロジェクトに無関係な、システム全体で共通の環境変数
.envrc プロジェクト固有の設定
~/.direnvrc .envrcから呼び出すコマンド

システム全体で共通ではないが、 複数プロジェクト間で共有したい環境変数がある場合は、 適当なファイル(~/Document/direnv/common.shとか)を作り、 各プロジェクトの.envrcsource ~/Document/direnv/common.shすればいいでしょう。

また、.envrcはGitやMarcurial等のVCSの管理対象にはしない方がいいと思います。 使いたい環境変数やインタープリタのパスなどは、開発者ごとに違う可能性があるからです(そもそもdirenvを使いたくない・使えないかもしれないですし)。

まとめ

direnvはGoで書かれた、Python・Ruby・Goでの開発を効率化するツールです。

そしてHDEでは製品開発にPython・Ruby・Goを(も)使っています。

*1:ちなみにrbenvが***envの元祖らしいです