0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

mise の `activate` を理解する 〜フォルダを移動するだけで Node のバージョンが切り替わる仕組み〜

0
Posted at

この記事について

mise(旧 rtx)を使い始めたとき、(&mise activate pwsh) | Out-String | Invoke-Expression という呪文のような一行を見て「これは一体何をしているのか」と戸惑う人は多いと思います。

この記事では、その mise activate が何をしているのか、なぜ「有効化」という手続きが必要なのか、そして PowerShell / cmd での使い分けまでを、順を追って整理します。特に、mise activate の一番の便利機能である「フォルダを移動するだけで、そのフォルダに合ったバージョンに自動で切り替わる」という点にフォーカスして解説します。

対象環境は Windows(PowerShell / cmd)ですが、考え方は macOS / Linux でも同じです。


mise activate の一番おいしい機能:フォルダ切替で自動バージョン切り替え

まず結論から。mise activate を有効化しておくと、次のことが自動で起きます。

C:\projectA\   ← このフォルダに入ると node は 18
C:\projectB\   ← このフォルダに入ると node は 22

cd C:\projectA した瞬間に node が 18 に、cd C:\projectB した瞬間に 22 に、あなたが何もコマンドを打たなくても自動で切り替わります。各プロジェクトに置いた mise.toml(または .tool-versions)に書かれたバージョンを mise が読み取り、そのフォルダにいる間だけそのバージョンを使えるようにしてくれるのです。

これが mise activate の最大の価値です。プロジェクトごとに nvm use 18 などと手で打つ必要がなくなります。


そもそも「有効化(activate)」とは何なのか

gitcurl のような普通のツールに「有効化」という概念はありません。呼び出せば動く、それだけです。ではなぜ mise には「有効化」が必要なのでしょうか。

答えは、mise が「呼ばれたときだけ働くツール」であると同時に、「あなたが何もしていないときにも裏で働き続けてほしいツール」でもあるからです。後者、つまり「フォルダ移動に反応して自動でバージョンを切り替える」機能のために、有効化が必要になります。

鍵は「子プロセスは親プロセスの環境変数を書き換えられない」という制約

バージョンの切り替えは、実体としては環境変数 PATH の書き換えです。「今いるフォルダに応じて PATH を書き換える」ことで、node と打ったときに呼ばれる実体が変わります。

ところがここに OS レベルの壁があります。子プロセスは、親プロセス(=あなたのシェル)の環境変数を書き換えられません。

mise.exe は、あなたが実行すると「子プロセス」として起動し、仕事を終えると消えます。その子プロセスがいくら「PATH をこう変えたい」と思っても、親である PowerShell の PATH に手を出すことはできません。つまり、mise.exe を普通に呼び出すだけでは、一番やりたい「自動バージョン切り替え」が原理的にできないのです。

解決策:シェル自身に書き換えさせる

そこで mise は発想を変えます。「自分(mise.exe)が環境変数を書き換えられないなら、シェル自身に書き換えさせればいい」と。

具体的には、次の 2 段階で動きます。

  1. 親シェルが特定のタイミング(cd した時やプロンプト表示時)で mise.exe を呼ぶ
  2. mise.exe は「$env:PATH をこう書き換えろ」という指示(シェルのコード)を文字列として出力するだけ
  3. その指示を、親シェル自身が受け取って実行し、自分の環境変数を書き換える

ポイントは、実際に PATH を書き換えているのが mise.exe(子プロセス)ではなく、親シェル自身だという点です。だから親の環境変数を変えられます。mise.exe は「どう変えるべきか」の指示書を渡すだけ、実際に手を動かすのはシェル、という分業です。

この「cd やプロンプト表示のたびに自動で mise を呼び、返ってきた指示を実行する仕掛けをシェルに埋め込む作業」こそが 有効化(activate) です。

たとえるなら
普通のツールは「呼べば来てくれる出張業者」。用があるときだけ呼ぶ。
一方 mise の有効化は「業者をオフィスに常駐スタッフとして配置する」ようなもの。一度配置すれば、部屋(フォルダ)を移動するたびに、そのスタッフが黙って部屋に合わせた準備(バージョン切り替え)をしてくれる。

nvm・pyenv・asdf・direnv なども、同じ理由で有効化(eval "$(...)" など)を必要とします。「環境変数を親シェルに反映させたいツールは、ほぼ例外なく有効化が必要」と覚えておくと応用が効きます。


あの一行を分解する

PowerShell での有効化コマンドはこれです。

(&mise activate pwsh) | Out-String | Invoke-Expression

各部分の意味は次の通りです。

部分 役割
&mise activate pwsh mise が PowerShell 用の有効化スクリプト(文字列)を出力する。この時点では実行されない
Out-String 行ごとの配列になりがちな出力を、1 つのまとまった文字列に結合する
Invoke-Expression その文字列を PowerShell コードとして実際に実行する(他シェルの eval 相当)

Out-String がなぜ必要か

mise activate pwsh は複数行のスクリプト(関数定義など)を出力します。PowerShell はコマンド出力を「行ごとの文字列の配列」として扱うことが多く、そのまま Invoke-Expression に渡すと関数定義が途中でぶつ切りになり正しく解釈できません。Out-String で 1 つの文字列にまとめてから渡すことで、スクリプト全体が 1 かたまりのコードとして評価されます。

() の意味

(&mise activate pwsh) の丸括弧は「グループ化演算子」で、中の式を先に評価してひとまとまりとして扱います。このケースでは括弧はあってもなくても動作は変わりませんが、「ここまでが 1 つのコマンド」と視覚的に明確にするために付けられています。

他シェルでは次のように書きます。中身は同じ「有効化」です。

eval "$(mise activate bash)"   # bash
eval "$(mise activate zsh)"    # zsh
mise activate fish | source    # fish

有効化はセッション(プロセス)限り → プロファイルに書く

有効化で仕込んだフック関数や環境変数の変更は、すべてそのシェルプロセスのメモリ内にだけ存在します。プロセスを閉じれば消えます。新しいシェルを開くと、そこには何も仕込まれていないので、cd しても自動切り替えは起きません。

そこで、新しいシェルを開くたびに自動で有効化されるよう、プロファイル(起動時に自動実行される初期化ファイル)にあの一行を書いておきます。

mise 公式が案内している追記コマンドはこれです(echo で一行を出力し、>> でプロファイル末尾に追記しているだけ)。

echo '(&mise activate pwsh) | Out-String | Invoke-Expression' >> $HOME\Documents\PowerShell\Microsoft.PowerShell_profile.ps1

ただし、プロファイルの実際の場所は環境によって異なります。パスを直書きするより、$PROFILE 変数を使うほうが確実です。

# プロファイルの親ディレクトリがなければ作成
if (-not (Test-Path (Split-Path $PROFILE))) {
    New-Item -ItemType Directory -Path (Split-Path $PROFILE) -Force
}

# 有効化コマンドをプロファイルに追記
'(&mise activate pwsh) | Out-String | Invoke-Expression' | Add-Content $PROFILE

💡 副作用に関する注記
上記の >>Add-Content は、プロファイルファイルへの書き込みを伴います。既存のプロファイルに追記する形なので、実行前に現在の $PROFILE の中身を確認しておくと安心です。


【重要】PowerShell のバージョンに注意(5.1 では自動切り替えが効かない)

ここが Windows ユーザーの大きな落とし穴です。

mise の PowerShell 実装は、cd に反応する機能(chpwd フック)に System.Management.Automation.LocationChangedEventArgs などの C# API を使っており、これには PowerShell 7.2 以上が必要です。Windows に古くから入っている Windows PowerShell 5.1 では、cd した瞬間の自動切り替えが動きません

自分のバージョンは次で確認できます。

$PSVersionTable.PSVersion
# Major が 5 → Windows PowerShell 5.1(自動切り替え不可)
# Major が 7 → PowerShell 7 系(OK)

5.1 で activate すると、cd 直後には切り替わらず、プロンプトが再描画される(Enter を一度押す)と切り替わる、という半端な挙動になりがちです。自動切り替えをフルに使いたいなら PowerShell 7 系(pwsh)を導入してください。 5.1 とは別物で共存できます。

winget install --id Microsoft.PowerShell --source winget

導入後は powershell ではなく pwsh で起動し、その pwsh$PROFILE(5.1 とはパスが別)に有効化の一行を書きます。


cmd はどうする? → activate は効かない。シム方式を使う

「PowerShell のプロファイルに書けば cmd でも効くのか?」という疑問がよくありますが、効きません

有効化とは「そのシェルのメモリにフックを埋め込むこと」であり、PowerShell のプロファイルは PowerShell 起動時にしか読まれません。cmd は PowerShell とは完全に別のシェル(別プロセス)なので、PowerShell のプロファイルを読むことはありません。そもそも (&mise activate pwsh) | ... は PowerShell 専用の構文で、cmd では文法として解釈すらできません。加えて、mise は cmd 用の activate を提供していません

cmd でフォルダごとの切り替えを効かせたい場合は、シム(shims)方式を使います。

シム方式とは

シムは mise が用意する「中継役の実行ファイル」です。node などの中継役を 1 か所のフォルダに置き、それを OS の PATH に通しておきます。node を呼ぶと、まず中継役が起動し、「今いるフォルダではどのバージョンを使うべきか」をその場で mise に問い合わせて本物へ橋渡しします。

シェルのフックに依存しないので、cmd でも PowerShell でも、PowerShell のバージョンを問わず動くのが利点です。

# シムフォルダのパス(PowerShell で確認する場合はダブルクォートで囲む)
"$env:LOCALAPPDATA\mise\shims"

このフォルダを Windows のユーザー/システム環境変数 PATH に登録すれば、そこから起動する cmd・PowerShell 双方に効きます。

activate 方式とシム方式の違い

activate 方式 シム方式
切り替わるタイミング cd した瞬間(PS 7 系) その node実行した瞬間にフォルダを見て解決
cd 時のバージョン表示 あり なし
対応シェル bash/zsh/fish/pwsh など シェル非依存(cmd 含む)
PowerShell バージョン依存 7.2 以上が必要 依存しない

「PowerShell 7 では activate、cmd も含めたいならシム」という使い分け、あるいは両方の併用が可能です。


主要コマンドの整理

mise usemise installmise exec

コマンド やること mise.toml への記録
mise install node@18 node 18 をダウンロードするだけ しない
mise use node@18 install に加え、mise.toml にバージョンを記録 する
mise exec node@18 -- node -v その場限りで、明示指定したバージョンで実行 しない

⚠️ mise use / mise install の副作用に注意
mise use node@18mise install node@18 は、指定バージョンが未インストールなら、ネットワーク経由で Node のバイナリを自動でダウンロード・展開(インストール)します。mise.toml に書くだけ」ではありません。実行時に extract node-vXX-win-x64.zip のような表示が出るのはこのためです。

「設定ファイルへの記録だけ」のつもりで打つと、意図せずネットワークアクセスとインストールが走る点に注意してください。ダウンロードは初回だけで、インストール済みのバージョンなら次回以降は記録だけで済みます。取り消したい場合は mise uninstall node@18 で実体を削除できます。

日常的に複数プロジェクトを行き来するなら、各プロジェクトで一度 mise use してバージョンを紐づけておき、あとは activate 方式(PS 7 系)かシム方式(cmd 含む)で自動適用させるのが楽です。単発利用なら mise exec が便利です。

設定を一覧で確認する

mise use は「設定の台帳」を持っているわけではなく、設定の実体は各フォルダの mise.toml に分散しています。今いるフォルダで効いているバージョンと、その出どころ(Source)を見るには次を使います。

mise ls              # mise が把握しているツールと、そのソース
mise ls node         # node だけに絞る
mise ls --current    # 今アクティブなものだけ
mise config          # 認識している設定ファイルの場所

いずれも「今いる場所から見える範囲」が原則です。全プロジェクト横断で一覧したい場合は、mise.toml をファイル検索するのが確実です。

Get-ChildItem -Path C:\projects -Recurse -Filter mise.toml -ErrorAction SilentlyContinue |
    ForEach-Object { Write-Host "`n=== $($_.FullName) ==="; Get-Content $_.FullName }

まとめ

  • mise activate の最大の価値は、フォルダを移動するだけで、そのフォルダの mise.toml に応じたバージョンへ自動で切り替えてくれること。
  • 「有効化」が必要なのは、子プロセス(mise.exe)が親シェルの環境変数を書き換えられないという制約のため。親シェルにフックを埋め込み、mise が返す指示を親自身に実行させる仕掛けが必要になる。
  • 有効化はプロセス限り。恒久化するにはプロファイル($PROFILE)に一行を追記する(=プロファイルへの書き込みという副作用を伴う)。
  • Windows PowerShell 5.1 では cd 時の自動切り替えが効かない。 フル活用には PowerShell 7 系が必要。
  • cmd では activate が使えないため、シムフォルダを PATH に通すシム方式を使う。
  • mise use / mise install は、未インストールのバージョンをネットワーク経由で自動ダウンロード・インストールする副作用がある点に注意。

以上を押さえておけば、あの呪文のような一行が「フォルダ移動に反応してバージョンを自動で切り替えるための、シェルへの仕込み」だと腑に落ちるはずです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?