Missing headers inside `shellHook`

I’m writing a shell.nix for a ruby project that uses rugged, a library of ruby bindings to libgit2. Building rugged requires cmake.

# shell.nix
with (import <nixpkgs> { });
mkShell {
  buildInputs = [
    bundler
    ruby
    # "rugged" dependencies
    cmake
    pkgconfig
    openssl
  ];
  shellHook = "export BUNDLE_PATH=.bundle";
}
# Gemfile
source "https://rubygems.org"
gem "rugged"

Entering the shell with nix-shell --pure, I can run bundle install and it builds and installs rugged as expected.

But if I add bundle install to the shellHook it fails to find a needed libgit2 header:

/path/to/project/.bundle/gems/rugged-0.28.2/vendor/libgit2/src/alloc.h:11:10: fatal error: git2/sys/alloc.h: No such file or directory
 #include "git2/sys/alloc.h"
          ^~~~~~~~~~~~~~~~~~

Adding libgit2 to buildInputs doesn’t seem to help. The only solution I’ve found so far is:

  shellHook = ''
    export BUNDLE_PATH=.bundle
    NIX_ENFORCE_PURITY=0 bundle install
  '';

I discovered this by comparing env inside the shellHook with env inside the nix-shell.

What’s the right way to resolve this?

A side-note: I’m aware of bundix, but cannot use it for this particular project. The above shell.nix is a minimal example that reproduces my issue, but the actual project uses an older ruby version and has a couple gems specified by local filepath.

After looking into this, I think your solution is actually the correct one. It appears that NIX_ENFORCE_PURITY=1 prevents tools from referencing paths outside of the Nix store, which is exactly what you’re doing here. And in fact the very next step that nix-shell takes after invoking the shellHook is to unset that env var.

I’m wondering if maybe nix-shell should be tweaked to unset NIX_ENFORCE_PURITY before invoking shellHook instead of after. Is there a reason why we need purity when running shellHook?

Thanks for clarifying. It looks like NIX_ENFORCE_PURITY=1 allows for referencing paths in /tmp as well as in the nix store. So this is another workaround:

  shellHook = ''
    export BUNDLE_PATH=.bundle
    tmp=$(mktemp -dt bundle-XXXXX)
    mkdir -p .bundle && cp -a .bundle/. "$tmp"

    BUNDLE_PATH="$tmp" bundle install
    mv "$tmp" .bundle
  '';

It’s more complicated though, I was just curious to try it out. I’ll stick with NIX_ENFORCE_PURITY=0 for now.

I suspect a better solution would be to add libgit2 to my buildInputs and get rugged to use that rather than compiling its own. But so far it insists on compiling its own copy even when libgit2 is present.

It turns out rugged can be configured to use a system-provided libgit2, but it requires a specific version of libgit2 so in this case it probably makes more sense to let it build the copy it carries.