Como usar direnv para variáveis de ambiente isoladas por projeto

1. Por que isolar variáveis de ambiente por projeto?

No desenvolvimento de software moderno, cada projeto geralmente exige configurações específicas: chaves de API, strings de conexão com banco de dados, tokens de autenticação e caminhos de diretórios. O problema do ambiente global é que, ao definir essas variáveis no ~/.bashrc ou ~/.zshrc, você cria conflitos entre projetos que usam o mesmo nome de variável com valores diferentes. Além disso, credenciais sensíveis podem vazar acidentalmente quando você compartilha seu perfil de shell.

Ferramentas como .env resolvem parte do problema, mas exigem que você carregue manualmente o arquivo com source .env ou use bibliotecas específicas. O export manual é tedioso e propenso a erros — um simples exit de terminal pode perder todo o contexto.

O direnv oferece uma solução elegante: carregar e descarregar automaticamente variáveis de ambiente quando você entra ou sai de um diretório. Isso garante reprodutibilidade (cada projeto tem seu ambiente isolado), segurança (variáveis não persistem além do diretório) e produtividade (sem comandos manuais).

2. Instalação e configuração inicial do direnv

A instalação varia conforme o sistema operacional:

Linux (Debian/Ubuntu):

sudo apt update && sudo apt install direnv

macOS (Homebrew):

brew install direnv

Windows (via WSL): Siga os passos do Linux dentro do WSL.

Após instalar, você precisa adicionar o hook do direnv no seu shell. Para bash, edite ~/.bashrc:

eval "$(direnv hook bash)"

Para zsh (~/.zshrc):

eval "$(direnv hook zsh)"

Para fish (~/.config/fish/config.fish):

direnv hook fish | source

Recarregue o shell ou execute source ~/.bashrc. Verifique a instalação:

direnv version

Se aparecer a versão (ex: 2.34.0), está pronto. Por segurança, o direnv exige permissão explícita para cada .envrc — você verá isso na prática.

3. Estrutura básica de um arquivo .envrc

O arquivo .envrc é um script shell que define variáveis e comandos executados automaticamente ao entrar no diretório. Exemplo básico:

export DATABASE_URL="postgres://user:pass@localhost:5432/meuprojeto"
export API_KEY="sk-123456abcdef"
export PORT=3000

Salve como .envrc na raiz do projeto. Ao entrar no diretório, o direnv alertará que o arquivo está bloqueado — você precisa autorizá-lo.

Se você já possui um arquivo .env tradicional, use o comando dotenv do direnv:

dotenv .env

Isso carrega todas as variáveis do .env sem precisar reescrevê-las. O .envrc pode conter qualquer comando shell válido:

export NODE_ENV=development
export PATH="./node_modules/.bin:$PATH"

4. Comandos essenciais do direnv no dia a dia

direnv allow — Autoriza o .envrc atual. Sempre que você criar ou modificar o arquivo, o direnv bloqueia por segurança. Execute no diretório:

direnv allow

direnv deny — Revoga a permissão do .envrc atual.

direnv reload — Recarrega as variáveis após editar o .envrc (útil se você não saiu do diretório).

direnv status — Mostra o status do direnv no diretório atual: se está carregado, bloqueado ou ausente.

direnv edit — Abre o .envrc no editor padrão e recarrega automaticamente ao salvar.

Exemplo prático: crie um .envrc, edite, execute direnv allow, e veja as variáveis com env | grep DATABASE. Saia do diretório (cd ..) e as variáveis desaparecem.

5. Integração com ferramentas de projeto

Docker Compose: Defina variáveis no .envrc que são usadas pelo docker-compose.yml:

export COMPOSE_PROJECT_NAME="meuapp"
export DB_PASSWORD="senha_segura"

Node.js: Evite conflitos de versão com nvm:

export NODE_VERSION="18.17.0"
export PATH="./node_modules/.bin:$PATH"

Python: Ative automaticamente o virtualenv:

source .venv/bin/activate
export PYTHONPATH="${PWD}/src"

Go: Configure o GOPATH por projeto:

export GOPATH="${PWD}/go"
export PATH="${GOPATH}/bin:$PATH"

Para automação, crie scripts que geram .envrc a partir de templates:

#!/bin/bash
cat > .envrc << EOF
export PROJECT_NAME="${PWD##*/}"
export NODE_ENV=development
EOF
direnv allow

6. Boas práticas de segurança e versionamento

Nunca commite .envrc com credenciais reais no Git. Estratégia recomendada:

  1. Crie um .envrc.example com valores placeholder:
    text export DATABASE_URL="postgres://user:pass@localhost:5432/exemplo" export API_KEY="sua-chave-aqui"

  2. Adicione .envrc ao .gitignore:
    text .envrc

  3. Instrua a equipe a copiar o exemplo e ajustar:
    text cp .envrc.example .envrc && direnv allow

Para segredos, use um .env privado (também no .gitignore) e carregue com dotenv .env no .envrc. Permissões restritas: chmod 600 .envrc. Audite alterações com git diff no .envrc.example.

7. Resolução de problemas comuns

Erro "direnv: error .envrc is blocked" — Execute direnv allow para autorizar. Isso é normal na primeira vez ou após alterações.

Variáveis que não persistem ao trocar de diretório — Verifique se o hook está configurado corretamente no shell. Teste com cd .. e cd - — as variáveis devem sumir e voltar.

Conflitos com hooks de shell — Ferramentas como pyenv, nvm ou rbenv podem interferir. Ordene os hooks no shell: o direnv deve ser o último eval no arquivo de configuração.

.envrc não executado — Confirme que o arquivo tem permissão de leitura e que você está no diretório correto. Use direnv status para diagnóstico.

8. Avançado: personalização e automação

O .envrc é um script shell completo. Use funções e condicionais:

# Carrega variáveis de ambiente de desenvolvimento ou produção
if [ -f .env.development ]; then
    dotenv .env.development
elif [ -f .env.production ]; then
    dotenv .env.production
fi

# Função para configurar PATH
layout_node() {
    export PATH="./node_modules/.bin:$PATH"
    export NODE_PATH="./node_modules"
}

layout_node

Para buscar variáveis de serviços externos (ex: AWS Secrets Manager):

# Requer aws-cli configurado
export DB_PASSWORD=$(aws secretsmanager get-secret-value --secret-id meu-secret --query SecretString --output text | jq -r '.password')

Combine com Nix flakes para ambientes ainda mais reprodutíveis. Crie um shell.nix e carregue com:

use nix

Isso garante que todas as dependências do sistema (versões de linguagens, bibliotecas) sejam idênticas em qualquer máquina.

Referências