direnv:进目录自动 load env(再也不 source 错文件)

起因

每个项目不同 env:

  • 项目 A:DATABASE_URL=postgres://localhost/a, API_KEY=xxx
  • 项目 B:DATABASE_URL=postgres://localhost/b, AWS_PROFILE=client

老办法:

  • source .env(每次开新 shell 要重 source)
  • .envrc shell script . ./envrc(容易忘)
  • dotenv lib 在应用里读(CLI 工具读不到)

direnv 解决:进目录自动 export env,出目录自动 unset。

brew install direnv
apt install direnv

# shell 集成
eval "$(direnv hook zsh)"        # 加到 .zshrc

cd ~/projects/myapp
echo 'export DATABASE_URL=postgres://localhost/myapp' > .envrc
direnv allow                      # 首次要批准

cd ..
# DATABASE_URL unset

cd ~/projects/myapp
# DATABASE_URL 自动 set

.envrc 是 bash script,可以做任何事。

安全

.envrc 是任意 shell code → 安全风险。
direnv 强制 direnv allow 才执行 → 你 review 后启用。
文件改了 → 重新 allow。

cd ~/some-random-project
# direnv: error .envrc is blocked. Run `direnv allow` to approve its content

加载 .env

# .envrc
dotenv          # 加载同目录 .env 文件

或者:

dotenv .env.local

支持标准 .env 格式:

DATABASE_URL=postgres://...
SECRET_KEY=abc

venv 自动 activate

# .envrc
layout python python3.12     # 自动建 venv + activate

或者用现有 venv:

# .envrc
source .venv/bin/activate

进项目目录 → venv 自动激活 + env vars 加载。
不用记 source .venv/bin/activate

Node 版本

# .envrc
use node 20         # mise / nvm 集成

或者:

# .envrc
PATH_add ./node_modules/.bin    # 加到 PATH,能调 local tool

PATH_add 在前 → 项目 local 工具优先。

多层 .envrc

~/.envrc                   # 全局(如 EDITOR=nvim)
~/projects/.envrc          # 所有 project 共享(如 PNPM_HOME=...)
~/projects/myapp/.envrc    # 项目特定

进 myapp → 3 个 .envrc 合并(深层覆盖浅层)。

与 dotenv-cli / 应用层 dotenv

应用层 dotenv lib 只对应用进程有效。
direnv 对 shell 内任何命令 有效(如手动 psql, aws cli 等)。

混用:direnv 给 shell + 应用 dotenv 给 production deploy(不用 direnv
在 server)。

与 mise

mise 也支持 dotenv-load:

# .mise.toml
[env]
_.file = ".env"

两个工具能 overlap。我用:mise 管 runtime 版本,direnv 管 env vars。
都装 fine(启动 hook 不冲突)。

安全 - secret in git

.env 通常 gitignore,.envrc 也 gitignore 较好(避免 secret 进 git)。

template:

# .envrc.example  (commit 这个)
export DATABASE_URL=postgres://localhost/myapp
export API_KEY=<your-key>

# .envrc  (gitignore,每人自己 cp 自己改)

或者用 1Password CLI 拉 secret:

# .envrc
export API_KEY="$(op read 'op://Personal/myapp/api_key')"

执行 direnv 时去 1Password 拉,不 commit 实际值。

CI

CI 通常不用 direnv(用 env var injection)。但 direnv 的 .envrc 可以
被 CI 间接利用:

# CI step
source .envrc

或者把 .envrc 改成 plain .env 用 dotenv CLI 跑。

真实工作流

我每个项目 .envrc:

dotenv .env.local                 # local 配置
layout python                     # venv
PATH_add ./bin                    # 项目 script
PATH_add ./node_modules/.bin
export PYTHONUNBUFFERED=1
export AWS_PROFILE=$(basename "$PWD")    # AWS profile = 项目名

进项目 → venv + env + AWS profile + PATH 一键就绪。
1 秒切换项目 context。

与 docker compose env_file 对比

# docker-compose.yml
services:
  app:
    env_file: .env

compose 自动 load .env。但只对 compose 启动的容器有效。
shell 里手动 psql / debug 仍要 direnv。

性能

direnv hook 在 shell prompt 前跑(PROMPT_COMMAND)。
单次 < 5ms(cache)。
首次进新目录加载 .envrc 慢点(看 .envrc 复杂度)。

踩过的坑

  1. 没装 hook:source 了 hook 但没重启 shell → direnv 不生效。
    exec zsh 或开新 terminal。

  2. .envrc 改了忘 allow:env 用旧值。每次改后 direnv allow
    工具会提示。

  3. export 漏:bash 习惯 KEY=value 而非 export KEY=value → 子
    进程拿不到。.envrc 内必须 export。

  4. dotenv 顺序.envrcdotenv 后又 export 覆盖。后者
    生效。

  5. cd 进子目录 env 不变:direnv 按 .envrc 文件位置加载。子目录
    没 .envrc 沿用父。OK 但有时困惑。

精确评价 共 0 人评价
可复现性
可复现 · 0 不可复现 · 0
文风
文风流畅 · 0 文风晦涩 · 0
立场
支持 · 0 反对 · 0

登录后即可对本帖作出评价。

评论区 0 条 · 所有人可在此交流

登录后参与评论。

还没有评论,来说两句。