バージョン管理 anyenv -> asdf な話
記事を書くのは久しぶりです
okamosです
はじめに
Manage multiple runtime versions with a single CLI tool
冒頭から引用ですが asdf はバージョン管理のツールです
私は今まで anyenv を使っておりました
良さそうと思ってasdfに飛びついたわけですがそのときの苦痛と学びを共有して、もし使いたいという方がいましたら是非使ってみてくださいという記事です
asdfを使うことのメリットについては ここ に書いておりますが私は特に以下の点で優位性があるなと思います
- consistent commands to manage all your languages: コマンドは統一されています。rbenvとtfenvのサブコマンドの違いに悩まされることはなくなります
- single .tool-versions config file per project: 複数のランタイムを使用しているプロジェクトでも1つのファイルで管理可能です。他の人がanyenvを使っていても構いません対応する設定があります
- 各ランタイム・コマンドはasdf-pluginによって追加しますがanyenvに比べて数が豊富です。プラグイン一覧
Getting Started
curlとgitをインストールして
# macOSなどでhomebrewが入っていればOK
apt install curl git # ubuntu
asdfをダウンロードして
brew install asdf
# or
# git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.9.0 # 0.9.0はバージョンなのでお好きなものを選んでください
asdfをインストールします
echo -e "\n. $(brew --prefix asdf)/libexec/asdf.sh" >> ${ZDOTDIR:-~}/.zshrc # これは ZSH & Homebrew
Plugins
ドキュメント と プラグイン一覧(再掲)
好きなものをインストールしましょう
後述しますがdirenvは入れておいた方が良いです
こちらも後述しますが逆にGitなどよく使うコマンドはasdfの管理下に置かないほうが良いかもしれません
Nodeを入れたい
asdf plugin add nodejs
asdf install nodejs latest # 最新バージョンはlatestが使える
asdf global nodejs latest # 普段は最新バージョンを使います
手動でいっぱい入れるのが面倒ならこんなスクリプト(Bash)を実行するだけで大丈夫です
最初に述べた通りコマンドが一緒なのでなんのプラグインを入れるかは意識しなくて良いですね
#!/bin/bash
local -a plugins=(
'direnv'
# ... 中略 ...
'yarn'
)
for index in ${!plugins[*]}
do
local plugin=`echo ${plugins[$index]} | cut -d' ' -f 1`
asdf plugin add ${plugin}
if [ $? -eq 2 ]; then
continue
fi
asdf install ${plugin} latest
asdf global ${plugin} latest
done
echo "$(tput setaf 2)Install asdf plugins complete. ✔︎$(tput sgr0)"
Migration
実は .node-version
や .ruby-version
といったファイルに対応することもできます
以下の設定を入れるだけで有効化されます
legacy_version_file = yes
ところでtfenvではこのような設定が可能です
latest:^0.15
asdfもしっかり認識できています
okamoto_shinichi[test] asdf install terraform 'latest:^0.15'
terraform 0.15.5 is already installed
かと思ったら実行しようとするとそんなのないよと言われるので
okamoto_shinichi[test] terraform --version
No preset version installed for command terraform
Please install a version by running one of the following:
asdf install terraform latest:^0.15
今のところ私は tool-versions
を使い明示しています
terraform 0.15.5
Faster than anyenv?
実はZSHの初期処理の際にanyenvが0.5秒ほどかかっているのが気になっておりました
私は作業時にtmuxのペインをよく切るのでその度にZSHが立ち上がっておりました
どのくらい差があるかって0.6秒くらい違いました(のちに判明しますがanyenvが特別に遅いとかそういう理由ではないようです。この辺の早さのハックはかなり難しいのだと思います)
# anyenv
okamoto_shinichi[~] time zsh -i -c exit
num calls time self name
-----------------------------------------------------------------------------------
1) 1 1.01 1.01 63.04% 1.01 1.01 63.04% colors
2) 1 0.44 0.44 27.21% 0.44 0.44 27.21% bashcompinit
3) 1 0.16 0.16 9.75% 0.16 0.16 9.75% has
-----------------------------------------------------------------------------------
zsh -i -c exit 0.41s user 0.56s system 104% cpu 0.929 total
okamoto_shinichi[~] cat ~/.zsh/.zshrc | grep anyenv
export PATH="$HOME/.anyenv/bin:$PATH"
eval "$(anyenv init -)"
# asdf
okamoto_shinichi[~] time zsh -i -c exit
num calls time self name
-----------------------------------------------------------------------------------
1) 1 0.85 0.85 61.97% 0.85 0.85 61.97% colors
2) 1 0.39 0.39 28.39% 0.39 0.39 28.39% bashcompinit
3) 1 0.13 0.13 9.64% 0.13 0.13 9.64% has
-----------------------------------------------------------------------------------
zsh -i -c exit 0.14s user 0.25s system 136% cpu 0.289 total
okamoto_shinichi[~] cat ~/.zsh/.zshrc | grep asdf
. /usr/local/opt/asdf/libexec/asdf.sh
eval "$(asdf exec direnv hook bash)"
1日程度使ってみて思ったわけです。ZSHの初期化は早くなっているんだけどなんかモリモリ使うtig(gitコマンドを内部で使っていそう)とかディレクトリ移動 cd $(ghq list -p | fzf)
とかが異常に遅いなと...
すぐに問題は判明しました
# with asdf
okamoto_shinichi[~] time git --version
git version 2.34.1
git --version 0.13s user 0.25s system 144% cpu 0.262 total
# without asdf
okamoto_shinichi[~] time git --version
git version 2.34.1
git --version 0.00s user 0.00s system 34% cpu 0.014 total
20倍くらい遅いじゃないか...
okamoto_shinichi[~] which git
/Users/okamoto_shinichi/.asdf/shims/git
okamoto_shinichi[~] time /Users/okamoto_shinichi/.asdf/shims/git --version
git version 2.34.1
/Users/okamoto_shinichi/.asdf/shims/git --version 0.12s user 0.23s system 142% cpu 0.250 total
okamoto_shinichi[~] asdf which git
/Users/okamoto_shinichi/.asdf/installs/git/2.34.1/bin/git
okamoto_shinichi[~] time /Users/okamoto_shinichi/.asdf/installs/git/2.34.1/bin/git --version
git version 2.34.1
/Users/okamoto_shinichi/.asdf/installs/git/2.34.1/bin/git --version 0.00s user 0.00s system 67% cpu 0.005 total
なるほど?
これがGitなどの多用するコマンドは管理に入れないほうが良いかもと言っていた理由となります
この問題はイシュー #290 にて議論されており執筆時(2021/12/12)asdfのコアパッケージで解決されておりません
これを解決するには下記のようにdirenvでasdfの設定を読み込む設定を入れます
# Hook direnv into your shell.
eval "$(asdf exec direnv hook bash)"
あるいはdirenvのグローバル設定に
source "$(asdf direnv hook asdf)"
と入れておきプロジェクトごとに
python 3.9.9
terraform 1.1.0
use asdf
のように設定を入れておきます。全てのasdf管理下のバージョンを解決するのでdirenv allowすると若干時間がかかりますが変更しない限りはキャッシュを使ってくれるので高速です
結局初期処理には時間はかかりませんがプロジェクトのディレクトリに行くごとに設定を読み込むのでキャッシュを使っている時でも差はほとんどないかasdfの方が管理するプラグインが多いので遅いくらいになりました
結局のところ普段打つコマンドの反応が早いことの方が大事だと気付かされました
冒頭に述べましたが早さ以外にもメリットは大きく感じますので私はasdfを使っていこうと思います
Ansible + Python
Ansibleで利用するPythonはvirtualenvで入ります
Pythonはどこ?ここ?
環境をactivateしてから必要なパッケージをインストールしましょう
(venv) okamoto_shinichi[ansible] ansible --version
ansible 2.10.16
config file = /Users/okamoto_shinichi/dev/github.com/samurai-engineer-juku/playbooks/ansible/ansible.cfg
configured module search path = ['/Users/okamoto_shinichi/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /Users/okamoto_shinichi/.asdf/installs/ansible-base/2.10.16/venv/lib/python3.9/site-packages/ansible
executable location = /Users/okamoto_shinichi/.asdf/installs/ansible-base/2.10.16/venv/bin/ansible
python version = 3.9.9 (main, Nov 21 2021, 03:23:42) [Clang 13.0.0 (clang-1300.0.29.3)]
use asdf
source ~/.asdf/installs/ansible-base/2.10.16/venv/bin/activate # venv/bin下にいる。これはZSHの場合で読み込むものはシェルにより違うので注意してください
okamoto_shinichi[ansible] pip --version
pip 21.3.1 from /Users/okamoto_shinichi/.asdf/installs/ansible-base/2.10.16/venv/lib/python3.9/site-packages/pip (python 3.9)
okamoto_shinichi[~] pip freeze | grep boto
okamoto_shinichi[ansible] ansible-playbook playbooks/wordpress.yml --list-hosts
[WARNING]: * Failed to parse /Users/okamoto_shinichi/dev/github.com/samurai-engineer-juku/playbooks/ansible/inventories/aws_ec2.yml with auto plugin: The ec2 dynamic inventory plugin requires boto3 and botocore.
[WARNING]: * Failed to parse /Users/okamoto_shinichi/dev/github.com/samurai-engineer-juku/playbooks/ansible/inventories/aws_ec2.yml with yaml plugin: Plugin configuration YAML file, not YAML inventory
# ... 省略 ...
Note that the implicit localhost does not match 'all'
playbook: playbooks/wordpress.yml
play #1 (all): WordPress server TAGS: []
pattern: ['all']
hosts (0):
okamoto_shinichi[ansible] pip install boto3 botocore
Collecting boto3
Using cached boto3-1.20.23-py3-none-any.whl (131 kB)
Collecting botocore
Using cached botocore-1.23.23-py3-none-any.whl (8.4 MB)
# ... 省略 ...
Successfully installed boto3-1.20.23 botocore-1.23.23
okamoto_shinichi[ansible] ansible-playbook playbooks/wordpress.yml -l slack --list-hosts
playbook: playbooks/wordpress.yml
play #1 (all): WordPress server TAGS: []
pattern: ['all']
hosts (1):
slack
okamoto_shinichi[ansible] cd ~
direnv: unloading
okamoto_shinichi[~] pip --version
pip 21.2.3 from /Users/okamoto_shinichi/.asdf/installs/python/3.10.0/lib/python3.10/site-packages/pip (python 3.10) # direnv設定外の箇所へ抜けるとグローバルに戻る
Breaking shims
asdfのアップグレードをした際に以下のようなメッセージが出るようになりました
okamoto_shinichi[~] nvim
/Users/okamoto_shinichi/.asdf/shims/nvim: line 4: /opt/homebrew/Cellar/asdf/0.9.0/libexec/bin/asdf: No such file or directory
/Users/okamoto_shinichi/.asdf/shims/nvim: line 4: exec: /opt/homebrew/Cellar/asdf/0.9.0/libexec/bin/asdf: cannot execute: No such file or directory
asdf reshimで直るかと思いましたが直らず以下のissueのコメントよりshimsのディレクトリを削除後にreshimすることで回復できました
rm -rf ~/.asdf/shims; asdf reshim
ちなみにshimsにあるファイルは以下のようにasdf経由で各種コマンドを実行するようになっています
#!/usr/bin/env bash
# asdf-plugin: ghq 1.3.0
exec /opt/homebrew/opt/asdf/libexec/bin/asdf exec "ghq" "$@" # asdf_allow: ' asdf '
現在は /opt/homebrew/opt/asdf/libexec
配下にhomebrewでlinkしたバージョンのファイル群が配置されるようになっており昔のようにasdfのバージョンごとに切られたディレクトリ配下ではなくなっているので今後は同じ問題は発生しないかと思います
おわりに
いかがでしたでしょうか
私がasdfを1週間程度使ってみてハマったところなりを自分なりに整理してみました
anyenvはアンインストール済みだしGitなどはasdf管理下に入れない方が良いかもといいつつ私は管理下に置いています。どんどん問題にハマっていって解決しようと思います
私の管理しているdotfilesの commit があるのでasdf以外の変更点も混ぜ込んでいて綺麗なものではないですが参考にしてみてください
Discussion