Проекты обрастают переменными окружения: строка подключения к базе, путь к локальным бинарникам, режим отладки, токен тестового стенда. Держать всё в ~/.bashrc неудобно, а экспортировать вручную перед каждым запуском — легко забыть. direnv читает файл .envrc при входе в каталог и снимает переменные при выходе. Настройка занимает несколько минут, но требует понимания, что .envrc — это исполняемый код.
Установка и подключение к оболочке
Пакет есть в основных дистрибутивах, поэтому начинайте с штатного репозитория. После установки добавьте hook в конфигурацию оболочки: без него direnv не узнает о смене каталога. Hook должен стоять после настройки PATH, но до приглашения, которое вы хотите видеть с подсказкой direnv.
sudo apt install direnv
sudo dnf install direnv
sudo pacman -S direnv
direnv version
Для bash добавьте в ~/.bashrc строку eval "$(direnv hook bash)". Для zsh — eval "$(direnv hook zsh)" в ~/.zshrc. В fish используют direnv hook fish | source, а в config.fish это можно обернуть в проверку наличия команды. После правки откройте новый терминал или выполните source для конфигурации.
# ~/.bashrc
eval "$(direnv hook bash)"
# ~/.zshrc
eval "$(direnv hook zsh)"
# ~/.config/fish/config.fish
direnv hook fish | source
Проверка простая: перейдите в каталог с .envrc и посмотрите на сообщение. Если hook не сработал, команда direnv status покажет, что файл найден, но не разрешён или оболочка не подключена.
Первый .envrc
Создайте каталог проекта и файл .envrc с экспортом переменных. Затем выполните direnv allow: без явного разрешения файл не исполняется. Это главная защита от чужого кода в клонированном репозитории, поэтому не отключайте её ради удобства.
mkdir -p ~/projects/demo && cd ~/projects/demo
printf 'export APP_ENV=development\nexport APP_PORT=8080\n' > .envrc
direnv allow
direnv status
После разрешения переменные появятся в окружении, а при выходе из каталога исчезнут. Файл .envrc можно хранить в Git: он описывает проект, а не содержит секретов. Всё, что меняется от машины к машине, выносите в .envrc.local и добавляйте его в .gitignore.
Безопасность: почему allow обязателен
.envrc — обычный shell-скрипт, который выполняется от вашего пользователя. Вредоносный репозиторий может через него прочитать ключи, отправить данные или изменить файлы. direnv allow запоминает хеш содержимого: после любого изменения файла разрешение снова требуется. Это не полноценная песочница, а защита от случайного запуска.
- Просматривайте
.envrcперед первымallow, особенно в незнакомом проекте. - Не запускайте
direnv allowвслепую по инструкции из issue или чата. - Секреты держите в
.envrc.localили менеджере секретов, а не в общем файле. - Проверяйте, какие каталоги добавлены в PATH: подмена бинарника опаснее лишней переменной.
- Для чужих проектов используйте контейнер или отдельного пользователя.
Отдельная ловушка — файлы, которые .envrc подключает через source_env. Проверять только основной файл недостаточно: прочитайте цепочку целиком, включая локальные переопределения. Если в монорепозитории несколько каталогов, direnv применяет ближайший .envrc, поэтому общий файл удобно держать в корне, а точечные настройки — рядом с сервисами.
Рабочий .envrc
Хороший файл описывает проект декларативно и не зависит от абсолютных путей. PATH_add добавляет каталог к PATH и корректно откатывает изменение при выходе. dotenv_if_exists читает переменные из .env, если файл есть, а source_env_if_exists подключает локальные переопределения.
export APP_ENV=development
export DATABASE_URL="postgresql://localhost/demo_dev"
PATH_add bin
dotenv_if_exists .env
source_env_if_exists .envrc.local
watch_file requirements.txt
watch_file pyproject.toml
watch_file заставляет direnv перезагрузить окружение при изменении зависимостей. Если проект использует виртуальное окружение Python, подойдёт layout python3: direnv создаст каталог .direnv и активирует окружение. Для Node.js и других стеков лучше вызывать штатный менеджер версий явно, а не полагаться на устаревшие рецепты из статей.
# вариант для Python
layout python3
# проверка после входа в каталог
which python
python --version
echo "$DATABASE_URL"
Каталог .direnv с кешем и виртуальными окружениями добавьте в .gitignore. Не храните там данные, которые нельзя восстановить: кеш можно удалить командой direnv prune, и он создастся заново.
Командная работа и CI
В репозитории обычно лежат .envrc и пример .env.example, а .envrc.local остаётся у разработчика. Новый участник клонирует проект, копирует пример, запускает direnv allow и получает одинаковый набор переменных. Это снижает число ошибок «у меня работает».
cp .env.example .env
direnv allow
direnv exec . make test
direnv export bash
В CI direnv exec выполняет команду в окружении проекта, но разрешение всё равно нужно. Не принимайте .envrc из непроверенного пулл-реквеста и не выводите direnv export в публичные логи: там могут оказаться токены. Для секретов CI используйте штатное хранилище переменных, а не файл в репозитории.
Диагностика
Если переменные не появились, начните со статуса: он показывает найденный .envrc, причину отказа и разницу между окружениями. Затем проверьте, что hook подключён именно в той оболочке, где вы работаете, и что вы не находитесь в подоболочке без интерактивного запуска.
direnv status
type _direnv_hook
direnv reload
direnv prune
direnv reload перечитывает файл, direnv prune чистит старые записи, а type _direnv_hook подтверждает, что функция оболочки загружена. Если вы запускаете скрипт из cron или systemd, hook не сработает: используйте direnv exec /path/to/project команда или передавайте переменные явно.
Чек-лист
- Hook добавлен в конфигурацию вашей оболочки и проверен в новом терминале.
.envrcпросмотрен перед первым разрешением.- Секреты не попадают в Git и в логи CI.
.direnvи.envrc.localдобавлены в.gitignore.- Зависимости отслеживаются через
watch_file. - Для cron и systemd используется
direnv exec, а не hook.
Итог
direnv убирает ручное переключение окружений и делает проект самодостаточным: вошёл в каталог — получил нужные переменные, вышел — всё вернулось. Главное — относиться к .envrc как к коду и не разрешать его без просмотра. Тогда инструмент экономит время и не превращается в способ незаметно выполнить чужой скрипт на вашей машине.