From 94def844450c3745cc6775dff29d747534f60fff Mon Sep 17 00:00:00 2001 From: K900 Date: Wed, 21 Aug 2024 16:50:26 +0300 Subject: [PATCH] Use ambient Nix when available, with a two stage fallback First, we try to use the ambient Nix version. Then, we try to use $NIX_DIRENV_FALLBACK_NIX, which is set by default, but can also be overridden by the user. Only then, if neither is available, we fail. Fixes #451. --- README.md | 8 ++++++++ default.nix | 31 +++++++++++++++++-------------- direnvrc | 40 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 62 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 9353e4d..8281aa4 100644 --- a/README.md +++ b/README.md @@ -311,6 +311,14 @@ invocation or are purely incidental and should not be relied upon. flake's devShell are invalid and nix-direnv has loaded the last known working shell. +nix-direnv also respects the following environment variables for configuration. + +- `NIX_DIRENV_FALLBACK_NIX`: Can be set to a fallback Nix binary location, to be + used when a compatible one isn't available in `PATH`. Defaults to + `config.nix.package` if installed via the NixOS module, otherwise needs to be + set manually. Leave unset or empty to fail immediately when a Nix + implementation can't be found on `PATH`. + ## General direnv tips - [Changing where direnv stores its cache][cache_location] diff --git a/default.nix b/default.nix index 5c1beeb..5fe1af4 100644 --- a/default.nix +++ b/default.nix @@ -2,8 +2,8 @@ resholve, lib, coreutils, - direnv, nix, + writeText, }: # resholve does not yet support `finalAttrs` call pattern hence `rec` @@ -17,11 +17,6 @@ resholve.mkDerivation rec { name = pname; }; - # skip min version checks which are redundant when built with nix - postPatch = '' - sed -i 1iNIX_DIRENV_SKIP_VERSION_CHECK=1 direnvrc - ''; - installPhase = '' install -m400 -D direnvrc $out/share/${pname}/direnvrc ''; @@ -30,10 +25,7 @@ resholve.mkDerivation rec { default = { scripts = [ "share/${pname}/direnvrc" ]; interpreter = "none"; - inputs = [ - coreutils - nix - ]; + inputs = [ coreutils ]; fake = { builtin = [ "PATH_add" @@ -48,15 +40,26 @@ resholve.mkDerivation rec { # cannot be reached when built with nix "shasum" ]; + external = [ + # We want to reference the ambient Nix when possible, and have custom logic + # for the fallback + "nix" + ]; }; keep = { "$cmd" = true; "$direnv" = true; + + # Nix fallback implementation + "$_nix_direnv_nix" = true; + "$ambient_nix" = true; + "$NIX_DIRENV_FALLBACK_NIX" = true; }; - execer = [ - "cannot:${direnv}/bin/direnv" - "cannot:${nix}/bin/nix" - ]; + prologue = + (writeText "prologue.sh" '' + NIX_DIRENV_SKIP_VERSION_CHECK=1 + NIX_DIRENV_FALLBACK_NIX=''${NIX_DIRENV_FALLBACK_NIX:-${lib.getExe nix}} + '').outPath; }; }; diff --git a/direnvrc b/direnvrc index 68cef33..61ef7b3 100644 --- a/direnvrc +++ b/direnvrc @@ -29,8 +29,10 @@ _nix_direnv_warning() { _nix_direnv_error() { log_error "${_NIX_DIRENV_LOG_PREFIX}$*"; } +_nix_direnv_nix="" + _nix() { - nix --extra-experimental-features "nix-command flakes" "$@" + ${_nix_direnv_nix} --extra-experimental-features "nix-command flakes" "$@" } _require_version() { @@ -53,6 +55,34 @@ _require_cmd_version() { _require_version "$cmd" "${BASH_REMATCH[1]}" "$required" } +_nix_direnv_resolve_nix() { + local ambient_nix + + if ambient_nix=$(command -v nix); then + if _require_cmd_version "${ambient_nix}" "${NIX_MIN_VERSION}"; then + echo "${ambient_nix}" + return 0 + else + _nix_direnv_warning "Nix version in PATH is too old, wanted ${NIX_MIN_VERSION}+, got $(${ambient_nix} --version), will attempt fallback" + fi + else + _nix_direnv_warning "Could not find Nix in PATH, will attempt fallback" + fi + + if [ -n "${NIX_DIRENV_FALLBACK_NIX}" ]; then + if _require_cmd_version "${NIX_DIRENV_FALLBACK_NIX}" "${NIX_MIN_VERSION}"; then + echo "${NIX_DIRENV_FALLBACK_NIX}" + return 0 + else + _nix_direnv_error "Fallback Nix version is too old, wanted ${NIX_MIN_VERSION}+, got $(${NIX_DIRENV_FALLBACK_NIX} --version)" + return 1 + fi + else + _nix_direnv_error "Could not find fallback Nix binary, please add Nix to PATH or set NIX_DIRENV_FALLBACK_NIX" + return 1 + fi +} + _nix_direnv_preflight() { if [[ -z $direnv ]]; then # shellcheck disable=2016 @@ -66,12 +96,16 @@ _nix_direnv_preflight() { # _require_cmd_version because _require_cmd_version uses =~ operator which would be # a syntax error on bash < 3 if ! _require_version bash "$BASH_VERSION" "$BASH_MIN_VERSION" || - ! _require_cmd_version "$direnv" "$DIRENV_MIN_VERSION" || # direnv stdlib defines $direnv - ! _require_cmd_version nix "$NIX_MIN_VERSION"; then + # direnv stdlib defines $direnv + ! _require_cmd_version "$direnv" "$DIRENV_MIN_VERSION"; then return 1 fi fi + if ! _nix_direnv_nix=$(_nix_direnv_resolve_nix); then + return 1 + fi + local layout_dir layout_dir=$(direnv_layout_dir)