多言語対応バージョンマネージャー mise 入門
miseでプロジェクトごとのNodeとPythonのバージョンを管理する方法を解説。既存のバージョンファイル、ツールの固定、環境変数、ローカルとCIでのビルドも紹介します。
mise は、Node、Python、Ruby などのランタイムのバージョンをディレクトリ単位でインストール・切り替えできる単一のコマンドラインツールです。バージョンはプロジェクトルートにある mise.toml ファイルから読み込まれます。
リポジトリに Vite のフロントエンドと、ビルド前にコンテンツを生成する Python スクリプトが同居している場合、おそらく nvm と pyenv を併用し、チームメンバー全員のシェルが CI と同じ状態になっていることを祈っているのではないでしょうか。たいてい、どこかで食い違いが起きます。
mise、nvm、pyenv、rustup、SDKMAN! の比較については、5 Version Managers Every Developer Should Know を参照してください。本記事では機能の一覧紹介は省略します。代わりに 1 つのリポジトリを題材に、cd 時の挙動、既存の .nvmrc ファイル、チームメンバーによる初回クローン、CI、2 つのランタイムにまたがるビルドタスク、環境変数の扱いを順に見ていきます。
重要なポイント
- シェルのアクティベーションを有効にすると、
mise.tomlを含むディレクトリに入った時点でそのプロジェクトで固定されたランタイムが PATH に追加され、ディレクトリを出ると以前のバージョンに戻ります。 - mise は、
idiomatic_version_file_enable_tools設定でツールごとに有効化しない限り、.nvmrcや.python-versionを読み込みません。 mise runはプロジェクトのツールと環境変数を自ら読み込み、デフォルトで未インストールのツールもインストールするため、シェルのアクティベーションなしで CI でも動作します。jdx/mise-action@v4は mise とmise.tomlに記載されたツールをインストールします。mise.lockがコミットされている場合は--locked付きでインストールします。[env]テーブルはコミットされるプレーンテキストです。設定値のためのものであり、シークレット用ではありません。
題材のプロジェクト:Python のビルドステップを持つ Vite フロントエンド
サンプルリポジトリ docs-preview は、2 つのランタイム、1 つの設定ファイル、1 つのワークフローで構成されています。
docs-preview/
├── content/*.md
├── scripts/build_content.py
├── src/
├── package.json # vite in devDependencies
├── mise.toml
└── .github/workflows/build.yml
Python スクリプトは標準ライブラリのみを使用し、Markdown ファイルをフロントエンドがインポートする JSON に変換します。
# scripts/build_content.py
import json, os, pathlib
src = pathlib.Path(os.environ.get("CONTENT_DIR", "content"))
out = pathlib.Path("src/generated/content.json")
out.parent.mkdir(parents=True, exist_ok=True)
items = [{"slug": p.stem, "body": p.read_text(encoding="utf-8")}
for p in sorted(src.glob("*.md"))]
out.write_text(json.dumps(items, indent=2), encoding="utf-8")
print(f"wrote {len(items)} items to {out}")
以下が mise.toml の全体です。以降のセクションでもこのファイルを参照します。
[tools]
node = "24"
python = "3.14"
[settings]
lockfile = true
[env]
CONTENT_DIR = "content"
VITE_SITE_NAME = "Docs Preview"
[tasks.install]
description = "Install JS dependencies"
run = "npm ci"
[tasks."build:content"]
description = "Convert content files to JSON for the frontend"
run = "python scripts/build_content.py"
[tasks.build]
description = "Generate content, then build the Vite app"
depends = ["install", "build:content"]
run = "npx vite build"
[tools] テーブルではバージョンのプレフィックス(Node 24 LTS、Python 3.14)を指定しています。後述するロックファイルには、それらが解決された正確なパッチバージョンが記録されます。
cd でディレクトリを出入りしたときの挙動
mise をアクティベートした状態で mise.toml を含むディレクトリに入ると、そのプロジェクトで固定されたランタイムが PATH に追加されます。ディレクトリを出ると、それ以前に有効だったバージョンに戻ります。シェルの起動ファイルに mise activate を記述しておけば、ディレクトリを移動するたびに mise がツールと環境変数を切り替えます。
cd ~
node --version # whatever your system or nvm provides
cd ~/code/docs-preview
node --version # v24.x from mise.toml
python3 --version # Python 3.14.x
echo "$CONTENT_DIR" # content
mise がバージョンを切り替えるタイミングはシェルによって異なります。shims のドキュメントによると、bash、zsh、fish、xonsh では cd を実行した直後に切り替わります。その他のシェルでは、次のプロンプトが表示されるまで切り替わりません。
固定したバージョンがまだインストールされていない場合
新しいマシンでは、プロジェクトディレクトリで mise install を実行してください。これが明示的かつ確実な解決策です。タスク経由でも自動的に対応できます。mise のタスクはプロジェクトのツールと環境変数を適用した状態で実行され、デフォルトで有効になっている auto_install 設定により、mise run を呼び出した際に未インストールの設定済みツールがインストールされます。shims に依存している場合、Python には注意が必要です。設定リファレンスによると、mise 管理のバージョンを解決できない shim(たとえば自動インストールが無効になっている場合)は、PATH 上の別の場所にある同名の最初のプログラムにフォールバックします。python3 のように OS にも同梱されているツールの場合、固定したバージョンが一度も実行されず、しかもエラーも出ないという事態になります。
特定のターミナルでのみ別のバージョンを固定するには、mise shell node@<version> を使用します。これには mise がアクティベート済みのシェルが必要です。
既存の .nvmrc と .python-version ファイル
mise は、idiomatic_version_file_enable_tools でツールごとに有効化しない限り、.nvmrc や .python-version を読み込みません。mise の FAQ でも、これらの idiomatic version files(各言語の慣習的なバージョンファイル)がデフォルトで無効であることが明記されています。このデフォルト設定は mise 2025.10.0 で導入されました。有効化するには次のように設定します。
[settings]
idiomatic_version_file_enable_tools = ["node", "python"]
設定ドキュメントでは、mise が node 用に読み込むファイルとして package.json も挙げられているため、node を有効にするとこのファイルも読み込まれる可能性があります。特定のファイルだけを除外するには、idiomatic_version_file_disable_files 設定に node:package.json のような tool:filename の組み合わせを追加します。
mise.toml で両方のランタイムを固定したら、.nvmrc はまだ nvm を使っているチームメンバーのためだけに残し、2 つのファイルの内容が一致するようにしてください。nvm がこのファイルをどう扱うかを確認したい場合は、macOS、Linux、Windows それぞれでの nvm による Node バージョン切り替えガイドを参照してください。
チームメンバーと CI
ローカルマシンと CI ランナーは同じ mise.toml を読み込みますが、ツールを PATH に追加する方法は異なります。
| ローカルマシン | GitHub Actions | |
|---|---|---|
| ツールを PATH に追加する仕組み | シェルのアクティベーションフック | jdx/mise-action が mise と固定されたツールをインストール |
| バージョンの参照元 | mise.toml + mise.lock | 同じファイル |
| ビルドコマンド | mise run build | mise run build |
チームメンバーによる初回クローン:
- mise をインストールします:
curl https://mise.run | sh - シェルの rc ファイルに
eval "$(~/.local/bin/mise activate zsh)"(または使用しているシェルに対応する行)を追加します。 mise.tomlの内容を確認してから、mise installを実行します。mise run buildを実行します。
設定ファイルはコードを実行できるため、mise の信頼モデルには注意が必要です。入門ガイドによると、CI 環境および paranoid モード以外では、mise install、mise exec、mise run などプロジェクトに対して作用するコマンドを実行すると、有効な設定が自動的に信頼されます。そのため、これらのコマンドを実行する前に mise.toml の内容を確認し、設定を明示的に信頼したい場合は mise trust を使用してください。
正確なバージョンをロックする。 ロックファイルである mise.lock は、mise lock コマンド、または前述の lockfile = true 設定によって作成されます。このファイルはコミットしてください。mise install --locked を使用すると、ロックエントリが存在しない場合にエラーとなるため、mise が新しいバージョンを暗黙的に選択することはありません。
CI ワークフロー:
# .github/workflows/build.yml
name: build
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: jdx/mise-action@v4
- run: mise run build
mise-action のステップは mise と mise.toml に記載されたツールをインストールするため、ワークフローでは開発者がローカルで使っているものと同じバージョンが使用されます。デフォルトでは mise install を実行し(install: true)、ツールをキャッシュします(cache: true)。mise.lock が存在する場合は --locked 付きでインストールします。また、${{ github.token }} も自動的に渡されます。ローカルマシンでインストール時にレート制限に引っかかる場合は、GitHub トークンのページに記載されているとおり、GITHUB_TOKEN または MISE_GITHUB_TOKEN を設定すると解消できます。
問題が発生したときは、次の 3 つのコマンドで原因を絞り込めます。
| 症状 | コマンド |
|---|---|
| アクティベーションやインストールの様子がおかしい | mise doctor |
| 意図しない設定ファイルが適用されているようだ | mise config ls |
| 意図しないバージョンが選択されている | mise ls --current |
実践的なタスク:2 つのランタイムにまたがる mise run build
mise.toml で定義したタスクは、プロジェクトで固定されたツールと [env] の変数が適用された状態で実行されます。つまり mise run build は、シェルのアクティベーションがなくても、ローカルマシンと CI で同じ Node と Python を使用します。
depends キーを指定すると、build はリストされたすべてのタスクが完了するまで待機します。そのため、Vite が src/generated/content.json を読み込む前に、Python のステップがこのファイルを書き出し終えています。リストされたタスク同士は並行して実行される可能性があります。タスクのドキュメントには、前提タスクは並列実行される場合があり、depends 内での記述順序は実行順序を保証しないと明記されています。この例で問題がないのは、npm ci が node_modules に、Python スクリプトが src/generated に書き込むため、どちらのステップも互いの出力に干渉しないからです。将来、一方のステップがもう一方の出力を利用するようになった場合は、そのステップに直接依存させてください。
mise tasks # lists install, build:content, build
mise run build # npm ci + Python step, then vite build
タスク実行ガイドによると、並列ジョブ数のデフォルト上限は 8 です。
mise によるプロジェクト単位の環境変数
[env] テーブルの値は、コミットされるファイル内のプレーンテキストです。mise の env は設定管理の仕組みであり、シークレット管理の仕組みではありません。この例では、Python スクリプトが os.environ 経由で CONTENT_DIR を読み込み、フロントエンドが import.meta.env.VITE_SITE_NAME を読み込みます。
VITE_ プレフィックスは、[env] の値がプレーンテキストであることのリスクを具体的に示しています。Vite の環境変数ドキュメントによると、VITE_ で始まる変数はすべてクライアントバンドルに含まれ、その値はビルド時にコードへ埋め込まれます。mise.toml 内の VITE_ で始まる値は、サイトを公開した時点で誰でも参照できる状態になります。
ビルド時に必要な変数が欠けていても、ビルドは失敗しません。誤った URL を呼び出すバンドルが生成されるだけです。セッションリプレイを使えば、この問題をユーザー側の視点から確認できます。リクエストが undefined のベース URL に送られている様子を観察し、ビルドが実行された環境まで原因をさかのぼることができます。
mise を導入する価値があるのはどんなときか
プロジェクトで Node しか使わないのであれば、nvm と .nvmrc で十分に役割を果たせるため、mise の導入は任意です。その構成については nvm のガイドで詳しく解説しています。mise の導入に価値があるのは、1 つのリポジトリで 2 つ以上のランタイムを固定し、なおかつ CI をすべての開発者のローカル環境と一致させる必要がある場合です。既存のリポジトリで試すには、[tools] でバージョンを固定した mise.toml を追加し、mise lock を実行して両方のファイルをコミットし、CI のビルドステップを mise run build に変更してください。
よくある質問
mise.toml をコミットする場合、シークレットはどこに保管すべきですか?
シークレットは gitignore 対象の mise.local.toml に保管するか、mise.toml で required = true を付けて宣言してください。後者の場合、mise は値を保存することなく、変数が設定されているかどうかをチェックします。redact = true を追加すると、キャプチャされたタスク出力内の値がマスクされますが、mise env では引き続きプレーンテキストで表示されます。どうしてもコミットする必要があるシークレットについては、mise は age で暗号化された SOPS ファイルを復号し、mise exec、タスク、シェルのアクティベーションで利用できます(実験的機能)。
mise は asdf の .tool-versions ファイルを読み込みますか?
はい。.nvmrc とは異なり、mise は追加設定なしで .tool-versions を読み込みます。Unix 環境では asdf のシェルプラグインも実行できますが、すべての asdf コマンドやプラグインの動作が保証されているわけではありません。同じディレクトリにある mise.toml と .tool-versions で同じツールが宣言されている場合は、mise.toml が優先されます。mise のドキュメントでは asdf と mise でインストールディレクトリを共有しないよう推奨されているため、移行後は mise install を実行してください。
エディタやスクリプトで、ターミナルとは異なる Node バージョンが使われるのはなぜですか?
エディタ、git フック、スケジューラは対話型シェルの rc ファイルを読み込まないことが多く、その場合 mise のアクティベーションフックが実行されません。これを解決するには、~/.zprofile などエディタが読み込むログインプロファイルに mise activate zsh --shims(または使用しているシェルに対応する行)を追加し、~/.zshrc には通常の mise activate を残しておきます。スクリプトの場合は、代わりに mise exec -- command または mise run を使用してください。なお、shim モードではほとんどのフックなど、一部のアクティベーション機能が利用できません。