Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Be mindful about the default arguments passed to nixpkgs in nix-shell #5543

Closed
wants to merge 4 commits into from

Conversation

andir
Copy link
Member

@andir andir commented Nov 12, 2021

Starting with 6eeb6f9 Nix started injecting inNixShell
unconditonally into all nix-shell calls. This caused to a minor breakage
with old Nix expressions that had a thin wrapper around nixpkgs. While
we can fix those projects we should try to be backwards compatible in
this regard.

The error encountered in those cases looked like this:

    error: anonymous function at /home/andi/dev/nixos/nix/tests/shell-empty-attrs-args.nix:1:1 called with unexpected argument 'inNixShell'
           at «string»:1:18:

                1| {...}@args: with import <nixpkgs> args; (pkgs.runCommandCC or pkgs.runCommand) "shell" { buildInputs = [ (foo) (run) (echo "$(foo)") ]; } ""
                 |                  ^

           … while evaluating anonymous lambda

           at «string»:1:1:

                1| {...}@args: with import <nixpkgs> args; (pkgs.runCommandCC or pkgs.runCommand) "shell" { buildInputs = [ (foo) (run) (echo "$(foo)") ]; } ""
                 | ^

           … from call site

This should be backported to 2.4.

@andir andir force-pushed the fix-2.4-nix-shell-breakage branch 3 times, most recently from bbb31fe to 28c9dd5 Compare November 12, 2021 14:04
@grahamc
Copy link
Member

grahamc commented Nov 21, 2021

Note a #!nix-shell script with -I nixpkgs= pinned to nixos-19.09 breaks without this patch.

@andir
Copy link
Member Author

andir commented Nov 22, 2021

@edolstra any chance you could have a look at this fix?

@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/nix-2-4-and-what-s-next/16257/1

@edolstra
Copy link
Member

I'm getting a test failure:

    ++ [[  - foo - bar = \ \-\ \f\o\o\ \-\ \b\a\r ]]
    +++ NIX_PATH=nixpkgs=/build/source/tests/ca-shell.nix
    +++ nix-shell --pure -p foo bar --run 'echo "$(foo) $(bar)"'
    warning: unknown experimental feature 'ca-references'
    these 2 derivations will be built:
      /build/nix-test/tests/ca/nix-shell/store/nc72sy0m12ya6w0rqaskrjk7x64hq58l-bar.drv
      /build/nix-test/tests/ca/nix-shell/store/ry7jj2wpkgddqjy0y1ww270hlbnplh0m-foo.drv
    Resolved derivation: '/build/nix-test/tests/ca/nix-shell/store/nc72sy0m12ya6w0rqaskrjk7x64hq58l-bar.drv' -> '/build/nix-test/tests/ca/nix-shell/store/x44b2vpcr9qwgfhi7140hjsvrirzs6wq-bar.drv'...
    Resolved derivation: '/build/nix-test/tests/ca/nix-shell/store/ry7jj2wpkgddqjy0y1ww270hlbnplh0m-foo.drv' -> '/build/nix-test/tests/ca/nix-shell/store/mbllmk66bnzhfjsvsmw0qy3kgcaqlrlk-foo.drv'...
    warning: unknown experimental feature 'ca-references'
    building '/build/nix-test/tests/ca/nix-shell/store/x44b2vpcr9qwgfhi7140hjsvrirzs6wq-bar.drv'...
    building '/build/nix-test/tests/ca/nix-shell/store/mbllmk66bnzhfjsvsmw0qy3kgcaqlrlk-foo.drv'...
    ++ output='foo bar'
    ++ '[' 'foo bar' = 'foo bar' ']'
    +++ NIX_PATH=nixpkgs=/build/source/tests/ca-shell.nix
    +++ nix-shell --pure -p foo --argstr fooContents baz --run 'echo "$(foo)"'
    warning: unknown experimental feature 'ca-references'
    ++ output=foo
    ++ '[' foo = baz ']'
make: *** [mk/lib.mk:123: tests/ca/nix-shell.sh.test] Error 1
make: *** Waiting for unfinished jobs....

tests/nix-shell.sh Outdated Show resolved Hide resolved
Starting with 6eeb6f9 Nix started injecting `inNixShell`
unconditonally into all nix-shell calls. This caused to a minor breakage
with old Nix expressions that had a thin wrapper around nixpkgs. While
we can fix those projects we should try to be backwards compatible in
this regard.

The error encountered in those cases looked like this:
    error: anonymous function at /home/andi/dev/nixos/nix/tests/shell-empty-attrs-args.nix:1:1 called with unexpected argument 'inNixShell'
           at «string»:1:18:

                1| {...}@Args: with import <nixpkgs> args; (pkgs.runCommandCC or pkgs.runCommand) "shell" { buildInputs = [ (foo) (run) (echo "$(foo)") ]; } ""
                 |                  ^

           … while evaluating anonymous lambda

           at «string»:1:1:

                1| {...}@Args: with import <nixpkgs> args; (pkgs.runCommandCC or pkgs.runCommand) "shell" { buildInputs = [ (foo) (run) (echo "$(foo)") ]; } ""
                 | ^

           … from call site
@andir andir force-pushed the fix-2.4-nix-shell-breakage branch from 28c9dd5 to e1b9bb7 Compare November 29, 2021 17:15
@andir
Copy link
Member Author

andir commented Nov 29, 2021

I'm getting a test failure:

    ++ [[  - foo - bar = \ \-\ \f\o\o\ \-\ \b\a\r ]]
    +++ NIX_PATH=nixpkgs=/build/source/tests/ca-shell.nix
    +++ nix-shell --pure -p foo bar --run 'echo "$(foo) $(bar)"'
    warning: unknown experimental feature 'ca-references'
    these 2 derivations will be built:
      /build/nix-test/tests/ca/nix-shell/store/nc72sy0m12ya6w0rqaskrjk7x64hq58l-bar.drv
      /build/nix-test/tests/ca/nix-shell/store/ry7jj2wpkgddqjy0y1ww270hlbnplh0m-foo.drv
    Resolved derivation: '/build/nix-test/tests/ca/nix-shell/store/nc72sy0m12ya6w0rqaskrjk7x64hq58l-bar.drv' -> '/build/nix-test/tests/ca/nix-shell/store/x44b2vpcr9qwgfhi7140hjsvrirzs6wq-bar.drv'...
    Resolved derivation: '/build/nix-test/tests/ca/nix-shell/store/ry7jj2wpkgddqjy0y1ww270hlbnplh0m-foo.drv' -> '/build/nix-test/tests/ca/nix-shell/store/mbllmk66bnzhfjsvsmw0qy3kgcaqlrlk-foo.drv'...
    warning: unknown experimental feature 'ca-references'
    building '/build/nix-test/tests/ca/nix-shell/store/x44b2vpcr9qwgfhi7140hjsvrirzs6wq-bar.drv'...
    building '/build/nix-test/tests/ca/nix-shell/store/mbllmk66bnzhfjsvsmw0qy3kgcaqlrlk-foo.drv'...
    ++ output='foo bar'
    ++ '[' 'foo bar' = 'foo bar' ']'
    +++ NIX_PATH=nixpkgs=/build/source/tests/ca-shell.nix
    +++ nix-shell --pure -p foo --argstr fooContents baz --run 'echo "$(foo)"'
    warning: unknown experimental feature 'ca-references'
    ++ output=foo
    ++ '[' foo = baz ']'
make: *** [mk/lib.mk:123: tests/ca/nix-shell.sh.test] Error 1
make: *** Waiting for unfinished jobs....

I am not able to reproduce that locally with make installcheck. However, I had to revert 09471d2 as that caused a test to fail with can't compare lists to lists (or something along the lines).

Edit: It looks like installcheck doesn't depend on install and I had an older build of Nix in the out path while running those tests. That might explain the issue with the list comparison.

@andir
Copy link
Member Author

andir commented Nov 29, 2021

I wonder how CI ever passed with my patch. 🤔 Anyway. The implementation is wrong and the fix doesn't appear to be as simple as I did hope.

First of, the current implementation (proposed here in e1b9bb7 and earlier) removes all arguments that are not part of the formal function arguments. Less than ideal and probably breaking someone's code.

Then there is also the whole story that is covered in #4003. While that issue was forward fixed in Nixpkgs we never thought about backwards compatibility in that PR (or related conversations).

The flaw in my implementation is that it might work in the tests but wouldn't work with actual Nixpkgs. In nixpkgs the attribute is never part of any function signature but is just removed from the arguments if passed:
https://github.com/Ma27/nixpkgs/blob/fa6064ad86a020f52a75219bff044714f2a9ebbd/pkgs/top-level/impure.nix#L84

If we want to retain backwards compatibility, we can't inject the inNixShell attribute without hacks such as evaluating without it if it fails the first time around.

I've reverted my changes in this PR and added test cases that should pass if we are backwards compatible instead.

@edolstra any pointers on what we should do here? Isn't the IN_NIX_SHELL environment variable enough to do whatever the feature was originally designed to do?

@edolstra
Copy link
Member

edolstra commented Dec 1, 2021

@andir See #3168. The problem with IN_NIX_SHELL is that it has unclear semantics. It's used to influence both setup hooks (like shellHook) and evaluation (as in src = if getEnv "IN_NIX_SHELL" != "" then null else ./.). The latter means that unrelated evaluations (e.g. a nix-build inside a nix-shell) will be affected by IN_NIX_SHELL even when that's not intended. Also, it's better not to rely on environment variables in Nix expressions, because they don't work in pure mode.

@andir
Copy link
Member Author

andir commented Dec 1, 2021

@andir See #3168. The problem with IN_NIX_SHELL is that it has unclear semantics. It's used to influence both setup hooks (like shellHook) and evaluation (as in src = if getEnv "IN_NIX_SHELL" != "" then null else ./.). The latter means that unrelated evaluations (e.g. a nix-build inside a nix-shell) will be affected by IN_NIX_SHELL even when that's not intended. Also, it's better not to rely on environment variables in Nix expressions, because they don't work in pure mode.

When I referred to the environment variable I meant during the shells runtime. Having it available during eval is already impure and that's bad. I don't get why the Nix argument is required at all. It sounds like a hack. We already have shell.nix and equivalent flake attributes for shell expressions. In the simplest case these are the same expression as for a nix build. And if someone wants to reuse the code they should pass that flag via one of the shell expressions.

Injecting inNixShell leaks the runtime environment (e.g. the Nix version used) into the expression. It is in no way better than the environment variable we had before.

@shurrman
Copy link

@andir @edolstra Hi, is there a way to avoid it in a pkgs pinned to 19.x?

@andir
Copy link
Member Author

andir commented Dec 14, 2021

@andir @edolstra Hi, is there a way to avoid it in a pkgs pinned to 19.x?

Use a stable nix release (<=2.3).

gilligan added a commit to nix-community/npmlock2nix that referenced this pull request Jan 16, 2022
More recent versions of Nix make backwards incompatible changes such as
NixOS/nix#5543 and apparently no interest
in fixing this - So we'll stick to a 2.3 version of Nix for now.
@andir
Copy link
Member Author

andir commented Feb 17, 2022

Since nobody cares about backwards compat I'll close this.

@andir andir closed this Feb 17, 2022
gleachkr added a commit to Carnap/Carnap that referenced this pull request May 4, 2022
Complained about here: NixOS/nix#5543
Ma27 added a commit to Ma27/nix that referenced this pull request Jun 13, 2022
Basically an attempt to resume fixing NixOS#5543 for a breakage introduced
earlier[1]. Basically, when evaluating an older `nixpkgs` with
`nix-shell` the following error occurs:

    λ ma27 [~] → nix-shell -I nixpkgs=channel:nixos-18.03 -p nix
    error: anonymous function at /nix/store/zakqwc529rb6xcj8pwixjsxscvlx9fbi-source/pkgs/top-level/default.nix:20:1 called with unexpected argument 'inNixShell'

           at /nix/store/zakqwc529rb6xcj8pwixjsxscvlx9fbi-source/pkgs/top-level/impure.nix:82:1:

               81|
               82| import ./. (builtins.removeAttrs args [ "system" "platform" ] // {
                 | ^
               83|   inherit config overlays crossSystem;

This is a problem because one of the main selling points of Nix is that
you can evaluate any old Nix expression and still get the same result
(which also means that it *still evaluates*). In fact we're deprecating,
but not removing a lot of stuff for that reason such as unquoted URLs[2]
or `builtins.toPath`. However this property was essentially thrown away
here.

The change is rather simple: check if `inNixShell` is specified in the
formals of an auto-called function. This means that

    { inNixShell ? false }:
    builtins.trace inNixShell
      (with import <nixpkgs> { }; makeShell { name = "foo"; })

will show `trace: true` while

    args@{ ... }:
    builtins.trace args.inNixShell
      (with import <nixpkgs> { }; makeShell { name = "foo"; })

will throw the following error:

    error: attribute 'inNixShell' missing

This is explicitly needed because the function in
`pkgs/top-level/impure.nix` of e.g. NixOS 18.03 has an ellipsis[3], but
passes the attribute-set on to another lambda with formals that doesn't
have an ellipsis anymore (hence the error from above). This was perhaps
a mistake, but we can't fix it anymore. This also means that there's
AFAICS no proper way to check if the attr-set that's passed to the Nix
code via `EvalState::autoCallFunction` is eventually passed to a lambda
with formals where `inNixShell` is missing.

However, this fix comes with a certain price. Essentially every
`shell.nix` that assumes `inNixShell` to be passed to the formals even
without explicitly specifying it would break with this[4]. However I think
that this is ugly, but preferable:

* Nix 2.3 was declared stable by NixOS up until recently (well, it still
  is as long as 21.11 is alive), so most people might not have even
  noticed that feature.

* We're talking about a way shorter time-span with this change being
  in the wild, so the fallout should be smaller IMHO.

[1] NixOS@9d612c3
[2] NixOS/rfcs#45 (comment)
[3] https://github.com/NixOS/nixpkgs/blob/release-18.03/pkgs/top-level/impure.nix#L75
[4] See e.g. the second expression in this commit-message or the changes
    for `tests/ca/nix-shell.sh`.
Ma27 added a commit to Ma27/nix that referenced this pull request Jun 13, 2022
Basically an attempt to resume fixing NixOS#5543 for a breakage introduced
earlier[1]. Basically, when evaluating an older `nixpkgs` with
`nix-shell` the following error occurs:

    λ ma27 [~] → nix-shell -I nixpkgs=channel:nixos-18.03 -p nix
    error: anonymous function at /nix/store/zakqwc529rb6xcj8pwixjsxscvlx9fbi-source/pkgs/top-level/default.nix:20:1 called with unexpected argument 'inNixShell'

           at /nix/store/zakqwc529rb6xcj8pwixjsxscvlx9fbi-source/pkgs/top-level/impure.nix:82:1:

               81|
               82| import ./. (builtins.removeAttrs args [ "system" "platform" ] // {
                 | ^
               83|   inherit config overlays crossSystem;

This is a problem because one of the main selling points of Nix is that
you can evaluate any old Nix expression and still get the same result
(which also means that it *still evaluates*). In fact we're deprecating,
but not removing a lot of stuff for that reason such as unquoted URLs[2]
or `builtins.toPath`. However this property was essentially thrown away
here.

The change is rather simple: check if `inNixShell` is specified in the
formals of an auto-called function. This means that

    { inNixShell ? false }:
    builtins.trace inNixShell
      (with import <nixpkgs> { }; makeShell { name = "foo"; })

will show `trace: true` while

    args@{ ... }:
    builtins.trace args.inNixShell
      (with import <nixpkgs> { }; makeShell { name = "foo"; })

will throw the following error:

    error: attribute 'inNixShell' missing

This is explicitly needed because the function in
`pkgs/top-level/impure.nix` of e.g. NixOS 18.03 has an ellipsis[3], but
passes the attribute-set on to another lambda with formals that doesn't
have an ellipsis anymore (hence the error from above). This was perhaps
a mistake, but we can't fix it anymore. This also means that there's
AFAICS no proper way to check if the attr-set that's passed to the Nix
code via `EvalState::autoCallFunction` is eventually passed to a lambda
with formals where `inNixShell` is missing.

However, this fix comes with a certain price. Essentially every
`shell.nix` that assumes `inNixShell` to be passed to the formals even
without explicitly specifying it would break with this[4]. However I think
that this is ugly, but preferable:

* Nix 2.3 was declared stable by NixOS up until recently (well, it still
  is as long as 21.11 is alive), so most people might not have even
  noticed that feature.

* We're talking about a way shorter time-span with this change being
  in the wild, so the fallout should be smaller IMHO.

[1] NixOS@9d612c3
[2] NixOS/rfcs#45 (comment)
[3] https://github.com/NixOS/nixpkgs/blob/release-18.03/pkgs/top-level/impure.nix#L75
[4] See e.g. the second expression in this commit-message or the changes
    for `tests/ca/nix-shell.sh`.
@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/nix-shell-error-called-with-unexpected-argument-innixshell/20356/2

tlxzilia pushed a commit to tlxzilia/npmlock2nix that referenced this pull request Aug 13, 2022
More recent versions of Nix make backwards incompatible changes such as
NixOS/nix#5543 and apparently no interest
in fixing this - So we'll stick to a 2.3 version of Nix for now.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants