|
|
||
|---|---|---|
| .github | ||
| scripts | ||
| template | ||
| tests | ||
| .gitignore | ||
| default.nix | ||
| direnvrc | ||
| flake.lock | ||
| flake.nix | ||
| LICENSE | ||
| README.md | ||
| run-tests.nix | ||
| shell.nix | ||
nix-direnv
A faster, persistent implementation of direnv's use_nix, to replace the built-in one.
Prominent features:
- significantly faster after the first run by caching the
nix-shellenvironment - prevents garbage collection of build dependencies by symlinking the resulting
shell derivation in the user's
gcroots(Life is too short to lose your project's build cache if you are on a flight with no internet connection)
Installation
There are different ways to install nix-direnv, pick your favourite:
- via home-manager (recommended)
- via configuration.nix in NixOS
- with nix-env
- from source
- with direnv source_url
Via home-manager
In $HOME/.config/nixpkgs/home.nix add
{ pkgs, ... }:
{
# ...other config, other config...
programs.direnv.enable = true;
programs.direnv.nix-direnv.enable = true;
# optional for nix flakes support in home-manager 21.11, not required in home-manager unstable or 22.05
programs.direnv.nix-direnv.enableFlakes = true;
programs.bash.enable = true;
# OR
programs.zsh.enable = true;
# Or any other shell you're using.
}
Optional: To protect your nix-shell against garbage collection you also need to add these options to your Nix configuration.
If you are on NixOS also add the following lines to your /etc/nixos/configuration.nix:
{ pkgs, ... }: {
nix.extraOptions = ''
keep-outputs = true
keep-derivations = true
'';
}
On other systems with Nix add the following configuration to your /etc/nix/nix.conf:
keep-derivations = true
keep-outputs = true
Via configuration.nix in NixOS
In /etc/nixos/configuration.nix:
{ pkgs, ... }: {
environment.systemPackages = with pkgs; [ direnv nix-direnv ];
# nix options for derivations to persist garbage collection
nix.extraOptions = ''
keep-outputs = true
keep-derivations = true
'';
environment.pathsToLink = [
"/share/nix-direnv"
];
# if you also want support for flakes (this makes nix-direnv use the
# unstable version of nix):
nixpkgs.overlays = [
(self: super: { nix-direnv = super.nix-direnv.override { enableFlakes = true; }; } )
];
}
Then source the direnvrc from this repository in your own $HOME/.direnvrc
# put this in ~/.direnvrc
source /run/current-system/sw/share/nix-direnv/direnvrc
With nix-env
As non-root user do the following:
nix-env -f '<nixpkgs>' -iA nix-direnv
Then add nix-direnv to $HOME/.direnvrc:
source $HOME/.nix-profile/share/nix-direnv/direnvrc
You also need to set keep-outputs and keep-derivations to nix.conf as described in the installation
via home-manager section.
From source
Clone the repository to some directory
$ git clone https://github.com/nix-community/nix-direnv $HOME/nix-direnv
Then source the direnvrc from this repository in your own .direnvrc
# put this in ~/.direnvrc
source $HOME/nix-direnv/direnvrc
You also need to set keep-outputs and keep-derivations to nix.conf as described in the installation
via home-manager section.
Direnv source_url
Put the following lines in your .envrc:
if ! has nix_direnv_version || ! nix_direnv_version 2.0.1; then
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.0.1/direnvrc" "sha256-5tSiHl8q9TnqoJ7Wizgal7sOUcKxiBR+7YSSqOmt7hg="
fi
Usage example
Either add shell.nix or a default.nix to the same directory:
# save this as shell.nix
{ pkgs ? import <nixpkgs> {}}:
pkgs.mkShell {
nativeBuildInputs = [ pkgs.hello ];
}
Then add the line use nix to your envrc:
$ echo "use nix" >> .envrc
$ direnv allow
If you haven't used direnv before, make sure to hook it into your shell first.
Using a non-standard file name
You may use a different file name than shell.nix or default.nix by passing the file name in .envrc, e.g.:
$ echo "use nix foo.nix" >> .envrc
Flakes support
nix-direnv also comes with an alternative use_flake implementation.
The code is tested and does work but the upstream flake api is not finalized,
so we we cannot guarantee stability after an nix upgrade.
Like use_nix, our use_flake will prevent garbage collection of downloaded packages, including flake inputs.
Creating a new flake-native project
This repository ships with a flake template
which provides a basic flake with devShell integration and a basic .envrc.
To make use of this template, you may issue the following command:
$ nix flake new -t github:nix-community/nix-direnv <desired output path>
You can view the template here.
Integrating with a existing flake
$ echo "use flake" >> .direnvrc
$ direnv allow
The use flake line also takes an additional arbitrary flake parameter,
so you can point at external flakes as follows:
use flake ~/myflakes#project
Advanced usage
Under the covers, use_flake calls nix print-dev-env.
The first argument to the use_flake function is the flake expression to use,
and all other arguments are proxied along to the call to print-dev-env.
You may make use of this fact for some more arcane invocations.
For instance, if you have a flake that needs to be called impurely under some conditions,
you may wish to pass --impure to the print-dev-env invocation
so that the environment of the calling shell is passed in.
You can do that as follows:
$ echo "use flake . --impure" > .envrc
$ direnv allow
Storing .direnv outside the project directory
By default, every direnv-enabled directory will contain a .direnv directory.
.direnv acts as a pure cache and is fully reproducible.
To that end, we do not recommend tracking this directory or its contents,
even in the scenario that the project tracks the a .direnvrc.
It is possible to override a function called direnv_layout_dir
in ~/.config/direnv/direnvrc (or in each project's .direnvrc or .envrc).
The following example will create a unique directory name per project
in ~/.cache/direnv/layouts/:
# Place in "$HOME"/.config/direnv/direnvrc
# Two things to know:
# * `direnv_layour_dir` is called once for every {.direnvrc,.envrc} sourced
# * The indicator for a different direnv file being sourced is a different `$PWD` value
# This means we can hash `$PWD` to get a fully unique cache path for any given environment
: ${XDG_CACHE_HOME:=$HOME/.cache}
declare -A direnv_layout_dirs
direnv_layout_dir() {
echo "${direnv_layout_dirs[$PWD]:=$(
echo -n "$XDG_CACHE_HOME"/direnv/layouts/
echo -n "$PWD" | shasum | cut -d ' ' -f 1
)}"
}
Watching additional files
To minimize the number of evaluations, nix-direnv maintains a list of files to check
for changes when deciding if an update of the cached environment is required. By default, use_flake watches
flake.nix
flake.lock
devshell.toml
use_nix watches
default.nix
shell.nix
To trigger an evaluation when other nix files change, register them by calling nix_direnv_watch_file PATH [PATH...] from .envrc.
nix_direnv_watch_file module.nix
nix_direnv_watch_file mod1.nix mod2.nix
use flake
Shell integration
See the wiki for helpers to quickly setup a direnv setup in a new project.
Known Bugs
At the moment nix-direnv depends on GNU Grep and a modern Bash version.
This might lead to problems on macOS.
As a work-around we suggest that macOS users install direnv/grep via Nix or Homebrew.
Why not use lorri instead?
- nix-direnv has flakes support.
- High CPU load/resource usage in some cases: When nixpkgs in
NIX_PATHis pointed to a directory, i.e. a git checkout, Lorri will try to evaluate nixpkgs everytime something changes causing high cpu load. Nix-direnv compromises between performance and correctness, and only re-evaluates direnv if either the project-specificdefault.nix/shell.nixchanges, or if there is a new commit added tonixpkgs. A re-evaluation can be also triggered by usingtouch .envrcin the same project. A different problem is that it might trigger mass-rebuilds when the same nixpkgs checkout is pointed to something like staging. - No additional daemon or services required: The codesize is small enough that it can be vendored into a project itself.