nvm
nvm copied to clipboard
Fixes bug where NVM init script is appended to the wrong startup file for ZSH users
Background
The existence of .bashrc, .bash_profile or .zshrc doesn't prove that the file is the currently utilized startup file or that it is the only file sourced. Because the current code operates under this assumption, the code:
export NVM_DIR="/Users/<USERNAME>/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm
is getting appended to the wrong profile for zsh users.
Steps To Reproduce
[ -d ~/.nvm ] && rm -rfv ~/.nvm- Install Zsh via https://github.com/robbyrussell/oh-my-zsh
- Run nvm install script with cURL.
- Run
which nvm. Observe it's not in$PATH - Open a new shell session. Observe the issue persists.
Solution
To fix this, the code in this PR checks:
- Is
$PROFILEset? If so, echo it. - What is the active shell that is executing this script? (the parent shell)
- If the shell is zsh, echo the zshrc file
- If the shell is bash:
- Is it login or non-login? Echo the neccessary file
- Is it interactive or non interactive? Echo the neccessary file.
This logic should address the primary bug and also prevent future bugs.
Note: bash by default reads ~/.profile last so it makes sense to write to ~/.bashrc or ~/.bash_profile instead of ~/.profile
See: https://www.gnu.org/software/bash/manual/html_node/Bash-Startup-Files.html
Passed Manual Tests: :white_check_mark: zsh :white_check_mark: bash from within script :white_check_mark: bash as default shell :white_check_mark: bash -l from within zsh
Thanks - can you please revert the indentation changes, and stick with the 2-space indentation already in the file?
In addition, could you add an install_script test that can be run in all supported shells?
Hi @ljharb,
Sure - I'll update the whitespace and add a test tomorrow evening. Thanks for creating this project - it's super useful! :smile_cat:
@iMerica any interest in completing this PR?
I forgot about this PR. Sorry! I can update my code with a more simplified version of the original that supports the two most common scenarios in OS X: bash and zsh in a standard login shell. This would fix the problem at hand and nothing more.
The problem of intelligently detecting which shell login file is used goes beyond the scope of this project, so I would prefer to write a utility for that separate from NVM. To illustrate how many edge cases there are see RVM's implementation here.
@iMerica Still have plans to update this one?
Minor note: https://github.com/robbyrussell/oh-my-zsh is not for installing Zsh; it's for Oh My Zsh, which is an additional customization layer on top of zsh, and includes a bit of nvm-specific stuff. I would target plain zsh (without Oh My Zsh running) for your installation logic.
This logic seems a little weird.
elif echo $shell | grep -q "bash"; then
local login_status=$(shopt -q login_shell)
local interactive_status=$([[ $- == *i* ]])
if [[ $login_status -eq 0 ]]; then
echo "$HOME/.bashrc"
elif [[ $interactive_status -eq 0 ]]; then
echo "$HOME/.bash_profile"
fi
This looks like if you run the installer from a login shell or a non-interactive shell (as opposed to an interactive non-login shell), then only login shells will have nvm available. I would think that most users would want nvm available in all their interactive shells, regardless of whether they're login shells. Conventionally, ~/.bash_profile (for login shells) will source ~/.bashrc (for interactive shells), so what's in interactive shells is available to interactive login shells as well. You could just install to .bashrc (if it exists) regardless of whether the installer is run from a login shell.
And since the home page tells users to install using curl .../install.sh | bash, many or most installations will be done from non-interactive non-login shells. This logic is mixing detection of the shell which called the curl | bash installer (the $PPID stuff) with the state of the shell that install.sh itself is running in.
Unconditionally using $HOME/.bash_profile might have problems, too. You might check for -f on all of .bash_profile, .proflie, and .bash_login, so you can work with whichever the user has. (Or just use .bashrc, which would avoid that quirk.)
Note that on a Mac, there is no .bashrc file by default, there's a .bash_profile - and I believe that "which file gets sourced when" is a bit different too.
The startup file order for bash on OS X is the same as elsewhere. You can compare the OS X man bash "INVOCATION" section against the main Bash manual Startup Files section to verify. I use both .bash_profile and .bashrc on OS X and it's consistent with the documentation.
Newly created users on Mac OS X (at least pre-10.10) have neither .bashrc nor .bash_profile files. (May be different for the very first admin user created after startup?) Their existence might be more dependent on what other software people have installed or what setup instructions they've followed. There are both systemwide /etc/bashrc and /etc/profile files, though. They follow the same pattern: /etc/profile sources /etc/bashrc if it's running bash.