設定ファイルの Mac 絶対パスを書き換えずに Windows で動かす:subst とジャンクション
複数台で同じリポジトリを回していると、設定ファイルに絶対パスが入っていて片方の OS でしか動かない、という状況にぶつかります。筆者の環境では、自動化タスクの定義に repoPath: "/Users/hashito/git/web/coffee" のような Mac の絶対パスが入っていました。実体は Windows 機では C:\Users\hasit\git\web\coffee です。
素直な解決は設定ファイルを OS ごとに分岐させることですが、この設定はMac 側が正で、Windows 側の都合で書き換えたくありませんでした。書き換えると両機で差分が出続けて、そのうち片方が壊れます。
そこで使ったのが、ディレクトリジャンクション+subst の仮想ドライブという組み合わせです。結果として、設定ファイルを1文字も変えずに Windows 上の Node.js が /Users/hashito/... を正しく解決するようになりました。
なぜ単純なシンボリックリンクでは足りないのか
Windows で /Users/hashito/git/web/coffee を解決しようとすると、現在のドライブのルートからの絶対パスとして扱われます。つまり cwd が C: 上なら C:\Users\hashito\git\web\coffee を探します。
ここが分かれ道です。C:\Users\ の下には既に実在のユーザープロファイル(この機では hasit)があり、そこに hashito という別名を混ぜるのは避けたい。ユーザープロファイル直下を触ると、権限まわりでも予想外のことが起きます。
したがって必要なのは「\Users\hashito\git\... というパス形だけを再現した、別の場所にあるルート」です。これは次の2段で作れます。
- どこか安全な場所に
Users\hashito\git\という階層を作り、その下から実体へジャンクションを張る - その「どこか」を
substでドライブレターに割り当てる
2 をやる理由は、Node がパスを解決するときの基準がドライブのルートだからです。cwd をその仮想ドライブ上に置けば、/Users/hashito/... は自動的に仮想ドライブのルートから解決されます。
実際の手順
まず受け皿のディレクトリを作り、実体へジャンクションを張ります。mklink /J は cmd の内部コマンドなので、PowerShell からは cmd /c 経由で呼びます。
1 | |
/J(ディレクトリジャンクション)を使うのがポイントです。/D(ディレクトリシンボリックリンク)だと既定で管理者権限が要りますが、ジャンクションは管理者権限なしで作れます。ローカルのディレクトリを指す用途ならジャンクションで足ります。
次に、この受け皿をドライブレターに割り当てます。
1 | |
これで V:\Users\hashito\git\web\coffee が C:\Users\hasit\git\web\coffee の中身を指すようになります。あとはcwd を V ドライブ側に置いてコマンドを実行します。
1 | |
この状態なら、スクリプト内の /Users/hashito/git/web/coffee は V:\Users\hashito\git\web\coffee として解決され、実体に届きます。
動作確認
解決できているかは、Node に直接聞くのが確実です。
1 | |
V ドライブ上で実行すると V:\Users\hashito\git\web\coffee が返ります。同じコマンドを C: 上で実行すると C:\Users\hashito\git\web\coffee になり、そこには何も無いので、この後のファイル読み込みが ENOENT で落ちます。
この「どのドライブで実行したか」で結果が変わる点が、この構成の最大の落とし穴です。筆者は実際に、C: 上の cwd から同じスクリプトを叩いて
1 | |
というエラーを踏みました。ファイルは存在しているのに「無い」と言われる形になるので、原因が分かるまで少し時間がかかります。エラーメッセージに出ているパスの先頭が C:\Users\hashito\(実在しない)になっていないかを最初に見てください。
再起動で消えるのはどちらか
2つの仕掛けは寿命が違います。ここを取り違えると、翌日になって突然動かなくなります。
| 仕掛け | 寿命 |
|---|---|
mklink /J のジャンクション |
ファイルシステム上の実体。再起動しても残る |
subst の仮想ドライブ |
ログオンセッション単位。再起動・再ログオンで消える |
つまり毎回必要なのは subst だけです。スクリプトの先頭に冪等な1行を置いておけば十分です。
1 | |
存在確認を「V ドライブがあるか」ではなく「目的のパスが見えるか」にしているのは、subst は成功しているのにジャンクションのほうが消えている、という壊れ方も拾うためです。
解除したくなったら次のとおりです。
1 | |
向いている場面・向いていない場面
向いているのは、自分の手元だけの互換レイヤとして使う場合です。設定ファイルは共有リポジトリの正のまま、ローカルの解決だけを合わせられます。
向いていないのは次の場合です。
- CI など、環境を毎回作り直す場所。
substを毎回張る手順が増えるだけで、設定を環境変数化したほうが早い - ネットワークドライブを指したい場合。ジャンクションはローカルボリューム向けで、UNC パスを指すなら別の手段が要る
- チームで共有する手順にする場合。ドライブレターの衝突(誰かが
V:を別用途で使っている)が起きる
要するにこれは、設定ファイルを「正しいまま」に保つためにローカル側を歪めるやり方です。歪めた事実は手順書に必ず書き残してください。この構成を知らない人が同じ機械で作業すると、cwd がたまたま C: にあるだけで同じスクリプトが失敗します。