This article is also available in English
Das Tool direnv kann hier Abhilfe schaffen, indem es erlaubt, projektspezifische Konfiguration automatisch zu laden, sobald ein Projektverzeichnis betreten wird.
Die folgende .envrc
sorgt z.B. dafür, dass die Binaries im node-modules
-Verzeichnis dem Pfad hinzugefügt werden, und setzt die JAVA_TOOL_OPTIONS
, um einen UTF-8-Zeichensatz zu erzwingen.
Die Direnv-Standardbibliothek verfügt noch über weitere Funktionalität, aber erst müssen wir uns der Installation des Tools widmen.
Installation
Da es sich bei Direnv um ein in der Programmiersprache Go geschriebenes Tool handelt, ist die Installation generell einfach. Wenn sich unter auf der Installationswebseite kein Paket findet, reicht es auch, das Binary aus den Releases in den Pfad zu kopieren.
Danach muss die Shell so konfiguriert werden, dass sie direnv
jedes Mal ausführt, wenn sich das Arbeitsverzeichnis ändert. Diese Konfiguration unterscheidet sich je nach Shell: Generell muss der Output von direnv hook <shell>
ausgeführt werden. Unter ZSH kann man dies mit eval $(direnv hook zsh)
tun, unter Bash mit eval $(direnv hook bash)
. Die Fish-, TCSH- und Elvish-Shells werden ebenfalls unterstützt, die Konfiguration sieht immer ähnlich aus. Die Hook-Konfiguration muss natürlich in der Shell-Konfigurationsdatei persistent gemacht werden, damit der Hook in jeder neuen Shell auch ausgeführt wird.
Der Hook-Befehl führt dazu, dass Direnv immer aktiviert wird, wenn der Benutzer einen neuen Befehl ausführt oder das Arbeitsverzeichnis wechselt. Bei Bash passiert dies über die PROMPT_COMMAND
-Variable, bei Zsh über Code in den precmd_functions
und chpwd_functions
-Arrays. Eine eventuell existierende .envrc
wird gefunden und geladen. All dies passiert relativ performant, sodass man im täglichen Gebrauch vom Direnv-Hook nichts mitbekommt (außer der komfortableren Handhabung natürlich).
Konfiguration
Direnv wird größtenteils über Dateien im Projektverzeichnis konfiguriert. Hier können beliebige Shell-Variablen gesetzt oder modifiziert werden. Das direnv-Programm, welches wir oben in die Shellkonfiguration eingebaut haben, sucht bei jeder Ausführung nach einer Datei namens .envrc
. Diese Datei wird dann in einer Kindshell ausgeführt, und alle exportierten Variablen in die aktuelle Shell übernommen. Aus Sicherheitsgründen muss jede .envrc
erst vom Benutzer erlaubt werden, bevor sie von direnv ausgeführt wird.
Ein Anwendungsbeispiel soll dies verdeutlichen. Ziel ist es, den HTTP-Proxy für das Projekt automatisch zu setzen. Hierzu erzeugt man eine neue Datei namens .envrc
im Projektverzeichnis. In diese Datei schreibt man dann die gewünschten Zuweisungen, etwa wie unten:
Wie man sieht, handelt es sich bei einer .envrc
um ein ganz reguläres Shellskript. Es kann beliebiger Code ausgeführt in der Datei ausgeführt werden. Sichtbar für die Benutzer sind jedoch nur Änderungen in den Umgebungsvariablen, da direnv nur diese Änderungen nach „außen“ an die Shell der Benutzer weitergibt.
Die neue Envrc wird jedoch nicht direkt geladen. Verständlicherweise ist die automatische Ausführung von Shellskripten ein Sicherheitsproblem. Deswegen muss jede .envrc
einmalig erlaubt werden. Nach Lektüre der Datei kann dies über direnv allow
erfolgen. Direnv speichert diese Freigabe, sodass man für jeden Pfad die .envrc
nur einmal freigeben muss.
Nach dem Whitelisting der .envrc
wird jetzt die Proxy-Variable jedes mal gesetzt, wenn man das Projektverzeichnis betritt, und wieder gelöscht, wenn man es wieder verlässt.
Fortgeschrittene Funktionen
Die Grundfunktionalität von direnv, das Exportieren und Modifizieren von Umgebungsvariablen, ist bereits sehr nützlich, um der Entwicklerin viel Kleinarbeit abzunehmen. Direnv verfügt jedoch auch über eine „Standardbibliothek“, die weitere Funktionalität zur Verfügung stellt.
Einerseits sind dies „Baukasten“-Funktionen, die es erlauben, kompliziertere Funktionalität in einer .envrc
zu realisieren. Mit fetchurl
können transparent Binärdateien von einer URL nachgeladen werden, die dann automatisch in den Pfad aufgenommen werden. Die verschiedenen source_
-Funktionen erlauben es, .envrc
-Dateien miteinander zu kombinieren, oder sie sogar von einer URL nachzuladen. Mit direnv_load
kann die Konfiguration von Umgebungsvariablen aus externen Tools nachgeladen werden, load_prefix
setzt automatisch diverse Variablen (Pfad, Linker-Verzeichnisse, Manpage-Verzeichnisse) für Programme, die mit --prefix
insstalliert wurden. on_git_branch
erlaubt es, Dinge nur auf bestimmten Branches zu tun. Mit diesem Baukasten fällt sicher jeder Entwicklerin, die in der Shell unterwegs ist, direkt ein Nutzen ein.
Mit weniger Konfiguration verbunden sind die use
und layout
-Befehle, die dazu dienen, sprach- und frameworkspezifische Layouts zu laden. layout node
erweitert z.B. den Pfad um das node-modules/.bin
-Verzeichnis, damit lokal mit Node installierte Programme ohne Pfadangabe ausgeführt werden können. layout python
erzeugt ein Virtualenv und aktiviert es, was bedeutet, dass alle Pakete in diese projektspezifische Python-Umgebung installiert werden. Mit use nix
können Pakete und deren Abhängigkeiten über den Nix-Paketmanager geladen werden, wenn ein Verzeichnis betreten wird.
Die Standardbibliothek von Direnv bringt eine Menge anderer Layouts mit, welche auf der Webseite dokumentiert sind.
Wem das alles nicht reicht, kann in der Konfigurationsdatei .direnvrc
im Homeverzeichnis noch weitere Skripte und Funktionen unterbringen, die dann in Projekten genutzt werden können. Man sollte allerdings bedenken, dass diese Skripte anderen Projektteilnehmern nicht verfügbar sind.
Direnv ist primär für Terminalbenutzer ausgelegt. Für die meisten verbreiteten Editoren und Entwicklungsumgebungen gibt es jedoch Plugins, die die Umgebungsvariablen aus der .envrc
laden. Für Jetbrains-Entwicklungsumgebungen gibt es das Plugin Direnv Integration, für VSCode kann man das direnv-Plugin installieren.