Ruby や Node.js などの開発ツールのバージョン管理は、rbenvnodenv などの *env 系ツールを経て、2019 年から asdf を使っていました。環境変数の管理は、さらに前の 2014 年から direnv を使っていました。

少し前から mise というツールが話題です。特に困っていたわけではありませんが、なんとなくの好奇心と、mise には direnv のような機能もあると聞いたので、ツールの数を減らすモチベーションもあって移行してみました。環境は macOS です。

mise とは

mise(ミーズ)は、開発ツールのバージョン管理と環境変数の管理を 1 つで行えるツールです。asdf 互換のランタイム管理に加えて、direnv 相当の環境変数管理機能も備えています。

使って感じた特徴は以下のとおりです。

  • .tool-versions を参照できる(.ruby-version 等は設定が必要。後述)
  • mise.toml で環境変数やタスクランナーも定義できる
  • mise.local.toml で .gitignore 前提のローカル設定を持てる。チーム開発で mise 導入前でも個人で先行導入できる
  • ツール管理の体験がよく整理されている
    • mise install 一発で .tool-versions のツールを全てインストールできる
    • mise list で全ツールのバージョンと参照元ファイルを一覧確認できる

移行で行ったこと

公式ドキュメントに asdf からの移行方法 があるので、まずはこちらを参照してください。以下は私の環境での具体的な手順です。

1. Homebrew パッケージの変更

asdf と direnv をアンインストールし、mise をインストールしました。

$ brew uninstall --force asdf
$ brew uninstall --force direnv
$ brew install mise

2. zsh の設定変更

~/.zshenv から asdf の設定を全て削除しました。

-MY_ASDF_CONFIG_HOME="${XDG_CONFIG_HOME}/asdf"
-export ASDF_CONFIG_FILE="${MY_ASDF_CONFIG_HOME}/asdfrc"
-export ASDF_DATA_DIR="${XDG_DATA_HOME}/asdf"
-export ASDF_GEM_DEFAULT_PACKAGES_FILE="${MY_ASDF_CONFIG_HOME}/default-gems"
-export ASDF_NPM_DEFAULT_PACKAGES_FILE="${MY_ASDF_CONFIG_HOME}/default-npm-packages"
-export ASDF_PERL_DEFAULT_PACKAGES_FILE="${MY_ASDF_CONFIG_HOME}/default-perl-modules"
-export ASDF_PYTHON_DEFAULT_PACKAGES_FILE="${MY_ASDF_CONFIG_HOME}/default-python-packages"
-export ASDF_RUBY_BUILD_VERSION=master
-PATH=$ASDF_DATA_DIR/shims:$PATH

~/.zshrc から direnv の設定も削除しました。

-eval "$(direnv hook zsh)"

代わりに ~/.zshrc に mise の設定を追加しました。この設定によって有効化される仕組みが PATH 管理を行うため、~/.zshenv への設定は不要です。

eval "$(mise activate zsh)"

実際のファイルは以下になります。

asdf 時代の環境変数がなくなり、zsh の設定がスッキリしました。

3. ツールのインストール

mise install で asdf が管理していたツールをインストールし直しました。

$ cat ~/.tool-versions
nodejs 24.14.0
ruby 4.0.2

$ mise install

$ mise list
Tool       Version   Source            Requested
node       24.14.0   ~/.tool-versions  24.14.0
ruby       4.0.2     ~/.tool-versions  4.0.2

4. default_packages_file の移行

asdf では ~/.zshenv の環境変数で管理していた default_packages_file を、mise では ~/.config/mise/config.toml に集約してみました。

[settings.node]
default_packages_file = "~/.config/mise/default-npm-packages"

[settings.ruby]
default_packages_file = "~/.config/mise/default-gems"

なお移行中に、Node.js の default_packages_file~ が展開されず default packages がインストールされないバグを見つけました。Discussion#8606 に報告したところ、PR #8709 で修正されていました 🙏 v2026.3.11 以降であれば問題ありません。

5. .ruby-version や .node-version の参照設定

asdf も mise も、デフォルトでは .ruby-version.node-version を参照しません。asdf では asdfrclegacy_version_file = yes を設定しますが、mise では以下の設定が必要です。リポジトリの mise.tomlmise.local.toml に設定しました。

[settings]
idiomatic_version_file_enable_tools = ["node", "ruby"]

移行しなかったもの

いくつかの設定は移行不要、または移行しない判断をしました。

  • Ruby の pre-install hook
    • asdf では pre_asdf_install_rubyRUBY_CONFIGURE_OPTS を設定していたが、mise では設定しなくても大丈夫そうだった
  • ASDF_RUBY_BUILD_VERSION=master
  • perl の default-perl-modules
    • mise は perl の default_packages_file を未サポートだった(対処方法は後述の付録参照)
  • direnvrc の PATH_add
    • Gemfile.lock がある Ruby プロジェクトで ./bin を PATH に自動追加する設定をしていた

    • mise にグローバルな同等機能はないが、必要になったときに各プロジェクトの mise.toml で以下の設定をする方針とした

      [env]
      _.path = "./bin"
      

まとめ

asdf + direnv の 2 ツールを mise 1 つに置き換えました。zsh の設定がスッキリしたのと、ツールバージョンと環境変数を mise.toml に集約できるのが良いところです。

後述の付録に書いた Emacs との相性問題などハマりどころはありましたが、総合的には移行してよかったと思います。

また、direnv が不要になったことで .envrc から .env に移行でき、後日 .env1Password Environments でマウントする構成にもつなげられました。

参考情報

付録

Emacs で ruby-lsp が動作しない問題への対応

グローバルと異なる Ruby バージョンのリポジトリで、ruby-lsp が Emacs 上で動作しない問題に遭遇しました。

mise activate zsh はフックベースのアプローチで、ディレクトリ移動時に zsh の chpwd フックが発火してツールのバージョンを切り替えます。しかし Emacs はこのフックを実行しないため、mise のバージョン切り替えが機能しません。

mise にはフックベースの activate とは別に、shims という方式があります。

activate shims
向いている環境 インタラクティブシェル 非インタラクティブ(IDE、Emacs、スクリプト)
cd などのフック 発火する 発火しない
which の結果 実際のツールのパスを返す shim のパスを返す

最初は ~/.zshrceval "$(mise activate zsh --shims)" を追加して両方を有効にしましたが、mise のドキュメント では activate と shims は排他的な使用を想定しています。

最終的には、以下の構成に落ち着きました。

  • eval "$(mise activate zsh)"~/.zshrc に配置(インタラクティブシェル用)
  • Emacs 起動時のみ shims ディレクトリを PATH に追加
alias emacs="LC_COLLATE=C PATH='${XDG_DATA_HOME}/mise/shims:$PATH' emacs"

通常の zsh セッションはフックベースのみ、Emacs 起動時だけ shims を使うという役割分担です。

asdf は shims のみのシンプルな設計だったので、一見すると複雑になったように見えます。しかしこれは mise が asdf の代替だけでなく、direnv の代替でもあることに起因するトレードオフだと理解しました。mise activate により環境変数の変更がリアルタイムでインタラクティブシェルに反映されるのは、shims では実現できない機能です。

perl の default-packages への対処方法

mise の default-packages の仕組みはコアプラグイン(Go, Node.js, Python, Ruby)にハードコードされた言語固有の機能です。Perl はコアプラグインではなく、Aqua バックエンド経由でインストールされるため対象外です。

回避策として mise.toml[hooks] postinstall で cpanm を実行する方法があります1。これにより mise install だけで Perl と CPAN モジュールの両方をセットアップできます。

[settings]
experimental = true

[hooks]
postinstall = "cpanm CGI HTML::Template"

注意点として、hooks は experimental 機能のため experimental = true が必要です。また postinstall は perl 以外のツールインストール時にも実行されるため、mise install のたびにこの hooks がトリガーされます。

mise と aqua との関係

aqua は CLI ツール(terraform、gh 等)のバージョン管理に特化したツールで、checksum 検証によるセキュリティが充実しています。mise とはカバー範囲が異なりますが、mise は aqua-registry をバックエンドとして利用できる(aqua: プレフィックス)ため、aqua が管理するツール群も mise からインストールできます。

イコールではないものの、個人の dotfiles 管理では mise だけで aqua 的な用途も十分カバーできると感じました。チームで aqua を採用していたり、CI/CD でサプライチェーン対策として checksum 検証が必要な場合は、aqua を別途使う価値があると思います。