`direnv` nedir? Ne işe yarar?
Environment Variable Yöneticisi
Projelerinizde environment variable’ları otomatik olarak yüklemek ve kullanmak
için go
ile ile geliştirilmiş harika bir shell extention’ından bahsetmek
istiyorum.
Geliştirme yaparken çok sık environment variable’ları kullanırız. Kimi zaman secret’ları (api token, aws credentials gibi) kimi zaman dinamik parametre amaçlı hep shell environment variable’larına ihtiyacımız olur.
Tüm yazılım dillerinin, environment variable’ları otomatik olarak yükleyebileceği bir kütüphanesi ya da paketi bulunur. Python dünyasında python-dotenv, Ruby dünyasında dotenv, Go dünyasında GoDotEnv gibi popüler paketler sıklıkla kullanılanlar arasındadır ve çok sayıda da alternatifleri bulunur.
Genelde bu paketler kodun içine monte edilir. Bu kütüphaneler çoğunlukla proje
dizininin altında .env
dosyalarını ararlar ve bulurlarsa otomatik olarak olarak
bu dosyayı okurlar, parse ederler ve değişkenlerin atamasını yaparlar. Günün
sonunda bu .env
dosyası bir text dosyasıdır ve bu kütüphaneler key=value
yaklaşımıyla ilgili değişkene okuduğu değeri atarlar.
Gerçek bir shell
ya da bash
environment yoktur aslında. Yani kendi shell
environment’ınızda bir bash dosyasını source
etmek gibi değildir. Örneğin
sizin ~/.profile
dosyanızda:
source variables.bash
gibi bir deklarasyon yapsanız, shell’iniz ayağa kalkarken, bu dosyada yazan her şeyi okur ve execute eder. Sizin bu environment variable’ları için yüklediğiniz (source) dosya aslında öz-be-öz shell script’lerinin de çalışabileceği bir source code dosyasıdır.
Ama .env
dosyalarını kullanan environment loader paketleri için durum böyle
değildir. Genelde doğru yazılmış environment variable ataması
export VARIABLE_NAME="VALUE"
şeklinde olmalıdır. Genelde dotenv paketlerinde export
kullanılmaz çünkü
bu dosya shell execution için değildir. Bu bakımdan da sıkıntı şudur, eğer
size projeniz içinde farklı farklı dillerle bir şeyler yapıyorsanız ve
ortak environment variable’ları kullanıyorsanız işler biraz karışır.
Örneğin ben sıklıkla projelerimda Rakefile
kullanıyorum, otomasyon için.
Dolayısıyla hem ruby
hem de go
’da aynı environment variable’ları kullanmak
durumunda kalıyorum. Rakefile
için ayrı paket, go
için ayrı paket
kullanmak gibi can sıkıcı, duplike bir durum oluşuyor.
Şu da bir çözümdür, eğer bahsi geçen değişkenler, sizin bilgisayarınızdaki
tüm projelerde de kullanılıyorsa gidip bu değerleri ~/.profile
ya da ~/.bashrc
gibi global shell sisteminize ekleyebilirsiniz ama bence bu kötü bir yöntem. Her
proje kendine has değişkenler ve değerler kullanabilir, boşu boşuna shell
ortamınızı şişirmeye gerek yok.
Peki çözüm ne?
direnv
direnv dizinlerin altında .envrc
dosyasına bakar ve otomatik olarak bu
dosyada yazanları source
eder. Projenize hiç bir ek paket kurmanıza da gerek
kalmaz. Neredeyse tüm işletim sistemleri için paketi bulunur. Ben macOS kullandığım
için:
brew install direnv
yapmam yeterli. Kurulum sonrasında tek yapmam gereken bash
environment’ıma
eklemek;
eval "$(direnv hook bash)"
Sadece bash ile değil, zsh
, fish
, tcsh
, elvish
, nushell
, powershell
gibi neredeyse tüm shell’lere desteği mevcut. İşin güzel yanı, .envrc
bildiğimiz
shell scripti. Yani bu dosya source .envrc
şeklinde de kullanılabilecek
şekilde bir dosya. Script derken, bu dosya içinde alias
ya da function
tanımı
yapamazsınız! Sadece environment variable deklarasyonu için kullanabilirsiniz.
Kurulum yaptıktan sonra, ilgili proje dizininize gidip .envrc
dosyasını
oluşturmanız yeterli. Ne zaman o dizine cd
yaparsanız, direnv
otomatik olarak
environment variable’ları yükler. Dizinden çıkınca da unload eder yani
directory seviyesinde çalışan environment variable’larınız var artık!
İşin güzel yanı, alt dizinlere de etki ediyor. Şimdi bir demo yapalım;
cd /tmp/
mkdir demo
cd demo/
touch .envrc
touch
dediğimiz anda bir hata mesajı alırız:
direnv: error /private/tmp/demo/.envrc is blocked. Run `direnv allow` to approve its content
çünkü .envrc
’nin otomatik yüklenmesi için izin vermemiz gerekir:
direnv allow
direnv: loading /private/tmp/demo/.envrc
Şimdi bir değişken tanımlayalım: nano .envrc
export DEMO="amiga"
Yine hata mesajı aldık:
direnv: error /private/tmp/demo/.envrc is blocked. Run `direnv allow` to approve its content
Çünkü direnv korumalı çalışır, yani dosya değiştiği zaman mutlaka direnv allow
dememiz gerekir:
direnv allow
direnv: loading /private/tmp/demo/.envrc
direnv: export +DEMO
+DEMO
yani DEMO
adında bir değişkeni otomatik yükledim der bize. Şimdi bir
üst dizine geçelim:
cd /tmp/
direnv: unloading # <- artık DEMO diye bir değişken yok!
echo "${DEMO}"
# boş...
Tekrar içeri girelim:
cd /tmp/demo/
direnv: loading /private/tmp/demo/.envrc
direnv: export +DEMO
echo "${DEMO}"
amiga
mkdir -p sub1/sub2
$ tree . -al
.
├── .envrc
└── sub1
└── sub2
cd sub1/sub2/
Şimdi sub1/sub2
altında nano test.bash
bir script oluşturalım:
#!/usr/bin/env bash
set -e
set -o pipefail
set -o errexit
set -o nounset
echo "DEMO? ${DEMO}"
Çalıştıralım:
bash test.bash
DEMO? amiga
Şimdi aynı yerde başka bir .envrc
dosyası yapalım ve DEMO
değişkenini
ezelim: nano .envrc
export DEMO="commodore"
Yine uyarı aldık:
direnv: error /private/tmp/demo/sub1/sub2/.envrc is blocked. Run `direnv allow` to approve its content
direnv allow
direnv: loading /private/tmp/demo/sub1/sub2/.envrc
direnv: export +DEMO
Şimdi bakalım:
echo "${DEMO}"
commodore
cd ../
direnv: loading /private/tmp/demo/.envrc # <- hemen bir üstteki
direnv: export +DEMO # .envrc devreye girdi.
echo "${DEMO}"
amiga # <- eski değer
Mesela .envrc
’yi kendi ${HOME}/.envrc
şeklinde yapıp, en dipteki
dizine kadar etki edebilirsiniz. Eğer her seferince direnv allow
ile
uğraşmak istemiyorsanız, whitelist’e ekleyebilirsiniz. Önce konfigürasyon
dosyası oluşturmak lazım. Genelde *nix
ortamlarında ${XDG_CONFIG_HOME}
dizini altında bu işler olur. Ben macOS’da olduğum için;
~/.config/direnv/direnv.toml
dosyasını kullananlardanım. Eğer ~/.config
diye bir dizininiz yoksa elle
oluşturabilirsiniz:
mkdir ~/.config # eğer yoksa
mkdir ~/.config/direnv
touch ~/.config/direnv/direnv.toml
Örnek .toml
:
[whitelist]
prefix = [ "/Users/vigo/Repos/Development" ]
Bu şu anlama geliyor, /Users/vigo/Repos/Development
dizini altında gördüğün
tüm .envrc
dosyalarını hiç sormadan otomatik allow
et. Konfigürasyonda
farklı seçenekler de var, detayları buradan görebilirsiniz.
Konu sadece bununla da bitmiyor, kendiniz custom extension’lar da
yazabiliyorsunuz, hatta direnv
size programatik işler yapmak için eksta
api
da sunuyor.
Şunu unutmayın, bilgisayarınızda sembolik linkler varsa, konfig dosyasına
gerçek path’i yazmanız lazım. Örneğin benim ~/Development
bir sembolik
link, gerçek path’i ilk başta yazmadığım için 2-3 saat uğraştım whitelist için;
sonra;
realpath ~/Development
/Users/vigo/Repos/Development # gerçek path bu!
yaptım ve sorunu çözdüm.