先日 Windows の nushell 環境で direnv を使えるようにした際、少しハマりどころがあったのでメモ。Windowsネイティブでも direnv が使えてとても便利
結論
結論から言ってしまうと scoop などで Windows 向けの direnv をインストールして、以下の手順を踏む
1. direnv そのものを使うための環境変数をセット
Windowsの場合以下の環境変数をセットしないとdirenvがちゃんと動かない。nvim関連のファイルがおかれるパスを参考に以下の通りに $nu.config-path
に追記したが、ここは「こうしなければいけない」という決まりはないので自分にとって都合のいいやり方でいいと思う
$env.DIRENV_CONFIG = $env.USERPROFILE + "\\AppData\\Local\\direnv"
$env.XDG_CACHE_HOME = $env.USERPROFILE + "\\AppData\\Local\\Temp"
$env.XDG_DATA_HOME = $env.USERPROFILE + "\\AppData\\Local"
参考(実際の自分のdotfilesの該当箇所): dotfiles/nushell/config.nu#L609-L612 at main · laughk/dotfiles
2. nushell の設定 $env.config.hooks.pre_prompt
にdirenv用のhookを追記
追記内容は次の通り。後述する環境変数 $envPath
($env.PATH
ではない) が null になってしまうので、そこをケアしてあげる感じ
--- (中略) ---
hooks: {
pre_prompt: [{||
if (which direnv | is-empty) {
return
}
mut envrc = (direnv export json | from json | default {})
if ($envrc | is-empty) {
return
}
if ($envrc.Path == null) {
$envrc.Path = ($envrc.PATH | split row ";")
}
$envrc | load-env
}]
--- (中略) ---
参考(実際の自分のdotfilesの該当箇所): dotfiles/nushell/config.nu#L308-L320 at main · laughk/dotfiles
試行錯誤メモ
nushell 公式ドキュメントの手順そのままではだめ
一応nushellの公式ドキュメントに以下のページがある
このページに従って設定し、envrc
のあるディレクトリに移動すると次のエラーが発生する。
× Input type not supported.
╭─[C:\Users\laughk\AppData\Roaming\nushell\env.nu:39:22]
38 │ from_string: { |s| $s | split row (char esep) }
39 │ to_string: { |v| $v | path expand | str join (char esep) }
· ─┬ ─────┬─────
· │ ╰── only string, record or list input data is supported
· ╰── input type: nothing
40 │ }
╰────
詰んでる様子
$env.Path
が null
になってしまい $nu.env-path
の設定で行われる環境変数回りの調整(?)でこけるようになり、エラー文言が非常に長く、実行したコマンドの結果がほとんど見えなくなって実質詰んでしまう。
試しに一度設定を無効にして direnv export json
を実行すると PATH
に中身はあるが、 Path
が null
になってしまうことがわかる。以下は実行例(一部マスク済み)
❯ direnv export json
direnv: loading ~/path/to/.envrc
direnv: export +COMMONPROGRAMFILES +COMSPEC +EXEPATH +HOME +MESSAGE_MAX_LENGTH +MSYSTEM +PATH +PLINK_PROTOCOL +PROGRAMFILES +SYSTEMDRIVE +SYSTEMROOT +TARGET_RECENT_DAYS +WINDIR -ComSpec -CommonProgramFiles -Path -ProgramFiles -SystemDrive -SystemRoot -windir
{
"COMMONPROGRAMFILES": "C:\\Program Files\\Common Files",
"COMSPEC": "C:\\Windows\\system32\\cmd.exe",
"ComSpec": null,
"CommonProgramFiles": null,
"DIRENV_DIFF": "eJzs****************"
"DIRENV_DIR": "-C:\\Path\\to\\direnvdir",
"DIRENV_FILE": "C:\\Path\\to\.envrc",
"DIRENV_WATCHES": "eJy0z**************************Ap_m",
"EXEPATH": "C:\\Users\\laughk\\scoop\\apps\\git\\2.44.0\\bin",
"HOME": "C:\\Users\\laughk",
"MESSAGE_MAX_LENGTH": "100",
"MSYSTEM": "MINGW64",
"PATH": "C:\\Users\\laughk\\scoop\\apps\\git\\2.44.0\\mingw64\\bin;C:\\Users\\laughk\\scoop\\apps\\git\\2.44.0\\usr\\bin;C:\\Users\\laughk\\bin;C:\\Windows\\System32;C:\\Windows;C:\\Windows\\System32\\wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0;C:\\Windows\\System32\\OpenSSH;C:\\Program Files\\Amazon\\SessionManagerPlugin\\bin;C:\\Program Files\\RedHat\\Podman;C:\\Users\\laughk\\go\\bin;C:\\Users\\laughk\\scoop\\persist\\python311\\scripts;C:\\Users\\laughk\\scoop\\apps\\python311\\3.11.9;C:\\Users\\laughk\\scoop\\persist\\nodejs-lts\\bin;C:\\Users\\laughk\\scoop\\apps\\nodejs-lts\\20.11.1;C:\\Users\\laughk\\scoop\\persist\\python\\scripts;C:\\Users\\laughk\\scoop\\apps\\python\\3.12.2;C:\\Users\\laughk\\scoop\\apps\\gcc\\13.2.0\\bin;C:\\Users\\laughk\\AppData\\Local\\aquaproj-aqua\\bat;C:\\Users\\laughk\\AppData\\Local\\aquaproj-aqua\\bin;C:\\Users\\laughk\\AppData\\Local\\Programs\\my_apps\\exe;C:\\Users\\laughk\\scoop\\persist\\python39\\Scripts;C:\\Users\\laughk\\scoop\\apps\\python39\\3.9.13;C:\\Users\\laughk\\scoop\\persist\\python310\\Scripts;C:\\Users\\laughk\\scoop\\apps\\python310\\3.10.11;C:\\Users\\laughk\\scoop\\shims;C:\\Users\\laughk\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Users\\laughk\\.local\\bin;C:\\Users\\laughk\\AppData\\Roaming\\Python\\Python311\\Scripts;C:\\Users\\laughk\\AppData\\Local\\Programs\\Microsoft VS Code\\bin",
"PLINK_PROTOCOL": "ssh",
"PROGRAMFILES": "C:\\Program Files",
"Path": null,
"ProgramFiles": null,
"SYSTEMDRIVE": "C:",
"SYSTEMROOT": "C:\\Windows",
"SystemDrive": null,
"SystemRoot": null,
"TARGET_RECENT_DAYS": "1",
"WINDIR": "C:\\Windows",
"windir": null
}
Windows 環境における nushell では $env.Path
にLists型としてPATHの情報が入る感じになっているので、hook 内でそれに合わせるようにすると次のようになる
mut envrc = (direnv export json | from json | default {})
if ($envrc | is-empty) {
return
}
if ($envrc.Path == null) {
$envrc.Path = ($envrc.PATH | split row ";")
}
$envrc | load-env
mut
で変数宣言することで、mutable な変数となる(let
の場合 imutable な変数となり、内容の変更ができなくなる)- direnv 経由で変更対象となる環境変数(変数
$envrc
) において、Path
がnull
の場合は$envrc.PATH
の内容からLists型に変換したものを$env.Path
へ格納 - 最終的な調整ができたものを
| load-env
することで$env.Path
がnull
のまま読み込まれてしまうことを防ぐ
direnv export json
の内容をよく見ると windir
とか ProgramFiles
なんかも null
になってしまっているので、何か問題が発生するようであれば同様にして null
になることを hook 内で防ぐ必要がありそうではある。ただ、現状は Path
だけケアしておけば問題ない感じではある。