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

M1 Mac reinstall -- -bash: nix-shell: command not found #5298

Closed
n8henrie opened this issue Sep 27, 2021 · 10 comments
Closed

M1 Mac reinstall -- -bash: nix-shell: command not found #5298

n8henrie opened this issue Sep 27, 2021 · 10 comments
Labels

Comments

@n8henrie
Copy link
Contributor

Describe the bug

I had a previous single-user install. I couldn't get nix-darwin working, so I wanted to retry with a multi-user install. After an uninstall and reinstall, when I try nix-shell -p nix-info --run "nix-info -m" I get -bash: nix-shell: command not found.

I have confirmed that I am sourcing /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh and that the launchdaemon is running.

Steps To Reproduce

For uninstallation: https://nixos.org/manual/nix/stable/#sect-multi-user-installation

$ sudo rm -rf /etc/profile/nix.sh /etc/nix /nix ~root/.nix-profile ~root/.nix-defexpr ~root/.nix-channels ~/.nix-profile ~/.nix-defexpr ~/.nix-channels
$ sudo launchctl unload /Library/LaunchDaemons/org.nixos.nix-daemon.plist
$ sudo rm /Library/LaunchDaemons/org.nixos.nix-daemon.plist

launchctl list | grep -i nix; sudo launchctl list | grep -i nix showed a straggler, so I also added:

$ sudo launchctl unload /Library/LaunchDaemons/org.nixos.activate-system.plist 
$ sudo rm /Library/LaunchDaemons/org.nixos.activate-system.plist

Also, after my first few attempts I started adding the below to avoid issues in the installation step:

$ sudo mv /etc/bashrc.backup-before-nix /etc/bashrc
$ sudo mv /etc/zshrc.backup-before-nix /etc/zshrc

Afterwards, from https://nix.dev/tutorials/install-nix#macos:

$ sh <(curl -L https://nixos.org/nix/install) --darwin-use-unencrypted-nix-store-volume --daemon
$ # say yes a few times, finish installation apparently without error
$ # open a fresh Terminal window
$ nix-shell -p nix-info --run "nix-info -m"
-bash: nix-shell: command not found

Expected behavior

Installation process at https://nix.dev/tutorials/install-nix#macos and https://nixos.org/manual/nix/stable/#sect-multi-user-installation to result in a working installation.

nix-env --version output

$ nix-env --version
-bash: nix-env: command not found

Additional context

I ran diff /etc/bashrc /etc/bashrc.backup-before-nix and added the following to my ~/.bashrc to see if that would fix a suspected PATH issue:

if [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then
  . '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'
fi

I can see several relevant envvars in $ env | grep NIX, but still command not found.

It ends up on my path if I run:

$ /bin/bash --noprofile --norc
$ source /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
$ nix-env --version
nix-env (Nix) 2.3.15

Also works with $ /opt/homebrew/bin/bash --noprofile --norc, or with sudo su, sourcing the nix-daemon.sh afterward each time, so it's obviously a problem with my local configuration. It doesn't work with sudo /bin/bash, so it seems less likely to be a permission / root profile thing. I think.

I thought it might be related to ~/.nix-profile (which was deleted in the uninstall step) being a broken symlink; /nix/var/nix/profiles/per-user/n8henrie/profile doesn't exist, only /nix/var/nix/profiles/per-user/root, but sudo nix-env --version also doesn't work, and when I use the --noprofile --norc workaround, it is still a symlink to nowhere.

Trying to pick through the MacOS threads at https://discourse.nixos.org/t/anyone-up-for-picking-at-some-nix-onboarding-improvements/13152/6, not much luck yet.

Searching the issues shows a lot of open / unresolved / marked stale problems with MacOS. The most relevant handful I've found:

@n8henrie n8henrie added the bug label Sep 27, 2021
@domenkozar
Copy link
Member

@toonn you had an uninstallation gist somewhere?

@abathur
Copy link
Member

abathur commented Sep 27, 2021

Short of fully uninstalling and reinstalling (whether via a gist from toon, or via iterative masochism...), I guess the other option is just logically stepping through and debugging things. Some ~starting thoughts:

  1. verify /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh exists (I'm not 100% certain from phrasing if you confirmed that it is getting sourced, or just that the source line is in your rc). If it doesn't exist, I guess you don't have a default profile for some reason.
  2. If it's getting sourced, it should be modifying your PATH (ref: https://github.com/NixOS/nix/blob/2.3-maintenance/scripts/nix-profile-daemon.sh.in#L38), so verify that it starts with /Users/<username>/.nix-profile/bin:/nix/var/nix/profiles/default/bin:. You might also find /run/current-system/sw/bin in here, from nix-darwin.
    • If PATH doesn't start with these, either nix-daemon.sh isn't actually getting sourced, or something is over-writing the PATH later. It may be pretty spammy depending on your profile, but enabling trace is one way to tell the difference. You could try running env -i /bin/bash -x, or editing set -x into the top of /etc/bashrc and opening a new terminal tab/window (and then go remove set -x before you forget). It should be enough to just search this for each instance of PATH=.
    • If PATH does start with these, check the directories. The first probably won't exist--but the second (/nix/var/nix/profiles/default/bin) IS where your nix binaries should be after install, so confirm it exists and contains them. (/nix/var/nix/profiles/default is a symlink into the store; I assume this will exist if the path in step 1 is present.)

@n8henrie
Copy link
Contributor Author

Thanks for your help!

Short of fully uninstalling and reinstalling

Isn't that what I'm doing? That's what I'm aiming for, and I'm following the official uninstall process AFAICT.

verify /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh exists

Yes, as noted it exists and I am manually sourcing it just to be sure (thought it's also in my ~/.bashrc).

If it's getting sourced, it should be modifying your PATH (ref: https://github.com/NixOS/nix/blob/2.3-maintenance/scripts/nix-profile-daemon.sh.in#L38), so verify that it starts with /Users//.nix-profile/bin:/nix/var/nix/profiles/default/bin:.

This is not working.

$ source /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh && echo "$PATH" | grep -i nix || echo "not found"
not found

If PATH doesn't start with these, either nix-daemon.sh isn't actually getting sourced or something is over-writing the PATH later.

As a less spammy alternative, I just added the following to my .bashrc and launched a new terminal window, which outputs "not found" at the top.

if [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then
  . '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'
  echo "$PATH" | grep -i nix || echo "not found"
fi

That should mean that /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh exists, is getting sourced, and isn't updating the path (since I'm checking immediately afterward). Right?

@toonn
Copy link
Contributor

toonn commented Sep 27, 2021

Here's the Gist but I think you're getting everything relevant, only missing the build users but they shouldn't interfere with a reinstall.

Maybe set -x in one of the rc files can help verify nix-daemon.sh is being sourced properly?

EDIT: I'd missed that second LaunchDaemon you found btw, thanks : )

@n8henrie
Copy link
Contributor Author

n8henrie commented Sep 27, 2021

Maybe set -x in one of the rc files can help verify nix-daemon.sh is being sourced properly?

I'm sourcing it manually with no change. I've also added a few debug echos to nix-daemon.sh; I can see that they are getting run, and that the PATH does get changed (EDIT: PATH is getting changed within the script, not outside).

Ok, I think I'm on to something -- the problem is:

# Only execute this file once per shell.
if [ -n "${__ETC_PROFILE_NIX_SOURCED:-}" ]; then return; fi
__ETC_PROFILE_NIX_SOURCED=1

I do override PATH in my .bashrc (adding a bunch of stuff to it for pyenv, rust, go, etc.). And I was sourcing this in my .bashrc.

But it looks like on a multi-user build, it's sourcing /etc/bashrc, which sources nix-daemon.sh, then sources ~/.bashrc, which I have set to source nix-daemon.sh, and then overrides PATH, which I didn't think would be an issue since it's also sourcing nix-daemon.sh. However, I'm guessing the second source isn't working because of the above. Let me tinker.

@n8henrie
Copy link
Contributor Author

Yup, that seems to be the issue.

In my .bashrc, I start near the top with PATH=$(getconf PATH), then build up the PATH, then finally export it at the end. I do it this way because otherwise $PATH gets longer and longer every time I re-source my .bashrc, which drives me nuts.

Sourcing nix-daemon.sh manually (or in ~/.bashrc) isn't working because __ETC_PROFILE_NIX_SOURCED is set.

I think I should be able to sort out a workaround, though I wonder if less surprising mechanism than __ETC_PROFILE_NIX_SOURCED might be just checking for .nix-profile/bin in PATH; if it's not there, then continue. It doesn't look like any of the other variables are appended (only set); if mutating PATH is the only side effect of sourcing this script, shouldn't checking PATH be a reasonable indicator of whether it should run again?

@n8henrie
Copy link
Contributor Author

Here's my workaround, put in my ~/.bashrc, which allows me to keep the PATH modifications.

# Nix
if [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then
  # if PATH does *not* contain `~/.nix-profile/bin`
  if [ -n "${PATH##*.nix-profile/bin*}" ]; then

    # If this flag is set, `nix-daemon.sh` returns early
    # https://github.com/NixOS/nix/issues/5298
    unset __ETC_PROFILE_NIX_SOURCED
    . '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'
  fi
fi
# End Nix

Fixed.

$ nix-env --version
nix-env (Nix) 2.3.15

Thanks again!

@danemacmillan
Copy link

This discussion prompted me to evaluate my own ${PATH} because none of the nix-* commands were available after what looked like a successful installation. Sure enough, my dotfiles were doing something to my path to ensure it started from scratch and would be built manually, because in the past some undesirable software insisted on automatically adding itself to the path. Thanks for the research!

By the way, @n8henrie, you wrote:

In my .bashrc, I start near the top with PATH=$(getconf PATH), then build up the PATH, then finally export it at the end. I do it this way because otherwise $PATH gets longer and longer every time I re-source my .bashrc, which drives me nuts.

I do something similar, but for other reasons. Nevertheless, it sounds like you want to use a general pathmunge function, that is common on a lot of Linux distributions. Here's a conversation that may be of interest to you: https://unix.stackexchange.com/a/124461

danemacmillan added a commit to danemacmillan/dotfiles that referenced this issue Nov 18, 2021
This was interfering with the Nix install path.

See conversation: NixOS/nix#5298
@n8henrie
Copy link
Contributor Author

n8henrie commented Jan 23, 2022

For those also using nix-darwin, nix-darwin uses __NIX_DARWIN_SET_ENVIRONMENT_DONE in a similar way. Additionally, /etc/static/bashrc looks for __ETC_BASHRC_SOURCED; if this is set, it never even gets to the point of checking for the former.

Unfortunately, if you just unset these additional vars like I recommended above, nix-darwin completely resets your path in /nix/store/wj547wd30qdpjy6kmm1fx4a6p46j0dp8-set-environment:

export PATH=$HOME/.nix-profile/bin:/run/current-system/sw/bin:/nix/var/nix/profiles/default/bin:/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin

To work around all this (without my PATH growing longer and longer), I initially tried putting a PATH deduplicator (in spirit like @danemacmillan recommended above) at the end of my .bashrc:

PATH=$(awk -v RS=: '!seen[$0]++' <<< "${PATH}" | sed '/^$/d' | paste -s -d:)
export PATH

This was taking ~5ms on my machine. Eventually, I figured out that a likely more robust / future-proof method would be to just spawn a clean subshell, source /etc/static/bashrc, and output the PATH

EDIT: Resetting the enivornment unfortunately meant that ~/.nix-profile/bin wasn't getting added to my PATH (due to lack of HOME), so I added HOME back to the env and it works better now.

# Nix
if [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then
  NIXPATH=$(
    env --ignore-environment HOME="${HOME}" sh -c '
      source /etc/static/bashrc
      echo "${PATH}"
      '
  )
  PATH+=${NIXPATH}
fi
# End Nix

It looks like this adds roughly the same amount of time, perhaps just a smidge faster

hyperfine $'env --ignore-environment sh -c \'source /etc/static/bashrc; echo "${PATH}"\''
Benchmark 1: env --ignore-environment sh -c 'source /etc/static/bashrc; echo "${PATH}"'
  Time (mean ± σ):       4.1 ms ±   0.7 ms    [User: 1.0 ms, System: 1.4 ms]
  Range (min … max):     2.8 ms …   9.8 ms    351 runs

Also, please note that I am appending the nix path to the end of my path: PATH+=${NIXPATH} whereas others probably want to follow the recommended approach of prepending: PATH=${NIXPATH}:${PATH}; I'm doing so because as noted above nix-darwin includes /usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin; prepending this to my PATH would put the MacOS BSD utils before the homebrew GNU utilities I'm used to.

EDIT: Alternatively, I suppose I could replace my getconf PATH from above with the PATH-overwriting command from this comment, which would serve the same purpose and shave off a couple ms.

EDIT2: For some reason EDIT1 doesn't work; MacOS is persisting the relevant environment variables even when I completely close out tmux, kill all sessions, and close out Terminal.app, so that nothing is re-sourced with a new shell. I don't get it.

@abathur
Copy link
Member

abathur commented Jan 24, 2022

A little oblique, but I've been trying to think out loud about improvements when opportunities present:

  1. The installer could be trying to open any ~supported+present shells to confirm nix is on path in the expected order and recommending next steps if not.
  2. I haven't thought about how feasible it is, but at least having a cross-shell path diagnostic script would be a qol win. If the output can be terse enough, it could run if the test above fails.
  3. It'd be great to have a proper installer suite that both tests and ~documents the anticipated profile/rc preconditions and makes incremental improvements easier.
  4. It'd be great to extend the above to also cover important integrations like nix-darwin and HM.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants