mise + linuxbrew 環境で cargo install したバイナリが VSCode に認識されない問題

目次

TL;DRmise で Rust を管理している環境では .cargo/env が no-op になる。.profileexport PATH="$HOME/.cargo/bin:$PATH" と無条件に書くのが確実。

前の記事RTK のバグ報告のために深夜にせこせこやってたんですよ。

で、cargo install --path . してローカルのバイナリで確認しようとしたら、Claude Code が旧バイナリを使い続ける。小一時間ハマってイライラしたので記録しておく。

症状

cargo install --path .   # ローカル fix 済みバイナリをインストール
which rtk                # → /home/linuxbrew/.linuxbrew/bin/rtk  ← 旧バイナリ

~/.cargo/bin/rtk のはずが、linuxbrew の rtk が勝っている。

なにやっても変わらねぇ…

調査

PATH の順序

1  ~/.local/share/mise/shims
2  /home/linuxbrew/.linuxbrew/bin   ← ここで rtk を拾う
...
7  ~/.cargo/bin

linuxbrew が .cargo/bin より前にいるんでよくある単純な PATH 順の問題だろうと甘く見てました。はい。

そこからはまりまくった。

.bashrc に追記しても効かない

Claude さんのおすすめにより、.bashrc 末尾に . "$HOME/.cargo/env" を追記して VSCode を再起動、結果変わらず。そりゃそうだろ。前調べた時って非インタラクティブじゃなかったっけ?

改めてどういうシェルで動いているか確認した。

echo $-
# → hmtBc   ← i がない = 非インタラクティブ

ps -p $$ -o args=
# → /bin/bash -c source ~/.claude/shell-snapshots/snapshot-bash-XXX.sh

ですよねー。

Claude Code は非インタラクティブ bash でコマンドを実行している。.bashrc の先頭には標準の非インタラクティブガードがあるので、追記した内容に到達する前に return してしまう。

case $- in
    *i*) ;;
      *) return;;   # ← ここで終わり
esac

Claude Code はシェルスナップショットを使う

/proc/$PPID/environ で VSCode プロセスの PATH を確認すると、Claude Code の PATH と同じ構造だった。Claude Code は VSCode プロセスが持つ環境変数をスナップショットとして保存し、それを各コマンド実行時に source している。

つまり VSCode プロセス自体の PATH を直さないといけない。

.profile に追記しても .cargo/env が no-op になる

さっきの .bashrc への追記だと駄目なことは分かったので、.profile 末尾の eval "$(mise activate bash --shims)" の後に . "$HOME/.cargo/env" を追記して再起動した。が、変わらず…

なんでだよ、と思いつつ.cargo/env を見てみたら中はこうなってた。

case ":${PATH}:" in
    *:"$HOME/.cargo/bin":*)
        ;;   # すでにある → 何もしない ← ここに入る
    *)
        export PATH="$HOME/.cargo/bin:$PATH"
        ;;
esac

抜けてるじゃねーか!最初に中身を確認しろよ!とセルフ突っ込み。

mise activate bash --shims が Rust(mise 管理)のパスとして ~/.cargo/bin をすでに PATH に追加している。.cargo/env はそれを検知して順序を変えずにスキップする。linuxbrew より後ろの位置に居座ったまま。

VSCode の環境取得

Claude さんに順序立てて調べてもらったところ以下の回答でした。

VSCode は起動時に bash --login(非インタラクティブ)を使って環境変数を取得する。.profile は実行されるが、.profile.bashrc を source しようとしても非インタラクティブガードで早期 return する。mise activate --shims だけが効いて mise/shims が先頭に入るが、.cargo/env は no-op で linuxbrew より後ろに .cargo/bin が残る。

だめじゃん。

解決策

ということで、.profile にパスを直接書く。

# ~/.profile(mise activate bash --shims の後)
# .cargo/env は mise が .cargo/bin を追加済みの場合スキップするので無条件で prepend する
export PATH="$HOME/.cargo/bin:$PATH"

PATH に重複エントリが生まれるが無害。確認。

# VSCode を完全終了してから、修正済みの .profile を持つシェルで起動
ps aux | grep code    # プロセスがないことを確認
code .

# Claude Code 内
which rtk   # → ~/.cargo/bin/rtk  ✅

まとめ

試したこと結果理由
.bashrc.cargo/env 追記非インタラクティブ bash、早期 return で効かない
.profile.cargo/env 追記mise が先に .cargo/bin を追加済みで no-op になる
.profileexport PATH=... 直接記載常に先頭に prepend される

こういうパスの解決問題、自分はよくはまるんだけどみんなどうやってるんだろうか。

エレガントな解決方法でも探してみるかぁ。

→ これ書いた後に Claude さんに相談してみたら、environment.d で管理する方法もあるとのこと。でもこれ一番最初に読まれて結局後勝ちになるので、.profile, .bashrc の2つで管理した方がいいとの結論に至った。