nvm-sh / nvm

Node Version Manager - POSIX-compliant bash script to manage multiple active node.js versions
MIT License
79.08k stars 7.92k forks source link

sourcing nvm.sh exits with code 3 when .nvmrc is present and version is not installed #1985

Open pvinis opened 5 years ago

pvinis commented 5 years ago

When I source nvm.sh like . "$HOME/.nvm/nvm.sh" for manual install and like . $(brew --prefix nvm)/nvm.sh, the exit code is 3.

Even though nvm works after that, in bash scripts with set -e, the script stops there, since the command exits with a non-zero value.

Is there a reason this fails? Is there a reason it exits with 3, if it's not an actual failure? What can we do about that?

Some extra info:

nvm --version: v0.34.0
$TERM_PROGRAM: iTerm.app
$SHELL: /bin/zsh
$SHLVL: 1
$HOME: /Users/pvinis
$NVM_DIR: '$HOME/.nvm'
$PATH: $HOME/.gem/ruby/2.5.1/bin:$HOME/.rubies/ruby-2.5.1/lib/ruby/gems/2.5.0/bin:$HOME/.rubies/ruby-2.5.1/bin:$NVM_DIR/versions/node/v11.0.0/bin:$HOME/.config/yarn/global/node_modules/.bin:$HOME/.yarn/bin:$HOME/Library/Python/3.7/bin:$HOME/.fastlane/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/MacGPG2/bin:/opt/X11/bin:$HOME/.rvm/bin
$PREFIX: ''
$NPM_CONFIG_PREFIX: ''
$NVM_NODEJS_ORG_MIRROR: ''
$NVM_IOJS_ORG_MIRROR: ''
shell version: 'zsh 5.3 (x86_64-apple-darwin18.0)'
uname -a: 'Darwin 18.2.0 Darwin Kernel Version 18.2.0: Mon Nov 12 20:24:46 PST 2018; root:xnu-4903.231.4~2/RELEASE_X86_64 x86_64'
OS version: Mac 10.14.2 18C54
curl: /usr/bin/curl, curl 7.54.0 (x86_64-apple-darwin18.0) libcurl/7.54.0 LibreSSL/2.6.4 zlib/1.2.11 nghttp2/1.24.1
wget: not found
git: /usr/local/bin/git, git version 2.19.1
grep: /usr/bin/grep, grep (BSD grep) 2.5.1-FreeBSD
awk: /usr/bin/awk, awk version 20070501
sed: illegal option -- -
usage: sed script [-Ealn] [-i extension] [file ...]
       sed [-Ealn] [-i extension] [-e script] ... [-f script_file] ... [file ...]
sed: /usr/bin/sed,
cut: illegal option -- -
usage: cut -b list [-n] [file ...]
       cut -c list [file ...]
       cut -f list [-s] [-d delim] [file ...]
cut: /usr/bin/cut,
basename: illegal option -- -
usage: basename string [suffix]
       basename [-a] [-s suffix] string [...]
basename: /usr/bin/basename,
rm: illegal option -- -
usage: rm [-f | -i] [-dPRrvW] file ...
       unlink file
rm: /bin/rm,
mkdir: illegal option -- -
usage: mkdir [-pv] [-m mode] directory ...
mkdir: /bin/mkdir,
xargs: illegal option -- -
usage: xargs [-0opt] [-E eofstr] [-I replstr [-R replacements]] [-J replstr]
             [-L number] [-n number [-x]] [-P maxprocs] [-s size]
             [utility [argument ...]]
xargs: /usr/bin/xargs,
nvm current: v11.0.0
which node: $NVM_DIR/versions/node/v11.0.0/bin/node
which iojs: iojs not found
which npm: $NVM_DIR/versions/node/v11.0.0/bin/npm
npm config get prefix: $NVM_DIR/versions/node/v11.0.0
npm root -g: $NVM_DIR/versions/node/v11.0.0/lib/node_modules

        v8.12.0
       v10.12.0
->      v11.0.0
         system
default -> v11 (-> v11.0.0)
node -> stable (-> v11.0.0) (default)
stable -> 11.0 (-> v11.0.0) (default)
iojs -> N/A (default)
unstable -> N/A (default)
lts/* -> lts/dubnium (-> N/A)
lts/argon -> v4.9.1 (-> N/A)
lts/boron -> v6.14.4 (-> N/A)
lts/carbon -> v8.12.0
lts/dubnium -> v10.13.0 (-> N/A)

pvinis commented 5 years ago

The issue template does not work well with the details.. I tried to fix it but I failed.

ljharb commented 5 years ago

Note that nvm is not supported with homebrew - if you brew uninstall it, and install it the proper way (in the readme), do you see the same behavior?

ljharb commented 5 years ago

Thanks for the -x output, but it's not clear to me where it's erroring :-/ it's getting to the nvm_tree_contains_path call on line 2178 (via the nvm_die_on_prefix call on line 2995), and the only variable compared with -n after that is NVM_USE_OUTPUT - which is declared with local on line 2977.

The exit code 3 is returned from nvm in a few places:

One possibility is, could you edit nvm.sh to change every return 3 to a different 2-digit number, and then see which callsite is causing the error?

pvinis commented 5 years ago

brew or manual, both give me this exit code.

pvinis commented 5 years ago

that's a good idea. I'll try that tomorrow morning since I'm on my phone now.

Is there a reason actually that it returns the same number Ina few places? why not use different numbers for different places and different errors by default?

ljharb commented 5 years ago

Typically I use unique numbers for each code path; but as nvm has gotten more complex, they've blurred together a bit. It would be a reasonable (but technically breaking) change to make sure every exit code in the app is unique (at least for things more complex than simple 0/1 predicates).

pvinis commented 5 years ago

omg I changed all numbers to unique ones (except the 312 :p)

screen shot 2019-01-20 at 09 25 01

and it exits with 0.

I'll start putting them back to 3 and see how when it breaks again.

pvinis commented 5 years ago

Hm, ok on my home macbook its 0 exit even with a freshly installed one. That's good. Then I'll check tomorrow at work with the problematic machine, and see what could be the issue.

ljharb commented 5 years ago

Thanks for your efforts! I’m looking forward to fixing this, and perhaps convincing me it’s time for unique exit codes :-D

pvinis commented 5 years ago

I found the reproducible way!

With nvm not in the system, uninstalled, cleared etc, no rc files, no nodes installed, try this:

Basically, the problem here is sourcing nvm in a dir that has a .nvmrc in it. If I install and source outside, then go in the dir, it's fine, and when I do nvm use it will complain and return 3, which is great.

I don't know how this should be handled. I am basically trying to setup a project with ci, and in the ci I am already in the dir, and I install nvm, then source nvm, then nvm install, then nvm use. This order would work if sourcing would not try to do nvm use inside it I'm guessing.

What do you think?

ljharb commented 5 years ago

Interesting. You can source with --no-use, or with --install to autoinstall - does this address it for you?

(I'd expect nvm to be sourced long before any CI system cd's into a project directory)

pvinis commented 5 years ago

I'll try it.

Well, if the ci is using a node image, usually nvm is there already. If it's booting a macos image though, nvm is probably missing, so the init happens, cds in my project and then I get control, when I would start installing nvm and other things that I need.

ljharb commented 5 years ago

This might be due to a bug in bash 3 (which ships on Macs) which makes set -e exit even on tests that exit nonzero.

pvinis commented 5 years ago

To be honest, I switched to nodenv since last month. Feel free to keep this issue open to solve this if it's still a problem, or close it if you feel it's not relevant anymore.

nvm, thank you for your service :D

ljharb commented 5 years ago

Sorry to hear that :-)

I believe I've solved it with 58d0933f72c3b819d8180e3fe68b66201a87c0a4, so I'll close.

pvinis commented 5 years ago

I will try master out of curiosity :D

JoseLion commented 3 years ago

@ljharb I just tested on Ubuntu 18.04.5 LTS with version v0.38.0, and I'm still getting the issue. This is usually a problem in CI environments. For instance, on CircleCI, when using the Node Orb, the installation will fail if you have a .nvmrc file in the working directory. Removing the .nvmrc file solves the issue, though. I hope this info is useful, and I'm also attaching an image of the output, which could be useful too 🙂

image

Cheers!

ljharb commented 3 years ago

@JoseLion is this the same setup, then? Circle is sourcing nvm after cd-ing into the project’s directory, and before running nvm install? If so, this might be something that circle’s image could fix.

JoseLion commented 3 years ago

I'm not sure I understand why CircleCI's image could fix it 😅 But just for clarity, I'm installing NVM (and NodeJS) manually in the job's steps, which is exactly what the Official Node Orb does:

jobs:
  build-web:
    working_directory: ~/repo/web

    docker:
      - image: cimg/base:stable

    steps:
      - checkout:
          path: ~/repo

      - run:
          name: Install NodeJS
          command: |
            curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
            echo 'export NVM_DIR="$HOME/.nvm"' >> $BASH_ENV
            echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $BASH_ENV
            source $BASH_ENV
            nvm install
            nvm alias default
            echo 'nvm use default &>/dev/null' >> $BASH_ENV

Any run command is executed in the working_directory where the .nvmrc file usually is. I'm actually trying to use that file when nvm install and nvm alias default are executed. Of course, as a workaround, I can do cd .. just before fetching the installer and set the NodeJS version manually in the command, but that's not ideal if I already have a file to track and centralize the version, right?

Also, it would be harder to for the Official Node Orb to use this workaround since it would have to check the working directory for a .nvmrc file and do cd <somewhere?> if the file exists. Which again, it's not ideal because not all projects will have the .nvmrc file.

I really hope this helps to clarify the issue, NVM is a great tool and the .nvmrc file is very helpful too! 🙂

ljharb commented 3 years ago

Right - the issue is that nvm expects that if an .nvmrc file is present with a node version in it, that before cd-ing into that directory, you'd source nvm. So, in your case, you can change to this:

          command: |
            curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
            echo 'export NVM_DIR="$HOME/.nvm"' >> $BASH_ENV
            echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" --install' >> $BASH_ENV
            source $BASH_ENV

(note that nvm alias default just echoes the current default alias, and that the nvm use default seems unnecessary)

JoseLion commented 3 years ago

@ljharb got you, that makes sense! I used --install as you suggested, and it worked perfectly. 👍🏼

Do you think it would be possible to add a fix for this? Probably ignoring the .nvmrc file when nvm.sh is sourced? (I'm not sure if that would break other features, though 🤔). If not, probably it's something worth adding to the documentation to help other folks understand what could cause this exit code 3 issue, WDYT? 🙂

ljharb commented 3 years ago

It would break other features, unfortunately :-/ specifically, the feature to auto-use the nvmrc version at source time, which in non-CI use cases, is already installed.

There may be a way to either detect CI, and do something different; or, only when sourcing nvm.sh, avoid failing when an nvmrc version is not present.

JoseLion commented 3 years ago

That would be awesome! Thanks 🙂 🎉

agarzola commented 3 years ago

I’m experiencing this issue in our Platform.sh configuration. Here’s what we’re running (following the most recently-suggested solution above):

hooks:
    build: |
        set -e
        unset NPM_CONFIG_PREFIX
        curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.38.0/install.sh | dash
        # Add required environment variables to profile.
        echo 'export NVM_DIR="$PLATFORM_APP_DIR/.nvm"' >> $PLATFORM_APP_DIR/.profile
        echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" --install' >> $PLATFORM_APP_DIR/.profile
        # Source profile.
        . $PLATFORM_APP_DIR/.profile
        nvm current
        # TODO: Specifying the version should not be necessary, but Platform
        # builds fail without a specific version.
        nvm install
        nvm use
        nvm install-latest-npm

_(Note that in Platform.sh environments, $PLATFORM_APP_DIR resolves to the directory where our application is installed.)_

nvm is exiting with 3. What am I missing?

ljharb commented 3 years ago

@agarzola what jumps out at me:

cig0 commented 3 years ago

Hi,

FWIW I'm experiencing this very same issue on macOS + Zsh when manually sourcing nvm.sh by following the official instructions:

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

Stack versions:

NVM: 0.38.0
Zsh: zsh 5.8 (x86_64-apple-darwin20.1.0)
macOS: 11.4
npm:
{
  npm: '7.18.1',
  node: '16.4.2',
  v8: '9.1.269.36-node.14',
  uv: '1.41.0',
  zlib: '1.2.11',
  brotli: '1.0.9',
  ares: '1.17.1',
  modules: '93',
  nghttp2: '1.42.0',
  napi: '8',
  llhttp: '6.0.2',
  openssl: '1.1.1k+quic',
  cldr: '39.0',
  icu: '69.1',
  tz: '2021a',
  unicode: '13.0',
  ngtcp2: '0.1.0-DEV',
  nghttp3: '0.1.0-DEV'
}

The interesting thing however is that everything seems to be working as expected, as I was able to correctly install latest 16.4.2 Node version.

I only got aware of nvm.sh sourcing exiting with code 3 when I decided to load NVM on-demand from the shell, instead of by automatically sourcing it on each new shell session to avoid the noticeable lag hit.

I'm pretty much new to Node and NVM, please let me know if I can help by providing additional info or logs.

Thanks!

agarzola commented 3 years ago

@agarzola what jumps out at me:

* the install script should add lines to your profile file already, using the default NVM_DIR - try setting NVM_DIR before running the install script, and you may not need the profile lines at all

* nvm use is not needed; nvm install auto-uses

* `nvm install --latest-npm` covers all the last three lines.

@ljharb Thanks for the assist! Setting NVM_DIR before installation seems like it helped it find .profile, which is an improvement. It’s still exiting with code 3, however.

Here’s the updated command:

hooks:
    build: |
        set -e
        unset NPM_CONFIG_PREFIX
        # Create .profile in the home directory.
        touch $PLATFORM_APP_DIR/.profile
        export NVM_DIR="$PLATFORM_APP_DIR/.nvm"
        curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.38.0/install.sh | dash
        # Source .profile from home directory.
        . $PLATFORM_APP_DIR/.profile
        nvm current
        # Install and use Node version specified in .nvmrc.
        nvm install --latest-npm

It still exits w/code 3. 🤔

agarzola commented 3 years ago

Another update on this. I set -x (technically, set -ex) and it appears our script is failing on sourcing .profile. The logs show that it does not get to the like where we invoke nvm current, which means the script dies when sourcing /app/.profile:

  Executing build hook...
    W: + unset NPM_CONFIG_PREFIX
    W: + touch /app/.profile
    W: + export NVM_DIR=/app/.nvm
    W: + curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.38.0/install.sh
    W: + dash
    W:   % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
    W:                                  Dload  Upload   Total   Spent    Left  Speed
100 14926  100 14926    0     0   5116      0  0:00:02  0:00:02 --:--:--  5116
    => Downloading nvm from git to '/app/.nvm'
    W: Cloning into '/app/.nvm'...
=> * (HEAD detached at FETCH_HEAD)
      master
    => Compressing and cleaning up git repository

    => Appending nvm source string to /app/.profile
    => bash_completion source string already in /app/.profile
    => Close and reopen your terminal to start using nvm or run the following to use it now:

    export NVM_DIR="$HOME/.nvm"
    [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
    W: + . /app/.profile
    W: + export NVM_DIR=/app/.nvm
    W: + [ -s /app/.nvm/nvm.sh ]
    W: + . /app/.nvm/nvm.sh
    W: + NVM_SCRIPT_SOURCE=

    … (a lot more commands that seem to come from nvm.sh)

  E: Error building project: Step failed with status code 3.
ljharb commented 3 years ago

The issue is that you’re setting -e; you shouldn’t be (largely ever). nvm does not survive that mode.

pkalliok-aiven commented 3 years ago

From what I read here, the "real" problem is that nvm is reporting an error situation (the node version requested in .nvmrc is not found on the system), but it's not telling very clearly what's going on. Some suggestions that would improve the situation:

pkalliok-aiven commented 3 years ago

I got bit by this issue too. I was happily running a build script on my own host, and it succeeded, presumably because I had tested it step by step so the required node version was already available. On CI machine, the same script fails with 3, because it doesn't have any nvm steps pre-made. In this way, the . $NVM_DIR/nvm.sh is not truly idempotent.

ljharb commented 3 years ago

The first bullet point is a nonstarter; if the docs are wrong then those should be fixed.

One solution might be to disable set -e (which the nvm function itself already does) which might prevent the erroneous early termination, which would actually show a better error message.

If after that, the error message still needs improvement, I'd be happy to review a PR that improved it.

the . $NVM_DIR/nvm.sh is not truly idempotent.

It's not expected to be nor is it declared as such anywhere.

AngelODeath commented 2 years ago

I am having this issue now when installing nvm in my jenkins pipeline... I'm circumventing this by changing the directory before install... Sucks this is still a thing

ljharb commented 2 years ago

@AngelODeath try unsettling the e option.

Sweetchuck commented 2 years ago

I wanted to install nvm in a CI/CD environment in a docker image. It took me ~2-3 hours, to figure out what the problem is. I had only an exit code 3. Without a proper error message what the problem is, and how to solve it. In the nvm.sh there are 2 exit(3) occurrences and 23 return 3 occurrences.

I totally agree with the suggestions made by @pkalliok-aiven commented on Aug 27 2021.

For me the source "${NVM_DIR}/nvm.sh" --install solved the problem.

bpossolo commented 2 years ago

i ran into this problem on CircleCI when trying to source the nvm.sh file in the project directory (where an .nvmrc file is present). it would exit with error code 3 and no helpful message. ~i solved it~ by explicitly setting the NVM_DIR variable before sourcing nvm.sh

echo 'export NVM_DIR=~/.nvm' >> $BASH_ENV
echo 'source ~/.nvm/nvm.sh' >> $BASH_ENV
source $BASH_ENV

edit: looks like this didn't fix it after all...

ljharb commented 2 years ago

https://github.com/nvm-sh/nvm/issues/1985#issuecomment-907332697 remains my position; a PR to improve the error message would be much appreciated.

bpossolo commented 2 years ago

it turns out my fix (defining NVM_DIR before sourcing nvm.sh) didn't actually fix it...

The issue is that you’re setting -e; you shouldn’t be (largely ever). nvm does not survive that mode. this

that option is being set by circleci when executing the build. it is helpful to have the build stop when the build script encounters an error.

more philosophically, it seems pretty counterintuitive that the nvm.sh script would fail prematurely simply due to the presence of another completely valid and core nvm-related file (.nvmrc).

a helpful error message would spare debugging time but it still requires the user to jump through weird hoops to get it to work.

if there is already a command line option (--install) to explicitly install node in the same step as sourcing, it feels like it would make sense for the default behaviour (without that option) to simply ignore an .nvmrc file if that doesn't make sense to be the default behaviour, then a new option (i.e. --ignore-nvmrc) would be equally helpful in overcoming this issue.

as it stands, the .nvm.sh script is failing prematurely which may not be a problem for the time being (since apparently nothing important happens after the failure), but it feels brittle/dangerous to assume this will always be the case. in other words, if the premature failure is indeed irrelevant, one could argue it should "fail" with a zero status code and a warning logged to the console

ljharb commented 2 years ago

You can unset it in your circle config, no?

I completely agree that a helpful error message isn't as good as improving the core problem, but it's a start.

The default behavior is "use", the optional behavior is "install". Both (must) read .nvmrc when present.

I think the action items here are initially to improve the error message.

After that is completed, it's worth considering some of these options:

  1. configure your CI to start its shell "not in the repo (which has .nvmrc)"
  2. disable set -e, because enabling it is a widespread bad practice
  3. add some kind of new option or changed behavior so it doesn't fail, but you don't end up with the node version you want
  4. add --install to the sourcing options in your profile file, which would result in you actually getting the node version you want
bpossolo commented 2 years ago

i tried adding --install as you suggested in line 4 and tested it out on my local workstation. it looks like it's attempting to install the wrong version of node (see below)... notice the project .nvmrc file contains 16.2.0 but the source nvm.sh step is reporting 10.4.0

M-14292:~/projects/member-management-ui$ nvm ls
->      v10.4.0
       v10.14.2
       v12.10.0
       v12.18.4
       v12.20.0
       v14.15.4
        v16.2.0
default -> 10.4.0 (-> v10.4.0)
node -> stable (-> v16.2.0) (default)
stable -> 16.2 (-> v16.2.0) (default)
iojs -> N/A (default)
unstable -> N/A (default)
lts/* -> lts/gallium (-> N/A)
lts/argon -> v4.9.1 (-> N/A)
lts/boron -> v6.17.1 (-> N/A)
lts/carbon -> v8.17.0 (-> N/A)
lts/dubnium -> v10.24.1 (-> N/A)
lts/erbium -> v12.22.10 (-> N/A)
lts/fermium -> v14.19.0 (-> N/A)
lts/gallium -> v16.14.1 (-> N/A)

M-14292:~/projects/member-management-ui$ cat .nvmrc 
16.2.0

M-14292:~/projects/member-management-ui$ . ~/.nvm/nvm.sh --install
v10.4.0 is already installed.

M-14292:~/projects/member-management-ui$ echo $?
0
ljharb commented 2 years ago

ah, the default alias takes precedence over .nvmrc when present. If you nvm unalias default you should be closer to the state CI is in.

omarrelativity commented 1 year ago

Still running into this issue with v0.39.4. My workaround is to rename .nvmrc, source nvm, then rename back. This is my Makefile target:

nvm:
    # Have to rename .nvmrc then source nvm or it errors, see https://github.com/nvm-sh/nvm/issues/1985#issuecomment-456022013
    mv .nvmrc .nvmrc_temp

    # Need source nvm this way in make, see https://github.com/nvm-sh/nvm/issues/1446#issuecomment-816446454
    . ${NVM_DIR}/nvm.sh && mv .nvmrc_temp .nvmrc && nvm install && nvm use 
ljharb commented 1 year ago

@omarrelativity you can source it with --no-use and skip the nvmrc rename step, and if you source it with --install instead, it'll do all that in one go for you.

omarrelativity commented 1 year ago

I tried both options already, still get Error 3:

nvm-install:
    . ${NVM_DIR}/nvm.sh --install

nvm-no-use:
    . ${NVM_DIR}/nvm.sh --no-use
ljharb commented 1 year ago

and you have +e set, since -e isn't a good thing to be enabling? if so, then that's a legitimate bug i'd love more info on.

omarrelativity commented 1 year ago

I'm not sure what is set, I'm running this in a simple ubuntu:latest Docker container with nvm/curl/make/azure-cli/pulumi installed. Makefile doesn't have anything fancy in it.

$SHELLOPTS shows braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor

Doing echo $$SHELLOPTS in the Makefile shows an empty line as output.

ljharb commented 1 year ago

@omarrelativity i think this warrants a separate issue, since those sourcing options should work. mind filing it?

omarrelativity commented 1 year ago

Issue created https://github.com/nvm-sh/nvm/issues/3177

rpatterson commented 11 months ago

I encountered this and under the same conditions I also reproduced #1769, so I think these issues are related, maybe duplicates depending on the root cause.

dssjoblom commented 5 months ago

Still encountering this while trying to install NVM in a Dockerfile based on Debian bullseye.