换项目还得重新 export 一串变量,direnv 接进 Shell 后信任闸门拦在哪?

每个项目一套 DATABASE_URL、API_BASE、NODE_OPTIONS,靠记忆 source .env 或往 ~/.bashrc 里堆 export,迟早会串台:A 项目的密钥还留在 shell 里,就进了 B 项目的终端。direnv 的定位很窄:按当前目录自动加载 / 卸载环境变量,把 .profile 从项目细节里解救出来。

工作方式

每次即将出提示符前,direnv 会检查当前及父目录有没有 .envrc(也可配合 .env)。文件存在且已授权时,它在 bash 子 shell 里执行,再把导出的变量差分捕回当前 shell。离开目录就卸载。官网强调:它不是把 .envrc 直接 eval 进你正在用的那一层 shell,所以别指望靠它导出 alias / function。

支持 bash、zsh、fish、tcsh 等常见 shell。装好二进制之后,必须把 hook 写进对应 rc,否则什么都不会发生。语言无关是它的优点之一:Node、Go、Python 项目可以共用同一套「进目录就备好环境」的习惯,而不必各装一套语言版本管理器只为了塞变量。

Hook:接到 Shell 上

Setup / hook 按壳分别给了一行。常见两例:

bash(~/.bashrc 末尾):

eval "$(direnv hook bash)"

zsh(~/.zshrc 末尾):

eval "$(direnv hook zsh)"

fish 则是 direnv hook fish | source。改完要重启 shell 或重开终端。官网提醒:这行尽量放在其他会改动 prompt 的插件后面,避免抢序。Oh My Zsh 用户也可以按 hook 页把 direnv 加进 plugins 数组,效果同类。

装完先用一个空目录验证 hook:还没 .envrc 时不应报错。有未 allow 的文件时应明确拒绝,而不是静默成功。

.envrc + direnv allow:信任闸门

官网 Quick demo 的顺序可以原样走一遍:

  1. 在项目目录写 .envrc(内容是 bash,例如 export FOO=foo)。
  2. 这时会看到 not allowed。安全机制默认拒绝执行。
  3. 你确认文件可信之后,执行 direnv allow(或 direnv allow .)。
  4. 变量出现在当前环境。cd 出去后被卸掉。

allow 就是信任闸门:任何人(或任意一次 git pull)改了 .envrc,都需要重新 allow,避免「克隆下来自动执行陌生脚本」。这和「公司密钥托管、SSO 发令牌」是不同层的事。direnv 管的是本机目录级加载,不要把它理解成绕过公司密钥系统的办法,本文也不写任何绕过步骤。

同事改了共享的 .envrc 之后你这边突然不加载,优先怀疑是文件哈希变了、需要重新 allow,而不是 hook 坏了。

密钥别进仓库

.envrc 若只写非秘密的路径、PATH_add、export NODE_ENV=development,可以提交,方便同事统一。真正的密钥、会话令牌、客户数据连接串:

不要写进会提交的 .envrc。官网建议:stdlib 若在 .direnv/ 落文件,把 .direnv/ 加进 .gitignore。本地覆盖可用 .envrc 里的 source_env_if_exists .envrc.local,并忽略 .envrc.local。

手滑把密钥推进远端,direnv 帮不了你撤回。那是密钥轮换和仓库清理的问题。allow 只降低「自动执行未审文件」的风险,不代替密钥管理。若团队已有统一的密钥注入方式(CI secret、本机钥匙串、公司代理),让 .envrc 只负责「指到那些工具」或加载非敏感默认值,边界会更干净。

stdlib 一点就够入门

不想手写一长串 export PATH=$PWD/bin:$PATH,可用官网提到的 PATH_add bin 这类 stdlib 函数,少踩 $PATH=bin 这种低级错误。完整函数表见 direnv-stdlib man page。需要同时读 .env 时,可在 .envrc 末尾 dotenv,官网写明 .env 本身吃不到 stdlib。

个人习惯扩展可以放进 ~/.config/direnv/direnvrc 或 lib/*.sh,在每个 .envrc 之前加载。先别急着堆全局魔法,等两三个项目重复同一段再抽。

装包优先用发行版 / 官网安装文档。接好 hook → 写最小 .envrc → direnv allow → cd 进出看加载卸载,比一上来抄复杂集成更稳。提示符里若集成了状态插件,偶发能看到「已加载 / 已卸载」提示,那只是体验增强,真正的依据仍是 echo 变量和 cd 进出符不符合预期。你们现在是「没 hook」、还是「allow 过但 pull 之后又不加载了」?