#!/usr/bin/env bash set -eo pipefail # Dumps all environment variables sorted alphabetically. Uses zero byte as a # delimiter since bash variables can not contain '\0'. dumpEnv() { env -0 | sort -z } # Retrieves the value of variable name passed as $1 in dumped environment # passed as $2 (see dumpEnv) getByName() { local name="$1" local envFile="$2" grep -z "^${name}=" $envFile | cut -z -f2- -d '=' | tr -d '\0' } # Produces a diff of two given environment dumps, suitable for consumption by # bash. Given environment dumps A and B (both generated by dumpEnv), # # source A; source <(diffEnvs A B) # # shall be equivalent to # # source B # diffEnvs() { local name local old="$1" local new="$2" while read -r -d '' line; do name=$(tr -d '\0' <<<"$line" | head -n1 | cut -f1 -d'=') if ! getByName "$name" "$new" >/dev/null; then echo "export -n '${name}'" fi done <"$old" while read -rd $'\0' line; do name=$(tr -d '\0' <<<"$line" | head -n1 | cut -f1 -d'=') if getByName "$name" "$old" >/dev/null; then # found in old env, check if values match if ! grep -z "^${line}\$" "$old" >/dev/null; then # variable has changed echo "export ${line@Q}" fi else # variable is new echo "export ${line@Q}" fi done <"$new" } usage() { echo "${BASH_SOURCE[0]} COMMAND" echo echo "Commands:" echo -e "\tstatus,s: Show cache status" echo -e "\treload,r: Recreate cache from .envrc" echo -e "\tclear,c: Remove cache file for current directory" } getCacheFilePath() { local cacheDir="${XDG_CACHE_HOME:-$HOME/.cache}/direnv-cache" echo "${cacheDir}/$(pwd | sha1sum | tr -d ' -')" } cmd="$1" if ! [[ -e .envrc ]]; then echo "Error, no .envrc found in current directory" exit 1 fi cacheFile=$(getCacheFilePath) case $cmd in reload|r) echo "Re-creating cache" mkdir -p "$(dirname ${cacheFile})" dumpEnv > "${cacheFile}.pre" source <(direnv stdlib) if [[ .envrc -nt ${cacheFile}.recipe ]]; then direnv exec . true > /dev/null fi source "${cacheFile}.recipe" dumpEnv > "${cacheFile}.post" diffEnvs "${cacheFile}.pre" "${cacheFile}.post" > "$cacheFile" rm "${cacheFile}.pre" "${cacheFile}.post" echo "Environment cached in $cacheFile, telling direnv to reload" direnv reload ;; status|s) shift ls "$cacheFile" "$@" ;; clear|c) if [[ -e "$cacheFile" ]]; then rm "$cacheFile" fi ;; *) usage exit 1 esac