MulmoTerminal の worktreeEnv — 作業ごとに違うポートと DB 名を、自動で配布する

MulmoTerminal の worktreeEnv — 作業ごとに違うポートと DB 名を、自動で配布する

課題:worktree を切っても、ポートは 1 台に 1 組しかない

git worktree で fix-login と add-search を別々のフォルダに切ると、ファイルは分離されます。ここまでは隔離できます。

ところが、両方で yarn dev を立ち上げると 2 本目でこうなります。

Error: Port 3000 is already in use

ポートというのは localhost:3000 の 3000 の部分で、同時に 1 つのプログラムしか使えません。フォルダは分かれていても、ポートはマシンに 1 組しかないので、2 本目が入れません。

データベースの場合はもっと悪いです。ポートは落ちるので気づけます。5 本の worktree が同じローカル DB を見ていると、1 本が migration を走らせた瞬間に残りの 4 本のスキーマが壊れます。エラーは出ないので「なぜか動かない」という形で現れます。

落ちるほうがまだ親切、という状況です。

既存のやり方:手で PORT=3010 を export する

worktree ごとに手で違うポートを渡す書き方はあります。

PORT=3010 yarn dev

ただし、

worktree を 1 本切るたびに「このツリーの番号は何番か」を決めて、全ターミナルに配り直す作業が増えます。worktree で削った手間を環境変数の管理で取り返しているような状態になります。

MulmoTerminal の解決:worktreeEnv を宣言すれば自動配布

プロジェクトの .mulmoterminal.json に、「このプロジェクトはツリーごとに専用のポートと名前が要る」ことを宣言しておきます。

{
  "worktreeEnv": {
    "PORT": { "kind": "port", "base": 3000 },
    "API_PORT": { "kind": "port", "base": 4000 },
    "DB_NAME": { "kind": "slug", "prefix": "myapp_" }
  }
}

これだけで、worktree を切るたびにそのツリー専用の番号と名前が予約され、そのツリーの全ターミナルに環境変数として渡ります。

ディレクトリ PORT DB_NAME
本体 ~/src/myapp 3000 myapp_myapp
…/worktrees/myapp-a1b2c3d4/fix-login 3010 myapp_fix_login
…/worktrees/myapp-a1b2c3d4/add-search 3020 myapp_add_search

3 本とも同時に yarn dev が立ちます。

詳細・gotcha

刻みが 1 ではなく 10 である理由

ここがこの機能で一番よく考えられているところです。なぜ 3001, 3002 ではなく 3010, 3020 なのか。

多くの開発サーバ(vite はその典型)は、指定されたポートが埋まっていると勝手に次のポートへずれます。PORT=3000 を渡されたフォルダで何かの理由で 3000 が埋まっていると、サーバ自身が 3001 にずれて立ち上がります。

刻みが 1 だと、その 3001 は隣の worktree の枠です。割り当ては正しくても、実際の動作で衝突します。10 刻みにしておけば、ツールが数回ずれても隣の枠には届きません。開発サーバの親切な挙動を前提に、あらかじめ枠を空けてあるわけです。

kind: "slug" は DB 名として安全な形で渡る

"slug" は worktree のタスク名から小文字 [a-z0-9_] の文字列を作り、指定した prefix を付けて 63 文字で切ります。

63 文字で切るのは Postgres のデータベース名の上限に合わせたもので、境界がはっきりしています。SQLite のファイル名、Docker コンテナ名、スキーマ名にそのまま使えます。

ただし、MulmoTerminal は DB を作りません。他とぶつからない名前を渡すところまでで、そのあと createdb するなりテーブルを作るなりは、プロジェクトの migrate スクリプトの仕事です。「名前を配る」と「DB を作る」は別の仕事で、後者はプロジェクトのほうが知っているから、という設計です。

一度予約した値は、二度と動かない

値は ~/.mulmoterminal/worktree-env.jsonl に追記されて固定されます。セルを開き直しても、サーバを再起動しても、マシンを再起動しても、同じツリーには同じ番号が返ります。

親切心ではなく必要性です。もし起動のたびにポートを測り直すと、そのツリー自身の yarn dev が掴んでいるせいで「使用中」と判定され、番号がずれます。自分自身から逃げる挙動になる。1 回目は 3010、2 回目は 3020、3 回目は 3030… 動いているサーバがあるほど逃げる、という壊れた動きになります。

もう一つ、既存の tmux セッションに再アタッチするセルは環境変数を読み直しません。動いた値は、実際に走っているプロセスと食い違ったままになります。

測るのは最初に配るときの 1 回だけ。そのときマシン上の他のものが掴んでいるポートは飛ばされます。

同じプロジェクトの 2 本目クローンにも効く

副産物として効いてくる場所があります。同じリポジトリを ~/src/acme と ~/src/acme2 の 2 つに clone している場合、worktree を使っていなくても同じ衝突が起きていました。

worktreeEnv は各 checkout をツリーとして扱うので、どちらも同じ base を宣言します。先に予約したほうが 3000、あとのほうが次の空きの 3010 に落ち着きます。本来の用途の副産物ですが、2 本目クローンを持っている人には地味に効きます。

衝突は起きても、残らない

MulmoTerminal を 2 つ並べて動かしていると、「予約を読む」と「予約を書く」の一瞬の間に両方が同じ値を選ぶことはあり得ます。

対処が面白いところです。ロックで防ぐのではなく、書いた直後に検出します。予約ファイルは追記のみなので、両方が同じように読んで「先に載っているほうが勝ち」という判定が一致します。負けたほうが解放して次の値を取ります。衝突は起きえますが、残りません。

もらった値はセルヘッダーに出る

そのツリーが何をもらったかは、ヘッダーに :3010 のように出ます。ポートはクリックすると開発サーバが新しいタブで開き、変数名はホバーで表示されます。

そのディレクトリの全セル(AI セル、Shell、launch コマンド)に出ます。特に効くのは launcher のセルで、yarn dev を走らせているのはそこだからです。

worktreeEnv を宣言していないプロジェクトでは何も描画しません。有効化操作は不要で、使わない人には何も見えません。

使ってみる

MulmoTerminal 完全ガイド のインストールまで済んでいれば、.mulmoterminal.json に 3 行書くだけで動きます。

{
  "worktreeEnv": {
    "PORT": { "kind": "port", "base": 3000 }
  }
}

そのプロジェクトをマスで開き、worktree を 2 本切ります。それぞれのヘッダーに :3000 と :3010 のように違う番号が出れば効いています。両方で yarn dev を走らせて同時に立てば確認は終わりです。

やめたいときは worktreeEnv の行を消します。予約の記録は残りますが、何も渡されなくなります。

まとめ

関連: MulmoTerminal 公式ガイドの設定(worktreeEnv) / MulmoTerminal の git worktree 隔離 / Cursor の代替としての MulmoTerminal / tmux で Claude Code を並列に走らせる限界

この記事をシェア

関連記事

記事一覧に戻る