Created
October 22, 2019 13:00
-
-
Save alexpeits/3dcd93c20bc0f5b1661dc43c9a48e984 to your computer and use it in GitHub Desktop.
direnv + nix + caching + only exporting useful things (https://github.com/direnv/direnv/wiki/Nix)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Usage: use_nix [...] | |
# | |
# Load environment variables from `nix-shell`. | |
# If you have a `default.nix` or `shell.nix` one of these will be used and | |
# the derived environment will be stored at ./.direnv/env-<hash> | |
# and symlink to it will be created at ./.direnv/default. | |
# Dependencies are added to the GC roots, such that the environment remains persistent. | |
# | |
# The resulting environment is cached for better performance. | |
# | |
# To trigger switch to a different environment: | |
# `rm -f .direnv/default` | |
# | |
# To derive a new environment: | |
# `rm -rf .direnv/env-$(md5sum {shell,default}.nix 2> /dev/null | cut -c -32)` | |
# | |
# To remove cache: | |
# `rm -f .direnv/dump-*` | |
# | |
# To remove all environments: | |
# `rm -rf .direnv/env-*` | |
# | |
# To remove only old environments: | |
# `find .direnv -name 'env-*' -and -not -name `readlink .direnv/default` -exec rm -rf {} +` | |
# | |
set -eo pipefail | |
use_nix() { | |
# define all local variables | |
local shell f env_hash dir default wd drv dump path_backup | |
local files_to_watch=() | |
declare opt | |
declare OPTARG | |
declare OPTIND | |
while getopts ":s:w:" opt; do | |
case "${opt}" in | |
s) | |
shell="${OPTARG}" | |
files_to_watch=("${files_to_watch[@]}" "${shell}") | |
;; | |
w) | |
files_to_watch=("${files_to_watch[@]}" "${OPTARG}") | |
;; | |
:) | |
>&2 echo "Invalid option: $OPTARG requires an argument" | |
;; | |
\?) | |
>&2 echo "Invalid option: $OPTARG" | |
exit 1 | |
;; | |
esac | |
done | |
shift $((OPTIND -1)) | |
if [[ -z "${shell}" ]]; then | |
>&2 echo "ERR: no shell was given" | |
exit 1 | |
fi | |
for f in "${files_to_watch[@]}"; do | |
if ! [[ -f "${f}" ]]; then | |
>&2 echo "cannot watch file ${f} because it does not exist" | |
exit 1 | |
fi | |
done | |
# compute the hash of all the files that makes up the development environment | |
env_hash="$(hashContents "${files_to_watch[@]}")" | |
dir="$(direnv_layout_dir)" | |
default="${dir}/default" | |
if [[ ! -L "${default}" ]] || [[ ! -d $(readlink "${default}") ]]; then | |
wd="${dir}/env-${env_hash}" | |
mkdir -p "${wd}" | |
drv="${wd}/env.drv" | |
if [[ ! -f "${drv}" ]]; then | |
log_status "use nix: deriving new environment" | |
IN_NIX_SHELL=1 nix-instantiate --add-root "${drv}" --indirect "${shell}" > /dev/null | |
nix-store -r $(nix-store --query --references "${drv}") --add-root "${wd}/dep" --indirect > /dev/null | |
fi | |
rm -f "${default}" | |
ln -s $(basename "${wd}") "${default}" | |
fi | |
drv=$(readlink "${default}/env.drv") | |
dump="${dir}/dump-$(hashFile ".envrc")-$(hashFile ${drv})" | |
if [[ ! -f "${dump}" ]] || [[ "${XDG_CONFIG_DIR}/direnv/direnvrc" -nt "${dump}" ]]; then | |
log_status "use nix: updating cache" | |
# old=$(find "${dir}" -name 'dump-*') | |
# nix-shell --pure "${drv}" --show-trace --run "$(join_args "$direnv" dump bash)" > "${dump}" | |
# rm -f ${old} | |
local all_exported | |
all_exported="declare -x | grep -E '^(declare -x )\w+=' | cut -d' ' -f3 | cut -d= -f1" | |
local whitelisted_vars='grep -Ev "^(_|XDG|TE?MP)" | grep -E "PATH|DIR|LOCALE_ARCHIVE"' | |
nix-shell "${env_drv}" --show-trace --pure "$@" > "${dump}.tmp" \ | |
--run "to_keep=\$(${all_exported} | ${whitelisted_vars}); export -n \$(${all_exported}); export \$to_keep; \"${direnv:-false}\" dump bash" | |
mv "${dump}.tmp" "${dump}" # prevents corrupted dump files when nix-shell fails | |
find "${dir}" -name 'dump-*' ! -samefile "${dump}" -ctime 15 -delete | |
fi | |
# evaluate the dump created by nix-shell earlier, but have to merge the PATH | |
# with the current PATH | |
# NOTE: we eval the dump here as opposed to direnv_load it because we don't | |
# want to persist environment variables coming from the shell at the time of | |
# the dump. See https://github.com/direnv/direnv/issues/405 for context. | |
path_backup="${PATH}" | |
eval $(cat "${dump}") | |
export PATH="${PATH}:${path_backup}" | |
for f in "${files_to_watch[@]}"; do | |
watch_file "${f}" | |
done | |
} | |
hashContents() { | |
if has md5sum; then | |
cat "${@}" | md5sum | cut -c -32 | |
elif has md5; then | |
cat "${@}" | md5 -q | |
fi | |
} | |
hashFile() { | |
if has md5sum; then | |
md5sum "${@}" | cut -c -32 | |
elif has md5; then | |
md5 -q "${@}" | |
fi | |
} | |
fail() { | |
log_error "${@}" | |
exit 1 | |
} | |
validateVersion() { | |
local version="$("${direnv}" version)" | |
local major="$(echo "${version}" | cut -d. -f1)" | |
local minor="$(echo "${version}" | cut -d. -f2)" | |
local patch="$(echo "${version}" | cut -d. -f3)" | |
if [[ "${major}" -gt 2 ]]; then return 0; fi | |
if [[ "${major}" -eq 2 ]] && [[ "${minor}" -gt 18 ]]; then return 0; fi | |
if [[ "${major}" -eq 2 ]] && [[ "${minor}" -eq 18 ]] && [[ "${patch}" -ge 2 ]]; then return 0; fi | |
return 1 | |
} | |
if ! validateVersion; then | |
echo "This .envrc requires direnv version 2.18.2 or above." | |
exit 1 | |
fi | |
use_nix -s default.nix -w nix/nixpkgs.json |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment