Homebrew の pyenv/rbenv/goenv/nodenv/tfenv を anyenv に一本化する移行手順
TL;DR
Homebrew で個別にインストールした pyenv, rbenv, nodenv, goenv, tfenv を anyenv に統合する。シェル設定がスッキリし、brew upgrade による依存関係破壊(expat 問題など)も回避できる。
背景: なぜ anyenv に移行するのか
Homebrew の brew upgrade で Python が壊れた。原因は expat ライブラリのバージョン不整合:
ImportError: Symbol not found: _XML_SetAllocTrackerActivationThreshold
Referenced from: pyexpat.cpython-313-darwin.so
Expected in: /usr/lib/libexpat.1.dylibHomebrew は Python を新しい expat ヘッダでビルドするが、実行時はシステムの古い /usr/lib/libexpat.1.dylib をロードする。pyenv でソースビルドすれば expat を静的リンクするのでこの問題が起きない。
ついでにバラバラだった *env ツール群を anyenv で一元管理することにした。
移行前の状態
全て Homebrew でインストール。.zshenv と .zshrc に個別の PATH/init 設定が散らばっている:
# .zshenv(移行前)
export PATH="$HOME/.rbenv/bin:$PATH"
export PATH="$HOME/.nodenv/bin:$PATH"
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
export GOENV_ROOT="$HOME/.goenv"
export PATH="$GOENV_ROOT/bin:$PATH"
# .zshrc(移行前)
eval "$(rbenv init - --no-rehash)"
eval "$(nodenv init - --no-rehash)"
eval "$(pyenv init - --no-rehash zsh)"
eval "$(goenv init - --no-rehash)"手順
1. anyenv をインストール
git clone https://github.com/anyenv/anyenv ~/.anyenv
export PATH="$HOME/.anyenv/bin:$PATH"
anyenv install --force-init2. anyenv 経由で各 *env をインストール
anyenv install pyenv
anyenv install rbenv
anyenv install nodenv
anyenv install goenv
anyenv install tfenv各ツールは ~/.anyenv/envs/<name>/ 以下にクローンされる。
3. 既存の versions を移行
Homebrew 版が ~/.<name>/versions/ に保存していた言語バージョンを、anyenv 配下にコピーする:
# pyenv
mkdir -p ~/.anyenv/envs/pyenv/versions
cp -a ~/.pyenv/versions/* ~/.anyenv/envs/pyenv/versions/
cp ~/.pyenv/version ~/.anyenv/envs/pyenv/version
# rbenv
mkdir -p ~/.anyenv/envs/rbenv/versions
cp -a ~/.rbenv/versions/* ~/.anyenv/envs/rbenv/versions/
cp ~/.rbenv/version ~/.anyenv/envs/rbenv/version
# goenv
mkdir -p ~/.anyenv/envs/goenv/versions
cp -a ~/.goenv/versions/* ~/.anyenv/envs/goenv/versions/
cp ~/.goenv/version ~/.anyenv/envs/goenv/version
# nodenv
mkdir -p ~/.anyenv/envs/nodenv/versions
cp -a ~/.nodenv/versions/* ~/.anyenv/envs/nodenv/versions/
cp ~/.nodenv/version ~/.anyenv/envs/nodenv/versiontfenv は Homebrew Cellar に versions が格納されているので注意:
mkdir -p ~/.anyenv/envs/tfenv/versions
cp -a /opt/homebrew/Cellar/tfenv/*/versions/* ~/.anyenv/envs/tfenv/versions/
cp /opt/homebrew/Cellar/tfenv/*/version ~/.anyenv/envs/tfenv/version4. シェル設定を書き換え
.zshenv:
# anyenv(個別の *env PATH 設定を全て置き換え)
export ANYENV_ROOT="$HOME/.anyenv"
export PATH="$ANYENV_ROOT/bin:$PATH".zshrc:
# anyenv(個別の *env init を全て置き換え)
if command -v anyenv >/dev/null 2>&1; then
eval "$(anyenv init - --no-rehash)"
fi5. 旧パスへのシンボリックリンクを作成
これが重要。 Ruby や Python のコンパイル済みバイナリは、ビルド時の絶対パス(例: ~/.rbenv/versions/2.7.8/lib/libruby.2.7.dylib)を rpath としてハードコードしている。cp でファイルを移動しても rpath は書き換わらないので、旧パスから anyenv 配下へシンボリックリンクを張る必要がある:
# 旧ディレクトリをバックアップ
for d in .pyenv .rbenv .goenv .nodenv; do
mv ~/$d ~/${d}.bak
done
# 旧パス → anyenv 配下にシンボリックリンク
for name in pyenv rbenv goenv nodenv; do
ln -s ~/.anyenv/envs/$name ~/.$name
doneこれがないと以下のようなエラーになる:
dyld: Library not loaded: /Users/<user>/.rbenv/versions/2.7.8/lib/libruby.2.7.dylib
Referenced from: /Users/<user>/.anyenv/envs/rbenv/versions/2.7.8/bin/ruby
Reason: tried: '/Users/<user>/.rbenv/versions/2.7.8/lib/libruby.2.7.dylib' (no such file)6. Homebrew の *env パッケージを削除
brew uninstall pyenv rbenv nodenv goenv tfenv7. 動作確認
新しいターミナルを開いて:
anyenv versions
# → 全ツール・全バージョンが表示される
python --version # pyenv 経由
ruby --version # rbenv 経由
node --version # nodenv 経由
go version # goenv 経由
terraform version # tfenv 経由移行後の pyenv で Python 3.13 を追加
pyenv install 3.13.13
cd /path/to/project
pyenv local 3.13.13 # .python-version が生成される
rm -rf .venv
python -m venv .venv
.venv/bin/pip install -r requirements.txtpyenv ビルドの Python は expat を静的リンクするので、Homebrew 版で起きた pyexpat の Symbol not found エラーは発生しない:
$ otool -L ~/.anyenv/envs/pyenv/versions/3.13.13/lib/python3.13/lib-dynload/pyexpat.cpython-313-darwin.so
# /usr/lib/libexpat.1.dylib がリンク先に含まれない → 静的リンク後片付け
問題なく動作したら旧バックアップを削除:
rm -rf ~/.pyenv.bak ~/.rbenv.bak ~/.goenv.bak ~/.nodenv.bak注意: ~/.pyenv, ~/.rbenv 等のシンボリックリンクは削除しないこと。コンパイル済みバイナリの rpath がこのパスを参照しているため、消すと dylib のロードに失敗する。新規にインストールしたバージョン(anyenv 配下でビルドされたもの)は anyenv のパスで rpath が設定されるので、将来的に旧バージョンを全て入れ直せばシンボリックリンクは不要になる。