Describe the bug
nix-shell can take a filename to source its expression from, or it can take a -p flag to declare packages to use, but it cannot mix the two. This is really unfortunate because it means I can't use a file that declares a customized/pinned version of nixpkgs and pull individual packages from that with -p. I have to instead construct an expression using -E, which is annoying and not something I want to teach my coworkers how to do. I can't use the -I nixpkgs=somePath option either as I have a file that vends nixpkgs rather than a nixpkgs checkout.
What I'd really like to do is to be able to write
#!/usr/bin/env nix-shell
#!nix-shell pinned.nix -i bash -p bash jq
echo script goes here
But today, if I do this, it tries to interpret pinned.nix as a package.
There's two issues here. The first is that the -p flag declares "all arguments not otherwise associated with a flag are to be interpreted as packages, regardless of their location on the command line", which is rather confusing as it makes nix-shell foo -p bar behave like nix-shell -p foo bar, and the second is that the package form hard-codes import <nixpkgs>.
Suggested resolution
I think two things should be done:
- Stop treating
nix-shell foo -p bar like nix-shell -p foo bar. I realize this is technically a backwards-incompatible change, although the nix-shell documentation indicates that the usage is -p packages....
- If I pass a single filename to
nix-shell prior to the -p flag, replace import <nixpkgs> {} with something like (let expr = import $path; in if builtins.isFunction expr then expr {} else expr). Ideally any --arg or --argstr flags would be supported here as well. If multiple filenames are given to nix-shell along with the -p flag then they could either all be imported and merged together, or just throw an error.
A less invasive change would be to define a new flag I can use to replace the import <nixpkgs> {} with, such that nix-shell foo -p bar still retains the current behavior (though as I stated before that behavior is confusing). Or this could be hacked further by replacing the import <nixpkgs> {} with something like (let customPath = builtins.tryEval (toString <nix-shell-pkgs>); expr = import (if customPath.success then customPath.value else <nixpkgs>); in if builtins.isFunction expr then expr {} else expr). This way I could run nix-shell -I nix-shell-pkgs=pinned.nix -p bash jq and it would pull from my custom file instead. I don't see any clear benefit to this over defining a new flag though, and the new flag would be easier to document.
Describe the bug
nix-shellcan take a filename to source its expression from, or it can take a-pflag to declare packages to use, but it cannot mix the two. This is really unfortunate because it means I can't use a file that declares a customized/pinned version of nixpkgs and pull individual packages from that with-p. I have to instead construct an expression using-E, which is annoying and not something I want to teach my coworkers how to do. I can't use the-I nixpkgs=somePathoption either as I have a file that vends nixpkgs rather than a nixpkgs checkout.What I'd really like to do is to be able to write
But today, if I do this, it tries to interpret
pinned.nixas a package.There's two issues here. The first is that the
-pflag declares "all arguments not otherwise associated with a flag are to be interpreted as packages, regardless of their location on the command line", which is rather confusing as it makesnix-shell foo -p barbehave likenix-shell -p foo bar, and the second is that the package form hard-codesimport <nixpkgs>.Suggested resolution
I think two things should be done:
nix-shell foo -p barlikenix-shell -p foo bar. I realize this is technically a backwards-incompatible change, although thenix-shelldocumentation indicates that the usage is-p packages....nix-shellprior to the-pflag, replaceimport <nixpkgs> {}with something like(let expr = import $path; in if builtins.isFunction expr then expr {} else expr). Ideally any--argor--argstrflags would be supported here as well. If multiple filenames are given tonix-shellalong with the-pflag then they could either all be imported and merged together, or just throw an error.A less invasive change would be to define a new flag I can use to replace the
import <nixpkgs> {}with, such thatnix-shell foo -p barstill retains the current behavior (though as I stated before that behavior is confusing). Or this could be hacked further by replacing theimport <nixpkgs> {}with something like(let customPath = builtins.tryEval (toString <nix-shell-pkgs>); expr = import (if customPath.success then customPath.value else <nixpkgs>); in if builtins.isFunction expr then expr {} else expr). This way I could runnix-shell -I nix-shell-pkgs=pinned.nix -p bash jqand it would pull from my custom file instead. I don't see any clear benefit to this over defining a new flag though, and the new flag would be easier to document.